Commit 1b104e92 authored by wolenetz@chromium.org's avatar wolenetz@chromium.org

MSE: Start letting SourceBuffer begin to do initialization segment received algorithm

This change is the first in a three-sided set of changes, and introduces
WebSourceBufferClient for handling media engine notifications
of newly received initialization segments.
It adds logic to make the addition of a SourceBuffer into
MediaSource.activeSourceBuffers more compliant once all three sides
land: addition will be done per the MSE spec's first initialization
flag logic rather than other non-compliant approaches like at
addSourceBuffer() time or on transition to HAVE_METADATA. Insertion
logic is included to preserve correct ordering of
activeSourceBuffers.
Updated and new layout tests are included.

R=acolwell@chromium.org,philipj@opera.com,tkent@chromium.org
TEST=No mediasource layout test regression
BUG=249428,397243

Review URL: https://codereview.chromium.org/552943002

git-svn-id: svn://svn.chromium.org/blink/trunk@181861 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent e16a59d6
...@@ -463,6 +463,13 @@ crbug.com/340206 [ Win ] http/tests/security/cross-origin-css.html [ Pass Timeou ...@@ -463,6 +463,13 @@ crbug.com/340206 [ Win ] http/tests/security/cross-origin-css.html [ Pass Timeou
crbug.com/283723 [ Win ] http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass Timeout ] crbug.com/283723 [ Win ] http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass Timeout ]
crbug.com/371716 [ Win ] http/tests/local/formdata/send-form-data.html [ Pass Timeout ] crbug.com/371716 [ Win ] http/tests/local/formdata/send-form-data.html [ Pass Timeout ]
# Temporary failures while landing three-sided changes
crbug.com/249428 http/tests/media/media-source/mediasource-addsourcebuffer.html [ Failure ]
crbug.com/249428 http/tests/media/media-source/mediasource-append-buffer.html [ Failure ]
crbug.com/249428 http/tests/media/media-source/mediasource-append-legacystream.html [ Failure ]
crbug.com/249428 http/tests/media/media-source/mediasource-buffered.html [ Failure ]
crbug.com/249428 http/tests/media/media-source/mediasource-sourcebufferlist.html [ Failure ]
# 309184 and 303419 track Android-specific failures # 309184 and 303419 track Android-specific failures
crbug.com/309184 crbug.com/303419 crbug.com/284782 http/tests/media/media-source/mediasource-duration.html [ Failure Pass ] crbug.com/309184 crbug.com/303419 crbug.com/284782 http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
"addSourceBuffer() threw an exception when passed an empty string."); "addSourceBuffer() threw an exception when passed an empty string.");
test.done(); test.done();
}, "Test addSourceBuffer() with empty type"); }, "Test addSourceBuffer() with empty type");
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
assert_throws("NotSupportedError", assert_throws("NotSupportedError",
...@@ -52,10 +52,10 @@ ...@@ -52,10 +52,10 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype); var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned"); assert_true(sourceBuffer != null, "New SourceBuffer returned");
assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() with Vorbis and VP8"); }, "Test addSourceBuffer() with Vorbis and VP8");
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
var videoMimetype = 'video/webm;codecs="vp8"'; var videoMimetype = 'video/webm;codecs="vp8"';
...@@ -67,9 +67,9 @@ ...@@ -67,9 +67,9 @@
var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype); var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype); var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferA is not in mediaSource.activeSourceBuffers");
assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers"); }, "Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers");
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype); var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned"); assert_true(sourceBuffer != null, "New SourceBuffer returned");
assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() video only"); }, "Test addSourceBuffer() video only");
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype); var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned"); assert_true(sourceBuffer != null, "New SourceBuffer returned");
assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() audio only"); }, "Test addSourceBuffer() audio only");
...@@ -108,7 +108,7 @@ ...@@ -108,7 +108,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype); var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned"); assert_true(sourceBuffer != null, "New SourceBuffer returned");
assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBuffer, "SourceBuffer is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBuffer is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() with AAC and H.264"); }, "Test addSourceBuffer() with AAC and H.264");
...@@ -123,9 +123,9 @@ ...@@ -123,9 +123,9 @@
var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype); var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype); var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferA, "sourceBufferA is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferA is not in mediaSource.activeSourceBuffers");
assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers"); assert_equals(mediaSource.sourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.activeSourceBuffers"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
test.done(); test.done();
}, "Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers"); }, "Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers");
......
...@@ -153,8 +153,8 @@ ...@@ -153,8 +153,8 @@
sourceBuffer.appendBuffer(mediaData); sourceBuffer.appendBuffer(mediaData);
assert_true(sourceBuffer.updating, 'updating attribute is true'); assert_true(sourceBuffer.updating, 'updating attribute is true');
assert_equals(mediaSource.activeSourceBuffers.length, 0, 'activeSourceBuffers.length');
test.expectEvent(mediaSource.activeSourceBuffers, 'removesourcebuffer', 'activeSourceBuffers');
test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'sourceBuffers'); test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'sourceBuffers');
mediaSource.removeSourceBuffer(sourceBuffer); mediaSource.removeSourceBuffer(sourceBuffer);
......
...@@ -185,8 +185,8 @@ ...@@ -185,8 +185,8 @@
sourceBuffer.appendStream(xhr.response); sourceBuffer.appendStream(xhr.response);
assert_true(sourceBuffer.updating, "updating attribute is true"); assert_true(sourceBuffer.updating, "updating attribute is true");
assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers.length");
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
mediaSource.removeSourceBuffer(sourceBuffer); mediaSource.removeSourceBuffer(sourceBuffer);
......
PASS Demuxed content with different lengths PASS Demuxed content with different lengths
PASS Process first init segment for sourceBuffer[0] first
PASS Process first init segment for sourceBuffer[1] first
PASS Muxed tracks with different lengths PASS Muxed tracks with different lengths
PASS Demuxed content with an empty buffered range on one SourceBuffer PASS Demuxed content with an empty buffered range on one SourceBuffer
PASS Muxed content empty buffered ranges. PASS Muxed content empty buffered ranges.
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
{ {
mediaSource.addSourceBuffer(typeA); mediaSource.addSourceBuffer(typeA);
mediaSource.addSourceBuffer(typeB); mediaSource.addSourceBuffer(typeB);
assert_equals(mediaSource.activeSourceBuffers.length, 2); assert_equals(mediaSource.sourceBuffers.length, 2);
callback(test, mediaElement, mediaSource, dataA, dataB); callback(test, mediaElement, mediaSource, dataA, dataB);
}); });
...@@ -46,27 +46,54 @@ ...@@ -46,27 +46,54 @@
}, description); }, description);
}; };
function appendData(test, mediaSource, dataA, dataB, callback) function appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, swapAppendBufferOrder, forceParsingOrder, callback)
{ {
var sourceBufferA = mediaSource.activeSourceBuffers[0]; // Verification here assumes no prior initialization segment received for any of mediaSource's sourceBuffers.
var sourceBufferB = mediaSource.activeSourceBuffers[1]; assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length before appends');
assert_equals(mediaSource.activeSourceBuffers.length, 0, 'mediaSource activeSourceBuffers length before appends');
test.expectEvent(sourceBufferA, 'update'); var sourceBufferA = mediaSource.sourceBuffers[0];
test.expectEvent(sourceBufferA, 'updateend'); var sourceBufferB = mediaSource.sourceBuffers[1];
sourceBufferA.appendBuffer(dataA);
var firstBuffer = swapAppendBufferOrder ? sourceBufferB : sourceBufferA;
var firstData = swapAppendBufferOrder ? dataB : dataA;
var secondBuffer = swapAppendBufferOrder ? sourceBufferA : sourceBufferB;
var secondData = swapAppendBufferOrder ? dataA : dataB;
test.expectEvent(firstBuffer, 'update');
test.expectEvent(firstBuffer, 'updateend');
firstBuffer.appendBuffer(firstData);
var doSecondAppend = function()
{
test.expectEvent(secondBuffer, 'update');
test.expectEvent(secondBuffer, 'updateend');
secondBuffer.appendBuffer(secondData);
};
test.expectEvent(sourceBufferB, 'update'); if (forceParsingOrder) {
test.expectEvent(sourceBufferB, 'updateend'); test.waitForExpectedEvents(function()
sourceBufferB.appendBuffer(dataB); {
assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length between completed appends');
assert_equals(mediaSource.activeSourceBuffers.length, 1, 'mediaSource activeSourceBuffers length between completed appends');
assert_equals(mediaSource.activeSourceBuffers[0], firstBuffer);
doSecondAppend();
});
} else {
doSecondAppend();
}
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
callback(); assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length after appends completed');
assert_equals(mediaSource.activeSourceBuffers.length, 2, 'mediaSource activeSourceBuffers length after appends completed');
assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferA);
assert_equals(mediaSource.activeSourceBuffers[1], sourceBufferB);
callback();
}); });
} }
mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) { mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
appendData(test, mediaSource, dataA, dataB, function() appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, false, false, function()
{ {
assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]'); assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]'); assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
...@@ -82,6 +109,26 @@ ...@@ -82,6 +109,26 @@
}); });
}, 'Demuxed content with different lengths'); }, 'Demuxed content with different lengths');
mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, false, true, function()
{
assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
assertBufferedEquals(mediaElement, expectationsB[subType], 'mediaElement.buffered');
test.done();
});
}, 'Process first init segment for sourceBuffer[0] first');
mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, true, true, function()
{
assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
assertBufferedEquals(mediaElement, expectationsB[subType], 'mediaElement.buffered');
test.done();
});
}, 'Process first init segment for sourceBuffer[1] first');
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
mediaElement.pause(); mediaElement.pause();
...@@ -118,9 +165,8 @@ ...@@ -118,9 +165,8 @@
}); });
}, 'Muxed tracks with different lengths'); }, 'Muxed tracks with different lengths');
mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) { mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
appendData(test, mediaSource, dataA, dataB.subarray(0, 318), function() appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB.subarray(0, 318), false, false, function()
{ {
assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]'); assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
assertBufferedEquals(mediaSource.activeSourceBuffers[1], '{ }', 'mediaSource.activeSourceBuffers[1]'); assertBufferedEquals(mediaSource.activeSourceBuffers[1], '{ }', 'mediaSource.activeSourceBuffers[1]');
...@@ -171,9 +217,7 @@ ...@@ -171,9 +217,7 @@
test.endOnEvent(mediaElement, 'ended'); test.endOnEvent(mediaElement, 'ended');
var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE); var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
// FIXME: verify activeSourceBuffers is empty until init segment with at least
// one active track is appended.
assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]'); assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]');
assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered'); assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');
test.done(); test.done();
...@@ -183,9 +227,9 @@ ...@@ -183,9 +227,9 @@
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{ {
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init); var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.'); test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
sourceBuffer.appendBuffer(initSegment); sourceBuffer.appendBuffer(initSegment);
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]'); assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]');
...@@ -199,7 +243,7 @@ ...@@ -199,7 +243,7 @@
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{ {
test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.'); test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
mediaSource.removeSourceBuffer(sourceBuffer); mediaSource.removeSourceBuffer(sourceBuffer);
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
......
...@@ -55,17 +55,17 @@ ...@@ -55,17 +55,17 @@
mediaSource.endOfStream(); mediaSource.endOfStream();
assert_true(mediaSource.readyState == 'ended', "MediaSource in ended state"); assert_true(mediaSource.readyState == 'ended', "MediaSource in ended state");
mediaSource.removeSourceBuffer(sourceBuffer); mediaSource.removeSourceBuffer(sourceBuffer);
assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty"); assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty");
assert_true(mediaSource.activeSourceBuffers.length == 0, "MediaSource.activesourceBuffers is empty"); assert_true(mediaSource.activeSourceBuffers.length == 0, "MediaSource.activesourceBuffers is empty");
test.done(); test.done();
}, "Test calling removeSourceBuffer() in ended state."); }, "Test calling removeSourceBuffer() in ended state.");
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{ {
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init); var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.'); test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
test.expectEvent(mediaElement, 'loadedmetadata', 'loadedmetadata done.'); test.expectEvent(mediaElement, 'loadedmetadata', 'loadedmetadata done.');
sourceBuffer.appendBuffer(initSegment); sourceBuffer.appendBuffer(initSegment);
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.'); test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
mediaSource.removeSourceBuffer(sourceBuffer); mediaSource.removeSourceBuffer(sourceBuffer);
}); });
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty"); assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty");
......
PASS Test SourceBufferList event dispatching. PASS Test SourceBufferList event dispatching.
PASS Test that only 1 removesourcebuffer event fires on each SourceBufferList when the MediaSource closes. PASS Test that only 1 removesourcebuffer event fires on sourceBuffers when the MediaSource closes.
PASS Test removesourcebuffer event firing on sourceBuffers and activeSourceBuffers when MediaSource closes.
...@@ -13,24 +13,20 @@ ...@@ -13,24 +13,20 @@
function verifySourceBufferLists(mediaSource, expected) function verifySourceBufferLists(mediaSource, expected)
{ {
assert_equals(mediaSource.sourceBuffers.length, expected.length, "sourceBuffers length"); assert_equals(mediaSource.sourceBuffers.length, expected.length, "sourceBuffers length");
assert_equals(mediaSource.activeSourceBuffers.length, expected.length, "activeSourceBuffers length"); assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers length");
for (var i = 0; i < expected.length; ++i) { for (var i = 0; i < expected.length; ++i)
assert_equals(mediaSource.sourceBuffers[i], expected[i], "Verifying mediaSource.sourceBuffers[" + i + "]"); assert_equals(mediaSource.sourceBuffers[i], expected[i], "Verifying mediaSource.sourceBuffers[" + i + "]");
assert_equals(mediaSource.activeSourceBuffers[i], expected[i], "Verifying mediaSource.activeSourceBuffers[" + i + "]");
}
} }
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE); var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
var sourceBufferB = null; var sourceBufferB = null;
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE); sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]); verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
...@@ -38,14 +34,12 @@ ...@@ -38,14 +34,12 @@
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
mediaSource.removeSourceBuffer(sourceBufferA); mediaSource.removeSourceBuffer(sourceBufferA);
verifySourceBufferLists(mediaSource, [sourceBufferB]); verifySourceBufferLists(mediaSource, [sourceBufferB]);
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE); sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
verifySourceBufferLists(mediaSource, [sourceBufferB, sourceBufferA]); verifySourceBufferLists(mediaSource, [sourceBufferB, sourceBufferA]);
...@@ -60,9 +54,7 @@ ...@@ -60,9 +54,7 @@
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE); var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
var sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE); var sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
...@@ -73,21 +65,53 @@ ...@@ -73,21 +65,53 @@
verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]); verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
// Force the media element to close the MediaSource object. // Force the media element to close the MediaSource object.
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers"); test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource, "sourceclose", "mediaSource closing"); test.expectEvent(mediaSource, "sourceclose", "mediaSource closing");
test.expectEvent(mediaElement, "error", "mediaElement error loading");
mediaElement.src = ""; mediaElement.src = "";
}); });
test.waitForExpectedEvents(function() test.waitForExpectedEvents(function()
{ {
assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, "Expected error code");
assert_equals(mediaSource.readyState, "closed", "mediaSource is closed."); assert_equals(mediaSource.readyState, "closed", "mediaSource is closed.");
verifySourceBufferLists(mediaSource, []); verifySourceBufferLists(mediaSource, []);
test.done(); test.done();
}); });
}, "Test that only 1 removesourcebuffer event fires on each SourceBufferList when the MediaSource closes."); }, "Test that only 1 removesourcebuffer event fires on sourceBuffers when the MediaSource closes.");
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{
// FIXME: Test two activeSourceBuffers, either demuxed or once multitrack is supported.
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
test.expectEvent(sourceBuffer, "updateend", "initSegment append ended.");
test.expectEvent(mediaElement, "loadedmetadata", "loadedmetadata done.");
sourceBuffer.appendBuffer(initSegment);
test.waitForExpectedEvents(function()
{
assert_equals(mediaSource.sourceBuffers.length, 1, "mediaSource sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers.length, 1, "mediaSource activeSourceBuffers");
// Force the media element to close the MediaSource object.
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourcebuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activesourcebuffers");
test.expectEvent(mediaSource, "sourceclose", "mediaSource closing");
test.expectEvent(mediaElement, "error", "mediaElement error loading");
mediaElement.src = "";
});
test.waitForExpectedEvents(function()
{
assert_equals(mediaElement.error.code, MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, "Expected error code");
assert_equals(mediaSource.readyState, "closed", "mediaSource is closed.");
assert_equals(mediaSource.sourceBuffers.length, 0, "mediaSource sourceBuffers");
assert_equals(mediaSource.activeSourceBuffers.length, 0, "mediaSource activeSourceBuffers");
test.done();
});
}, "Test removesourcebuffer event firing on sourceBuffers and activeSourceBuffers when MediaSource closes.", { allow_media_element_error: true } );
</script> </script>
</body> </body>
</html> </html>
...@@ -325,16 +325,16 @@ ...@@ -325,16 +325,16 @@
}; };
window['MediaSourceUtil'] = MediaSourceUtil; window['MediaSourceUtil'] = MediaSourceUtil;
window['media_test'] = function(testFunction, description, options) window['media_test'] = function(testFunction, description, properties)
{ {
options = options || {}; properties = properties || {};
return async_test(function(test) return async_test(function(test)
{ {
addExtraTestMethods(test); addExtraTestMethods(test);
testFunction(test); testFunction(test);
}, description, options); }, description, properties);
}; };
window['mediasource_test'] = function(testFunction, description, options) window['mediasource_test'] = function(testFunction, description, properties)
{ {
return media_test(function(test) return media_test(function(test)
{ {
...@@ -357,10 +357,12 @@ ...@@ -357,10 +357,12 @@
{ {
testFunction(test, mediaTag, mediaSource); testFunction(test, mediaTag, mediaSource);
}); });
}, description, options); }, description, properties);
}; };
window['mediasource_testafterdataloaded'] = function(testFunction, description, options) // In addition to test harness's async_test() properties parameter, this
// function recognizes the property allow_media_element_error.
window['mediasource_testafterdataloaded'] = function(testFunction, description, properties)
{ {
mediasource_test(function(test, mediaElement, mediaSource) mediasource_test(function(test, mediaElement, mediaSource)
{ {
...@@ -371,15 +373,16 @@ ...@@ -371,15 +373,16 @@
return; return;
} }
test.failOnEvent(mediaElement, 'error'); if (properties == null || properties.allow_media_element_error == null || !properties.allow_media_element_error)
test.failOnEvent(mediaElement, 'error');
var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type); var sourceBuffer = mediaSource.addSourceBuffer(segmentInfo.type);
MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData) MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData)
{ {
testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData); testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData);
}); });
}, description, options); }, description, properties);
} };
function timeRangesToString(ranges) function timeRangesToString(ranges)
{ {
......
...@@ -155,7 +155,9 @@ SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& e ...@@ -155,7 +155,9 @@ SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& e
SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get()); SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get());
// 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object. // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
m_sourceBuffers->add(buffer); m_sourceBuffers->add(buffer);
m_activeSourceBuffers->add(buffer); // FIXME: Remove the following once Chromium calls WebSourceBufferClient::InitSegmentReceived()
setSourceBufferActive(buffer);
// 7. Return the new object to the caller. // 7. Return the new object to the caller.
return buffer; return buffer;
} }
...@@ -480,6 +482,29 @@ bool MediaSource::isOpen() const ...@@ -480,6 +482,29 @@ bool MediaSource::isOpen() const
return readyState() == openKeyword(); return readyState() == openKeyword();
} }
void MediaSource::setSourceBufferActive(SourceBuffer* sourceBuffer)
{
if (m_activeSourceBuffers->contains(sourceBuffer))
return;
// https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-activeSourceBuffers
// SourceBuffer objects in SourceBuffer.activeSourceBuffers must appear in
// the same order as they appear in SourceBuffer.sourceBuffers.
// SourceBuffer transitions to active are not guaranteed to occur in the
// same order as buffers in |m_sourceBuffers|, so this method needs to
// insert |sourceBuffer| into |m_activeSourceBuffers|.
size_t indexInSourceBuffers = m_sourceBuffers->find(sourceBuffer);
ASSERT(indexInSourceBuffers != kNotFound);
size_t insertPosition = 0;
while (insertPosition < m_activeSourceBuffers->length()
&& m_sourceBuffers->find(m_activeSourceBuffers->item(insertPosition)) < indexInSourceBuffers) {
++insertPosition;
}
m_activeSourceBuffers->insert(insertPosition, sourceBuffer);
}
bool MediaSource::isClosed() const bool MediaSource::isClosed() const
{ {
return readyState() == closedKeyword(); return readyState() == closedKeyword();
......
...@@ -100,6 +100,7 @@ public: ...@@ -100,6 +100,7 @@ public:
// Used by SourceBuffer. // Used by SourceBuffer.
void openIfInEndedState(); void openIfInEndedState();
bool isOpen() const; bool isOpen() const;
void setSourceBufferActive(SourceBuffer*);
// Used by MediaSourceRegistry. // Used by MediaSourceRegistry.
void addedToRegistry(); void addedToRegistry();
......
...@@ -89,6 +89,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou ...@@ -89,6 +89,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou
, m_timestampOffset(0) , m_timestampOffset(0)
, m_appendWindowStart(0) , m_appendWindowStart(0)
, m_appendWindowEnd(std::numeric_limits<double>::infinity()) , m_appendWindowEnd(std::numeric_limits<double>::infinity())
, m_firstInitializationSegmentReceived(false)
, m_pendingAppendDataOffset(0) , m_pendingAppendDataOffset(0)
, m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart) , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
, m_pendingRemoveStart(-1) , m_pendingRemoveStart(-1)
...@@ -100,6 +101,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou ...@@ -100,6 +101,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou
{ {
ASSERT(m_webSourceBuffer); ASSERT(m_webSourceBuffer);
ASSERT(m_source); ASSERT(m_source);
m_webSourceBuffer->setClient(this);
} }
SourceBuffer::~SourceBuffer() SourceBuffer::~SourceBuffer()
...@@ -111,6 +113,7 @@ SourceBuffer::~SourceBuffer() ...@@ -111,6 +113,7 @@ SourceBuffer::~SourceBuffer()
ASSERT(isRemoved()); ASSERT(isRemoved());
ASSERT(!m_loader); ASSERT(!m_loader);
ASSERT(!m_stream); ASSERT(!m_stream);
ASSERT(!m_webSourceBuffer);
#endif #endif
} }
...@@ -419,6 +422,29 @@ void SourceBuffer::removedFromMediaSource() ...@@ -419,6 +422,29 @@ void SourceBuffer::removedFromMediaSource()
m_asyncEventQueue = 0; m_asyncEventQueue = 0;
} }
void SourceBuffer::initializationSegmentReceived()
{
ASSERT(m_source);
ASSERT(m_updating);
// https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-init-segment-received
// FIXME: Make steps 1-7 synchronous with this call.
// FIXME: Augment the interface to this method to implement compliant steps 4-7 here.
// Step 3 (if the first initialization segment received flag is true) is
// implemented by caller.
if (!m_firstInitializationSegmentReceived) {
// 5. If active track flag equals true, then run the following steps:
// 5.1. Add this SourceBuffer to activeSourceBuffers.
// 5.2. Queue a task to fire a simple event named addsourcebuffer at
// activesourcebuffers.
m_source->setSourceBufferActive(this);
// 6. Set first initialization segment received flag to true.
m_firstInitializationSegmentReceived = true;
}
}
bool SourceBuffer::hasPendingActivity() const bool SourceBuffer::hasPendingActivity() const
{ {
return m_source; return m_source;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "modules/EventTargetModules.h" #include "modules/EventTargetModules.h"
#include "platform/AsyncMethodRunner.h" #include "platform/AsyncMethodRunner.h"
#include "platform/weborigin/KURL.h" #include "platform/weborigin/KURL.h"
#include "public/platform/WebSourceBufferClient.h"
#include "wtf/Forward.h" #include "wtf/Forward.h"
#include "wtf/RefCounted.h" #include "wtf/RefCounted.h"
#include "wtf/text/WTFString.h" #include "wtf/text/WTFString.h"
...@@ -50,7 +51,7 @@ class Stream; ...@@ -50,7 +51,7 @@ class Stream;
class TimeRanges; class TimeRanges;
class WebSourceBuffer; class WebSourceBuffer;
class SourceBuffer FINAL : public RefCountedGarbageCollectedWillBeGarbageCollectedFinalized<SourceBuffer>, public ActiveDOMObject, public EventTargetWithInlineData, public FileReaderLoaderClient { class SourceBuffer FINAL : public RefCountedGarbageCollectedWillBeGarbageCollectedFinalized<SourceBuffer>, public ActiveDOMObject, public EventTargetWithInlineData, public FileReaderLoaderClient, public WebSourceBufferClient {
DEFINE_EVENT_TARGET_REFCOUNTING_WILL_BE_REMOVED(RefCountedGarbageCollected<SourceBuffer>); DEFINE_EVENT_TARGET_REFCOUNTING_WILL_BE_REMOVED(RefCountedGarbageCollected<SourceBuffer>);
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(SourceBuffer); WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(SourceBuffer);
...@@ -92,6 +93,9 @@ public: ...@@ -92,6 +93,9 @@ public:
virtual ExecutionContext* executionContext() const OVERRIDE; virtual ExecutionContext* executionContext() const OVERRIDE;
virtual const AtomicString& interfaceName() const OVERRIDE; virtual const AtomicString& interfaceName() const OVERRIDE;
// WebSourceBufferClient interface
virtual void initializationSegmentReceived() OVERRIDE;
virtual void trace(Visitor*) OVERRIDE; virtual void trace(Visitor*) OVERRIDE;
private: private:
...@@ -125,6 +129,7 @@ private: ...@@ -125,6 +129,7 @@ private:
double m_timestampOffset; double m_timestampOffset;
double m_appendWindowStart; double m_appendWindowStart;
double m_appendWindowEnd; double m_appendWindowEnd;
bool m_firstInitializationSegmentReceived;
Vector<unsigned char> m_pendingAppendData; Vector<unsigned char> m_pendingAppendData;
size_t m_pendingAppendDataOffset; size_t m_pendingAppendDataOffset;
......
...@@ -56,6 +56,12 @@ void SourceBufferList::add(SourceBuffer* buffer) ...@@ -56,6 +56,12 @@ void SourceBufferList::add(SourceBuffer* buffer)
scheduleEvent(EventTypeNames::addsourcebuffer); scheduleEvent(EventTypeNames::addsourcebuffer);
} }
void SourceBufferList::insert(size_t position, SourceBuffer* buffer)
{
m_list.insert(position, buffer);
scheduleEvent(EventTypeNames::addsourcebuffer);
}
void SourceBufferList::remove(SourceBuffer* buffer) void SourceBufferList::remove(SourceBuffer* buffer)
{ {
size_t index = m_list.find(buffer); size_t index = m_list.find(buffer);
......
...@@ -55,7 +55,9 @@ public: ...@@ -55,7 +55,9 @@ public:
SourceBuffer* item(unsigned long index) const { return (index < m_list.size()) ? m_list[index].get() : 0; } SourceBuffer* item(unsigned long index) const { return (index < m_list.size()) ? m_list[index].get() : 0; }
void add(SourceBuffer*); void add(SourceBuffer*);
void insert(size_t position, SourceBuffer*);
void remove(SourceBuffer*); void remove(SourceBuffer*);
size_t find(SourceBuffer* buffer) { return m_list.find(buffer); }
bool contains(SourceBuffer* buffer) { return m_list.find(buffer) != kNotFound; } bool contains(SourceBuffer* buffer) { return m_list.find(buffer) != kNotFound; }
void clear(); void clear();
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
namespace blink { namespace blink {
class WebSourceBufferClient;
class WebSourceBuffer { class WebSourceBuffer {
public: public:
enum AppendMode { enum AppendMode {
...@@ -43,6 +45,14 @@ public: ...@@ -43,6 +45,14 @@ public:
}; };
virtual ~WebSourceBuffer() { } virtual ~WebSourceBuffer() { }
// This will only be called once and only with a non-null pointer to a
// client whose ownership is not transferred to this WebSourceBuffer.
virtual void setClient(WebSourceBufferClient*)
{
// FIXME: Remove default implementation once Chromium impl lands.
}
virtual bool setMode(AppendMode) = 0; virtual bool setMode(AppendMode) = 0;
virtual WebTimeRanges buffered() = 0; virtual WebTimeRanges buffered() = 0;
...@@ -60,6 +70,8 @@ public: ...@@ -60,6 +70,8 @@ public:
// Set presentation timestamp for the end of append window. // Set presentation timestamp for the end of append window.
virtual void setAppendWindowEnd(double) = 0; virtual void setAppendWindowEnd(double) = 0;
// After this method is called, this WebSourceBuffer should never use the
// client pointer passed to setClient().
virtual void removedFromMediaSource() = 0; virtual void removedFromMediaSource() = 0;
}; };
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WebSourceBufferClient_h
#define WebSourceBufferClient_h
namespace blink {
class WebSourceBufferClient {
public:
virtual ~WebSourceBufferClient() { }
// FIXME: Add a track collection parameter here.
virtual void initializationSegmentReceived() = 0;
};
} // namespace blink
#endif // WebSourceBufferClient_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment