Add the duration attribute to MediaSource
authorvrk@chromium.org <vrk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Sep 2012 01:58:35 +0000 (01:58 +0000)
committervrk@chromium.org <vrk@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Sep 2012 01:58:35 +0000 (01:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=95149

Reviewed by Eric Carlson.

Add support for the duration attribute recently added to the MediaSource spec.
http://dev.w3.org/html5/spec/media-elements.html#dom-media-duration

Source/WebCore:

Test: http/tests/media/media-source/video-media-source-duration-changed.html

* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::duration): Added duration method.
(WebCore):
(WebCore::MediaSource::setDuration): Added duration setter.
* Modules/mediasource/MediaSource.h:
(MediaSource):
* Modules/mediasource/MediaSource.idl:
* platform/graphics/MediaPlayer.cpp:
(WebCore::NullMediaPlayerPrivate::sourceSetDuration): Add empty definition.
(WebCore::MediaPlayer::sourceSetDuration): Forward call to m_private.
(WebCore):
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::sourceSetDuration): Add empty definition.

Source/WebKit/chromium:

* public/WebMediaPlayer.h:
(WebKit::WebMediaPlayer::sourceSetDuration): Add empty definition.
* src/WebMediaPlayerClientImpl.cpp:
(WebKit::WebMediaPlayerClientImpl::sourceSetDuration): Forward call to m_webMediaPlayer.
(WebKit):
* src/WebMediaPlayerClientImpl.h:
(WebMediaPlayerClientImpl):

LayoutTests:

* http/tests/media/media-source/media-source.js:
(MediaSourceTest.SegmentHelper): Add parameter to specify whether full file should be loaded.
(MediaSourceTest.SegmentHelper.prototype.appendAllMediaSegments): Added method to append all file segments.
(MediaSourceTest.roundedEquals_): Added to see if values are equivalent after rounding.
(MediaSourceTest.expectDuration): Added to check for expected duration.
(MediaSourceTest.expectBufferedRange): Added to check for the expected buffered attribute value.
* http/tests/media/media-source/video-media-source-duration-changed-expected.txt: Added.
* http/tests/media/media-source/video-media-source-duration-changed.html: Added.

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

15 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/media/media-source/media-source.js
LayoutTests/http/tests/media/media-source/video-media-source-duration-changed-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/media/media-source/video-media-source-duration-changed.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediasource/MediaSource.cpp
Source/WebCore/Modules/mediasource/MediaSource.h
Source/WebCore/Modules/mediasource/MediaSource.idl
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 2416954..0f031f3 100644 (file)
@@ -1,3 +1,22 @@
+2012-09-05  Victoria Kirst  <vrk@chromium.org>
+
+        Add the duration attribute to MediaSource
+        https://bugs.webkit.org/show_bug.cgi?id=95149
+
+        Reviewed by Eric Carlson.
+
+        Add support for the duration attribute recently added to the MediaSource spec.
+        http://dev.w3.org/html5/spec/media-elements.html#dom-media-duration
+
+        * http/tests/media/media-source/media-source.js:
+        (MediaSourceTest.SegmentHelper): Add parameter to specify whether full file should be loaded.
+        (MediaSourceTest.SegmentHelper.prototype.appendAllMediaSegments): Added method to append all file segments.
+        (MediaSourceTest.roundedEquals_): Added to see if values are equivalent after rounding.
+        (MediaSourceTest.expectDuration): Added to check for expected duration.
+        (MediaSourceTest.expectBufferedRange): Added to check for the expected buffered attribute value.
+        * http/tests/media/media-source/video-media-source-duration-changed-expected.txt: Added.
+        * http/tests/media/media-source/video-media-source-duration-changed.html: Added.
+
 2012-09-05  Kenichi Ishibashi  <bashi@chromium.org>
 
         [Chromium] unreviewed gardening after r127564
index 8c706ab..036d81a 100644 (file)
@@ -1,7 +1,7 @@
 var MediaSourceTest = {};
 var mediaSource = new WebKitMediaSource();
 
