[MSE] Bring end-of-stream algorithm section up to current spec.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2013 06:43:27 +0000 (06:43 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 8 Dec 2013 06:43:27 +0000 (06:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=125270

Reviewed by Darin Adler.

Source/WebCore:

Test: media/media-source/media-source-end-of-stream.html

Separate the "endOfStream()" method from the "end of stream algorithm".

* Modules/mediasource/MediaSource.cpp:
(WebCore::SourceBufferIsUpdating): Added predicate function.
(WebCore::MediaSource::endOfStream): Call streamEndedWithError().
(WebCore::MediaSource::streamEndedWithError): Added.
* Modules/mediasource/MediaSource.h:
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::appendBufferTimerFired): Call streamEndedWithError().
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Ditto.
* Modules/mediasource/SourceBuffer.h:
* html/HTMLMediaElement.cpp:
(HTMLMediaElement::mediaLoadingFailedFatally): Renamed from mediaEngineError.
(HTMLMediaElement::mediaLoadingFailed): Call renamed method.
* html/HTMLMediaElement.h:
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
(WebCore::MediaSourcePrivateAVFObjC::markEndOfStream): Set load state to Loaded.
* platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:
(WebCore::MockMediaPlayerMediaSource::setNetworkState): Simple setter.
* platform/mock/mediasource/MockMediaPlayerMediaSource.h:
* platform/mock/mediasource/MockMediaSourcePrivate.cpp:
(WebCore::MockMediaSourcePrivate::MockMediaSourcePrivate): Set the intitial duration to NaN.
(WebCore::MockMediaSourcePrivate::markEndOfStream): Set load state to Loaded.

LayoutTests:

* media/media-source/media-source-end-of-stream-expected.txt: Added.
* media/media-source/media-source-end-of-stream.html: Added.

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

14 files changed:
LayoutTests/ChangeLog
LayoutTests/media/media-source/media-source-end-of-stream-expected.txt [new file with mode: 0644]
LayoutTests/media/media-source/media-source-end-of-stream.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/SourceBuffer.cpp
Source/WebCore/Modules/mediasource/SourceBuffer.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm
Source/WebCore/platform/mock/mediasource/MockMediaPlayerMediaSource.cpp
Source/WebCore/platform/mock/mediasource/MockMediaPlayerMediaSource.h
Source/WebCore/platform/mock/mediasource/MockMediaSourcePrivate.cpp

index 0e43bd7..3f07b9e 100644 (file)
@@ -1,3 +1,13 @@
+2013-12-05  Jer Noble  <jer.noble@apple.com>
+
+        [MSE] Bring end-of-stream algorithm section up to current spec.
+        https://bugs.webkit.org/show_bug.cgi?id=125270
+
+        Reviewed by Darin Adler.
+
+        * media/media-source/media-source-end-of-stream-expected.txt: Added.
+        * media/media-source/media-source-end-of-stream.html: Added.
+
 2013-12-06  Filip Pizlo  <fpizlo@apple.com>
 
         FTL should support all of Branch/LogicalNot
diff --git a/LayoutTests/media/media-source/media-source-end-of-stream-expected.txt b/LayoutTests/media/media-source/media-source-end-of-stream-expected.txt
new file mode 100644 (file)
index 0000000..d8b99a8
--- /dev/null
@@ -0,0 +1,37 @@
+
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(source.endOfStream("network"))
+EVENT(error)
+EXPECTED (video.error.code == '4') OK
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(source.endOfStream("decode"))
+EVENT(error)
+EXPECTED (video.error.code == '4') OK
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(updateend)
+RUN(source.endOfStream("network"))
+EVENT(error)
+EXPECTED (video.error.code == '2') OK
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(updateend)
+RUN(source.endOfStream("decode"))
+EVENT(error)
+EXPECTED (video.error.code == '3') OK
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(updateend)
+RUN(source.endOfStream(""))
+EVENT(suspend)
+EXPECTED (video.networkState == '1') OK
+END OF TEST
+
diff --git a/LayoutTests/media/media-source/media-source-end-of-stream.html b/LayoutTests/media/media-source/media-source-end-of-stream.html
new file mode 100644 (file)
index 0000000..9fb40bc
--- /dev/null
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>mock-media-source</title>
+    <script src="mock-media-source.js"></script>
+    <script src="../video-test.js"></script>
+    <script>
+    var source;
+    var sourceBuffer;
+    var initSegment;
+
+    if (window.internals)
+        internals.initializeMockMediaSource();
+
+    function runTest() {
+        findMediaElement();
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen1, false, true);
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen1() {
+        waitForEventOnce('error', videoError1);
+        run('source.endOfStream("network")');
+    }
+
+    function videoError1() {
+        testExpected('video.error.code', MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen2, false, true);
+        run('video.src = URL.createObjectURL(source)');
+    }    
+
+    function sourceOpen2() {
+        waitForEventOnce('error', videoError2);
+        run('source.endOfStream("decode")');
+    }
+
+    function videoError2() {
+        testExpected('video.error.code', MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen3, false, true);
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen3() {
+        run('sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer, 'updateend', updateEnd3);
+        initSegment = makeAInit(100, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+    }
+
+    function updateEnd3() {
+        waitForEventOnce('error', videoError3);
+        run('source.endOfStream("network")');
+    }
+
+    function videoError3() {
+        testExpected('video.error.code', MediaError.MEDIA_ERR_NETWORK);
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen4, false, true);
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen4() {
+        run('sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer, 'updateend', updateEnd4);
+        initSegment = makeAInit(100, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+    }
+
+    function updateEnd4() {
+        waitForEventOnce('error', videoError4);
+        run('source.endOfStream("decode")');
+    }
+
+    function videoError4() {
+        testExpected('video.error.code', MediaError.MEDIA_ERR_DECODE);
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen5, false, true);
+        run('video.src = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen5() {
+        run('sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer, 'updateend', updateEnd5);
+        initSegment = makeAInit(100, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+    }
+
+    function updateEnd5() {
+        waitForEventOnce('suspend', videoSuspend5);
+        run('source.endOfStream("")');
+    }
+
+    function videoSuspend5() {
+        testExpected('video.networkState', HTMLMediaElement.NETWORK_IDLE);
+
+        endTest();
+    }
+
+    </script>
+</head>
+<body onload="runTest()">
+    <video></video>
+</body>
+</html>
index f4f04a4..90b3f60 100644 (file)
@@ -1,3 +1,36 @@
+2013-12-05  Jer Noble  <jer.noble@apple.com>
+
+        [MSE] Bring end-of-stream algorithm section up to current spec.
+        https://bugs.webkit.org/show_bug.cgi?id=125270
+
+        Reviewed by Darin Adler.
+
+        Test: media/media-source/media-source-end-of-stream.html
+
+        Separate the "endOfStream()" method from the "end of stream algorithm".
+
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::SourceBufferIsUpdating): Added predicate function.
+        (WebCore::MediaSource::endOfStream): Call streamEndedWithError().
+        (WebCore::MediaSource::streamEndedWithError): Added.
+        * Modules/mediasource/MediaSource.h:
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::appendBufferTimerFired): Call streamEndedWithError().
+        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Ditto.
+        * Modules/mediasource/SourceBuffer.h:
+        * html/HTMLMediaElement.cpp:
+        (HTMLMediaElement::mediaLoadingFailedFatally): Renamed from mediaEngineError.
+        (HTMLMediaElement::mediaLoadingFailed): Call renamed method.
+        * html/HTMLMediaElement.h:
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
+        (WebCore::MediaSourcePrivateAVFObjC::markEndOfStream): Set load state to Loaded.
+        * platform/mock/mediasource/MockMediaPlayerMediaSource.cpp:
+        (WebCore::MockMediaPlayerMediaSource::setNetworkState): Simple setter.
+        * platform/mock/mediasource/MockMediaPlayerMediaSource.h:
+        * platform/mock/mediasource/MockMediaSourcePrivate.cpp:
+        (WebCore::MockMediaSourcePrivate::MockMediaSourcePrivate): Set the intitial duration to NaN.
+        (WebCore::MockMediaSourcePrivate::markEndOfStream): Set load state to Loaded.
+
 2013-12-04  Jer Noble  <jer.noble@apple.com>
 
         [MSE][Mac] Crash when removing MediaSource from HTMLMediaElement.
index cb15521..7a32946 100644 (file)
@@ -43,6 +43,7 @@
 #include "HTMLMediaElement.h"
 #include "Logging.h"
 #include "MIMETypeRegistry.h"
+#include "MediaError.h"
 #include "MediaPlayer.h"
 #include "MediaSourceRegistry.h"
 #include "SourceBufferPrivate.h"
@@ -303,12 +304,14 @@ void MediaSource::setReadyState(const AtomicString& state)
     onReadyStateChange(oldState, state);
 }
 
-void MediaSource::endOfStream(const AtomicString& error, ExceptionCode& ec)
+static bool SourceBufferIsUpdating(RefPtr<SourceBuffer>& sourceBuffer)
 {
-    DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
-    DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
+    return sourceBuffer->updating();
+}
 
-    // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-endofstream
+void MediaSource::endOfStream(const AtomicString& error, ExceptionCode& ec)
+{
+    // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-endOfStream-void-EndOfStreamError-error
     // 1. If the readyState attribute is not in the "open" state then throw an
     // INVALID_STATE_ERR exception and abort these steps.
     if (!isOpen()) {
@@ -316,22 +319,75 @@ void MediaSource::endOfStream(const AtomicString& error, ExceptionCode& ec)
         return;
     }
 
-    MediaSourcePrivate::EndOfStreamStatus eosStatus = MediaSourcePrivate::EosNoError;
-
-    if (error.isNull() || error.isEmpty())
-        eosStatus = MediaSourcePrivate::EosNoError;
-    else if (error == network)
-        eosStatus = MediaSourcePrivate::EosNetworkError;
-    else if (error == decode)
-        eosStatus = MediaSourcePrivate::EosDecodeError;
-    else {
-        ec = INVALID_ACCESS_ERR;
+    // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
+    // INVALID_STATE_ERR exception and abort these steps.
+    if (std::any_of(m_sourceBuffers->begin(), m_sourceBuffers->end(), SourceBufferIsUpdating)) {
+        ec = INVALID_STATE_ERR;
         return;
     }
 
-    // 2. Change the readyState attribute value to "ended".
+    // 3. Run the end of stream algorithm with the error parameter set to error.
+    streamEndedWithError(error, ec);
+}
+
+void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode& ec)
+{
+    DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
+    DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
+
+    // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm
+    // 1. Change the readyState attribute value to "ended".
+    // 2. Queue a task to fire a simple event named sourceended at the MediaSource.
     setReadyState(endedKeyword());
-    m_private->markEndOfStream(eosStatus);
+
+    // 3.
+    if (error.isEmpty()) {
+        // ↳ If error is not set, is null, or is an empty string
+        // 1. Run the duration change algorithm with new duration set to the highest end timestamp
+        // across all SourceBuffer objects in sourceBuffers.
+        MediaTime maxEndTimestamp;
+        for (auto it = m_sourceBuffers->begin(), end = m_sourceBuffers->end(); it != end; ++it)
+            maxEndTimestamp = std::max((*it)->highestPresentationEndTimestamp(), maxEndTimestamp);
+        m_private->setDuration(maxEndTimestamp.toDouble());
+
+        // 2. Notify the media element that it now has all of the media data.
+        m_private->markEndOfStream(MediaSourcePrivate::EosNoError);
+    } else if (error == network) {
+        // ↳ If error is set to "network"
+        ASSERT(m_mediaElement);
+        if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
+            //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
+            //    Run the "If the media data cannot be fetched at all, due to network errors, causing
+            //    the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
+            //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
+            m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError);
+        } else {
+            //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
+            //    Run the "If the connection is interrupted after some media data has been received, causing the
+            //    user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
+            //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
+            m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
+        }
+    } else if (error == decode) {
+        // ↳ If error is set to "decode"
+        ASSERT(m_mediaElement);
+        if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
+            //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
+            //    Run the "If the media data can be fetched but is found by inspection to be in an unsupported
+            //    format, or can otherwise not be rendered at all" steps of the resource fetch algorithm.
+            //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
+            m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError);
+        } else {
+            //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
+            //    Run the media data is corrupted steps of the resource fetch algorithm.
+            //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
+            m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError);
+        }
+    } else {
+        // ↳ Otherwise
+        //   Throw an INVALID_ACCESS_ERR exception.
+        ec = INVALID_ACCESS_ERR;
+    }
 }
 
 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
