Title: [171624] trunk
Revision
171624
Author
[email protected]
Date
2014-07-25 15:39:06 -0700 (Fri, 25 Jul 2014)

Log Message

[MSE] Playback stalls & readyState drops to HAVE_CURRENT_DATA at end of stream with unbalanced buffered SourceBuffers
https://bugs.webkit.org/show_bug.cgi?id=135291
<rdar://problem/17715503>

Reviewed by Sam Weinig.

Source/WebCore:
Test: media/media-source/media-source-end-of-stream-buffered.html

When determining the correct ReadyState for the MediaSource in monitorSourceBuffers(), use the same
definition of "buffered" as is used in the calculation of HTMLMediaElement.buffered and in the
Stream Ended algorithm. Namely, when the stream has ended, treat each SourceBuffer as if its last
buffered range extends to the duration of the stream. This allows playback to continue through to
the duration without stalling due to monitorSourceBuffers().

* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::bufferedAccountingForEndOfStream): Added; extends the last range in buffered
    to MediaSource::duration() if the MediaSource is ended.
(WebCore::SourceBuffer::hasCurrentTime): Uses bufferedAccountingForEndOfStream().
(WebCore::SourceBuffer::hasFutureTime): Ditto.
(WebCore::SourceBuffer::canPlayThrough): Ditto.
* Modules/mediasource/SourceBuffer.h:

Add a convenience method for determining whether the MediaSource has ended:
* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::isEnded):
* Modules/mediasource/MediaSource.h:

Add start() and end() methods that don't take a (usually ignored) isValid inout parameter. Add duration()
and maximumBufferedTime() convenience methods:
* platform/graphics/PlatformTimeRanges.cpp:
(WebCore::PlatformTimeRanges::start):
(WebCore::PlatformTimeRanges::end):
(WebCore::PlatformTimeRanges::duration):
(WebCore::PlatformTimeRanges::maximumBufferedTime):
* platform/graphics/PlatformTimeRanges.h:

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (171623 => 171624)


--- trunk/LayoutTests/ChangeLog	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/LayoutTests/ChangeLog	2014-07-25 22:39:06 UTC (rev 171624)
@@ -1,3 +1,14 @@
+2014-07-25  Jer Noble  <[email protected]>
+
+        [MSE] Playback stalls & readyState drops to HAVE_CURRENT_DATA at end of stream with unbalanced buffered SourceBuffers
+        https://bugs.webkit.org/show_bug.cgi?id=135291
+        <rdar://problem/17715503>
+
+        Reviewed by Sam Weinig.
+
+        * media/media-source/media-source-end-of-stream-buffered-expected.txt: Added.
+        * media/media-source/media-source-end-of-stream-buffered.html: Added.
+
 2014-07-25  Filip Pizlo  <[email protected]>
 
         Merge r169795, r169819, r169864, r169902, r169949, r169950, r170016, r170017, r170060, r170064 from ftlopt.

Added: trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered-expected.txt (0 => 171624)


--- trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered-expected.txt	2014-07-25 22:39:06 UTC (rev 171624)
@@ -0,0 +1,16 @@
+
+RUN(video.src = ""
+EVENT(sourceopen)
+RUN(sourceBuffer1 = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer1.appendBuffer(mediaSegment))
+EVENT(updateend)
+RUN(sourceBuffer2 = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer2.appendBuffer(mediaSegment))
+EVENT(updateend)
+RUN(source.endOfStream())
+RUN(video.currentTime = 5)
+EVENT(seeked)
+RUN(video.play())
+EVENT(playing)
+END OF TEST
+

Added: trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered.html (0 => 171624)


--- trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered.html	2014-07-25 22:39:06 UTC (rev 171624)
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>mock-media-source</title>
+    <script src=""
+    <script src=""
+    <script>
+    var source;
+    var sourceBuffer1;
+    var sourceBuffer2;
+    var initSegment;
+    var mediaSegment;
+
+    if (window.internals)
+        internals.initializeMockMediaSource();
+
+    function runTest() {
+        findMediaElement();
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen, false, true);
+        run('video.src = ""
+    }
+
+    function sourceOpen() {
+        run('sourceBuffer1 = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer1, 'updateend', updateEndInit1);
+        mediaSegment = concatenateSamples([
+            makeAInit(10, [makeATrack(1, 'mock', TRACK_KIND.AUDIO)]), 
+            makeASample(0, 0, 10, 1, SAMPLE_FLAG.SYNC, 0),
+        ]);
+        run('sourceBuffer1.appendBuffer(mediaSegment)');
+    }
+
+    function updateEndInit1() {
+        run('sourceBuffer2 = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer2, 'updateend', updateEndInit2);
+        mediaSegment = concatenateSamples([
+            makeAInit(10, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]),
+            makeASample(0, 0, 5, 1, SAMPLE_FLAG.SYNC, 0),
+        ]);
+        run('sourceBuffer2.appendBuffer(mediaSegment)');
+    }
+
+    function updateEndInit2() {
+        run('source.endOfStream()');
+        waitForEvent('seeked', seeked);
+        run('video.currentTime = 5');
+    }
+
+    function seeked() {
+        run('video.play()');
+        waitForEventAndEnd('playing');
+        waitForEventAndFail('waiting');
+    }
+
+    </script>
+</head>
+<body _onload_="runTest()">
+    <video></video>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (171623 => 171624)


--- trunk/Source/WebCore/ChangeLog	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/ChangeLog	2014-07-25 22:39:06 UTC (rev 171624)
@@ -1,3 +1,41 @@
+2014-07-25  Jer Noble  <[email protected]>
+
+        [MSE] Playback stalls & readyState drops to HAVE_CURRENT_DATA at end of stream with unbalanced buffered SourceBuffers
+        https://bugs.webkit.org/show_bug.cgi?id=135291
+        <rdar://problem/17715503>
+
+        Reviewed by Sam Weinig.
+
+        Test: media/media-source/media-source-end-of-stream-buffered.html
+
+        When determining the correct ReadyState for the MediaSource in monitorSourceBuffers(), use the same
+        definition of "buffered" as is used in the calculation of HTMLMediaElement.buffered and in the
+        Stream Ended algorithm. Namely, when the stream has ended, treat each SourceBuffer as if its last
+        buffered range extends to the duration of the stream. This allows playback to continue through to
+        the duration without stalling due to monitorSourceBuffers().
+
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::bufferedAccountingForEndOfStream): Added; extends the last range in buffered
+            to MediaSource::duration() if the MediaSource is ended.
+        (WebCore::SourceBuffer::hasCurrentTime): Uses bufferedAccountingForEndOfStream().
+        (WebCore::SourceBuffer::hasFutureTime): Ditto.
+        (WebCore::SourceBuffer::canPlayThrough): Ditto.
+        * Modules/mediasource/SourceBuffer.h:
+
+        Add a convenience method for determining whether the MediaSource has ended:
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::isEnded):
+        * Modules/mediasource/MediaSource.h:
+
+        Add start() and end() methods that don't take a (usually ignored) isValid inout parameter. Add duration()
+        and maximumBufferedTime() convenience methods:
+        * platform/graphics/PlatformTimeRanges.cpp:
+        (WebCore::PlatformTimeRanges::start):
+        (WebCore::PlatformTimeRanges::end):
+        (WebCore::PlatformTimeRanges::duration):
+        (WebCore::PlatformTimeRanges::maximumBufferedTime):
+        * platform/graphics/PlatformTimeRanges.h:
+
 2014-07-25  Pratik Solanki  <[email protected]>
 
         [iOS] REGRESSION(r171526): Images fail to load sometimes

Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp (171623 => 171624)


--- trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2014-07-25 22:39:06 UTC (rev 171624)
@@ -739,6 +739,11 @@
     return readyState() == closedKeyword();
 }
 
+bool MediaSource::isEnded() const
+{
+    return readyState() == endedKeyword();
+}
+
 void MediaSource::close()
 {
     setReadyState(closedKeyword());

Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.h (171623 => 171624)


--- trunk/Source/WebCore/Modules/mediasource/MediaSource.h	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.h	2014-07-25 22:39:06 UTC (rev 171624)
@@ -65,6 +65,8 @@
     void removedFromRegistry();
     void openIfInEndedState();
     bool isOpen() const;
+    bool isClosed() const;
+    bool isEnded() const;
     void sourceBufferDidChangeAcitveState(SourceBuffer*, bool);
     void streamEndedWithError(const AtomicString& error, ExceptionCode&);
 
@@ -76,7 +78,6 @@
 
     bool attachToElement(HTMLMediaElement*);
     void close();
-    bool isClosed() const;
     void monitorSourceBuffers();
     void completeSeek();
 

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (171623 => 171624)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-07-25 22:39:06 UTC (rev 171624)
@@ -1454,13 +1454,27 @@
     LOG(MediaSource, "SourceBuffer::monitorBufferingRate(%p) - m_avegareBufferRate: %lf", this, m_averageBufferRate);
 }
 
+std::unique_ptr<PlatformTimeRanges> SourceBuffer::bufferedAccountingForEndOfStream() const
+{
+    // FIXME: Revisit this method once the spec bug <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26436> is resolved.
+    std::unique_ptr<PlatformTimeRanges> virtualRanges = PlatformTimeRanges::create(m_buffered->ranges());
+    if (m_source->isEnded()) {
+        MediaTime start = virtualRanges->maximumBufferedTime();
+        MediaTime end = MediaTime::createWithDouble(m_source->duration());
+        if (start <= end)
+            virtualRanges->add(start, end);
+    }
+    return virtualRanges;
+}
+
 bool SourceBuffer::hasCurrentTime() const
 {
     if (isRemoved() || !m_buffered->length())
         return false;
 
     MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
-    return abs(m_buffered->ranges().nearest(currentTime) - currentTime) <= currentTimeFudgeFactor();
+    std::unique_ptr<PlatformTimeRanges> ranges = bufferedAccountingForEndOfStream();
+    return abs(ranges->nearest(currentTime) - currentTime) <= currentTimeFudgeFactor();
 }
 
 bool SourceBuffer::hasFutureTime() const