-MediaSourceTest.SegmentHelper = function(segmentInfo)
+MediaSourceTest.SegmentHelper = function(segmentInfo, fullDuration)
 {
     this.MediaSegmentsToLoad = 0;
     this.sourceBuffer = null;
@@ -14,7 +14,7 @@ MediaSourceTest.SegmentHelper = function(segmentInfo)
     // Limit how many media segments we load based on time.
     var maxDuration = 3;
     for (var i in this.segmentInfo.media) {
-        if (this.segmentInfo.media[i].timecode > maxDuration)
+        if (!fullDuration && this.segmentInfo.media[i].timecode > maxDuration)
             break;
         this.MediaSegmentsToLoad++;
     }
@@ -137,6 +137,12 @@ MediaSourceTest.SegmentHelper.prototype.appendMediaSegment = function(index)
     this.sourceBuffer.append(this.mediaSegments[index]);
 };
 
+MediaSourceTest.SegmentHelper.prototype.appendAllMediaSegments = function()
+{
+    for (var i = 0; i < this.mediaSegments.length; i++)
+        this.appendMediaSegment(i);
+};
+
 MediaSourceTest.SegmentHelper.prototype.appendUntilEndOfStream = function(startIndex)
 {
     if (!this.sourceBuffer) {
@@ -308,6 +314,37 @@ MediaSourceTest.expectReadyState = function(videoTag, expected)
     }
 };
 
