Source/WebCore: Update Media Source implementation to reflect changes in 0.5 spec.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 May 2012 01:08:46 +0000 (01:08 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 May 2012 01:08:46 +0000 (01:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=83607

Patch by Aaron Colwell <acolwell@chromium.org> on 2012-05-18
Reviewed by Darin Fisher.

Tests: http/tests/media/media-source/video-media-source-event-attributes.html
       http/tests/media/media-source/webm/video-media-source-abort.html
       http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html

* html/HTMLAttributeNames.in: Add onwebkitsourcexxx event attribute names.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::parseAttribute): Add code to register event listeners for the onwebkitsourcexxx attributes.
(WebCore::HTMLMediaElement::pauseInternal):
(WebCore):
(WebCore::HTMLMediaElement::webkitSourceAddId): Changed code to pass the parsed type & codecs to m_player instead of the whole MIME-type string. This was done to allow some simple parsing & validation code to be shared in WebKit instead of reimplemented in every port.
(WebCore::HTMLMediaElement::webkitSourceBuffered): New method in the v0.5 spec. It validates the parameters & that it is being called in the correct state. If so it forwards the call to m_player.
(WebCore::HTMLMediaElement::webkitSourceAppend): Adds id parameter to match v0.5 signature. Minor logic changes that avoid calling m_player for 0 byte arrays and fixes exceptions reported for a null array and a parse error so they match the spec text.
(WebCore::HTMLMediaElement::webkitSourceAbort): New method in the v0.5 spec. It validates the parameter & that it is being called in athe correct state. If so it forwards the call to m_player.
* html/HTMLMediaElement.h:
(HTMLMediaElement): Added new methods, signature updates, and onwebitsourcexxx event listener attributes.
* html/HTMLMediaElement.idl: Added new methods, signature updates, and onwebitsourcexxx event listener attributes.
* platform/ContentType.cpp:
(WebCore::ContentType::codecs): A new method that parses the codecs parameter into a vector of codec strings.
(WebCore):
* platform/ContentType.h:
(ContentType):
* platform/graphics/MediaPlayer.cpp:
(WebCore::NullMediaPlayerPrivate::sourceAddId): Update the signature to take the parsed type and codecs vector.
(WebCore::NullMediaPlayerPrivate::sourceBuffered): Add default implementation for this new method.
(WebCore::NullMediaPlayerPrivate::sourceRemoveId): Remove id parameter name to match other methods.
(WebCore::NullMediaPlayerPrivate::sourceAppend): Updated signature to include id parameter.
(WebCore::NullMediaPlayerPrivate::sourceAbort): Add default implementation for this new method.
(WebCore::NullMediaPlayerPrivate::sourceEndOfStream): Remove parameter name to match other methods.
(WebCore::MediaPlayer::sourceAddId): Update the signature to take the parsed type and codecs vector.
(WebCore):
(WebCore::MediaPlayer::sourceBuffered): Add implementation for new method that forwards the call to the MediaPlayerPrivate interface.
(WebCore::MediaPlayer::sourceAppend): Update method to forward the new id parameter in the signature.
(WebCore::MediaPlayer::sourceAbort): Add implementation for new method that forwards the call to the MediaPlayerPrivate interface.
* platform/graphics/MediaPlayer.h: Update interface to include new methods and updated signatures.
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::sourceAddId): Update the signature to take the parsed type and codecs vector.
(WebCore::MediaPlayerPrivateInterface::sourceBuffered): Add default implementation for new method.
(WebCore::MediaPlayerPrivateInterface::sourceAppend): Update method to forward the new id parameter in the signature.
(WebCore::MediaPlayerPrivateInterface::sourceAbort): Add default implementation for new method.

Source/WebKit/chromium: Update Media Source implementation to reflect changes in 0.5 spec.
https://bugs.webkit.org/show_bug.cgi?id=83607

Patch by Aaron Colwell <acolwell@chromium.org> on 2012-05-18
Reviewed by Darin Fisher.

* public/WebMediaPlayer.h:
(WebKit::WebMediaPlayer::sourceAddId): Update the signature to take the parsed type and codecs vector.
(WebKit::WebMediaPlayer::sourceBuffered): Add default implementation for new method.
(WebKit::WebMediaPlayer::sourceAppend): Update signature to include id parameter.
(WebKit::WebMediaPlayer::sourceAbort): Add default implementation for new method.
* src/WebMediaPlayerClientImpl.cpp:
(WebKit::WebMediaPlayerClientImpl::sourceAddId): Update the signature to take the parsed type and codecs vector.
(WebKit::WebMediaPlayerClientImpl::sourceBuffered): Added implementation for new method. Converts WebKit::WebTimeRanges to WebCore::TimeRanges.
(WebKit):
(WebKit::WebMediaPlayerClientImpl::sourceAppend): Update signature to include id parameter.
(WebKit::WebMediaPlayerClientImpl::sourceAbort): Implementation for new method.
* src/WebMediaPlayerClientImpl.h:
(WebMediaPlayerClientImpl): Added declarations for new methods and updated signatures for existing ones.

LayoutTests: Updated Media Source LayoutTests to use v0.5 spec signatures and added
new tests to verify the behavior of the new methods.
https://bugs.webkit.org/show_bug.cgi?id=83607

Patch by Aaron Colwell <acolwell@chromium.org> on 2012-05-18
Reviewed by Darin Fisher.

