<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[171624] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/171624">171624</a></dd>
<dt>Author</dt> <dd>jer.noble@apple.com</dd>
<dt>Date</dt> <dd>2014-07-25 15:39:06 -0700 (Fri, 25 Jul 2014)</dd>
</dl>

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

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 &quot;buffered&quot; 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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasourceMediaSourcecpp">trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasourceMediaSourceh">trunk/Source/WebCore/Modules/mediasource/MediaSource.h</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasourceSourceBuffercpp">trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasourceSourceBufferh">trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPlatformTimeRangescpp">trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPlatformTimeRangesh">trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediamediasourcemediasourceendofstreambufferedexpectedtxt">trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediasourcemediasourceendofstreambufferedhtml">trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-07-25 22:38:37 UTC (rev 171623)
+++ trunk/LayoutTests/ChangeLog        2014-07-25 22:39:06 UTC (rev 171624)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2014-07-25  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        [MSE] Playback stalls &amp; readyState drops to HAVE_CURRENT_DATA at end of stream with unbalanced buffered SourceBuffers
+        https://bugs.webkit.org/show_bug.cgi?id=135291
+        &lt;rdar://problem/17715503&gt;
+
+        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.
+
</ins><span class="cx"> 2014-07-25  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Merge r169795, r169819, r169864, r169902, r169949, r169950, r170016, r170017, r170060, r170064 from ftlopt.
</span></span></pre></div>
<a id="trunkLayoutTestsmediamediasourcemediasourceendofstreambufferedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered-expected.txt (0 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+
+RUN(video.src = URL.createObjectURL(source))
+EVENT(sourceopen)
+RUN(sourceBuffer1 = source.addSourceBuffer(&quot;video/mock; codecs=mock&quot;))
+RUN(sourceBuffer1.appendBuffer(mediaSegment))
+EVENT(updateend)
+RUN(sourceBuffer2 = source.addSourceBuffer(&quot;video/mock; codecs=mock&quot;))
+RUN(sourceBuffer2.appendBuffer(mediaSegment))
+EVENT(updateend)
+RUN(source.endOfStream())
+RUN(video.currentTime = 5)
+EVENT(seeked)
+RUN(video.play())
+EVENT(playing)
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediasourcemediasourceendofstreambufferedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-source/media-source-end-of-stream-buffered.html (0 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,62 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;title&gt;mock-media-source&lt;/title&gt;
+    &lt;script src=&quot;mock-media-source.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;../video-test.js&quot;&gt;&lt;/script&gt;
+    &lt;script&gt;
+    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 = URL.createObjectURL(source)');
+    }
+
+    function sourceOpen() {
+        run('sourceBuffer1 = source.addSourceBuffer(&quot;video/mock; codecs=mock&quot;)');
+        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(&quot;video/mock; codecs=mock&quot;)');
+        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');
+    }
+
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+    &lt;video&gt;&lt;/video&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,41 @@
</span><ins>+2014-07-25  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        [MSE] Playback stalls &amp; readyState drops to HAVE_CURRENT_DATA at end of stream with unbalanced buffered SourceBuffers
+        https://bugs.webkit.org/show_bug.cgi?id=135291
+        &lt;rdar://problem/17715503&gt;
+
+        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 &quot;buffered&quot; 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:
+
</ins><span class="cx"> 2014-07-25  Pratik Solanki  &lt;psolanki@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [iOS] REGRESSION(r171526): Images fail to load sometimes
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceMediaSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -739,6 +739,11 @@
</span><span class="cx">     return readyState() == closedKeyword();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool MediaSource::isEnded() const
+{
+    return readyState() == endedKeyword();
+}
+
</ins><span class="cx"> void MediaSource::close()
</span><span class="cx"> {
</span><span class="cx">     setReadyState(closedKeyword());
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceMediaSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.h (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -65,6 +65,8 @@
</span><span class="cx">     void removedFromRegistry();
</span><span class="cx">     void openIfInEndedState();
</span><span class="cx">     bool isOpen() const;
</span><ins>+    bool isClosed() const;
+    bool isEnded() const;
</ins><span class="cx">     void sourceBufferDidChangeAcitveState(SourceBuffer*, bool);
</span><span class="cx">     void streamEndedWithError(const AtomicString&amp; error, ExceptionCode&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -76,7 +78,6 @@
</span><span class="cx"> 
</span><span class="cx">     bool attachToElement(HTMLMediaElement*);
</span><span class="cx">     void close();
</span><del>-    bool isClosed() const;
</del><span class="cx">     void monitorSourceBuffers();
</span><span class="cx">     void completeSeek();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceSourceBuffercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1454,13 +1454,27 @@
</span><span class="cx">     LOG(MediaSource, &quot;SourceBuffer::monitorBufferingRate(%p) - m_avegareBufferRate: %lf&quot;, this, m_averageBufferRate);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+std::unique_ptr&lt;PlatformTimeRanges&gt; SourceBuffer::bufferedAccountingForEndOfStream() const
+{
+    // FIXME: Revisit this method once the spec bug &lt;https://www.w3.org/Bugs/Public/show_bug.cgi?id=26436&gt; is resolved.
+    std::unique_ptr&lt;PlatformTimeRanges&gt; virtualRanges = PlatformTimeRanges::create(m_buffered-&gt;ranges());
+    if (m_source-&gt;isEnded()) {
+        MediaTime start = virtualRanges-&gt;maximumBufferedTime();
+        MediaTime end = MediaTime::createWithDouble(m_source-&gt;duration());
+        if (start &lt;= end)
+            virtualRanges-&gt;add(start, end);
+    }
+    return virtualRanges;
+}
+
</ins><span class="cx"> bool SourceBuffer::hasCurrentTime() const
</span><span class="cx"> {
</span><span class="cx">     if (isRemoved() || !m_buffered-&gt;length())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     MediaTime currentTime = MediaTime::createWithDouble(m_source-&gt;currentTime());
</span><del>-    return abs(m_buffered-&gt;ranges().nearest(currentTime) - currentTime) &lt;= currentTimeFudgeFactor();
</del><ins>+    std::unique_ptr&lt;PlatformTimeRanges&gt; ranges = bufferedAccountingForEndOfStream();
+    return abs(ranges-&gt;nearest(currentTime) - currentTime) &lt;= currentTimeFudgeFactor();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool SourceBuffer::hasFutureTime() const
</span><span class="lines">@@ -1468,21 +1482,21 @@
</span><span class="cx">     if (isRemoved())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    const PlatformTimeRanges&amp; ranges = m_buffered-&gt;ranges();
-    if (!ranges.length())
</del><ins>+    std::unique_ptr&lt;PlatformTimeRanges&gt; ranges = bufferedAccountingForEndOfStream();
+    if (!ranges-&gt;length())
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     MediaTime currentTime = MediaTime::createWithDouble(m_source-&gt;currentTime());
</span><del>-    MediaTime nearest = ranges.nearest(currentTime);
</del><ins>+    MediaTime nearest = ranges-&gt;nearest(currentTime);
</ins><span class="cx">     if (abs(nearest - currentTime) &gt; currentTimeFudgeFactor())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    size_t found = ranges.find(nearest);
</del><ins>+    size_t found = ranges-&gt;find(nearest);
</ins><span class="cx">     if (found == notFound)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     bool ignoredValid = false;
</span><del>-    return ranges.end(found, ignoredValid) - currentTime &gt; currentTimeFudgeFactor();
</del><ins>+    return ranges-&gt;end(found, ignoredValid) - currentTime &gt; currentTimeFudgeFactor();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool SourceBuffer::canPlayThrough()
</span><span class="lines">@@ -1498,18 +1512,16 @@
</span><span class="cx">         return true;
</span><span class="cx"> 
</span><span class="cx">     // Add up all the time yet to be buffered.
</span><del>-    MediaTime unbufferedTime = MediaTime::zeroTime();
</del><span class="cx">     MediaTime currentTime = MediaTime::createWithDouble(m_source-&gt;currentTime());
</span><span class="cx">     MediaTime duration = MediaTime::createWithDouble(m_source-&gt;duration());
</span><span class="cx"> 
</span><del>-    PlatformTimeRanges unbufferedRanges = m_buffered-&gt;ranges();
-    unbufferedRanges.invert();
-    unbufferedRanges.intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
-    bool valid = true;
</del><ins>+    std::unique_ptr&lt;PlatformTimeRanges&gt; unbufferedRanges = bufferedAccountingForEndOfStream();
+    unbufferedRanges-&gt;invert();
+    unbufferedRanges-&gt;intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
+    MediaTime unbufferedTime = unbufferedRanges-&gt;totalDuration();
+    if (!unbufferedTime.isValid())
+        return true;
</ins><span class="cx"> 
</span><del>-    for (size_t i = 0, end = unbufferedRanges.length(); i &lt; end; ++i)
-        unbufferedTime += unbufferedRanges.end(i, valid) - unbufferedRanges.start(i, valid);
-
</del><span class="cx">     MediaTime timeRemaining = duration - currentTime;
</span><span class="cx">     return unbufferedTime.toDouble() / m_averageBufferRate &lt; timeRemaining.toDouble();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceSourceBufferh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx"> 
</span><span class="cx"> class AudioTrackList;
</span><span class="cx"> class MediaSource;
</span><ins>+class PlatformTimeRanges;
</ins><span class="cx"> class SourceBufferPrivate;
</span><span class="cx"> class TextTrackList;
</span><span class="cx"> class TimeRanges;
</span><span class="lines">@@ -160,6 +161,8 @@
</span><span class="cx"> 
</span><span class="cx">     void reportExtraMemoryCost();
</span><span class="cx"> 
</span><ins>+    std::unique_ptr&lt;PlatformTimeRanges&gt; bufferedAccountingForEndOfStream() const;
+
</ins><span class="cx">     // Internals
</span><span class="cx">     friend class Internals;
</span><span class="cx">     Vector&lt;String&gt; bufferedSamplesForTrackID(const AtomicString&amp;);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPlatformTimeRangescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -115,6 +115,12 @@
</span><span class="cx">     m_ranges.swap(unioned.m_ranges);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+MediaTime PlatformTimeRanges::start(unsigned index) const
+{
+    bool ignoredValid;
+    return start(index, ignoredValid);
+}
+
</ins><span class="cx"> MediaTime PlatformTimeRanges::start(unsigned index, bool&amp; valid) const
</span><span class="cx"> { 
</span><span class="cx">     if (index &gt;= length()) {
</span><span class="lines">@@ -126,6 +132,12 @@
</span><span class="cx">     return m_ranges[index].m_start;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+MediaTime PlatformTimeRanges::end(unsigned index) const
+{
+    bool ignoredValid;
+    return end(index, ignoredValid);
+}
+
</ins><span class="cx"> MediaTime PlatformTimeRanges::end(unsigned index, bool&amp; valid) const
</span><span class="cx"> { 
</span><span class="cx">     if (index &gt;= length()) {
</span><span class="lines">@@ -137,6 +149,22 @@
</span><span class="cx">     return m_ranges[index].m_end;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+MediaTime PlatformTimeRanges::duration(unsigned index) const
+{
+    if (index &gt;= 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;
+}
+
</ins><span class="cx"> void PlatformTimeRanges::add(const MediaTime&amp; start, const MediaTime&amp; end)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(start &lt;= end);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPlatformTimeRangesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.h (171623 => 171624)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -46,8 +46,12 @@
</span><span class="cx"> 
</span><span class="cx">     PlatformTimeRanges&amp; operator=(const PlatformTimeRanges&amp;);
</span><span class="cx"> 
</span><ins>+    MediaTime start(unsigned index) const;
</ins><span class="cx">     MediaTime start(unsigned index, bool&amp; valid) const;
</span><ins>+    MediaTime end(unsigned index) const;
</ins><span class="cx">     MediaTime end(unsigned index, bool&amp; valid) const;
</span><ins>+    MediaTime duration(unsigned index) const;
+    MediaTime maximumBufferedTime() const;
</ins><span class="cx"> 
</span><span class="cx">     void invert();
</span><span class="cx">     void intersectWith(const PlatformTimeRanges&amp;);
</span></span></pre>
</div>
</div>

</body>
</html>