+MediaSourceTest.roundedEquals_ = function(expected, actual)
+{
+    return expected.toFixed(3) == actual.toFixed(3);
+};
+
+MediaSourceTest.expectDuration = function(videoTag, mediaSource, expected)
+{
+    if (!this.roundedEquals_(expected, videoTag.duration))
+        failTest("Unexpected duration. Expected " + expected + " got " + videoTag.duration);
+    if (!this.roundedEquals_(expected, mediaSource.duration))
+        failTest("Unexpected duration. Expected " + expected + " got " + mediaSource.duration);
+};
+
+MediaSourceTest.expectBufferedRange = function(sourceBuffer, expected)
+{
+    if (sourceBuffer.buffered.length != expected.length)
+        failTest("Unexpected number of buffered regions. Expected " + expected.length + " got " + sourceBuffer.buffered.length);
+
+    for (var i = 0; i < expected.length; i++) {
+        var expectedStart = expected[i][0];
+        var expectedEnd = expected[i][1];
+        var actualStart = sourceBuffer.buffered.start(i);
+        var actualEnd = sourceBuffer.buffered.end(i);
+        if (!this.roundedEquals_(expectedStart, actualStart) ||
+            !this.roundedEquals_(expectedEnd, actualEnd)) {
+            failTest("Unexpected buffered region. Expected (" + expectedStart + ", " + expectedEnd + ") got ("
+                     + actualStart + ", " + actualEnd +")");
+        }
+    }
+};
+
 MediaSourceTest.getReadyStateName = function(state)
 {
     var stateName = "UNKNOWN";
diff --git a/LayoutTests/http/tests/media/media-source/video-media-source-duration-changed-expected.txt b/LayoutTests/http/tests/media/media-source/video-media-source-duration-changed-expected.txt
new file mode 100644 (file)
index 0000000..ba64038
--- /dev/null
@@ -0,0 +1,31 @@
+Tests duration attribute on MediaSource object
+
+Test setting the duration when closed.
+Got expected exception Error: INVALID_STATE_ERR: DOM Exception 11
+Test getting the duration when closed.
+
+running testExpectExceptionOnNegativeDuration
+EVENT(webkitsourceopen)
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+
+running testExpectExceptionOnNaNDuration
+EVENT(webkitsourceopen)
+Got expected exception Error: INVALID_ACCESS_ERR: DOM Exception 15
+
+running testDurationChange
+EVENT(webkitsourceopen)
+EVENT(loadedmetadata)
+EVENT(durationchange)
+
+running testDurationChangeThenOverwriteDuration
+EVENT(webkitsourceopen)
+EVENT(loadedmetadata)
+EVENT(durationchange)
+EVENT(durationchange)
+
+running testDurationChangeTruncates
+EVENT(webkitsourceopen)
+EVENT(loadedmetadata)
+EVENT(durationchange)
+END OF TEST
+
diff --git a/LayoutTests/http/tests/media/media-source/video-media-source-duration-changed.html b/LayoutTests/http/tests/media/media-source/video-media-source-duration-changed.html
new file mode 100644 (file)
index 0000000..79d64fb
--- /dev/null
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="/media-resources/video-test.js"></script>
+        <script src="/media/resources/media-source/webm/segment-info.js"></script>
+        <script src="media-source.js"></script>
+        <script>
+            var segmentHelper = new MediaSourceTest.SegmentHelper(WebMSegmentInfo.testWebM, true);
+            var defaultSourceMimetype = segmentHelper.segmentInfo.type;
+
+            function expectExceptionOnSetDuration(value, error)
+            {
+                try {
+                    mediaSource.duration = value;
+                    failTest("Expected an exception");
+                } catch (e) {
+                    if (!(e.code == error)) {
+                        failTest("Unexpected exception " + e);
+                        throw e;
+                    }
+                    consoleWrite("Got expected exception " + e);
+                }
+            }
+
+            function testExpectExceptionOnSetDurationWhenClosed(mediaSource)
+            {
+                consoleWrite("Test setting the duration when closed.");
+                expectExceptionOnSetDuration(10, DOMException.INVALID_STATE_ERR);
+            }
+
+            function testExpectNaNOnGetDurationWhenClosed(mediaSource)
+            {
+                consoleWrite("Test getting the duration when closed.");
+                if (!isNaN(mediaSource.duration))
+                    failTest("Unexpected duration value. Expected NaN got " + mediaSource.duration);
+            }
+
+            function testExpectExceptionOnNegativeDuration(runNextTestCase, videoTag)
+            {
+                expectExceptionOnSetDuration(-1, DOMException.INVALID_ACCESS_ERR);
+                runNextTestCase();
+            }
+
+            function testExpectExceptionOnNaNDuration(runNextTestCase, videoTag)
+            {
+                expectExceptionOnSetDuration(NaN, DOMException.INVALID_ACCESS_ERR);
+                runNextTestCase();
+            }
+
+            function testDurationChange(runNextTestCase, videoTag)
+            {
+                segmentHelper.addSourceBuffer();
+                segmentHelper.appendInitSegment();
+
+                waitForEventOnce('loadedmetadata', function() {
+                    waitForEventOnce('durationchange', function() {
+                        MediaSourceTest.expectDuration(videoTag, mediaSource, 10);
+                        var expected_buffered = [];
+                        for (var i = 0; i < mediaSource.sourceBuffers.length; i++)
+                          MediaSourceTest.expectBufferedRange(mediaSource.sourceBuffers[i], expected_buffered);
+                        runNextTestCase();
+                    }, false, false, videoTag);
+                    mediaSource.duration = 10;
+
+                }, false, false, videoTag);
+            }
+
+            function testDurationChangeThenOverwriteDuration(runNextTestCase, videoTag)
+            {
+                segmentHelper.addSourceBuffer();
+                segmentHelper.appendInitSegment();
+
+                waitForEventOnce('loadedmetadata', function() {
+                    waitForEventOnce('durationchange', function() {
+                        MediaSourceTest.expectDuration(videoTag, mediaSource, 1);
+
+                        waitForEventOnce('durationchange', function() {
+                            MediaSourceTest.expectDuration(videoTag, mediaSource, 1.593);
+                            var expected_buffered = [[0, 1.593]];
+                            for (var i = 0; i < mediaSource.sourceBuffers.length; i++)
+                                MediaSourceTest.expectBufferedRange(mediaSource.sourceBuffers[i], expected_buffered);
+                            runNextTestCase();
+                        }, false, false, videoTag);
+                        var segments = segmentHelper.segmentInfo.media;
+                        for (var i = 0; i < segments.length; i++) {
+                            segmentHelper.appendMediaSegment(i);
+                            // Stop appending after exceeding 1s.
+                            if (segments[i].timecode > 1)
+                                break;
+                        }
+
+                    }, false, false, videoTag);
+                    mediaSource.duration = 1;
+
+                }, false, false, videoTag);
+            }
+
+            function testDurationChangeTruncates(runNextTestCase, videoTag)
+            {
+                segmentHelper.addSourceBuffer();
+                segmentHelper.appendInitSegment();
+
+                waitForEventOnce('loadedmetadata', function() {
+                    segmentHelper.appendAllMediaSegments();
+                    var segmentDuration = segmentHelper.segmentInfo.duration;
+                    MediaSourceTest.expectDuration(videoTag, mediaSource, segmentDuration);
+
+                    waitForEventOnce('durationchange', function() {
+                        MediaSourceTest.expectDuration(videoTag, mediaSource, 2);
+                        var expected_buffered = [[0,2]];
+                        for (var i = 0; i < mediaSource.sourceBuffers.length; i++)
+                          MediaSourceTest.expectBufferedRange(mediaSource.sourceBuffers[i], expected_buffered);
+                        runNextTestCase();
+                    }, false, false, videoTag);
+
+                    mediaSource.duration = 2;
+
+                }, false, false, videoTag);
+            }
+
+            function onLoad()
+            {
+                findMediaElement();
+
+                mediaSource = new WebKitMediaSource();
+                waitForEvent('webkitsourceopen', "", false, false, mediaSource);
+
+                testExpectExceptionOnSetDurationWhenClosed(mediaSource);
+                testExpectNaNOnGetDurationWhenClosed(mediaSource);
+
+                segmentHelper.init(video, function(success) {
+                    if (!success) {
+                        failTest("Failed to load segment data");
+                        return;
+                    }
+
+                    var testCases = [
+                        testExpectExceptionOnNegativeDuration,
+                        testExpectExceptionOnNaNDuration,
+                        testDurationChange,
+                        testDurationChangeThenOverwriteDuration,
+                        testDurationChangeTruncates,
+                    ];
+                    MediaSourceTest.startSourceOpenTesting(video, testCases);
+                });
+            }
+        </script>
+    </head>
+    <body onload="onLoad()">
+        <video> </video>
+        <p>Tests duration attribute on MediaSource object</p>
+    </body>
+</html>
index 6c8b556..1735875 100644 (file)
@@ -1,3 +1,30 @@
+2012-09-05  Victoria Kirst  <vrk@chromium.org>
+
+        Add the duration attribute to MediaSource
+        https://bugs.webkit.org/show_bug.cgi?id=95149
+
+        Reviewed by Eric Carlson.
+
+        Add support for the duration attribute recently added to the MediaSource spec.
+        http://dev.w3.org/html5/spec/media-elements.html#dom-media-duration
+
+        Test: http/tests/media/media-source/video-media-source-duration-changed.html
+
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::duration): Added duration method.
+        (WebCore):
+        (WebCore::MediaSource::setDuration): Added duration setter.
+        * Modules/mediasource/MediaSource.h:
+        (MediaSource):
+        * Modules/mediasource/MediaSource.idl:
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::NullMediaPlayerPrivate::sourceSetDuration): Add empty definition.
+        (WebCore::MediaPlayer::sourceSetDuration): Forward call to m_private.
+        (WebCore):
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::sourceSetDuration): Add empty definition.
+
 2012-09-05  Mihai Parparita  <mihaip@chromium.org>
 
         [Chromium] history.{push,replace}State should no longer be V8EnabledAtRuntime
