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
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 ]
# 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
crbug.com/309184 crbug.com/303419 crbug.com/284782 http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
......
......@@ -26,7 +26,7 @@
"addSourceBuffer() threw an exception when passed an empty string.");
test.done();
}, "Test addSourceBuffer() with empty type");
mediasource_test(function(test, mediaElement, mediaSource)
{
assert_throws("NotSupportedError",
......@@ -52,10 +52,10 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned");
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 addSourceBuffer() with Vorbis and VP8");
mediasource_test(function(test, mediaElement, mediaSource)
{
var videoMimetype = 'video/webm;codecs="vp8"';
......@@ -67,9 +67,9 @@
var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
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.activeSourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.activeSourceBuffers");
assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
test.done();
}, "Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers");
......@@ -82,7 +82,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned");
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 addSourceBuffer() video only");
......@@ -95,7 +95,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned");
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 addSourceBuffer() audio only");
......@@ -108,7 +108,7 @@
var sourceBuffer = mediaSource.addSourceBuffer(mimetype);
assert_true(sourceBuffer != null, "New SourceBuffer returned");
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 addSourceBuffer() with AAC and H.264");
......@@ -123,9 +123,9 @@
var sourceBufferA = mediaSource.addSourceBuffer(videoMimetype);
var sourceBufferB = mediaSource.addSourceBuffer(audioMimetype);
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.activeSourceBuffers[1], sourceBufferB, "sourceBufferB is in mediaSource.activeSourceBuffers");
assert_equals(mediaSource.activeSourceBuffers.length, 0, "SourceBufferB is not in mediaSource.activeSourceBuffers");
test.done();
}, "Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers");
......
......@@ -153,8 +153,8 @@
sourceBuffer.appendBuffer(mediaData);
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');
mediaSource.removeSourceBuffer(sourceBuffer);
......
......@@ -185,8 +185,8 @@
sourceBuffer.appendStream(xhr.response);
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");
mediaSource.removeSourceBuffer(sourceBuffer);
......
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 Demuxed content with an empty buffered range on one SourceBuffer
PASS Muxed content empty buffered ranges.
......
......@@ -38,7 +38,7 @@
{
mediaSource.addSourceBuffer(typeA);
mediaSource.addSourceBuffer(typeB);
assert_equals(mediaSource.activeSourceBuffers.length, 2);
assert_equals(mediaSource.sourceBuffers.length, 2);
callback(test, mediaElement, mediaSource, dataA, dataB);
});
......@@ -46,27 +46,54 @@
}, description);
};
function appendData(test, mediaSource, dataA, dataB, callback)
function appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, swapAppendBufferOrder, forceParsingOrder, callback)
{
var sourceBufferA = mediaSource.activeSourceBuffers[0];
var sourceBufferB = mediaSource.activeSourceBuffers[1];
test.expectEvent(sourceBufferA, 'update');
test.expectEvent(sourceBufferA, 'updateend');
sourceBufferA.appendBuffer(dataA);
// Verification here assumes no prior initialization segment received for any of mediaSource's sourceBuffers.
assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length before appends');
assert_equals(mediaSource.activeSourceBuffers.length, 0, 'mediaSource activeSourceBuffers length before appends');
var sourceBufferA = mediaSource.sourceBuffers[0];
var sourceBufferB = mediaSource.sourceBuffers[1];
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');
test.expectEvent(sourceBufferB, 'updateend');
sourceBufferB.appendBuffer(dataB);
if (forceParsingOrder) {
test.waitForExpectedEvents(function()
{
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()
{
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) {
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[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
......@@ -82,6 +109,26 @@
});
}, '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)
{
mediaElement.pause();
......@@ -118,9 +165,8 @@
});
}, 'Muxed tracks with different lengths');
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[1], '{ }', 'mediaSource.activeSourceBuffers[1]');
......@@ -171,9 +217,7 @@
test.endOnEvent(mediaElement, 'ended');
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(mediaElement, '{ }', 'mediaElement.buffered');
test.done();
......@@ -183,9 +227,9 @@
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
sourceBuffer.appendBuffer(initSegment);
sourceBuffer.appendBuffer(initSegment);
test.waitForExpectedEvents(function()
{
assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]');
......@@ -199,7 +243,7 @@
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{
test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
mediaSource.removeSourceBuffer(sourceBuffer);
mediaSource.removeSourceBuffer(sourceBuffer);
test.waitForExpectedEvents(function()
{
......
......@@ -55,17 +55,17 @@
mediaSource.endOfStream();
assert_true(mediaSource.readyState == 'ended', "MediaSource in ended state");
mediaSource.removeSourceBuffer(sourceBuffer);
assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty");
assert_true(mediaSource.activeSourceBuffers.length == 0, "MediaSource.activesourceBuffers is empty");
test.done();
}, "Test calling removeSourceBuffer() in ended state.");
mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
{
var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);
test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
test.expectEvent(mediaElement, 'loadedmetadata', 'loadedmetadata done.');
sourceBuffer.appendBuffer(initSegment);
......@@ -80,7 +80,7 @@
test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
mediaSource.removeSourceBuffer(sourceBuffer);
});
test.waitForExpectedEvents(function()
{
assert_true(mediaSource.sourceBuffers.length == 0, "MediaSource.sourceBuffers is empty");
......
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 @@
function verifySourceBufferLists(mediaSource, expected)
{
assert_equals(mediaSource.sourceBuffers.length, expected.length, "sourceBuffers length");
assert_equals(mediaSource.activeSourceBuffers.length, expected.length, "activeSourceBuffers length");
for (var i = 0; i < expected.length; ++i) {
assert_equals(mediaSource.activeSourceBuffers.length, 0, "activeSourceBuffers length");
for (var i = 0; i < expected.length; ++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)
{
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
var sourceBufferB = null;
test.waitForExpectedEvents(function()
{
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
......@@ -38,14 +34,12 @@
test.waitForExpectedEvents(function()
{
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
mediaSource.removeSourceBuffer(sourceBufferA);
verifySourceBufferLists(mediaSource, [sourceBufferB]);
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
verifySourceBufferLists(mediaSource, [sourceBufferB, sourceBufferA]);
......@@ -60,9 +54,7 @@
mediasource_test(function(test, mediaElement, mediaSource)
{
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "addsourcebuffer", "sourceBuffers");
test.expectEvent(mediaSource.activeSourceBuffers, "addsourcebuffer", "activeSourceBuffers");
var sourceBufferA = mediaSource.addSourceBuffer(MediaSourceUtil.VIDEO_ONLY_TYPE);
var sourceBufferB = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);
......@@ -73,21 +65,53 @@
verifySourceBufferLists(mediaSource, [sourceBufferA, sourceBufferB]);
// Force the media element to close the MediaSource object.
test.expectEvent(mediaSource.activeSourceBuffers, "removesourcebuffer", "activeSourceBuffers");
test.expectEvent(mediaSource.sourceBuffers, "removesourcebuffer", "sourceBuffers");
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.");
verifySourceBufferLists(mediaSource, []);
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>
</body>
</html>
......@@ -325,16 +325,16 @@
};
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)
{
addExtraTestMethods(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)
{
......@@ -357,10 +357,12 @@
{
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)
{
......@@ -371,15 +373,16 @@
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);
MediaSourceUtil.loadBinaryData(test, segmentInfo.url, function(mediaData)
{
testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData);
});
}, description, options);
}
}, description, properties);
};
function timeRangesToString(ranges)
{
......
......@@ -155,7 +155,9 @@ SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& e
SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get());
// 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
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.
return buffer;
}
......@@ -480,6 +482,29 @@ bool MediaSource::isOpen() const
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
{
return readyState() == closedKeyword();
......
......@@ -100,6 +100,7 @@ public:
// Used by SourceBuffer.
void openIfInEndedState();
bool isOpen() const;
void setSourceBufferActive(SourceBuffer*);
// Used by MediaSourceRegistry.
void addedToRegistry();
......
......@@ -89,6 +89,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou
, m_timestampOffset(0)
, m_appendWindowStart(0)
, m_appendWindowEnd(std::numeric_limits<double>::infinity())
, m_firstInitializationSegmentReceived(false)
, m_pendingAppendDataOffset(0)
, m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
, m_pendingRemoveStart(-1)
......@@ -100,6 +101,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou
{
ASSERT(m_webSourceBuffer);
ASSERT(m_source);
m_webSourceBuffer->setClient(this);
}
SourceBuffer::~SourceBuffer()
......@@ -111,6 +113,7 @@ SourceBuffer::~SourceBuffer()
ASSERT(isRemoved());
ASSERT(!m_loader);
ASSERT(!m_stream);
ASSERT(!m_webSourceBuffer);
#endif
}
......@@ -419,6 +422,29 @@ void SourceBuffer::removedFromMediaSource()
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
{
return m_source;
......
......@@ -36,6 +36,7 @@
#include "modules/EventTargetModules.h"
#include "platform/AsyncMethodRunner.h"
#include "platform/weborigin/KURL.h"
#include "public/platform/WebSourceBufferClient.h"
#include "wtf/Forward.h"
#include "wtf/RefCounted.h"
#include "wtf/text/WTFString.h"
......@@ -50,7 +51,7 @@ class Stream;
class TimeRanges;
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_WRAPPERTYPEINFO();
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(SourceBuffer);
......@@ -92,6 +93,9 @@ public:
virtual ExecutionContext* executionContext() const OVERRIDE;
virtual const AtomicString& interfaceName() const OVERRIDE;
// WebSourceBufferClient interface
virtual void initializationSegmentReceived() OVERRIDE;
virtual void trace(Visitor*) OVERRIDE;
private:
......@@ -125,6 +129,7 @@ private:
double m_timestampOffset;
double m_appendWindowStart;
double m_appendWindowEnd;
bool m_firstInitializationSegmentReceived;
Vector<unsigned char> m_pendingAppendData;
size_t m_pendingAppendDataOffset;
......
......@@ -56,6 +56,12 @@ void SourceBufferList::add(SourceBuffer* buffer)
scheduleEvent(EventTypeNames::addsourcebuffer);
}
void SourceBufferList::insert(size_t position, SourceBuffer* buffer)
{
m_list.insert(position, buffer);
scheduleEvent(EventTypeNames::addsourcebuffer);
}
void SourceBufferList::remove(SourceBuffer* buffer)
{
size_t index = m_list.find(buffer);
......
......@@ -55,7 +55,9 @@ public:
SourceBuffer* item(unsigned long index) const { return (index < m_list.size()) ? m_list[index].get() : 0; }
void add(SourceBuffer*);
void insert(size_t position, SourceBuffer*);
void remove(SourceBuffer*);
size_t find(SourceBuffer* buffer) { return m_list.find(buffer); }
bool contains(SourceBuffer* buffer) { return m_list.find(buffer) != kNotFound; }
void clear();
......
......@@ -35,6 +35,8 @@
namespace blink {
class WebSourceBufferClient;
class WebSourceBuffer {
public:
enum AppendMode {
......@@ -43,6 +45,14 @@ public:
};
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 WebTimeRanges buffered() = 0;
......@@ -60,6 +70,8 @@ public:
// Set presentation timestamp for the end of append window.
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;
};
......
// 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