Skip to content

Commit

Permalink
Bug 1538433 [wpt PR 15991] - MSE: Fix mediasource-changetype-play to …
Browse files Browse the repository at this point in the history
…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
marco-c committed Oct 4, 2019
1 parent f40a57f commit 94d4bd6
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,26 @@
<div id="log"></div>
<script>

function generateChangeTypeTests(audio_types, video_types)
{
async_test(function(test) {
assert_true(audio_types.length > 1, "Browser doesn't support at least 2 types of audio test media" + audio_types);
assert_true(video_types.length > 1, "Browser doesn't support at least 2 types of video test media");
test.done();
}, "Check if browser supports enough test media types");
function generateChangeTypeTests(audio_types, video_types) {
async_test((test) => {
assert_true(audio_types.length > 1, "Browser doesn't support at least 2 types of audio test media");
assert_true(video_types.length > 1, "Browser doesn't support at least 2 types of video test media");
test.done();
}, "Check if browser supports enough test media types");

// Generate audio-only changeType tests
for (let audio1 of audio_types) {
for (let audio2 of audio_types) {
mediaSourceChangeTypeTest(audio1, audio2, "Test audio-only changeType for " + audio1.type + " <-> " + audio2.type);
}
// Generate audio-only changeType tests
for (let audio1 of audio_types) {
for (let audio2 of audio_types) {
mediaSourceChangeTypeTest(audio1, audio2, "Test audio-only changeType for " + audio1.type + " <-> " + audio2.type);
}
}

// Generate video-only changeType tests
for (let video1 of video_types) {
for (let video2 of video_types) {
mediaSourceChangeTypeTest(video1, video2, "Test video-only changeType for " + video1.type + " <-> " + video2.type);
}
// Generate video-only changeType tests
for (let video1 of video_types) {
for (let video2 of video_types) {
mediaSourceChangeTypeTest(video1, video2, "Test video-only changeType for " + video1.type + " <-> " + video2.type);
}
}
}

findSupportedChangeTypeTestTypes(generateChangeTypeTests);
Expand Down
330 changes: 197 additions & 133 deletions testing/web-platform/tests/media-source/mediasource-changetype-util.js
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);
}

0 comments on commit 94d4bd6

Please sign in to comment.