index f13a399..3f25d41 100644 (file)
@@ -64,6 +64,7 @@ public:
     void openIfInEndedState();
     bool isOpen() const;
     void sourceBufferDidChangeAcitveState(SourceBuffer*, bool);
+    void streamEndedWithError(const AtomicString& error, ExceptionCode&);
 
     // HTMLMediaSource
     virtual bool attachToElement(HTMLMediaElement*) OVERRIDE;
index aefd3b9..895aab4 100644 (file)
@@ -429,7 +429,7 @@ void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>*)
         // 2. If the input buffer contains bytes that violate the SourceBuffer byte stream format specification,
         // then run the end of stream algorithm with the error parameter set to "decode" and abort this algorithm.
         if (result == SourceBufferPrivate::ParsingFailed) {
-            m_source->endOfStream(decodeError(), IgnorableExceptionCode());
+            m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
             break;
         }
 
@@ -806,7 +806,7 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
             // abort these steps.
             MediaTime presentationStartTime = MediaTime::zeroTime();
             if (presentationTimestamp < presentationStartTime || decodeTimestamp < presentationStartTime) {
-                m_source->endOfStream(decodeError(), IgnorableExceptionCode());
+                m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
                 return;
             }
         }
@@ -985,6 +985,11 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
         if (trackBuffer.highestPresentationTimestamp.isInvalid() || frameEndTimestamp > trackBuffer.highestPresentationTimestamp)
             trackBuffer.highestPresentationTimestamp = frameEndTimestamp;
 
+        // 1.21 If highest presentation end timestamp is unset or frame end timestamp is greater than highest
+        // presentation end timestamp, then set highest presentation end timestamp equal to frame end timestamp.
+        if (m_highestPresentationEndTimestamp.isInvalid() || frameEndTimestamp > m_highestPresentationEndTimestamp)
+            m_highestPresentationEndTimestamp = frameEndTimestamp;
+
         m_buffered->add(presentationTimestamp.toDouble(), (presentationTimestamp + frameDuration + microsecond).toDouble());
 
         break;
index 396bde4..7563a34 100644 (file)
@@ -75,6 +75,7 @@ public:
 
     void abortIfUpdating();
     void removedFromMediaSource();
+    const MediaTime& highestPresentationEndTimestamp() const { return m_highestPresentationEndTimestamp; }
 
 #if ENABLE(VIDEO_TRACK)
     VideoTrackList* videoTracks();
index 06b222f..7448bd9 100644 (file)
@@ -1669,9 +1669,9 @@ void HTMLMediaElement::noneSupported()
         renderer()->updateFromElement();
 }
 
-void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
+void HTMLMediaElement::mediaLoadingFailedFatally(MediaPlayer::NetworkState error)
 {
-    LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
+    LOG(Media, "HTMLMediaElement::mediaLoadingFailedFatally(%d)", static_cast<int>(error));
 
     // 1 - The user agent should cancel the fetching process.
     stopPeriodicTimers();
@@ -1679,7 +1679,12 @@ void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
 
     // 2 - Set the error attribute to a new MediaError object whose code attribute is 
     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
-    m_error = err;
+    if (error == MediaPlayer::NetworkError)
+        m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
+    else if (error == MediaPlayer::DecodeError)
+        m_error = MediaError::create(MediaError::MEDIA_ERR_DECODE);
+    else
+        ASSERT_NOT_REACHED();
 
     // 3 - Queue a task to fire a simple event named error at the media element.
     scheduleEvent(eventNames().errorEvent);
@@ -1783,10 +1788,8 @@ void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
         return;
     }
     
-    if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA)
-        mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
-    else if (error == MediaPlayer::DecodeError)
-        mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
+    if ((error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) || error == MediaPlayer::DecodeError)
+        mediaLoadingFailedFatally(error);
     else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
         noneSupported();
     
index 1c7dd05..4418256 100644 (file)
@@ -395,6 +395,9 @@ public:
 
     unsigned long long fileSize() const;
 
+    void mediaLoadingFailed(MediaPlayer::NetworkState);
+    void mediaLoadingFailedFatally(MediaPlayer::NetworkState);
+
 protected:
     HTMLMediaElement(const QualifiedName&, Document&, bool);
     virtual ~HTMLMediaElement();
@@ -563,15 +566,12 @@ private:
     void clearMediaPlayer(int flags);
     bool havePotentialSourceChild();
     void noneSupported();
-    void mediaEngineError(PassRefPtr<MediaError> err);
     void cancelPendingEventsAndCallbacks();
     void waitForSourceChange();
     void prepareToPlay();
 
     URL selectNextSourceChild(ContentType*, String* keySystem, InvalidURLAction);
 
-    void mediaLoadingFailed(MediaPlayer::NetworkState);
-
 #if ENABLE(VIDEO_TRACK)
     void updateActiveTextTrackCues(double);
     HTMLTrackElement* showingTrackWithSameKind(HTMLTrackElement*) const;
