-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1538433 [wpt PR 15991] - MSE: Fix mediasource-changetype-play to …
…work with MseBufferByPts, a=testonly Automatic update from web-platform-tests MSE: Fix mediasource-changetype-play to work with MseBufferByPts This web-platform-test exercises changeType as it splice-overlaps pairs of audio or video media streams at varying offsets in the presentation timeline. Splice-overlapping an out-of-order decode stream (such as the test AVC MP4 media) at arbitrary times can, per spec, drop significant decode dependencies from a partially-overlapped GOP such that a buffered range gap could result. This change is more careful about where it performs splice-overlaps when the overlapped media is out-of-order-decode, adjusting the splice point to be at or very near to the next overlapped keyframe. This prevents removing out-of-order non-keyframes and their dependents from the overlapped media such that no buffered range gap nor playback stall should result. Note that Chromium is sensitive to such out-of-order buffering overlaps with the new, compliant, MseBufferByPts behavior. Fixing w3c/media-source#160 could greatly simplify this problem by allowing apps to explicitly control how the user agent behaves at these small gaps. BUG=807793 Change-Id: I020e244c230756eaa1804f81b58a577124a6a28b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1428601 Commit-Queue: Matthew Wolenetz <wolenetzchromium.org> Reviewed-by: Dan Sanders <sandersdchromium.org> Cr-Commit-Position: refs/heads/master{#643473} -- wpt-commits: 27ad6759d421b95b4572f20cabaeb750b3eb9799 wpt-pr: 15991 UltraBlame original commit: fc846bf83bfdae75b97fec2fb139b4b88dc9be7f
- Loading branch information
Showing
2 changed files
with
213 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
330 changes: 197 additions & 133 deletions
330
testing/web-platform/tests/media-source/mediasource-changetype-util.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,156 +1,220 @@ | ||
|
||
|
||
function findSupportedChangeTypeTestTypes(cb) | ||
{ | ||
var CHANGE_TYPE_MEDIA_LIST = [ | ||
{ | ||
type: 'video/webm; codecs="vp8"', | ||
is_video: true, | ||
url: 'webm/test-v-128k-320x240-24fps-8kfr.webm' | ||
}, | ||
{ | ||
type: 'video/webm; codecs="vp9"', | ||
is_video: true, | ||
url: 'webm/test-vp9.webm' | ||
}, | ||
{ | ||
type: 'video/mp4; codecs="avc1.4D4001"', | ||
is_video: true, | ||
url: 'mp4/test-v-128k-320x240-24fps-8kfr.mp4' | ||
}, | ||
{ | ||
type: 'audio/webm; codecs="vorbis"', | ||
is_video: false, | ||
url: 'webm/test-a-128k-44100Hz-1ch.webm' | ||
}, | ||
{ | ||
type: 'audio/mp4; codecs="mp4a.40.2"', | ||
is_video: false, | ||
url: 'mp4/test-a-128k-44100Hz-1ch.mp4' | ||
}, | ||
{ | ||
type: 'audio/mpeg', | ||
is_video: false, | ||
url: 'mp3/sound_5.mp3' | ||
} | ||
]; | ||
|
||
var audio_result = []; | ||
var video_result = []; | ||
|
||
for (var i = 0; i < CHANGE_TYPE_MEDIA_LIST.length; ++i) { | ||
var media = CHANGE_TYPE_MEDIA_LIST[i]; | ||
if (window.MediaSource && MediaSource.isTypeSupported(media.type)) { | ||
if (media.is_video === true) { | ||
video_result.push(media); | ||
} else { | ||
audio_result.push(media); | ||
} | ||
} | ||
function findSupportedChangeTypeTestTypes(cb) { | ||
let CHANGE_TYPE_MEDIA_LIST = [ | ||
{ | ||
type: 'video/webm; codecs="vp8"', | ||
is_video: true, | ||
url: 'webm/test-v-128k-320x240-24fps-8kfr.webm', | ||
start_time: 0.0 | ||
|
||
|
||
}, | ||
{ | ||
type: 'video/webm; codecs="vp9"', | ||
is_video: true, | ||
url: 'webm/test-vp9.webm', | ||
start_time: 0.0 | ||
|
||
|
||
}, | ||
{ | ||
type: 'video/mp4; codecs="avc1.4D4001"', | ||
is_video: true, | ||
url: 'mp4/test-v-128k-320x240-24fps-8kfr.mp4', | ||
start_time: 0.083333, | ||
keyframe_interval: 0.333333 | ||
}, | ||
{ | ||
type: 'audio/webm; codecs="vorbis"', | ||
is_video: false, | ||
url: 'webm/test-a-128k-44100Hz-1ch.webm', | ||
start_time: 0.0 | ||
|
||
|
||
|
||
}, | ||
{ | ||
type: 'audio/mp4; codecs="mp4a.40.2"', | ||
is_video: false, | ||
url: 'mp4/test-a-128k-44100Hz-1ch.mp4', | ||
start_time: 0.0 | ||
|
||
|
||
|
||
}, | ||
{ | ||
type: 'audio/mpeg', | ||
is_video: false, | ||
url: 'mp3/sound_5.mp3', | ||
start_time: 0.0 | ||
|
||
|
||
|
||
} | ||
]; | ||
|
||
let audio_result = []; | ||
let video_result = []; | ||
|
||
for (let i = 0; i < CHANGE_TYPE_MEDIA_LIST.length; ++i) { | ||
let media = CHANGE_TYPE_MEDIA_LIST[i]; | ||
if (window.MediaSource && MediaSource.isTypeSupported(media.type)) { | ||
if (media.is_video === true) { | ||
video_result.push(media); | ||
} else { | ||
audio_result.push(media); | ||
} | ||
} | ||
} | ||
|
||
|
||
cb(audio_result, video_result); | ||
cb(audio_result, video_result); | ||
} | ||
|
||
function appendBuffer(test, sourceBuffer, data) | ||
{ | ||
test.expectEvent(sourceBuffer, "update"); | ||
test.expectEvent(sourceBuffer, "updateend"); | ||
sourceBuffer.appendBuffer(data); | ||
function appendBuffer(test, sourceBuffer, data) { | ||
test.expectEvent(sourceBuffer, "update"); | ||
test.expectEvent(sourceBuffer, "updateend"); | ||
sourceBuffer.appendBuffer(data); | ||
} | ||
|
||
function trimBuffered(test, mediaElement, sourceBuffer, minimumPreviousDuration, newDuration) | ||
{ | ||
assert_less_than(newDuration, minimumPreviousDuration); | ||
assert_less_than(minimumPreviousDuration, mediaElement.duration); | ||
test.expectEvent(sourceBuffer, "update"); | ||
test.expectEvent(sourceBuffer, "updateend"); | ||
sourceBuffer.remove(newDuration, Infinity); | ||
function trimBuffered(test, mediaElement, sourceBuffer, minimumPreviousDuration, newDuration) { | ||
assert_less_than(newDuration, minimumPreviousDuration); | ||
assert_less_than(minimumPreviousDuration, mediaElement.duration); | ||
test.expectEvent(sourceBuffer, "update"); | ||
test.expectEvent(sourceBuffer, "updateend"); | ||
sourceBuffer.remove(newDuration, Infinity); | ||
} | ||
|
||
function trimDuration(test, mediaElement, mediaSource, newDuration) | ||
{ | ||
assert_less_than(newDuration, mediaElement.duration); | ||
test.expectEvent(mediaElement, "durationchange"); | ||
mediaSource.duration = newDuration; | ||
function trimDuration(test, mediaElement, mediaSource, newDuration) { | ||
assert_less_than(newDuration, mediaElement.duration); | ||
test.expectEvent(mediaElement, "durationchange"); | ||
mediaSource.duration = newDuration; | ||
} | ||
|
||
function runChangeTypeTest(test, mediaElement, mediaSource, typeA, dataA, typeB, dataB) | ||
{ | ||
var sourceBuffer = mediaSource.addSourceBuffer(typeA); | ||
|
||
appendBuffer(test, sourceBuffer, dataA); | ||
function runChangeTypeTest(test, mediaElement, mediaSource, metadataA, dataA, metadataB, dataB) { | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
function findSafeOffset(targetTime, overlappedMediaMetadata, overlappedStartTime, overlappingMediaMetadata) { | ||
assert_greater_than_equal(targetTime, overlappedStartTime); | ||
|
||
let offset = targetTime; | ||
if ("start_time" in overlappingMediaMetadata) { | ||
offset -= overlappingMediaMetadata["start_time"]; | ||
} | ||
|
||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
sourceBuffer.changeType(typeB); | ||
sourceBuffer.timestampOffset = 0.5; | ||
appendBuffer(test, sourceBuffer, dataB); | ||
}); | ||
|
||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
sourceBuffer.changeType(typeB); | ||
sourceBuffer.timestampOffset = 1.0; | ||
appendBuffer(test, sourceBuffer, dataB); | ||
}); | ||
if (!("keyframe_interval" in overlappedMediaMetadata)) { | ||
return { "offset": offset, "adjustedTime": targetTime }; | ||
} | ||
|
||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
sourceBuffer.changeType(typeA); | ||
sourceBuffer.timestampOffset = 1.5; | ||
appendBuffer(test, sourceBuffer, dataA); | ||
}); | ||
|
||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
sourceBuffer.changeType(typeA); | ||
sourceBuffer.timestampOffset = 1.3; | ||
appendBuffer(test, sourceBuffer, dataA); | ||
}); | ||
|
||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
trimBuffered(test, mediaElement, sourceBuffer, 2.1, 2); | ||
}); | ||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
trimDuration(test, mediaElement, mediaSource, 2); | ||
}); | ||
|
||
test.waitForExpectedEvents(function() | ||
{ | ||
assert_equals(mediaElement.currentTime, 0); | ||
test.expectEvent(mediaSource, "sourceended"); | ||
test.expectEvent(mediaElement, "play"); | ||
test.expectEvent(mediaElement, "ended"); | ||
mediaSource.endOfStream(); | ||
mediaElement.play(); | ||
}); | ||
let gopsToRetain = Math.ceil((targetTime - overlappedStartTime) / overlappedMediaMetadata["keyframe_interval"]); | ||
let adjustedTime = overlappedStartTime + gopsToRetain * overlappedMediaMetadata["keyframe_interval"]; | ||
|
||
assert_greater_than_equal(adjustedTime, targetTime); | ||
offset += adjustedTime - targetTime; | ||
return { "offset": offset, "adjustedTime": adjustedTime }; | ||
} | ||
|
||
let sourceBuffer = mediaSource.addSourceBuffer(metadataA.type); | ||
|
||
appendBuffer(test, sourceBuffer, dataA); | ||
let lastStart = metadataA["start_time"]; | ||
if (lastStart == null) { | ||
lastStart = 0.0; | ||
} | ||
|
||
|
||
|
||
|
||
test.waitForExpectedEvents(() => { | ||
let safeOffset = findSafeOffset(0.5, metadataA, lastStart, metadataB); | ||
lastStart = safeOffset["adjustedTime"]; | ||
sourceBuffer.changeType(metadataB.type); | ||
sourceBuffer.timestampOffset = safeOffset["offset"]; | ||
appendBuffer(test, sourceBuffer, dataB); | ||
}); | ||
|
||
|
||
|
||
test.waitForExpectedEvents(() => { | ||
assert_less_than(lastStart, 1.0); | ||
let safeOffset = findSafeOffset(1.0, metadataB, lastStart, metadataB); | ||
lastStart = safeOffset["adjustedTime"]; | ||
sourceBuffer.changeType(metadataB.type); | ||
sourceBuffer.timestampOffset = safeOffset["offset"]; | ||
appendBuffer(test, sourceBuffer, dataB); | ||
}); | ||
|
||
|
||
|
||
test.waitForExpectedEvents(() => { | ||
assert_less_than(lastStart, 1.5); | ||
let safeOffset = findSafeOffset(1.5, metadataB, lastStart, metadataA); | ||
|
||
|
||
sourceBuffer.changeType(metadataA.type); | ||
sourceBuffer.timestampOffset = safeOffset["offset"]; | ||
appendBuffer(test, sourceBuffer, dataA); | ||
}); | ||
|
||
test.waitForExpectedEvents(function() { | ||
test.done(); | ||
}); | ||
|
||
|
||
test.waitForExpectedEvents(() => { | ||
assert_less_than(lastStart, 1.3); | ||
|
||
|
||
let safeOffset = findSafeOffset(1.3, metadataB, lastStart, metadataA); | ||
sourceBuffer.changeType(metadataA.type); | ||
sourceBuffer.timestampOffset = safeOffset["offset"]; | ||
appendBuffer(test, sourceBuffer, dataA); | ||
}); | ||
|
||
|
||
test.waitForExpectedEvents(() => { | ||
trimBuffered(test, mediaElement, sourceBuffer, 2.1, 2); | ||
}); | ||
|
||
test.waitForExpectedEvents(() => { | ||
trimDuration(test, mediaElement, mediaSource, 2); | ||
}); | ||
|
||
test.waitForExpectedEvents(() => { | ||
assert_equals(mediaElement.currentTime, 0); | ||
test.expectEvent(mediaSource, "sourceended"); | ||
test.expectEvent(mediaElement, "play"); | ||
test.expectEvent(mediaElement, "ended"); | ||
mediaSource.endOfStream(); | ||
mediaElement.play(); | ||
}); | ||
|
||
test.waitForExpectedEvents(() => { | ||
test.done(); | ||
}); | ||
} | ||
|
||
function mediaSourceChangeTypeTest(metadataA, metadataB, description) | ||
{ | ||
mediasource_test(function(test, mediaElement, mediaSource) | ||
{ | ||
mediaElement.pause(); | ||
mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'")); | ||
MediaSourceUtil.loadBinaryData(test, metadataA.url, function(dataA) { | ||
MediaSourceUtil.loadBinaryData(test, metadataB.url, function(dataB) { | ||
runChangeTypeTest(test, mediaElement, mediaSource, metadataA.type, dataA, metadataB.type, dataB); | ||
}); | ||
}); | ||
}, description); | ||
function mediaSourceChangeTypeTest(metadataA, metadataB, description) { | ||
mediasource_test((test, mediaElement, mediaSource) => { | ||
mediaElement.pause(); | ||
mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'")); | ||
MediaSourceUtil.loadBinaryData(test, metadataA.url, (dataA) => { | ||
MediaSourceUtil.loadBinaryData(test, metadataB.url, (dataB) => { | ||
runChangeTypeTest(test, mediaElement, mediaSource, metadataA, dataA, metadataB, dataB); | ||
}); | ||
}); | ||
}, description); | ||
} | ||
|