index 34fa15c..97a5738 100644 (file)
@@ -84,6 +84,24 @@ SourceBufferList* MediaSource::activeSourceBuffers()
     return m_activeSourceBuffers.get();
 }
 
+double MediaSource::duration() const
+{
+    return m_readyState == closedKeyword() ? std::numeric_limits<float>::quiet_NaN() : m_player->duration();
+}
+
+void MediaSource::setDuration(double duration, ExceptionCode& ec)
+{
+    if (duration < 0.0 || isnan(duration)) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    if (m_readyState != openKeyword()) {
+        ec = INVALID_STATE_ERR;
+        return;
+    }
+    m_player->sourceSetDuration(duration);
+}
+
 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
 {
     // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-addsourcebuffer
index 8bede03..2ed2bf2 100644 (file)
@@ -54,6 +54,9 @@ public:
     SourceBufferList* sourceBuffers();
     SourceBufferList* activeSourceBuffers();
 
+    double duration() const;
+    void setDuration(double, ExceptionCode&);
+
     SourceBuffer* addSourceBuffer(const String& type, ExceptionCode&);
     void removeSourceBuffer(SourceBuffer*, ExceptionCode&);
 
index 25f7dec..fff6a6f 100644 (file)
@@ -43,6 +43,8 @@ module html {
         // Subset of sourceBuffers that provide data for the selected/enabled tracks.
         readonly attribute SourceBufferList activeSourceBuffers;
 
+        attribute double duration setter raises (DOMException);
+
         SourceBuffer addSourceBuffer(in DOMString type) raises (DOMException);
         void removeSourceBuffer(in SourceBuffer buffer) raises (DOMException);
 
index 3122335..b11610f 100644 (file)
@@ -154,6 +154,7 @@ public:
     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 sourceSetDuration(double) { }
     virtual void sourceEndOfStream(MediaPlayer::EndOfStreamStatus) { }
     virtual bool sourceSetTimestampOffset(const String&, double) { return false; }
 #endif
@@ -490,6 +491,11 @@ bool MediaPlayer::sourceAbort(const String& id)
     return m_private->sourceAbort(id);
 }
 
+void MediaPlayer::sourceSetDuration(double duration)
+{
+    m_private->sourceSetDuration(duration);
+}
+
 void MediaPlayer::sourceEndOfStream(MediaPlayer::EndOfStreamStatus status)
 {
     return m_private->sourceEndOfStream(status);
index 2e060b4..9a2c6b7 100644 (file)
@@ -266,6 +266,7 @@ public:
     bool sourceRemoveId(const String& id);
     PassRefPtr<TimeRanges> sourceBuffered(const String& id);
     bool sourceAppend(const String& id, const unsigned char* data, unsigned length);
+    void sourceSetDuration(double);
     bool sourceAbort(const String& id);
     enum EndOfStreamStatus { EosNoError, EosNetworkError, EosDecodeError };
     void sourceEndOfStream(EndOfStreamStatus);
index 28d2d9e..87851e4 100644 (file)
@@ -173,6 +173,7 @@ public:
     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 sourceSetDuration(double) { }
     virtual void sourceEndOfStream(MediaPlayer::EndOfStreamStatus) { };
     virtual bool sourceSetTimestampOffset(const String& id, double offset) { return false; }
 #endif
index 5f1c89b..622201f 100644 (file)
@@ -1,3 +1,21 @@
+2012-09-05  Victoria Kirst  <vrk@chromium.org>
+
+        Add the duration attribute to MediaSource
+        https://bugs.webkit.org/show_bug.cgi?id=95149
+
+        Reviewed by Eric Carlson.
+
+        Add support for the duration attribute recently added to the MediaSource spec.
+        http://dev.w3.org/html5/spec/media-elements.html#dom-media-duration
+
+        * public/WebMediaPlayer.h:
+        (WebKit::WebMediaPlayer::sourceSetDuration): Add empty definition.
+        * src/WebMediaPlayerClientImpl.cpp:
+        (WebKit::WebMediaPlayerClientImpl::sourceSetDuration): Forward call to m_webMediaPlayer.
+        (WebKit):
+        * src/WebMediaPlayerClientImpl.h:
+        (WebMediaPlayerClientImpl):
+
 2012-09-05  Mihai Parparita  <mihaip@chromium.org>
 
         [Chromium] history.{push,replace}State should no longer be V8EnabledAtRuntime
index ed62754..e0395cc 100644 (file)
@@ -186,6 +186,7 @@ public:
     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 sourceSetDuration(double) { }
     virtual void sourceEndOfStream(EndOfStreamStatus)  { }
     virtual bool sourceSetTimestampOffset(const WebString& id, double offset) { return false; }
 
index 0881f09..13d0032 100644 (file)
@@ -443,6 +443,12 @@ bool WebMediaPlayerClientImpl::sourceAbort(const String& id)
     return m_webMediaPlayer->sourceAbort(id);
 }
 
+void WebMediaPlayerClientImpl::sourceSetDuration(double duration)
+{
+    if (m_webMediaPlayer)
+        m_webMediaPlayer->sourceSetDuration(duration);
+}
+
 void WebMediaPlayerClientImpl::sourceEndOfStream(WebCore::MediaPlayer::EndOfStreamStatus status)
 {
     if (m_webMediaPlayer)
index bd2907e..dd170e5 100644 (file)
@@ -163,6 +163,7 @@ public:
     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 sourceSetDuration(double);
     virtual void sourceEndOfStream(WebCore::MediaPlayer::EndOfStreamStatus);
     virtual bool sourceSetTimestampOffset(const String&, double offset);
 #endif