* http/tests/media/media-source/video-media-source-event-attributes-expected.txt: Added.
* http/tests/media/media-source/video-media-source-event-attributes.html: Added. Tests onwebkitsourcexxx event attributes.
* http/tests/media/media-source/webm/video-media-source-abort-expected.txt: Added.
* http/tests/media/media-source/webm/video-media-source-abort.html: Added. Tests basic webkitSourceAbort() functionality.
* http/tests/media/media-source/webm/video-media-source-add-and-remove-ids-expected.txt: Added.
* http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html: Added. Tests webkitSourceAddId() & webkitSourceRemoveId().
* http/tests/media/media-source/webm/video-media-source-errors.html: Style fixes and add addSourceId() since we need to add an ID before appending data now.
* http/tests/media/media-source/webm/video-media-source-play.html: Style fixes and add addSourceId().
* http/tests/media/media-source/webm/video-media-source-seek.html: Style fixes and add addSourceId().
* http/tests/media/media-source/webm/video-media-source-state-changes.html: Style fixes and add addSourceId().
* http/tests/media/media-source/webm/webm-media-source.js:
(getData.request.onload): Style fix.
(getData): Style fix.
(loadWebMData): Style fix.
(addSourceId): New helper method that adds SOURCE_ID to the video tag so data can be appended with this ID.
(appendHeaders): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.
(appendCluster): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.
(appendUntilEndOfStream): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@117662 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/media/media-source/video-media-source-event-attributes-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/video-media-source-event-attributes.html [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/webm/video-media-source-abort-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/webm/video-media-source-abort.html [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/webm/video-media-source-errors.html
LayoutTests/http/tests/media/media-source/webm/video-media-source-play.html
LayoutTests/http/tests/media/media-source/webm/video-media-source-seek.html
LayoutTests/http/tests/media/media-source/webm/video-media-source-state-changes.html
LayoutTests/http/tests/media/media-source/webm/webm-media-source.js
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/html/HTMLMediaElement.idl
Source/WebCore/platform/ContentType.cpp
Source/WebCore/platform/ContentType.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/public/WebMediaPlayer.h
Source/WebKit/chromium/src/WebMediaPlayerClientImpl.cpp
Source/WebKit/chromium/src/WebMediaPlayerClientImpl.h

index ce49957..bd6d376 100644 (file)
@@ -1,3 +1,30 @@
+2012-05-18  Aaron Colwell  <acolwell@chromium.org>
+
+        Updated Media Source LayoutTests to use v0.5 spec signatures and added
+        new tests to verify the behavior of the new methods.
+        https://bugs.webkit.org/show_bug.cgi?id=83607
+
+        Reviewed by Darin Fisher.
+
+        * http/tests/media/media-source/video-media-source-event-attributes-expected.txt: Added.
+        * http/tests/media/media-source/video-media-source-event-attributes.html: Added. Tests onwebkitsourcexxx event attributes.
+        * http/tests/media/media-source/webm/video-media-source-abort-expected.txt: Added.
+        * http/tests/media/media-source/webm/video-media-source-abort.html: Added. Tests basic webkitSourceAbort() functionality.
+        * http/tests/media/media-source/webm/video-media-source-add-and-remove-ids-expected.txt: Added.
+        * http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html: Added. Tests webkitSourceAddId() & webkitSourceRemoveId().
+        * http/tests/media/media-source/webm/video-media-source-errors.html: Style fixes and add addSourceId() since we need to add an ID before appending data now.
+        * http/tests/media/media-source/webm/video-media-source-play.html: Style fixes and add addSourceId().
+        * http/tests/media/media-source/webm/video-media-source-seek.html: Style fixes and add addSourceId().
+        * http/tests/media/media-source/webm/video-media-source-state-changes.html: Style fixes and add addSourceId().
+        * http/tests/media/media-source/webm/webm-media-source.js:
+        (getData.request.onload): Style fix.
+        (getData): Style fix.
+        (loadWebMData): Style fix.
+        (addSourceId): New helper method that adds SOURCE_ID to the video tag so data can be appended with this ID.
+        (appendHeaders): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.
+        (appendCluster): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.
+        (appendUntilEndOfStream): Add SOURCE_ID parameter since the webkitSourceAppend() signature changed.
+
 2012-05-18  Eric Seidel  <eric@webkit.org>
 
         Assertion failure in BidiResolver::commitExplicitEmbedding() (!inIsolate() || m_currentExplicitEmbeddingSequence.isEmpty()) at wikipedia.org
diff --git a/LayoutTests/http/tests/media/media-source/video-media-source-event-attributes-expected.txt b/LayoutTests/http/tests/media/media-source/video-media-source-event-attributes-expected.txt
new file mode 100644 (file)
index 0000000..1e7875c
--- /dev/null
@@ -0,0 +1,10 @@
+Test Media Source event handler attributes
+
+onLoad()
+onSourceOpen()
+Calling webkitSourceEndOfStream to trigger a webkitsourceended event.
+onSourceEnded()
+Setting src attribute to "" to trigger a webkitsourceclosed event.
+onSourceClose()
+END OF TEST
+
diff --git a/LayoutTests/http/tests/media/media-source/video-media-source-event-attributes.html b/LayoutTests/http/tests/media/media-source/video-media-source-event-attributes.html
new file mode 100644 (file)
index 0000000..305f457
--- /dev/null
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+   <head>
+       <script src="../../media-resources/video-test.js"></script>
+       <script type="text/javascript">
+           function onSourceOpen(video)
+           {
+               consoleWrite("onSourceOpen()");
+
+               consoleWrite("Calling webkitSourceEndOfStream to trigger a webkitsourceended event.");
+               video.webkitSourceEndOfStream(HTMLMediaElement.EOS_NO_ERROR);
+           }
+
+           function onSourceEnded(video)
+           {
+               consoleWrite("onSourceEnded()");
+               consoleWrite("Setting src attribute to \"\" to trigger a webkitsourceclosed event.");
+               video.src = "";
+           }
+
+           function onSourceClose(video)
+           {
+               consoleWrite("onSourceClose()");
+               endTest();
+           }
+
+           function onLoad(e)
+           {
+               consoleWrite("onLoad()");
+               var video = document.getElementById('v');
+               video.src = video.webkitMediaSourceURL;
+           }
+       </script>
+   </head>
+   <body onload="onLoad()">
+       <p>Test Media Source event handler attributes</p>
+       <video id='v' onwebkitsourceopen='onSourceOpen(this)' onwebkitsourceended='onSourceEnded(this);' onwebkitsourceclose='onSourceClose(this);'></video>
+   </body>
+</html>
diff --git a/LayoutTests/http/tests/media/media-source/webm/video-media-source-abort-expected.txt b/LayoutTests/http/tests/media/media-source/webm/video-media-source-abort-expected.txt
new file mode 100644 (file)
index 0000000..8bb0f9c
--- /dev/null
@@ -0,0 +1,24 @@
+Tests webkitSourceAbort() functionality
+
+EVENT(loadstart)
+EVENT(webkitsourceopen)
+
+running abortDuringHeader
+Test aborting during headers.
+Appending partial header.
+Aborting append.
+Appending full header.
+Appending enough clusters to trigger loadeddata.
+EVENT(loadeddata)
+EVENT(emptied)
+EVENT(loadstart)
+EVENT(webkitsourceopen)
+
+running abortDuringCluster
+Test aborting in the middle of a cluster.
+Appending partial cluster.
+Aborting append.
+Appending full header.
+Appending enough clusters to trigger loadeddata.
+END OF TEST
+
diff --git a/LayoutTests/http/tests/media/media-source/webm/video-media-source-abort.html b/LayoutTests/http/tests/media/media-source/webm/video-media-source-abort.html
new file mode 100644 (file)
index 0000000..15d4cb0
--- /dev/null
@@ -0,0 +1,163 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="../../../media-resources/video-test.js"></script>
+        <script src="webm-media-source.js"></script>
+        <script>
+            function abortDuringHeader(event)
+            {
+                var videoTag = event.target;
+
+                consoleWrite("Test aborting during headers.");
+                try {
+                  var headers = getHeaders();
+                  var partialHeader = headers.subarray(0, headers.length / 2);
+
+                  consoleWrite("Appending partial header.");
+                  videoTag.webkitSourceAppend(SOURCE_ID, partialHeader);
+
+                  consoleWrite("Aborting append.");
+                  videoTag.webkitSourceAbort(SOURCE_ID);
+
+                  consoleWrite("Appending full header.");
+                  videoTag.webkitSourceAppend(SOURCE_ID, headers);
+
+                  consoleWrite("Appending enough clusters to trigger loadeddata.");
+                  appendCluster(videoTag, 0);
+                  appendCluster(videoTag, 1);
+
+                  var eventHandler = function(event)
+                  {
+                      var v = event.target;
+                      v.removeEventListener('loadeddata', eventHandler);
+                      runNextTestCase(v)
+                  };
+                  videoTag.addEventListener('loadeddata', eventHandler);
+                } catch (e) {
+                    consoleWrite("Unexpected exception " + e);
+                }
+            }
+
+            function abortDuringCluster(event)
+            {
+                var videoTag = event.target;
+
+                consoleWrite("Test aborting in the middle of a cluster.");
+                try {
+                  appendHeaders(videoTag);
+                  var cluster = getCluster(0);
+                  var partialCluster = cluster.subarray(0, cluster.length / 2);
+
+                  consoleWrite("Appending partial cluster.");
+                  videoTag.webkitSourceAppend(SOURCE_ID, partialCluster);
+
+                  consoleWrite("Aborting append.");
+                  videoTag.webkitSourceAbort(SOURCE_ID);
+
+                  consoleWrite("Appending full header.");
+                  videoTag.webkitSourceAppend(SOURCE_ID, cluster);
+
+                  consoleWrite("Appending enough clusters to trigger loadeddata.");
+                  appendCluster(videoTag, 2);
+
+                  var eventHandler = function(event)
+                  {
+                      var v = event.target;
+                      v.removeEventListener('loadedmetadata', eventHandler);
+                      runNextTestCase(v)
+                  };
+                  videoTag.addEventListener('loadedmetadata', eventHandler);
+                } catch (e) {
+                    consoleWrite("Unexpected exception " + e);
+                }
+            }
+
+            var testCases = [
+                abortDuringHeader,
+                abortDuringCluster,
+            ];
+
+            var testCaseIndex = 0;
+
+            function runNextTestCase(videoTag)
+            {
+                if (testCaseIndex >= testCases.length) {
+                    endTest();
+                    return;
+                }
+
+                var onOpenFunction = testCases[testCaseIndex];
+                var functionName = onOpenFunction.name;
+                testCaseIndex++;
+
+                var eventHandlerFunction = function (event)
+                {
+                    consoleWrite("");
+                    consoleWrite("running " + functionName);
+                    event.target.removeEventListener('webkitsourceopen', eventHandlerFunction);
+                    addSourceId(event.target);
+                    onOpenFunction(event);
+                };
+                videoTag.addEventListener('webkitsourceopen', eventHandlerFunction);
+                setSrcToMediaSourceURL(videoTag);
+            }
+
+            function onError(event)
+            {
+                var videoTag = event.target;
+
+                var errorString = "UNKNOWN";
+                switch(videoTag.error.code) {
+                    case MediaError.MEDIA_ERR_ABORTED:
+                        errorString = "MEDIA_ERR_ABORTED";
+                        break;
+                    case MediaError.MEDIA_ERR_NETWORK:
+                        errorString = "MEDIA_ERR_NETWORK";
+                        break;
+                    case MediaError.MEDIA_ERR_DECODE:
+                        errorString = "MEDIA_ERR_DECODE";
+                        break;
+                    case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
+                        errorString = "MEDIA_ERR_SRC_NOT_SUPPORTED";
+                        break;
+                }
+                consoleWrite("EVENT(error) : " + errorString);
+
+                if (videoTag.webkitSourceState != HTMLMediaElement.SOURCE_CLOSED) {
+                    consoleWrite("Unexpected source state. Expected SOURCE_CLOSED" +
+                                 " got " + getSourceStateName(videoTag.webkitSourceState));
+                }
+
+                runNextTestCase(videoTag);
+            }
+
+            function onLoad()
+            {
+                findMediaElement();
+
+                waitForEvent('loadstart');
+                waitForEvent('loadeddata');
+                waitForEvent('webkitsourceopen');
+                waitForEvent('playing');
+                waitForEvent('webkitsourceended');
+                waitForEvent('ended');
+                waitForEvent('emptied');
+
+                video.addEventListener('error', onError);
+
+                loadWebMData(function(success)
+                {
+                    if (!success) {
+                        failTest("Failed to load WebM data");
+                        return;
+                    }
+                    runNextTestCase(video);
+                });
+            }
+        </script>
+    </head>
+    <body onload="onLoad()">
+        <video> </video>
+        <p>Tests webkitSourceAbort() functionality</p>
+    </body>
+</html>
diff --git a/LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids-expected.txt b/LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids-expected.txt
new file mode 100644 (file)
index 0000000..8acf45a
--- /dev/null
@@ -0,0 +1,44 @@
+Tests webkitSourceAddId() & webkitSourceRemoveId() methods
+
+
+running testAddIdFailureCases
+Got expected exception Error: INVALID_STATE_ERR: DOM Exception 11
+EVENT(loadstart)
+EVENT(webkitsourceopen)
+Test empty ID case
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+Test adding the same ID again.
+Got expected exception Error: INVALID_STATE_ERR: DOM Exception 11
+Test empty type.
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+Test an unsupported type.
+Got expected exception Error: NOT_SUPPORTED_ERR: DOM Exception 9
+Test a supported type with an unsupported codec.
+Got expected exception Error: NOT_SUPPORTED_ERR: DOM Exception 9
+Test reaching sourceID limit.
+Got expected exception Error: INVALID_STATE_ERR: DOM Exception 11
+
+running testRemoveIdFailureCases
+EVENT(loadstart)
+EVENT(webkitsourceopen)
+Test empty ID case
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+Test removing an ID that was never added.
+Got expected exception Error: SYNTAX_ERR: DOM Exception 12
+Got expected exception Error: SYNTAX_ERR: DOM Exception 12
+Test adding the same ID again.
+Test that an ID can be removed while in the ended state.
+
+running testAppendFailureCases
+EVENT(loadstart)
+EVENT(webkitsourceopen)
+Test append with empty ID.
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+Test append with an invalid ID
+Got expected exception Error: SYNTAX_ERR: DOM Exception 12
+Test append with a null buffer.
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+Test a successful append.
+Got expected exception Error: SYNTAX_ERR: DOM Exception 12
+END OF TEST
+
diff --git a/LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html b/LayoutTests/http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html
new file mode 100644 (file)
index 0000000..4d51431
--- /dev/null
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="../../../media-resources/video-test.js"></script>
+        <script src="webm-media-source.js"></script>
+        <script>
+            var defaultSourceMimetype = "video/webm; codecs=\"vp8, vorbis\"";
+
+            function runOnSourceOpen(videoTag, onOpenFunction)
+            {
+                var eventHandlerFunction = function (event)
+                {
+                    event.target.removeEventListener('webkitsourceopen', eventHandlerFunction);
+                    onOpenFunction(event.target);
+                };
+                videoTag.addEventListener('webkitsourceopen', eventHandlerFunction);
+                setSrcToMediaSourceURL(videoTag);
+            }
+
+            // The index of the next cluster to send to appendData().
+            var nextClusterIndex = 0;
+
+            function expectExceptionOnAddId(videoTag, id, type, error)
+            {
+                try {
+                    videoTag.webkitSourceAddId(id, type);
+                    failTest("Expected an exception");
+                } catch (e) {
+                    if (!(e.code == error)) {
+                        failTest("Unexpected exception " + e);
+                        throw e;
+                    }
+                    consoleWrite("Got expected exception " + e);
+                }
+            }
+
+            function expectExceptionOnRemoveId(videoTag, id, error)
+            {
+                try {
+                    videoTag.webkitSourceRemoveId(id);
+                    failTest("Expected an exception");
+                } catch (e) {
+                    if (!(e.code == error)) {
+                        failTest("Unexpected exception " + e);
+                        throw e;
+                    }
+                    consoleWrite("Got expected exception " + e);
+                }
+            }
+
+            function expectExceptionOnAppend(videoTag, id, buf, error)
+            {
+                try {
+                    videoTag.webkitSourceAppend(id, buf);
+                    failTest("Expected an exception");
+                } catch (e) {
+                    if (!(e.code == error)) {
+                        failTest("Unexpected exception " + e);
+                        throw e;
+                    }
+                    consoleWrite("Got expected exception " + e);
+                }
+            }
+
+            function testAddIdFailureCases(videoTag)
+            {
+                // Test adding while closed.
+                expectExceptionOnAddId(videoTag, "123", defaultSourceMimetype, DOMException.INVALID_STATE_ERR);
+
+                runOnSourceOpen(videoTag, function (vt)
+                {
+                    consoleWrite("Test empty ID case");
+                    expectExceptionOnAddId(vt, "", defaultSourceMimetype, DOMException.INVALID_ACCESS_ERR);
+
+                    vt.webkitSourceAddId("123", defaultSourceMimetype);
+
+                    consoleWrite("Test adding the same ID again.");
+                    expectExceptionOnAddId(vt, "123", defaultSourceMimetype, DOMException.INVALID_STATE_ERR);
+                    vt.webkitSourceRemoveId("123");
+
+                    consoleWrite("Test empty type.");
+                    expectExceptionOnAddId(vt, "234", "", DOMException.INVALID_ACCESS_ERR);
+
+                    consoleWrite("Test an unsupported type.");
+                    expectExceptionOnAddId(vt, "234", "audio/x-unsupported-format", DOMException.NOT_SUPPORTED_ERR);
+
+                    consoleWrite("Test a supported type with an unsupported codec.");
+                    expectExceptionOnAddId(vt, "234", "video/webm; codecs=\"vp8, speex\"", DOMException.NOT_SUPPORTED_ERR);
+
+                    consoleWrite("Test reaching sourceID limit.");
+                    var reachedIdLimit = false;
+
+                    // The 20 here is an arbitrary upper limit to make sure the test terminates. This test
+                    // assumes that implementations won't support more than 20 sourceID's simultaneously.
+                    for (var i = 0; i < 20; ++i) {
+                        var sourceID = "sourceID-" + i;
+                        try {
+                            vt.webkitSourceAddId(sourceID, defaultSourceMimetype);
+                        } catch(e) {
+                            if (e.code != DOMException.QUOTA_EXCEEDED_ERR) {
+                                failTest("Unexpected exception " + e);
+                                throw e;
+                            }
+                            reachedIdLimit = true;
+                            break;
+                        }
+                    }
+
+                    if (!reachedIdLimit) {
+                        failTest("Failed to reach SourceID limit.");
+                        return;
+                    }
+
+                    // Test that SourceIDs can't be added while in the ended state.
+                    vt.webkitSourceEndOfStream(vt.EOS_NO_ERROR);
+                    expectExceptionOnAddId(vt, "123", defaultSourceMimetype, DOMException.INVALID_STATE_ERR);
+
+                    runNextTestCase(vt);
+                });
+            }
+
+            function testRemoveIdFailureCases(videoTag)
+            {
+                runOnSourceOpen(videoTag, function (vt)
+                {
+                    consoleWrite("Test empty ID case");
+                    expectExceptionOnRemoveId(vt, "", DOMException.INVALID_ACCESS_ERR);
+
+                    consoleWrite("Test removing an ID that was never added.");
+                    expectExceptionOnRemoveId(vt, "345", DOMException.SYNTAX_ERR);
+
+                    vt.webkitSourceAddId("123", defaultSourceMimetype);
+                    vt.webkitSourceRemoveId("123");
+                    expectExceptionOnRemoveId(vt, "123", DOMException.SYNTAX_ERR);
+
+                    consoleWrite("Test adding the same ID again.");
+                    vt.webkitSourceAddId("123", defaultSourceMimetype);
+
+                    consoleWrite("Test that an ID can be removed while in the ended state.");
+                    vt.webkitSourceEndOfStream(vt.EOS_NO_ERROR);
+                    vt.webkitSourceRemoveId("123");
+
+                    runNextTestCase(vt);
+                });
+            }
+
+            function testAppendFailureCases(videoTag)
+            {
+                runOnSourceOpen(videoTag, function (vt)
+                {
+                    vt.webkitSourceAddId("123", defaultSourceMimetype);
+
+                    consoleWrite("Test append with empty ID.");
+                    expectExceptionOnAppend(vt, "", getHeaders(), DOMException.INVALID_ACCESS_ERR);
+
+                    consoleWrite("Test append with an invalid ID");
+                    expectExceptionOnAppend(vt, "234", getHeaders(), DOMException.SYNTAX_ERR);
+
+                    consoleWrite("Test append with a null buffer.");
+                    expectExceptionOnAppend(vt, "123", null, DOMException.INVALID_ACCESS_ERR);
+
+                    consoleWrite("Test a successful append.");
+                    vt.webkitSourceAppend("123", getHeaders());
+
+                    vt.webkitSourceRemoveId("123");
+                    expectExceptionOnAppend(vt, "123", getCluster(0), DOMException.SYNTAX_ERR);
+
+                    runNextTestCase(vt);
+                });
+
+            }
+
+            var testCases = [
+                testAddIdFailureCases,
+                testRemoveIdFailureCases,
+                testAppendFailureCases,
+            ];
+
+            var testCaseIndex = 0;
+
+            function runNextTestCase(videoTag)
+            {
+                if (testCaseIndex >= testCases.length) {
+                    endTest();
+                    return;
+                }
+
+                var testCaseFunction = testCases[testCaseIndex];
+                testCaseIndex++;
+
+                consoleWrite("");
+                consoleWrite("running " + testCaseFunction.name);
+                testCaseFunction(videoTag);
+            }
+
+            function onLoad()
+            {
+                findMediaElement();
+
+                waitForEvent('loadstart');
+                waitForEvent('webkitsourceopen');
+
+                loadWebMData(function(success)
+                {
+                    if (!success) {
+                        failTest("Failed to load WebM data");
+                        return;
+                    }
+                    runNextTestCase(video);
+                });
+            }
+        </script>
+    </head>
+    <body onload="onLoad()">
+        <video> </video>
+        <p>Tests webkitSourceAddId() &amp; webkitSourceRemoveId() methods</p>
+    </body>
+</html>
index c620614..9135130 100644 (file)
                 var functionName = onOpenFunction.name;
                 testCaseIndex++;
 
-                var eventHandlerFunction = function (event) {
+                var eventHandlerFunction = function (event)
+                {
                     consoleWrite("");
                     consoleWrite("running " + functionName);
                     event.target.removeEventListener('webkitsourceopen', eventHandlerFunction);
+                    addSourceId(event.target);
                     onOpenFunction(event);
                 };
                 videoTag.addEventListener('webkitsourceopen', eventHandlerFunction);
index 818a65a..f62d055 100644 (file)
@@ -7,6 +7,7 @@
             function onSourceOpen(event) 
             {
                 var videoTag = event.target;
+                addSourceId(videoTag);
                 appendHeaders(videoTag);
                 appendUntilEndOfStream(videoTag, 0);
             }
@@ -24,7 +25,8 @@
 
                 video.addEventListener('webkitsourceopen', onSourceOpen);
 
-                loadWebMData(function(success) {
+                loadWebMData(function(success)
+                {
                     if (!success) {
                         failTest("Failed to load WebM data");
                         return;
index 8da48d8..d67992e 100644 (file)
@@ -15,6 +15,7 @@
                 if (firstOpen) {
                     firstOpen = false;
 
+                    addSourceId(videoTag);
                     appendHeaders(videoTag);
                     appendUntilEndOfStream(videoTag, 0);
                 }
@@ -68,7 +69,8 @@
                 video.addEventListener('seeking', onSeeking);
                 video.addEventListener('seeked', onSeeked);
 
-                loadWebMData(function(success) {
+                loadWebMData(function(success)
+                {
                     if (!success) {
                         failTest("Failed to load WebM data");
                         return;
index 420c77f..b1b37a3 100644 (file)
@@ -6,22 +6,22 @@
         <script>
             var seekTime = getClusterTimeForIndex(4);
 
-            function appendUntilEnd(videoTag, startIndex) 
+            function appendUntilEnd(videoTag, startIndex)
             {
                 for (var index = startIndex; index < getClusterCount(); index++) {
-                    videoTag.webkitSourceAppend(getCluster(index));
+                    appendCluster(videoTag, index);
                 }
 
                 videoTag.webkitSourceEndOfStream(HTMLMediaElement.EOS_NO_ERROR);
             }
 
-            function appendClustersForSeek(videoTag, clusterSeekTime) 
+            function appendClustersForSeek(videoTag, clusterSeekTime)
             {
                 var clusterIndex = getClusterIndexForTimestamp(clusterSeekTime);
                 var endIndex = clusterIndex + 2;
 
                 for (; (clusterIndex < endIndex) && (clusterIndex < getClusterCount()); clusterIndex++) {
-                    videoTag.webkitSourceAppend(getCluster(clusterIndex));
+                    appendCluster(videoTag, clusterIndex);
                 }
 
                 if (clusterIndex >= getClusterCount()) {
                 }
             }
 
-            function onSourceEvent(event) 
+            function onSourceEvent(event)
             {
                 consoleWrite('EVENT(' + event.type + ') : ' + getSourceStateName(event.target.webkitSourceState));
             }
 
-            function onFirstSourceOpen(event) 
+            function onFirstSourceOpen(event)
             {
                 // This is the first time the source is opened. We just want to append
                 // the headers and clusters until the end of stream. This is testing the
@@ -43,6 +43,7 @@
 
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceopen', onFirstSourceOpen);
+                addSourceId(videoTag);
 
                 expectSourceState(videoTag, HTMLMediaElement.SOURCE_OPEN);
 
@@ -54,7 +55,7 @@
                 appendUntilEnd(videoTag, 0);
             }
 
-            function onFirstSourceEnded(event) 
+            function onFirstSourceEnded(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceended', onFirstSourceEnded);
@@ -65,7 +66,8 @@
                 videoTag.addEventListener('playing', triggerFirstSeek);
             }
 
-            function triggerFirstSeek(event) {
+            function triggerFirstSeek(event)
+            {
                 var videoTag = event.target;
                 videoTag.removeEventListener('playing', triggerFirstSeek);
 
@@ -76,7 +78,7 @@
                 videoTag.currentTime = seekTime;
             }
 
-            function onFirstSeeking(event) 
+            function onFirstSeeking(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('seeking', onFirstSeeking);
@@ -87,7 +89,7 @@
                 appendClustersForSeek(videoTag, videoTag.currentTime);
             }
 
-            function onFirstSeeked(event) 
+            function onFirstSeeked(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('seeked', onFirstSeeked);
                 videoTag.currentTime = seekTime;
             }
 
-            function onSecondSeeking(event) 
+            function onSecondSeeking(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('seeking', onSecondSeeking);
                 appendClustersForSeek(videoTag, videoTag.currentTime);
             }
 
-            function onSecondSeeked(event) 
+            function onSecondSeeked(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('seeked', onSecondSeeked);
                 setSrcToMediaSourceURL(videoTag);
             }
 
-            function onFirstSourceClose(event) 
+            function onFirstSourceClose(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceclose', onFirstSourceClose);
                 videoTag.addEventListener('webkitsourceopen', onSecondSourceOpen);
             }
 
-            function onSecondSourceOpen(event) 
+            function onSecondSourceOpen(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceopen', onSecondSourceOpen);
+                addSourceId(videoTag);
 
                 expectSourceState(videoTag, HTMLMediaElement.SOURCE_OPEN);
 
                 appendUntilEnd(videoTag, 0);
             }
 
-            function onSecondSourceEnded(event) 
+            function onSecondSourceEnded(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceended', onSecondSourceEnded);
                 videoTag.addEventListener('playing', triggerSecondSourceClose);
             }
 
-            function triggerSecondSourceClose(event) {
+            function triggerSecondSourceClose(event)
+            {
                 var videoTag = event.target;
                 videoTag.removeEventListener('playing', triggerSecondSourceClose);
 
                 setSrcToMediaSourceURL(videoTag);
             }
 
-            function onSecondSourceClose(event) 
+            function onSecondSourceClose(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceclose', onSecondSourceClose);
                 videoTag.addEventListener('webkitsourceopen', onThirdSourceOpen);
             }
 
-            function onThirdSourceOpen(event) 
+            function onThirdSourceOpen(event)
             {
                 var videoTag = event.target;
                 videoTag.removeEventListener('webkitsourceopen', onThirdSourceOpen);
+                addSourceId(videoTag);
 
                 expectSourceState(videoTag, HTMLMediaElement.SOURCE_OPEN);
 
                 endTest();
             }
 
-            function onLoad() 
+            function onLoad()
             {
                 findMediaElement();
 
                 video.addEventListener('webkitsourceopen', onFirstSourceOpen);
 
                 logSourceState(video);
-                loadWebMData(function(success) {
+                loadWebMData(function(success)
+                {
                     if (!success) {
                         failTest("Failed to load WebM data");
                         return;
index 0a3dedb..4f1fe1d 100644 (file)
@@ -11,13 +11,15 @@ var clusterInfo = [
 
 var headerData = null;
 var clusterData = [];
+var SOURCE_ID = "sourceID1";
 
 function getData(url, callback)
 {
     var request = new XMLHttpRequest();
     request.open("GET", url, true);
     request.responseType = 'arraybuffer';
-    request.onload = function() {
+    request.onload = function()
+    {
         if (request.status != 200) {
             failTest("Unexpected status code " + request.status + " for " + url);
             callback(null);
@@ -29,8 +31,10 @@ function getData(url, callback)
     request.send();
 }
 
-function createClusterGetFunction(clusterIndex, callback) {
-    return function(data) {
+function createClusterGetFunction(clusterIndex, callback)
+{
+    return function(data)
+    {
         if (!data) {
             callback(false);
             return;
@@ -49,8 +53,10 @@ function createClusterGetFunction(clusterIndex, callback) {
     };
 }
 
-function loadWebMData(callback) {
-    getData("/media/resources/media-source/webm/test.webm.headers", function(data) {
+function loadWebMData(callback)
+{
+    getData("/media/resources/media-source/webm/test.webm.headers", function(data)
+    {
         if (!data) {
             callback(false);
             return;
@@ -108,6 +114,15 @@ function setSrcToMediaSourceURL(videoTag)
     videoTag.src = videoTag.webkitMediaSourceURL;
 }
 
+function addSourceId(videoTag)
+{
+    try {
+        videoTag.webkitSourceAddId(SOURCE_ID, 'video/webm; codecs="vp8, vorbis"');
+    } catch (e) {
+        failTest("Unexpected webkitSourceAddId() exception " + e);
+    }
+}
+
 function appendHeaders(videoTag)
 {
     if (!videoTag.webkitSourceAppend) {
@@ -115,7 +130,7 @@ function appendHeaders(videoTag)
         return;
     }
 
-    videoTag.webkitSourceAppend(getHeaders());
+    videoTag.webkitSourceAppend(SOURCE_ID, getHeaders());
 }
 
 function appendCluster(videoTag, clusterIndex)
@@ -132,7 +147,7 @@ function appendCluster(videoTag, clusterIndex)
 
     try {
         var cluster = getCluster(clusterIndex);
-        videoTag.webkitSourceAppend(cluster);
+        videoTag.webkitSourceAppend(SOURCE_ID, cluster);
     } catch (err) {
         consoleWrite(err);
     }
@@ -146,7 +161,7 @@ function appendUntilEndOfStream(videoTag, startIndex)
     }
 
     for (var clusterIndex = startIndex; clusterIndex < getClusterCount(); clusterIndex++) {
-        videoTag.webkitSourceAppend(getCluster(clusterIndex));
+        videoTag.webkitSourceAppend(SOURCE_ID, getCluster(clusterIndex));
     }
     videoTag.webkitSourceEndOfStream(videoTag.EOS_NO_ERROR);
 }
index 90cc8c2..3fc99f7 100644 (file)
@@ -1,3 +1,50 @@
+2012-05-18  Aaron Colwell  <acolwell@chromium.org>
+
+        Update Media Source implementation to reflect changes in 0.5 spec.
+        https://bugs.webkit.org/show_bug.cgi?id=83607
+
+        Reviewed by Darin Fisher.
+
+        Tests: http/tests/media/media-source/video-media-source-event-attributes.html
+               http/tests/media/media-source/webm/video-media-source-abort.html
+               http/tests/media/media-source/webm/video-media-source-add-and-remove-ids.html
+
+        * html/HTMLAttributeNames.in: Add onwebkitsourcexxx event attribute names.
+        * html/HTMLMediaElement.cpp: 
+        (WebCore::HTMLMediaElement::parseAttribute): Add code to register event listeners for the onwebkitsourcexxx attributes.
+        (WebCore::HTMLMediaElement::pauseInternal):
+        (WebCore):
+        (WebCore::HTMLMediaElement::webkitSourceAddId): Changed code to pass the parsed type & codecs to m_player instead of the whole MIME-type string. This was done to allow some simple parsing & validation code to be shared in WebKit instead of reimplemented in every port.
+        (WebCore::HTMLMediaElement::webkitSourceBuffered): New method in the v0.5 spec. It validates the parameters & that it is being called in the correct state. If so it forwards the call to m_player.
+        (WebCore::HTMLMediaElement::webkitSourceAppend): Adds id parameter to match v0.5 signature. Minor logic changes that avoid calling m_player for 0 byte arrays and fixes exceptions reported for a null array and a parse error so they match the spec text.
+        (WebCore::HTMLMediaElement::webkitSourceAbort): New method in the v0.5 spec. It validates the parameter & that it is being called in athe correct state. If so it forwards the call to m_player.
+        * html/HTMLMediaElement.h:
+        (HTMLMediaElement): Added new methods, signature updates, and onwebitsourcexxx event listener attributes.
+        * html/HTMLMediaElement.idl: Added new methods, signature updates, and onwebitsourcexxx event listener attributes.
+        * platform/ContentType.cpp:
+        (WebCore::ContentType::codecs): A new method that parses the codecs parameter into a vector of codec strings.
+        (WebCore):
+        * platform/ContentType.h: 
+        (ContentType):
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::NullMediaPlayerPrivate::sourceAddId): Update the signature to take the parsed type and codecs vector.
+        (WebCore::NullMediaPlayerPrivate::sourceBuffered): Add default implementation for this new method.
+        (WebCore::NullMediaPlayerPrivate::sourceRemoveId): Remove id parameter name to match other methods.
+        (WebCore::NullMediaPlayerPrivate::sourceAppend): Updated signature to include id parameter.
+        (WebCore::NullMediaPlayerPrivate::sourceAbort): Add default implementation for this new method.
+        (WebCore::NullMediaPlayerPrivate::sourceEndOfStream): Remove parameter name to match other methods.
+        (WebCore::MediaPlayer::sourceAddId): Update the signature to take the parsed type and codecs vector.
+        (WebCore):
+        (WebCore::MediaPlayer::sourceBuffered): Add implementation for new method that forwards the call to the MediaPlayerPrivate interface.
+        (WebCore::MediaPlayer::sourceAppend): Update method to forward the new id parameter in the signature.
+        (WebCore::MediaPlayer::sourceAbort): Add implementation for new method that forwards the call to the MediaPlayerPrivate interface.
+        * platform/graphics/MediaPlayer.h: Update interface to include new methods and updated signatures.
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::sourceAddId): Update the signature to take the parsed type and codecs vector.
+        (WebCore::MediaPlayerPrivateInterface::sourceBuffered): Add default implementation for new method.
+        (WebCore::MediaPlayerPrivateInterface::sourceAppend): Update method to forward the new id parameter in the signature.
+        (WebCore::MediaPlayerPrivateInterface::sourceAbort): Add default implementation for new method.
+
 2012-05-18  Levi Weintraub  <leviw@chromium.org>
 
         Revert to float method of selection rect alignment for line box tree
index 888e195..a6e75bc 100644 (file)
@@ -253,6 +253,9 @@ onwebkitkeyadded
 onwebkitkeyerror
 onwebkitkeymessage
 onwebkitneedkey
+onwebkitsourceclose
+onwebkitsourceended
+onwebkitsourceopen
 onwebkittransitionend
 open
 optimum
index 33e1205..3c4079a 100644 (file)
@@ -424,6 +424,14 @@ void HTMLMediaElement::parseAttribute(const Attribute& attribute)
         setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attribute));
     else if (attribute.name() == onwebkitendfullscreenAttr)
         setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attribute));
+#if ENABLE(MEDIA_SOURCE)
+    else if (attribute.name() == onwebkitsourcecloseAttr)
+        setAttributeEventListener(eventNames().webkitsourcecloseEvent, createAttributeEventListener(this, attribute));
+    else if (attribute.name() == onwebkitsourceendedAttr)
+        setAttributeEventListener(eventNames().webkitsourceendedEvent, createAttributeEventListener(this, attribute));
+    else if (attribute.name() == onwebkitsourceopenAttr)
+        setAttributeEventListener(eventNames().webkitsourceopenEvent, createAttributeEventListener(this, attribute));
+#endif
     else
         HTMLElement::parseAttribute(attribute);
 }
@@ -2291,7 +2299,7 @@ void HTMLMediaElement::pauseInternal()
         scheduleLoad(MediaResource);
 
     m_autoplaying = false;
-    
+
     if (!m_paused) {
         m_paused = true;
         scheduleTimeupdateEvent(false);
@@ -2302,6 +2310,7 @@ void HTMLMediaElement::pauseInternal()
 }
 
 #if ENABLE(MEDIA_SOURCE)
+
 void HTMLMediaElement::webkitSourceAddId(const String& id, const String& type, ExceptionCode& ec)
 {
     if (id.isNull() || id.isEmpty()) {
@@ -2324,7 +2333,15 @@ void HTMLMediaElement::webkitSourceAddId(const String& id, const String& type, E
         return;
     }
 
-    switch (m_player->sourceAddId(id, type)) {
+    ContentType contentType(type);
+    Vector<String> codecs = contentType.codecs();
+
+    if (!codecs.size()) {
+        ec = NOT_SUPPORTED_ERR;
+        return;
+    }
+
+    switch (m_player->sourceAddId(id, contentType.type(), codecs)) {
     case MediaPlayer::Ok:
         m_sourceIDs.add(id);
         return;
@@ -2355,19 +2372,57 @@ void HTMLMediaElement::webkitSourceRemoveId(const String& id, ExceptionCode& ec)
     m_sourceIDs.remove(id);
 }
 
-void HTMLMediaElement::webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode& ec)
+PassRefPtr<TimeRanges> HTMLMediaElement::webkitSourceBuffered(const String& id, ExceptionCode& ec)
+{
+    if (!isValidSourceId(id, ec))
+        return 0;
+
+    if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
+        ec = INVALID_STATE_ERR;
+        return 0;
+    }
+
+    return m_player->sourceBuffered(id);
+}
+
+void HTMLMediaElement::webkitSourceAppend(const String& id, PassRefPtr<Uint8Array> data, ExceptionCode& ec)
 {
+    if (!isValidSourceId(id, ec))
+        return;
+
     if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
         ec = INVALID_STATE_ERR;
         return;
     }
 
-    if (!data.get() || !m_player->sourceAppend(data->data(), data->length())) {
+    if (!data.get()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+
+    if (!data->length())
+        return;
+
+    if (!m_player->sourceAppend(id, data->data(), data->length())) {
         ec = SYNTAX_ERR;
         return;
     }
 }
 
+void HTMLMediaElement::webkitSourceAbort(const String& id, ExceptionCode& ec)
+{
+    if (!isValidSourceId(id, ec))
+        return;
+
+    if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
+        ec = INVALID_STATE_ERR;
+        return;
+    }
+
+    if (!m_player->sourceAbort(id))
+        ASSERT_NOT_REACHED();
+}
+
 void HTMLMediaElement::webkitSourceEndOfStream(unsigned short status, ExceptionCode& ec)
 {
     if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
index 671b364..d8cb8c5 100644 (file)
@@ -174,12 +174,18 @@ public:
     const KURL& webkitMediaSourceURL() const { return m_mediaSourceURL; }
     void webkitSourceAddId(const String&, const String&, ExceptionCode&);
     void webkitSourceRemoveId(const String&, ExceptionCode&);
-    void webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode&);
+    PassRefPtr<TimeRanges> webkitSourceBuffered(const String&, ExceptionCode&);
+    void webkitSourceAppend(const String&, PassRefPtr<Uint8Array> data, ExceptionCode&);
+    void webkitSourceAbort(const String&, ExceptionCode&);
     enum EndOfStreamStatus { EOS_NO_ERROR, EOS_NETWORK_ERR, EOS_DECODE_ERR };
     void webkitSourceEndOfStream(unsigned short, ExceptionCode&);
     enum SourceState { SOURCE_CLOSED, SOURCE_OPEN, SOURCE_ENDED };
     SourceState webkitSourceState() const;
     void setSourceState(SourceState);
+
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitsourceopen);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitsourceended);
+    DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitsourceclose);
 #endif 
 
 #if ENABLE(ENCRYPTED_MEDIA)
index 87854be..ac0fa46 100644 (file)
@@ -99,8 +99,18 @@ module html {
     // URL passed to src attribute to enable the media source logic.
     readonly attribute [V8EnabledAtRuntime=mediaSource, URL] DOMString webkitMediaSourceURL;
 
-    // Appends media to to the source.
-    [V8EnabledAtRuntime=mediaSource] void webkitSourceAppend(in Uint8Array data) raises (DOMException);
+    // Manages IDs for appending media to the source.
+    [V8EnabledAtRuntime=mediaSource] void webkitSourceAddId(in DOMString id, in DOMString type) raises (DOMException);
+    [V8EnabledAtRuntime=mediaSource] void webkitSourceRemoveId(in DOMString id) raises (DOMException);
+
+    // Returns the time ranges buffered for a specific source ID.
+    [V8EnabledAtRuntime=mediaSource] TimeRanges webkitSourceBuffered(in DOMString id) raises (DOMException);
+    
+    // Appends segment data.
+    [V8EnabledAtRuntime=mediaSource] void webkitSourceAppend(in DOMString id, in Uint8Array data) raises (DOMException);
+
+    // Aborts the current segment.
+    [V8EnabledAtRuntime=mediaSource] void webkitSourceAbort(in DOMString id) raises (DOMException);
 
     // Signals the end of stream.
     [V8EnabledAtRuntime=mediaSource] const unsigned short EOS_NO_ERROR = 0; // End of stream reached w/o error.
@@ -113,6 +123,10 @@ module html {
     [V8EnabledAtRuntime=mediaSource] const unsigned short SOURCE_OPEN = 1;
     [V8EnabledAtRuntime=mediaSource] const unsigned short SOURCE_ENDED = 2;
     readonly attribute [V8EnabledAtRuntime=mediaSource] unsigned short webkitSourceState;
+
+    attribute [V8EnabledAtRuntime=mediaSource] EventListener onwebkitsourceopen;
+    attribute [V8EnabledAtRuntime=mediaSource] EventListener onwebkitsourceended;
+    attribute [V8EnabledAtRuntime=mediaSource] EventListener onwebkitsourceclose;
 #endif
 
 #if defined(ENABLE_ENCRYPTED_MEDIA) && ENABLE_ENCRYPTED_MEDIA
index b02bc7c..575e480 100644 (file)
@@ -76,4 +76,19 @@ String ContentType::type() const
     return strippedType;
 }
 
+Vector<String> ContentType::codecs() const
+{
+    String codecsParameter = parameter("codecs");
+
+    if (codecsParameter.isEmpty())
+        return Vector<String>();
+
+    Vector<String> codecs;
+    codecsParameter.split(",", codecs);
+    for (size_t i = 0; i < codecs.size(); ++i)
+        codecs[i] = codecs[i].simplifyWhiteSpace();
+
+    return codecs;
+}
+
 } // namespace WebCore
index bf8fc4c..4583232 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 
         String parameter (const String& parameterName) const;
         String type() const;
+        Vector<String> codecs() const;
         const String& raw() const { return m_type; }
     private:
         String m_type;
index 253d9a2..af30eb1 100644 (file)
@@ -149,10 +149,12 @@ public:
     virtual bool hasSingleSecurityOrigin() const { return true; }
 
 #if ENABLE(MEDIA_SOURCE)
-    virtual MediaPlayer::AddIdStatus sourceAddId(const String& id, const String& type) { return MediaPlayer::NotSupported; }
-    virtual bool sourceRemoveId(const String& id) { return false; }
-    virtual bool sourceAppend(const unsigned char*, unsigned) { return false; }
-    virtual void sourceEndOfStream(MediaPlayer::EndOfStreamStatus status) { }
+    virtual MediaPlayer::AddIdStatus sourceAddId(const String& id, const String& type, const Vector<String>& codecs) { return MediaPlayer::NotSupported; }
+    virtual PassRefPtr<TimeRanges> sourceBuffered(const String&) { return TimeRanges::create(); }
+    virtual bool sourceRemoveId(const String&) { return false; }
+    virtual bool sourceAppend(const String&, const unsigned char*, unsigned) { return false; }
+    virtual bool sourceAbort(const String&) { return false; }
+    virtual void sourceEndOfStream(MediaPlayer::EndOfStreamStatus) { }
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA)
@@ -461,9 +463,14 @@ void MediaPlayer::pause()
 
 #if ENABLE(MEDIA_SOURCE)
 
-MediaPlayer::AddIdStatus MediaPlayer::sourceAddId(const String& id, const String& type)
+MediaPlayer::AddIdStatus MediaPlayer::sourceAddId(const String& id, const String& type, const Vector<String>& codecs)
 {
-    return m_private->sourceAddId(id, type);
+    return m_private->sourceAddId(id, type, codecs);
+}
+
+PassRefPtr<TimeRanges> MediaPlayer::sourceBuffered(const String& id)
+{
+    return m_private->sourceBuffered(id);
 }
 
 bool MediaPlayer::sourceRemoveId(const String& id)
@@ -471,9 +478,14 @@ bool MediaPlayer::sourceRemoveId(const String& id)
     return m_private->sourceRemoveId(id);
 }
 
-bool MediaPlayer::sourceAppend(const unsigned char* data, unsigned length)
+bool MediaPlayer::sourceAppend(const String& id, const unsigned char* data, unsigned length)
+{
+    return m_private->sourceAppend(id, data, length);
+}
+
+bool MediaPlayer::sourceAbort(const String& id)
 {
-    return m_private->sourceAppend(data, length);
+    return m_private->sourceAbort(id);
 }
 
 void MediaPlayer::sourceEndOfStream(MediaPlayer::EndOfStreamStatus status)
index 044d153..a03ade7 100644 (file)
@@ -238,9 +238,11 @@ public:
 
 #if ENABLE(MEDIA_SOURCE)
     enum AddIdStatus { Ok, NotSupported, ReachedIdLimit };
-    AddIdStatus sourceAddId(const String& id, const String& type);
+    AddIdStatus sourceAddId(const String& id, const String& type, const Vector<String>& codecs);
     bool sourceRemoveId(const String& id);
-    bool sourceAppend(const unsigned char* data, unsigned length);
+    PassRefPtr<TimeRanges> sourceBuffered(const String& id);
+    bool sourceAppend(const String& id, const unsigned char* data, unsigned length);
+    bool sourceAbort(const String& id);
     enum EndOfStreamStatus { EosNoError, EosNetworkError, EosDecodeError };
     void sourceEndOfStream(EndOfStreamStatus);
 #endif
index c3a3dfa..f76d49e 100644 (file)
@@ -165,9 +165,11 @@ public:
 #endif
 
 #if ENABLE(MEDIA_SOURCE)
-    virtual MediaPlayer::AddIdStatus sourceAddId(const String&, const String&) { return MediaPlayer::NotSupported; }
-    virtual bool sourceRemoveId(const String&) { return false; }
-    virtual bool sourceAppend(const unsigned char*, unsigned) { return false; }
+    virtual MediaPlayer::AddIdStatus sourceAddId(const String& id, const String& type, const Vector<String>& codecs) { return MediaPlayer::NotSupported; }
+    virtual PassRefPtr<TimeRanges> sourceBuffered(const String& id) { return TimeRanges::create(); }
+    virtual bool sourceRemoveId(const String& id) { return false; }
+    virtual bool sourceAppend(const String& id, const unsigned char* data, unsigned length) { return false; }
+    virtual bool sourceAbort(const String& id) { return false; }
     virtual void sourceEndOfStream(MediaPlayer::EndOfStreamStatus) { };
 #endif
 
index 86aca81..7cdece1 100644 (file)
@@ -1,3 +1,24 @@
+2012-05-18  Aaron Colwell  <acolwell@chromium.org>
+
+        Update Media Source implementation to reflect changes in 0.5 spec.
+        https://bugs.webkit.org/show_bug.cgi?id=83607
+
+        Reviewed by Darin Fisher.
+
+        * public/WebMediaPlayer.h:
+        (WebKit::WebMediaPlayer::sourceAddId): Update the signature to take the parsed type and codecs vector.
+        (WebKit::WebMediaPlayer::sourceBuffered): Add default implementation for new method.
+        (WebKit::WebMediaPlayer::sourceAppend): Update signature to include id parameter.
+        (WebKit::WebMediaPlayer::sourceAbort): Add default implementation for new method.
+        * src/WebMediaPlayerClientImpl.cpp:
+        (WebKit::WebMediaPlayerClientImpl::sourceAddId): Update the signature to take the parsed type and codecs vector.
+        (WebKit::WebMediaPlayerClientImpl::sourceBuffered): Added implementation for new method. Converts WebKit::WebTimeRanges to WebCore::TimeRanges.
+        (WebKit):
+        (WebKit::WebMediaPlayerClientImpl::sourceAppend): Update signature to include id parameter.
+        (WebKit::WebMediaPlayerClientImpl::sourceAbort): Implementation for new method.
+        * src/WebMediaPlayerClientImpl.h:
+        (WebMediaPlayerClientImpl): Added declarations for new methods and updated signatures for existing ones.
+
 2012-05-18  Shawn Singh  <shawnsingh@chromium.org>
 
         [chromium] add back-face visibility check for renderSurfaces
index 670630f..3312f5f 100644 (file)
@@ -173,9 +173,12 @@ public:
 
     virtual WebAudioSourceProvider* audioSourceProvider() { return 0; }
 
-    virtual AddIdStatus sourceAddId(const WebString& id, const WebString& type) { return AddIdStatusNotSupported; }
+    virtual AddIdStatus sourceAddId(const WebString& id, const WebString& type,
+                                    const WebVector<WebString>& codecs) { return AddIdStatusNotSupported; }
     virtual bool sourceRemoveId(const WebString& id) { return false; }
-    virtual bool sourceAppend(const unsigned char* data, unsigned length) { return false; }
+    virtual WebTimeRanges sourceBuffered(const WebString& id) { return WebTimeRanges(); };
+    virtual bool sourceAppend(const WebString& id, const unsigned char* data, unsigned length) { return false; }
+    virtual bool sourceAbort(const WebString& id) { return false; }
     virtual void sourceEndOfStream(EndOfStreamStatus)  { }
 
     // Returns whether keySystem is supported. If true, the result will be
index 950068d..2411d9c 100644 (file)
@@ -359,12 +359,12 @@ bool WebMediaPlayerClientImpl::canEnterFullscreen() const
 #endif
 
 #if ENABLE(MEDIA_SOURCE)
-WebCore::MediaPlayer::AddIdStatus WebMediaPlayerClientImpl::sourceAddId(const String& id, const String& type)
+WebCore::MediaPlayer::AddIdStatus WebMediaPlayerClientImpl::sourceAddId(const String& id, const String& type, const Vector<String>& codecs)
 {
     if (!m_webMediaPlayer)
         return WebCore::MediaPlayer::NotSupported;
 
-    return static_cast<WebCore::MediaPlayer::AddIdStatus>(m_webMediaPlayer->sourceAddId(id, type));
+    return static_cast<WebCore::MediaPlayer::AddIdStatus>(m_webMediaPlayer->sourceAddId(id, type, codecs));
 }
 
 bool WebMediaPlayerClientImpl::sourceRemoveId(const String& id)
@@ -375,13 +375,33 @@ bool WebMediaPlayerClientImpl::sourceRemoveId(const String& id)
     return m_webMediaPlayer->sourceRemoveId(id);
 }
 
-bool WebMediaPlayerClientImpl::sourceAppend(const unsigned char* data, unsigned length)
+PassRefPtr<TimeRanges> WebMediaPlayerClientImpl::sourceBuffered(const String& id)
+{
+    if (!m_webMediaPlayer)
+        return TimeRanges::create();
+
+    WebTimeRanges webRanges = m_webMediaPlayer->sourceBuffered(id);
+    RefPtr<TimeRanges> ranges = TimeRanges::create();
+    for (size_t i = 0; i < webRanges.size(); ++i)
+        ranges->add(webRanges[i].start, webRanges[i].end);
+    return ranges.release();
+}
+
+bool WebMediaPlayerClientImpl::sourceAppend(const String& id, const unsigned char* data, unsigned length)
 {
     if (m_webMediaPlayer)
-        return m_webMediaPlayer->sourceAppend(data, length);
+        return m_webMediaPlayer->sourceAppend(id, data, length);
     return false;
 }
 
+bool WebMediaPlayerClientImpl::sourceAbort(const String& id)
+{
+    if (!m_webMediaPlayer)
+        return false;
+
+    return m_webMediaPlayer->sourceAbort(id);
+}
+
 void WebMediaPlayerClientImpl::sourceEndOfStream(WebCore::MediaPlayer::EndOfStreamStatus status)
 {
     if (m_webMediaPlayer)
index e06d3e5..72170f7 100644 (file)
@@ -154,9 +154,11 @@ public:
 #endif
 
 #if ENABLE(MEDIA_SOURCE)
-    virtual WebCore::MediaPlayer::AddIdStatus sourceAddId(const String& id, const String& type);
+    virtual WebCore::MediaPlayer::AddIdStatus sourceAddId(const String& id, const String& type, const Vector<String>& codecs);
     virtual bool sourceRemoveId(const String&);
-    virtual bool sourceAppend(const unsigned char* data, unsigned length);
+    virtual WTF::PassRefPtr<WebCore::TimeRanges> sourceBuffered(const String&);
+    virtual bool sourceAppend(const String&, const unsigned char* data, unsigned length);
+    virtual bool sourceAbort(const String&);
     virtual void sourceEndOfStream(WebCore::MediaPlayer::EndOfStreamStatus);
 #endif