index c2d3e38..2109eaa 100644 (file)
@@ -101,9 +101,10 @@ void MediaSourcePrivateAVFObjC::setDuration(double duration)
     m_duration = duration;
 }
 
-void MediaSourcePrivateAVFObjC::markEndOfStream(EndOfStreamStatus
+void MediaSourcePrivateAVFObjC::markEndOfStream(EndOfStreamStatus status)
 {
-    // FIXME(125159): implement markEndOfStream()
+    if (status == EosNoError)
+        m_player->setNetworkState(MediaPlayer::Loaded);
     m_isEnded = true;
 }
 
index 587e173..2f23048 100644 (file)
@@ -236,6 +236,15 @@ void MockMediaPlayerMediaSource::setReadyState(MediaPlayer::ReadyState readyStat
     m_player->readyStateChanged();
 }
 
+void MockMediaPlayerMediaSource::setNetworkState(MediaPlayer::NetworkState networkState)
+{
+    if (networkState == m_networkState)
+        return;
+
+    m_networkState = networkState;
+    m_player->networkStateChanged();
+}
+
 }
 
 #endif
index 6c86e4c..d1cf8b5 100644 (file)
@@ -51,6 +51,7 @@ public:
 
     virtual MediaPlayer::ReadyState readyState() const OVERRIDE;
     void setReadyState(MediaPlayer::ReadyState);
+    void setNetworkState(MediaPlayer::NetworkState);
 
 private:
     MockMediaPlayerMediaSource(MediaPlayer*);
index e9f5208..4b2a35f 100644 (file)
@@ -42,7 +42,7 @@ RefPtr<MockMediaSourcePrivate> MockMediaSourcePrivate::create(MockMediaPlayerMed
 
 MockMediaSourcePrivate::MockMediaSourcePrivate(MockMediaPlayerMediaSource* parent)
     : m_player(parent)
-    , m_duration(0)
+    , m_duration(std::numeric_limits<float>::quiet_NaN())
     , m_isEnded(false)
 {
 }
@@ -96,7 +96,8 @@ void MockMediaSourcePrivate::setDuration(double duration)
 
 void MockMediaSourcePrivate::markEndOfStream(EndOfStreamStatus status)
 {
-    UNUSED_PARAM(status);
+    if (status == EosNoError)
+        m_player->setNetworkState(MediaPlayer::Loaded);
     m_isEnded = true;
 }