@@ -1468,21 +1482,21 @@
     if (isRemoved())
         return false;
 
-    const PlatformTimeRanges& ranges = m_buffered->ranges();
-    if (!ranges.length())
+    std::unique_ptr<PlatformTimeRanges> ranges = bufferedAccountingForEndOfStream();
+    if (!ranges->length())
         return false;
 
     MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
-    MediaTime nearest = ranges.nearest(currentTime);
+    MediaTime nearest = ranges->nearest(currentTime);
     if (abs(nearest - currentTime) > currentTimeFudgeFactor())
         return false;
 
-    size_t found = ranges.find(nearest);
+    size_t found = ranges->find(nearest);
     if (found == notFound)
         return false;
 
     bool ignoredValid = false;
-    return ranges.end(found, ignoredValid) - currentTime > currentTimeFudgeFactor();
+    return ranges->end(found, ignoredValid) - currentTime > currentTimeFudgeFactor();
 }
 
 bool SourceBuffer::canPlayThrough()
@@ -1498,18 +1512,16 @@
         return true;
 
     // Add up all the time yet to be buffered.
-    MediaTime unbufferedTime = MediaTime::zeroTime();
     MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
     MediaTime duration = MediaTime::createWithDouble(m_source->duration());
 
-    PlatformTimeRanges unbufferedRanges = m_buffered->ranges();
-    unbufferedRanges.invert();
-    unbufferedRanges.intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
-    bool valid = true;
+    std::unique_ptr<PlatformTimeRanges> unbufferedRanges = bufferedAccountingForEndOfStream();
+    unbufferedRanges->invert();
+    unbufferedRanges->intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
+    MediaTime unbufferedTime = unbufferedRanges->totalDuration();
+    if (!unbufferedTime.isValid())
+        return true;
 
-    for (size_t i = 0, end = unbufferedRanges.length(); i < end; ++i)
-        unbufferedTime += unbufferedRanges.end(i, valid) - unbufferedRanges.start(i, valid);
-
     MediaTime timeRemaining = duration - currentTime;
     return unbufferedTime.toDouble() / m_averageBufferRate < timeRemaining.toDouble();
 }

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h (171623 => 171624)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-07-25 22:39:06 UTC (rev 171624)
@@ -52,6 +52,7 @@
 
 class AudioTrackList;
 class MediaSource;
+class PlatformTimeRanges;
 class SourceBufferPrivate;
 class TextTrackList;
 class TimeRanges;
@@ -160,6 +161,8 @@
 
     void reportExtraMemoryCost();
 
+    std::unique_ptr<PlatformTimeRanges> bufferedAccountingForEndOfStream() const;
+
     // Internals
     friend class Internals;
     Vector<String> bufferedSamplesForTrackID(const AtomicString&);

Modified: trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp (171623 => 171624)


--- trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp	2014-07-25 22:39:06 UTC (rev 171624)
@@ -115,6 +115,12 @@
     m_ranges.swap(unioned.m_ranges);
 }
 
+MediaTime PlatformTimeRanges::start(unsigned index) const
+{
+    bool ignoredValid;
+    return start(index, ignoredValid);
+}
+
 MediaTime PlatformTimeRanges::start(unsigned index, bool& valid) const
 { 
     if (index >= length()) {
@@ -126,6 +132,12 @@
     return m_ranges[index].m_start;
 }
 
+MediaTime PlatformTimeRanges::end(unsigned index) const
+{
+    bool ignoredValid;
+    return end(index, ignoredValid);
+}
+
 MediaTime PlatformTimeRanges::end(unsigned index, bool& valid) const
 { 
     if (index >= length()) {
@@ -137,6 +149,22 @@
     return m_ranges[index].m_end;
 }
 
+MediaTime PlatformTimeRanges::duration(unsigned index) const
+{
+    if (index >= length())
+        return MediaTime::invalidTime();
+
+    return m_ranges[index].m_end - m_ranges[index].m_start;
+}
+
+MediaTime PlatformTimeRanges::maximumBufferedTime() const
+{
+    if (!length())
+        return MediaTime::invalidTime();
+
+    return m_ranges[length() - 1].m_end;
+}
+
 void PlatformTimeRanges::add(const MediaTime& start, const MediaTime& end)
 {
     ASSERT(start <= end);

Modified: trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.h (171623 => 171624)


--- trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.h	2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.h	2014-07-25 22:39:06 UTC (rev 171624)
@@ -46,8 +46,12 @@
 
     PlatformTimeRanges& operator=(const PlatformTimeRanges&);
 
+    MediaTime start(unsigned index) const;
     MediaTime start(unsigned index, bool& valid) const;
+    MediaTime end(unsigned index) const;
     MediaTime end(unsigned index, bool& valid) const;
+    MediaTime duration(unsigned index) const;
+    MediaTime maximumBufferedTime() const;
 
     void invert();
     void intersectWith(const PlatformTimeRanges&);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to