<!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>[206001] 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/206001">206001</a></dd>
<dt>Author</dt> <dd>jer.noble@apple.com</dd>
<dt>Date</dt> <dd>2016-09-15 16:13:56 -0700 (Thu, 15 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[media-source] web-platform-test/media-source/mediasource-remove.html test failing
https://bugs.webkit.org/show_bug.cgi?id=161950

Reviewed by Eric Carlson.

Source/WebCore:

Fixes test: web-platform-test/media-source/mediasource-remove.html

The mediasource-remove.html test was failing in a number of ways:

- Tests with invalid start or end times were not throwing the correct exception
  code, or not throwing exception codes at all

- Tests were showing an incorrect start buffered range at the beginning of each test.

- Tests which removed samples were not getting the expected buffered values at the end
  each test.

For the exception failures, update the implementation of abort() and remove() to throw
the correct exceptions at the correct times.

For the incorrect initial buffered range, update our buffered calculations to store
individual PlatformTimeRanges on each TrackBuffer, and coalesce them into a single
value when an append operation completes, a remove operation completes, or when the
MediaSource's ready state changes.

For the incorrect buffered ranges after removal, this is caused because the &quot;samples&quot;
that make up an audio track are actually a collection of a large number of individual
samples.  So when we are asked to remove media data in a given range, break these audio
meta-samples into two pieces at the removal points. This allows the removal algorithm
to operate on a individual audio sample basis. (We should look into using this technique
when audio samples are evicted during an append operation.) This requires adding some
methods to MediaSample and it's concrete subclasses to &quot;divide&quot; a sample into two at
a given presentation time.

Fixing these behaviors, however, breaks the media-source-end-of-stream-buffered.html
test, which expects the buffered range for the entire element to expand to the maximum
buffered time of any of the element's MediaSource's active sourceBuffers. To fix this,
update the MediaSource's monitorSourceBuffer() implementation to match the current
specification. The new spec no longer queries the individual SourceBuffers, but rather
queries the already coalesced buffered ranges. So move the helper methods hasCurrentTime()
hasFutureTime(), and canPlayThrough() up into MediaSource from SourceBuffer. Also, update
seekToTime, since it has the same problem as monitorSourceBuffer().

However, this breaks the media-source-monitor-source-buffers.html test, which appends
10s of movie data instantaneously, then never appends again. The SourceBuffer's
monitorBufferingRate() method only re-evaluates the rate after data has been appended,
so the SourceBuffer thinks it's buffered data at a prodigious rate forever. Instead,
allow the SourceBuffer to continuously re-evalute it's buffering rate by modifying the
exponential moving average so that the co-efficient scales based on how frequently the
method is called. Call the method more often, and the moving average changes less quickly,
and it means that when media is stalled out at the end of a buffered range, the readyState
of a video element will eventually drop to HAVE_CURRENT_DATA when the average buffering
rate falls below the level where playback would continue uninterrupted.

* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::seekToTime):
(WebCore::MediaSource::currentTimeFudgeFactor):
(WebCore::MediaSource::hasBufferedTime):
(WebCore::MediaSource::hasCurrentTime):
(WebCore::MediaSource::hasFutureTime):
(WebCore::MediaSource::monitorSourceBuffers):
* Modules/mediasource/MediaSource.h:
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::abort):
(WebCore::SourceBuffer::remove):
(WebCore::SourceBuffer::abortIfUpdating):
(WebCore::SourceBuffer::removeCodedFrames):
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):
(WebCore::SourceBuffer::updateBufferedFromTrackBuffers):
(WebCore::SourceBuffer::canPlayThroughRange):
(WebCore::SourceBuffer::monitorBufferingRate):
(WebCore::currentTimeFudgeFactor): Deleted.
(WebCore::SourceBuffer::hasCurrentTime): Deleted.
(WebCore::SourceBuffer::hasFutureTime): Deleted.
(WebCore::SourceBuffer::canPlayThrough): Deleted.
* platform/MediaSample.h:
* platform/cf/CoreMediaSoftLink.cpp:
* platform/cf/CoreMediaSoftLink.h:
* platform/graphics/avfoundation/MediaSampleAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm:
(WebCore::MediaSampleAVFObjC::isDivisable):
(WebCore::MediaSampleAVFObjC::divide):
* platform/mock/mediasource/MockSourceBufferPrivate.cpp:

LayoutTests:

* platform/mac/TestExpectations:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformmacTestExpectations">trunk/LayoutTests/platform/mac/TestExpectations</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="#trunkSourceWebCoreplatformMediaSampleh">trunk/Source/WebCore/platform/MediaSample.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcfCoreMediaSoftLinkcpp">trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformcfCoreMediaSoftLinkh">trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPlatformTimeRangescpp">trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationMediaSampleAVFObjCh">trunk/Source/WebCore/platform/graphics/avfoundation/MediaSampleAVFObjC.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaSampleAVFObjCmm">trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformmockmediasourceMockSourceBufferPrivatecpp">trunk/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/LayoutTests/ChangeLog        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2016-09-13  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        [media-source] web-platform-test/media-source/mediasource-remove.html test failing
+        https://bugs.webkit.org/show_bug.cgi?id=161950
+
+        Reviewed by Eric Carlson.
+
+        * platform/mac/TestExpectations:
+
</ins><span class="cx"> 2016-09-15  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ASSERTION FAILED: willBeComposited == needsToBeComposited(layer) in WebCore::RenderLayerCompositor::computeCompositingRequirements
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -1052,6 +1052,7 @@
</span><span class="cx"> [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-endofstream-invaliderror.html [ Pass ]
</span><span class="cx"> [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-multiple-attach.html [ Pass ]
</span><span class="cx"> [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-play.html [ Pass ]
</span><ins>+[ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-remove.html [ Pass ]
</ins><span class="cx"> [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-sourcebufferlist.html [ Pass ]
</span><span class="cx"> 
</span><span class="cx"> # Flaky Media Source tests
</span><span class="lines">@@ -1061,7 +1062,6 @@
</span><span class="cx"> # Newly failing Media Source tests
</span><span class="cx"> webkit.org/b/161725 [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-append-buffer.html [ Failure ]
</span><span class="cx"> webkit.org/b/161725 [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-is-type-supported.html [ Failure ]
</span><del>-webkit.org/b/161725 [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-remove.html [ Failure ]
</del><span class="cx"> webkit.org/b/161725 [ Yosemite+ ] imported/w3c/web-platform-tests/media-source/mediasource-sourcebuffer-mode.html [ Failure ]
</span><span class="cx"> 
</span><span class="cx"> # These two tests have &quot;InvalidStateError (DOM Exception 11): The object is in an invalid state.&quot; in output.
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/ChangeLog        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -1,3 +1,89 @@
</span><ins>+2016-09-13  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        [media-source] web-platform-test/media-source/mediasource-remove.html test failing
+        https://bugs.webkit.org/show_bug.cgi?id=161950
+
+        Reviewed by Eric Carlson.
+
+        Fixes test: web-platform-test/media-source/mediasource-remove.html
+
+        The mediasource-remove.html test was failing in a number of ways:
+
+        - Tests with invalid start or end times were not throwing the correct exception
+          code, or not throwing exception codes at all
+
+        - Tests were showing an incorrect start buffered range at the beginning of each test.
+
+        - Tests which removed samples were not getting the expected buffered values at the end
+          each test.
+
+        For the exception failures, update the implementation of abort() and remove() to throw
+        the correct exceptions at the correct times.
+
+        For the incorrect initial buffered range, update our buffered calculations to store
+        individual PlatformTimeRanges on each TrackBuffer, and coalesce them into a single
+        value when an append operation completes, a remove operation completes, or when the
+        MediaSource's ready state changes.
+
+        For the incorrect buffered ranges after removal, this is caused because the &quot;samples&quot;
+        that make up an audio track are actually a collection of a large number of individual
+        samples.  So when we are asked to remove media data in a given range, break these audio
+        meta-samples into two pieces at the removal points. This allows the removal algorithm
+        to operate on a individual audio sample basis. (We should look into using this technique
+        when audio samples are evicted during an append operation.) This requires adding some
+        methods to MediaSample and it's concrete subclasses to &quot;divide&quot; a sample into two at
+        a given presentation time.
+
+        Fixing these behaviors, however, breaks the media-source-end-of-stream-buffered.html
+        test, which expects the buffered range for the entire element to expand to the maximum
+        buffered time of any of the element's MediaSource's active sourceBuffers. To fix this,
+        update the MediaSource's monitorSourceBuffer() implementation to match the current
+        specification. The new spec no longer queries the individual SourceBuffers, but rather
+        queries the already coalesced buffered ranges. So move the helper methods hasCurrentTime()
+        hasFutureTime(), and canPlayThrough() up into MediaSource from SourceBuffer. Also, update
+        seekToTime, since it has the same problem as monitorSourceBuffer().
+
+        However, this breaks the media-source-monitor-source-buffers.html test, which appends
+        10s of movie data instantaneously, then never appends again. The SourceBuffer's
+        monitorBufferingRate() method only re-evaluates the rate after data has been appended,
+        so the SourceBuffer thinks it's buffered data at a prodigious rate forever. Instead,
+        allow the SourceBuffer to continuously re-evalute it's buffering rate by modifying the
+        exponential moving average so that the co-efficient scales based on how frequently the
+        method is called. Call the method more often, and the moving average changes less quickly,
+        and it means that when media is stalled out at the end of a buffered range, the readyState
+        of a video element will eventually drop to HAVE_CURRENT_DATA when the average buffering
+        rate falls below the level where playback would continue uninterrupted.
+
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::seekToTime):
+        (WebCore::MediaSource::currentTimeFudgeFactor):
+        (WebCore::MediaSource::hasBufferedTime):
+        (WebCore::MediaSource::hasCurrentTime):
+        (WebCore::MediaSource::hasFutureTime):
+        (WebCore::MediaSource::monitorSourceBuffers):
+        * Modules/mediasource/MediaSource.h:
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::abort):
+        (WebCore::SourceBuffer::remove):
+        (WebCore::SourceBuffer::abortIfUpdating):
+        (WebCore::SourceBuffer::removeCodedFrames):
+        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):
+        (WebCore::SourceBuffer::updateBufferedFromTrackBuffers):
+        (WebCore::SourceBuffer::canPlayThroughRange):
+        (WebCore::SourceBuffer::monitorBufferingRate):
+        (WebCore::currentTimeFudgeFactor): Deleted.
+        (WebCore::SourceBuffer::hasCurrentTime): Deleted.
+        (WebCore::SourceBuffer::hasFutureTime): Deleted.
+        (WebCore::SourceBuffer::canPlayThrough): Deleted.
+        * platform/MediaSample.h:
+        * platform/cf/CoreMediaSoftLink.cpp:
+        * platform/cf/CoreMediaSoftLink.h:
+        * platform/graphics/avfoundation/MediaSampleAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm:
+        (WebCore::MediaSampleAVFObjC::isDivisable):
+        (WebCore::MediaSampleAVFObjC::divide):
+        * platform/mock/mediasource/MockSourceBufferPrivate.cpp:
+
</ins><span class="cx"> 2016-09-15  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ASSERTION FAILED: willBeComposited == needsToBeComposited(layer) in WebCore::RenderLayerCompositor::computeCompositingRequirements
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceMediaSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -214,7 +214,7 @@
</span><span class="cx"> void MediaSource::seekToTime(const MediaTime&amp; time)
</span><span class="cx"> {
</span><span class="cx">     // 2.4.3 Seeking
</span><del>-    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking
</del><ins>+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-seeking
</ins><span class="cx"> 
</span><span class="cx">     m_pendingSeekTime = time;
</span><span class="cx"> 
</span><span class="lines">@@ -221,25 +221,21 @@
</span><span class="cx">     // Run the following steps as part of the &quot;Wait until the user agent has established whether or not the
</span><span class="cx">     // media data for the new playback position is available, and, if it is, until it has decoded enough data
</span><span class="cx">     // to play back that position&quot; step of the seek algorithm:
</span><del>-    // 1. The media element looks for media segments containing the new playback position in each SourceBuffer
-    // object in activeSourceBuffers.
-    for (auto&amp; sourceBuffer : *m_activeSourceBuffers) {
-        // ↳ If one or more of the objects in activeSourceBuffers is missing media segments for the new
-        // playback position
-        if (!sourceBuffer-&gt;buffered()-&gt;ranges().contain(time)) {
-            // 1.1 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
-            m_private-&gt;setReadyState(MediaPlayer::HaveMetadata);
</del><ins>+    // ↳ If new playback position is not in any TimeRange of HTMLMediaElement.buffered
+    if (!hasBufferedTime(time)) {
+        // 1. If the HTMLMediaElement.readyState attribute is greater than HAVE_METADATA,
+        // then set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
+        m_private-&gt;setReadyState(MediaPlayer::HaveMetadata);
</ins><span class="cx"> 
</span><del>-            // 1.2 The media element waits until an appendBuffer() or an appendStream() call causes the coded
-            // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater
-            // than HAVE_METADATA.
-            LOG(MediaSource, &quot;MediaSource::seekToTime(%p) - waitForSeekCompleted()&quot;, this);
-            m_private-&gt;waitForSeekCompleted();
-            return;
-        }
-        // ↳ Otherwise
-        // Continue
</del><ins>+        // 2. The media element waits until an appendBuffer() or an appendStream() call causes the coded
+        // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater
+        // than HAVE_METADATA.
+        LOG(MediaSource, &quot;MediaSource::seekToTime(%p) - waitForSeekCompleted()&quot;, this);
+        m_private-&gt;waitForSeekCompleted();
+        return;
</ins><span class="cx">     }
</span><ins>+    // ↳ Otherwise
+    // Continue
</ins><span class="cx"> 
</span><span class="cx">     completeSeek();
</span><span class="cx"> }
</span><span class="lines">@@ -265,10 +261,58 @@
</span><span class="cx">     monitorSourceBuffers();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+const MediaTime&amp; MediaSource::currentTimeFudgeFactor()
+{
+    // Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
+    static NeverDestroyed&lt;MediaTime&gt; fudgeFactor(2002, 24000);
+    return fudgeFactor;
+}
+
+bool MediaSource::hasBufferedTime(const MediaTime&amp; time)
+{
+    if (time &gt;= duration())
+        return false;
+
+    auto ranges = buffered();
+    if (!ranges-&gt;length())
+        return false;
+
+    return abs(ranges-&gt;nearest(time) - time) &lt;= currentTimeFudgeFactor();
+}
+
+bool MediaSource::hasCurrentTime()
+{
+    return hasBufferedTime(currentTime());
+}
+
+bool MediaSource::hasFutureTime()
+{
+    MediaTime currentTime = this-&gt;currentTime();
+    MediaTime duration = this-&gt;duration();
+
+    if (currentTime &gt;= duration)
+        return true;
+
+    auto ranges = buffered();
+    MediaTime nearest = ranges-&gt;nearest(currentTime);
+    if (abs(nearest - currentTime) &gt; currentTimeFudgeFactor())
+        return false;
+
+    size_t found = ranges-&gt;find(nearest);
+    if (found == notFound)
+        return false;
+
+    MediaTime localEnd = ranges-&gt;end(found);
+    if (localEnd == duration)
+        return true;
+
+    return localEnd - currentTime &gt; currentTimeFudgeFactor();
+}
+
</ins><span class="cx"> void MediaSource::monitorSourceBuffers()
</span><span class="cx"> {
</span><span class="cx">     // 2.4.4 SourceBuffer Monitoring
</span><del>-    // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#buffer-monitoring
</del><ins>+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#buffer-monitoring
</ins><span class="cx"> 
</span><span class="cx">     // Note, the behavior if activeSourceBuffers is empty is undefined.
</span><span class="cx">     if (!m_activeSourceBuffers) {
</span><span class="lines">@@ -276,7 +320,6 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // http://w3c.github.io/media-source/#buffer-monitoring, change from 11 December 2014
</del><span class="cx">     // ↳ If the the HTMLMediaElement.readyState attribute equals HAVE_NOTHING:
</span><span class="cx">     if (mediaElement()-&gt;readyState() == HTMLMediaElement::HAVE_NOTHING) {
</span><span class="cx">         // 1. Abort these steps.
</span><span class="lines">@@ -283,13 +326,8 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // ↳ If buffered for all objects in activeSourceBuffers do not contain TimeRanges for the current
-    // playback position:
-    auto begin = m_activeSourceBuffers-&gt;begin();
-    auto end = m_activeSourceBuffers-&gt;end();
-    if (std::all_of(begin, end, [](auto&amp; sourceBuffer) {
-        return !sourceBuffer-&gt;hasCurrentTime();
-    })) {
</del><ins>+    // ↳ If HTMLMediaElement.buffered does not contain a TimeRange for the current playback position:
+    if (!hasCurrentTime()) {
</ins><span class="cx">         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
</span><span class="cx">         // 2. If this is the first transition to HAVE_METADATA, then queue a task to fire a simple event
</span><span class="cx">         // named loadedmetadata at the media element.
</span><span class="lines">@@ -299,10 +337,11 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // ↳ If buffered for all objects in activeSourceBuffers contain TimeRanges that include the current
-    // playback position and enough data to ensure uninterrupted playback:
-    if (std::all_of(begin, end, [](auto&amp; sourceBuffer) {
-        return sourceBuffer-&gt;hasFutureTime() &amp;&amp; sourceBuffer-&gt;canPlayThrough();
</del><ins>+    // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current
+    //  playback position and enough data to ensure uninterrupted playback:
+    auto ranges = buffered();
+    if (std::all_of(m_activeSourceBuffers-&gt;begin(), m_activeSourceBuffers-&gt;end(), [&amp;](auto&amp; sourceBuffer) {
+        return sourceBuffer-&gt;canPlayThroughRange(*ranges);
</ins><span class="cx">     })) {
</span><span class="cx">         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_ENOUGH_DATA.
</span><span class="cx">         // 2. Queue a task to fire a simple event named canplaythrough at the media element.
</span><span class="lines">@@ -316,11 +355,9 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // ↳ If buffered for all objects in activeSourceBuffers contain a TimeRange that includes
-    // the current playback position and some time beyond the current playback position, then run the following steps:
-    if (std::all_of(begin, end, [](auto&amp; sourceBuffer) {
-        return sourceBuffer-&gt;hasFutureTime();
-    })) {
</del><ins>+    // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current playback
+    //  position and some time beyond the current playback position, then run the following steps:
+    if (hasFutureTime()) {
</ins><span class="cx">         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_FUTURE_DATA.
</span><span class="cx">         // 2. If the previous value of HTMLMediaElement.readyState was less than HAVE_FUTURE_DATA, then queue a task to fire a simple event named canplay at the media element.
</span><span class="cx">         // 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
</span><span class="lines">@@ -333,9 +370,7 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // ↳ If buffered for at least one object in activeSourceBuffers contains a TimeRange that ends
-    // at the current playback position and does not have a range covering the time immediately
-    // after the current position:
</del><ins>+    // ↳ If HTMLMediaElement.buffered contains a TimeRange that ends at the current playback position and does not have a range covering the time immediately after the current position:
</ins><span class="cx">     // NOTE: Logically, !(all objects do not contain currentTime) == (some objects contain current time)
</span><span class="cx"> 
</span><span class="cx">     // 1. Set the HTMLMediaElement.readyState attribute to HAVE_CURRENT_DATA.
</span><span class="lines">@@ -840,6 +875,9 @@
</span><span class="cx"> 
</span><span class="cx"> void MediaSource::onReadyStateChange(const AtomicString&amp; oldState, const AtomicString&amp; newState)
</span><span class="cx"> {
</span><ins>+    for (auto&amp; buffer : *m_sourceBuffers)
+        buffer-&gt;readyStateChanged();
+
</ins><span class="cx">     if (isOpen()) {
</span><span class="cx">         scheduleEvent(eventNames().sourceopenEvent);
</span><span class="cx">         return;
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceMediaSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.h (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasource/MediaSource.h        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.h        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -114,6 +114,8 @@
</span><span class="cx">     // ActiveDOMObject API.
</span><span class="cx">     bool hasPendingActivity() const override;
</span><span class="cx"> 
</span><ins>+    static const MediaTime&amp; currentTimeFudgeFactor();
+
</ins><span class="cx"> protected:
</span><span class="cx">     explicit MediaSource(ScriptExecutionContext&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -129,6 +131,10 @@
</span><span class="cx">     void scheduleEvent(const AtomicString&amp; eventName);
</span><span class="cx">     GenericEventQueue&amp; asyncEventQueue() { return m_asyncEventQueue; }
</span><span class="cx"> 
</span><ins>+    bool hasBufferedTime(const MediaTime&amp;);
+    bool hasCurrentTime();
+    bool hasFutureTime();
+
</ins><span class="cx">     void regenerateActiveSourceBuffers();
</span><span class="cx"> 
</span><span class="cx">     static URLRegistry* s_registry;
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceSourceBuffercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -65,13 +65,6 @@
</span><span class="cx"> 
</span><span class="cx"> static const double ExponentialMovingAverageCoefficient = 0.1;
</span><span class="cx"> 
</span><del>-// Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
-static const MediaTime&amp; currentTimeFudgeFactor()
-{
-    static NeverDestroyed&lt;MediaTime&gt; fudgeFactor(2002, 24000);
-    return fudgeFactor;
-}
-
</del><span class="cx"> struct SourceBuffer::TrackBuffer {
</span><span class="cx">     MediaTime lastDecodeTimestamp;
</span><span class="cx">     MediaTime lastFrameDuration;
</span><span class="lines">@@ -84,6 +77,7 @@
</span><span class="cx">     SampleMap samples;
</span><span class="cx">     DecodeOrderSampleMap::MapType decodeQueue;
</span><span class="cx">     RefPtr&lt;MediaDescription&gt; description;
</span><ins>+    PlatformTimeRanges buffered;
</ins><span class="cx"> 
</span><span class="cx">     TrackBuffer()
</span><span class="cx">         : lastDecodeTimestamp(MediaTime::invalidTime())
</span><span class="lines">@@ -288,7 +282,7 @@
</span><span class="cx"> void SourceBuffer::abort(ExceptionCode&amp; ec)
</span><span class="cx"> {
</span><span class="cx">     // Section 3.2 abort() method steps.
</span><del>-    // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
</del><ins>+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
</ins><span class="cx">     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
</span><span class="cx">     //    then throw an INVALID_STATE_ERR exception and abort these steps.
</span><span class="cx">     // 2. If the readyState attribute of the parent media source is not in the &quot;open&quot; state
</span><span class="lines">@@ -298,16 +292,22 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
</del><ins>+    // 3. If the range removal algorithm is running, then throw an InvalidStateError exception and abort these steps.
+    if (m_removeTimer.isActive()) {
+        ec = INVALID_STATE_ERR;
+        return;
+    }
+
+    // 4. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
</ins><span class="cx">     abortIfUpdating();
</span><span class="cx"> 
</span><del>-    // 4. Run the reset parser state algorithm.
</del><ins>+    // 5. Run the reset parser state algorithm.
</ins><span class="cx">     resetParserState();
</span><span class="cx"> 
</span><del>-    // 5. Set appendWindowStart to the presentation start time.
</del><ins>+    // 6. Set appendWindowStart to the presentation start time.
</ins><span class="cx">     m_appendWindowStart = MediaTime::zeroTime();
</span><span class="cx"> 
</span><del>-    // 6. Set appendWindowEnd to positive Infinity.
</del><ins>+    // 7. Set appendWindowEnd to positive Infinity.
</ins><span class="cx">     m_appendWindowEnd = MediaTime::positiveInfiniteTime();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -320,25 +320,26 @@
</span><span class="cx"> {
</span><span class="cx">     LOG(MediaSource, &quot;SourceBuffer::remove(%p) - start(%lf), end(%lf)&quot;, this, start.toDouble(), end.toDouble());
</span><span class="cx"> 
</span><ins>+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-remove
</ins><span class="cx">     // Section 3.2 remove() method steps.
</span><del>-    // 1. If duration equals NaN, then throw an InvalidAccessError exception and abort these steps.
-    // 2. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
-    // 3. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
-
-    // FIXME: reorder/revisit this section once &lt;https://www.w3.org/Bugs/Public/show_bug.cgi?id=27857&gt; got resolved
-    // as it seems wrong to check mediaSource duration before checking isRemoved().
-    if ((m_source &amp;&amp; m_source-&gt;duration().isInvalid())
-        || start &lt; MediaTime::zeroTime() || (m_source &amp;&amp; start &gt; m_source-&gt;duration())
-        || end &lt;= start) {
-        ec = INVALID_ACCESS_ERR;
</del><ins>+    // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw
+    //    an InvalidStateError exception and abort these steps.
+    // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
+    if (isRemoved() || m_updating) {
+        ec = INVALID_STATE_ERR;
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // 4. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
-    //    InvalidStateError exception and abort these steps.
-    // 5. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        ec = INVALID_STATE_ERR;
</del><ins>+    // 3. If duration equals NaN, then throw a TypeError exception and abort these steps.
+    // 4. If start is negative or greater than duration, then throw a TypeError exception and abort these steps.
+    // 5. If end is less than or equal to start or end equals NaN, then throw a TypeError exception and abort these steps.
+    if (m_source-&gt;duration().isInvalid()
+        || end.isInvalid()
+        || start.isInvalid()
+        || start &lt; MediaTime::zeroTime()
+        || start &gt; m_source-&gt;duration()
+        || end &lt;= start) {
+        ec = TypeError;
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -371,27 +372,23 @@
</span><span class="cx"> 
</span><span class="cx"> void SourceBuffer::abortIfUpdating()
</span><span class="cx"> {
</span><del>-    // Section 3.2 abort() method step 3 substeps.
-    // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
</del><ins>+    // Section 3.2 abort() method step 4 substeps.
+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
</ins><span class="cx"> 
</span><span class="cx">     if (!m_updating)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
</del><ins>+    // 4.1. Abort the buffer append algorithm if it is running.
</ins><span class="cx">     m_appendBufferTimer.stop();
</span><span class="cx">     m_pendingAppendData.clear();
</span><span class="cx"> 
</span><del>-    m_removeTimer.stop();
-    m_pendingRemoveStart = MediaTime::invalidTime();
-    m_pendingRemoveEnd = MediaTime::invalidTime();
-
-    // 3.2. Set the updating attribute to false.
</del><ins>+    // 4.2. Set the updating attribute to false.
</ins><span class="cx">     m_updating = false;
</span><span class="cx"> 
</span><del>-    // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
</del><ins>+    // 4.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
</ins><span class="cx">     scheduleEvent(eventNames().abortEvent);
</span><span class="cx"> 
</span><del>-    // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
</del><ins>+    // 4.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
</ins><span class="cx">     scheduleEvent(eventNames().updateendEvent);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -407,6 +404,11 @@
</span><span class="cx">     return highestTime;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void SourceBuffer::readyStateChanged()
+{
+    updateBufferedFromTrackBuffers();
+}
+
</ins><span class="cx"> void SourceBuffer::removedFromMediaSource()
</span><span class="cx"> {
</span><span class="cx">     if (isRemoved())
</span><span class="lines">@@ -595,6 +597,10 @@
</span><span class="cx">     if (isRemoved())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+    // Resolve the changes it TrackBuffers' buffered ranges
+    // into the SourceBuffer's buffered ranges
+    updateBufferedFromTrackBuffers();
+
</ins><span class="cx">     // Section 3.5.5 Buffer Append Algorithm, ctd.
</span><span class="cx">     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
</span><span class="cx"> 
</span><span class="lines">@@ -757,6 +763,29 @@
</span><span class="cx">         // 3.1. Let remove end timestamp be the current value of duration
</span><span class="cx">         // 3.2 If this track buffer has a random access point timestamp that is greater than or equal to end, then update
</span><span class="cx">         // remove end timestamp to that random access point timestamp.
</span><ins>+
+        // NOTE: To handle MediaSamples which may be an amalgamation of multiple shorter samples, find samples whose presentation
+        // interval straddles the start and end times, and divide them if possible:
+        auto divideSampleIfPossibleAtPresentationTime = [&amp;] (const MediaTime&amp; time) {
+            auto sampleIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
+            if (sampleIterator == trackBuffer.samples.presentationOrder().end())
+                return;
+            if (!sampleIterator-&gt;second-&gt;isDivisable())
+                return;
+            std::pair&lt;RefPtr&lt;MediaSample&gt;, RefPtr&lt;MediaSample&gt;&gt; replacementSamples = sampleIterator-&gt;second-&gt;divide(time);
+            if (!replacementSamples.first || !replacementSamples.second)
+                return;
+            LOG(MediaSource, &quot;SourceBuffer::removeCodedFrames(%p) - splitting sample (%s) into\n\t(%s)\n\t(%s)&quot;, this,
+                toString(sampleIterator-&gt;second).utf8().data(),
+                toString(replacementSamples.first).utf8().data(),
+                toString(replacementSamples.second).utf8().data());
+            trackBuffer.samples.removeSample(sampleIterator-&gt;second.get());
+            trackBuffer.samples.addSample(*replacementSamples.first);
+            trackBuffer.samples.addSample(*replacementSamples.second);
+        };
+        divideSampleIfPossibleAtPresentationTime(start);
+        divideSampleIfPossibleAtPresentationTime(end);
+
</ins><span class="cx">         // NOTE: findSyncSampleAfterPresentationTime will return the next sync sample on or after the presentation time
</span><span class="cx">         // or decodeOrder().end() if no sync sample exists after that presentation time.
</span><span class="cx">         DecodeOrderSampleMap::iterator removeDecodeEnd = trackBuffer.samples.decodeOrder().findSyncSampleAfterPresentationTime(end);
</span><span class="lines">@@ -792,7 +821,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         erasedRanges.invert();
</span><del>-        m_buffered-&gt;ranges().intersectWith(erasedRanges);
</del><ins>+        trackBuffer.buffered.intersectWith(erasedRanges);
</ins><span class="cx">         setBufferedDirty(true);
</span><span class="cx"> 
</span><span class="cx">         // 3.4 If this object is in activeSourceBuffers, the current playback position is greater than or equal to start
</span><span class="lines">@@ -801,6 +830,8 @@
</span><span class="cx">         if (m_active &amp;&amp; currentMediaTime &gt;= start &amp;&amp; currentMediaTime &lt; end &amp;&amp; m_private-&gt;readyState() &gt; MediaPlayer::HaveMetadata)
</span><span class="cx">             m_private-&gt;setReadyState(MediaPlayer::HaveMetadata);
</span><span class="cx">     }
</span><ins>+    
+    updateBufferedFromTrackBuffers();
</ins><span class="cx"> 
</span><span class="cx">     // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
</span><span class="cx">     // No-op
</span><span class="lines">@@ -1535,14 +1566,11 @@
</span><span class="cx">                 // NOTE: Searching from the end of the trackBuffer will be vastly more efficient if the search range is
</span><span class="cx">                 // near the end of the buffered range. Use a linear-backwards search if the search range is within one
</span><span class="cx">                 // frame duration of the end:
</span><del>-                if (!m_buffered)
-                    break;
-
-                unsigned bufferedLength = m_buffered-&gt;ranges().length();
</del><ins>+                unsigned bufferedLength = trackBuffer.buffered.length();
</ins><span class="cx">                 if (!bufferedLength)
</span><span class="cx">                     break;
</span><span class="cx"> 
</span><del>-                MediaTime highestBufferedTime = m_buffered-&gt;ranges().maximumBufferedTime();
</del><ins>+                MediaTime highestBufferedTime = trackBuffer.buffered.maximumBufferedTime();
</ins><span class="cx"> 
</span><span class="cx">                 PresentationOrderSampleMap::iterator_range range;
</span><span class="cx">                 if (highestBufferedTime - trackBuffer.highestPresentationTimestamp &lt; trackBuffer.lastFrameDuration)
</span><span class="lines">@@ -1581,7 +1609,7 @@
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             erasedRanges.invert();
</span><del>-            m_buffered-&gt;ranges().intersectWith(erasedRanges);
</del><ins>+            trackBuffer.buffered.intersectWith(erasedRanges);
</ins><span class="cx">             setBufferedDirty(true);
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -1624,15 +1652,15 @@
</span><span class="cx">         // Eliminate small gaps between buffered ranges by coalescing
</span><span class="cx">         // disjoint ranges separated by less than a &quot;fudge factor&quot;.
</span><span class="cx">         auto presentationEndTime = presentationTimestamp + frameDuration;
</span><del>-        auto nearestToPresentationStartTime = m_buffered-&gt;ranges().nearest(presentationTimestamp);
-        if ((presentationTimestamp - nearestToPresentationStartTime).isBetween(MediaTime::zeroTime(), currentTimeFudgeFactor()))
</del><ins>+        auto nearestToPresentationStartTime = trackBuffer.buffered.nearest(presentationTimestamp);
+        if (nearestToPresentationStartTime.isValid() &amp;&amp; (presentationTimestamp - nearestToPresentationStartTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
</ins><span class="cx">             presentationTimestamp = nearestToPresentationStartTime;
</span><span class="cx"> 
</span><del>-        auto nearestToPresentationEndTime = m_buffered-&gt;ranges().nearest(presentationEndTime);
-        if ((nearestToPresentationEndTime - presentationEndTime).isBetween(MediaTime::zeroTime(), currentTimeFudgeFactor()))
</del><ins>+        auto nearestToPresentationEndTime = trackBuffer.buffered.nearest(presentationEndTime);
+        if (nearestToPresentationStartTime.isValid() &amp;&amp; (nearestToPresentationEndTime - presentationEndTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
</ins><span class="cx">             presentationEndTime = nearestToPresentationEndTime;
</span><span class="cx"> 
</span><del>-        m_buffered-&gt;ranges().add(presentationTimestamp, presentationEndTime);
</del><ins>+        trackBuffer.buffered.add(presentationTimestamp, presentationEndTime);
</ins><span class="cx">         m_bufferedSinceLastMonitor += frameDuration.toDouble();
</span><span class="cx">         setBufferedDirty(true);
</span><span class="cx"> 
</span><span class="lines">@@ -1878,9 +1906,6 @@
</span><span class="cx"> 
</span><span class="cx"> void SourceBuffer::monitorBufferingRate()
</span><span class="cx"> {
</span><del>-    if (!m_bufferedSinceLastMonitor)
-        return;
-
</del><span class="cx">     double now = monotonicallyIncreasingTime();
</span><span class="cx">     double interval = now - m_timeOfBufferingMonitor;
</span><span class="cx">     double rateSinceLastMonitor = m_bufferedSinceLastMonitor / interval;
</span><span class="lines">@@ -1888,68 +1913,54 @@
</span><span class="cx">     m_timeOfBufferingMonitor = now;
</span><span class="cx">     m_bufferedSinceLastMonitor = 0;
</span><span class="cx"> 
</span><del>-    m_averageBufferRate = m_averageBufferRate * (1 - ExponentialMovingAverageCoefficient) + rateSinceLastMonitor * ExponentialMovingAverageCoefficient;
</del><ins>+    m_averageBufferRate += (interval * ExponentialMovingAverageCoefficient) * (rateSinceLastMonitor - m_averageBufferRate);
</ins><span class="cx"> 
</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><del>-std::unique_ptr&lt;PlatformTimeRanges&gt; SourceBuffer::bufferedAccountingForEndOfStream() const
</del><ins>+void SourceBuffer::updateBufferedFromTrackBuffers()
</ins><span class="cx"> {
</span><del>-    // FIXME: Revisit this method once the spec bug &lt;https://www.w3.org/Bugs/Public/show_bug.cgi?id=26436&gt; is resolved.
-    auto virtualRanges = std::make_unique&lt;PlatformTimeRanges&gt;(m_buffered-&gt;ranges());
-    if (m_source-&gt;isEnded()) {
-        MediaTime start = virtualRanges-&gt;maximumBufferedTime();
-        MediaTime end = m_source-&gt;duration();
-        if (start &lt;= end)
-            virtualRanges-&gt;add(start, end);
</del><ins>+    // 3.1 Attributes, buffered
+    // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-buffered
+
+    // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
+    MediaTime highestEndTime = MediaTime::negativeInfiniteTime();
+    for (auto&amp; trackBuffer : m_trackBufferMap.values()) {
+        if (!trackBuffer.buffered.length())
+            continue;
+        highestEndTime = std::max(highestEndTime, trackBuffer.buffered.maximumBufferedTime());
</ins><span class="cx">     }
</span><del>-    return virtualRanges;
-}
</del><span class="cx"> 
</span><del>-bool SourceBuffer::hasCurrentTime() const
-{
-    if (isRemoved() || !m_buffered-&gt;length())
-        return false;
</del><ins>+    // NOTE: Short circuit the following if none of the TrackBuffers have buffered ranges to avoid generating
+    // a single range of {0, 0}.
+    if (highestEndTime.isNegativeInfinite()) {
+        m_buffered-&gt;ranges() = PlatformTimeRanges();
+        return;
+    }
</ins><span class="cx"> 
</span><del>-    MediaTime currentTime = m_source-&gt;currentTime();
-    MediaTime duration = m_source-&gt;duration();
-    if (currentTime &gt;= duration)
-        return true;
</del><ins>+    // 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
+    PlatformTimeRanges intersectionRanges { MediaTime::zeroTime(), highestEndTime };
</ins><span class="cx"> 
</span><del>-    std::unique_ptr&lt;PlatformTimeRanges&gt; ranges = bufferedAccountingForEndOfStream();
-    return abs(ranges-&gt;nearest(currentTime) - currentTime) &lt;= currentTimeFudgeFactor();
-}
</del><ins>+    // 4. For each audio and video track buffer managed by this SourceBuffer, run the following steps:
+    for (auto&amp; trackBuffer : m_trackBufferMap.values()) {
+        // 4.1 Let track ranges equal the track buffer ranges for the current track buffer.
+        PlatformTimeRanges trackRanges = trackBuffer.buffered;
+        // 4.2 If readyState is &quot;ended&quot;, then set the end time on the last range in track ranges to highest end time.
+        if (m_source-&gt;isEnded())
+            trackRanges.add(trackRanges.maximumBufferedTime(), highestEndTime);
</ins><span class="cx"> 
</span><del>-bool SourceBuffer::hasFutureTime() const
-{
-    if (isRemoved())
-        return false;
</del><ins>+        // 4.3 Let new intersection ranges equal the intersection between the intersection ranges and the track ranges.
+        // 4.4 Replace the ranges in intersection ranges with the new intersection ranges.
+        intersectionRanges.intersectWith(trackRanges);
+    }
</ins><span class="cx"> 
</span><del>-    std::unique_ptr&lt;PlatformTimeRanges&gt; ranges = bufferedAccountingForEndOfStream();
-    if (!ranges-&gt;length())
-        return false;
-
-    MediaTime currentTime = m_source-&gt;currentTime();
-    MediaTime duration = m_source-&gt;duration();
-    if (currentTime &gt;= duration)
-        return true;
-
-    MediaTime nearest = ranges-&gt;nearest(currentTime);
-    if (abs(nearest - currentTime) &gt; currentTimeFudgeFactor())
-        return false;
-
-    size_t found = ranges-&gt;find(nearest);
-    if (found == notFound)
-        return false;
-
-    MediaTime localEnd = ranges-&gt;end(found);
-    if (localEnd == duration)
-        return true;
-
-    return localEnd - currentTime &gt; currentTimeFudgeFactor();
</del><ins>+    // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute,
+    //    then update the current value of this attribute to intersection ranges.
+    m_buffered-&gt;ranges() = intersectionRanges;
+    setBufferedDirty(true);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool SourceBuffer::canPlayThrough()
</del><ins>+bool SourceBuffer::canPlayThroughRange(PlatformTimeRanges&amp; ranges)
</ins><span class="cx"> {
</span><span class="cx">     if (isRemoved())
</span><span class="cx">         return false;
</span><span class="lines">@@ -1965,10 +1976,10 @@
</span><span class="cx">     MediaTime currentTime = m_source-&gt;currentTime();
</span><span class="cx">     MediaTime duration = m_source-&gt;duration();
</span><span class="cx"> 
</span><del>-    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();
</del><ins>+    PlatformTimeRanges unbufferedRanges = ranges;
+    unbufferedRanges.invert();
+    unbufferedRanges.intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
+    MediaTime unbufferedTime = unbufferedRanges.totalDuration();
</ins><span class="cx">     if (!unbufferedTime.isValid())
</span><span class="cx">         return true;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasourceSourceBufferh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -92,9 +92,7 @@
</span><span class="cx">     void removedFromMediaSource();
</span><span class="cx">     void seekToTime(const MediaTime&amp;);
</span><span class="cx"> 
</span><del>-    bool hasCurrentTime() const;
-    bool hasFutureTime() const;
-    bool canPlayThrough();
</del><ins>+    bool canPlayThroughRange(PlatformTimeRanges&amp;);
</ins><span class="cx"> 
</span><span class="cx">     bool hasVideo() const;
</span><span class="cx">     bool hasAudio() const;
</span><span class="lines">@@ -125,6 +123,7 @@
</span><span class="cx">     void setBufferedDirty(bool flag) { m_bufferedDirty = flag; }
</span><span class="cx"> 
</span><span class="cx">     MediaTime highestPresentationTimestamp() const;
</span><ins>+    void readyStateChanged();
</ins><span class="cx"> 
</span><span class="cx">     // ActiveDOMObject API.
</span><span class="cx">     bool hasPendingActivity() const override;
</span><span class="lines">@@ -191,7 +190,7 @@
</span><span class="cx">     size_t extraMemoryCost() const;
</span><span class="cx">     void reportExtraMemoryAllocated();
</span><span class="cx"> 
</span><del>-    std::unique_ptr&lt;PlatformTimeRanges&gt; bufferedAccountingForEndOfStream() const;
</del><ins>+    void updateBufferedFromTrackBuffers();
</ins><span class="cx"> 
</span><span class="cx">     // Internals
</span><span class="cx">     friend class Internals;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformMediaSampleh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/MediaSample.h (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/MediaSample.h        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/MediaSample.h        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -62,6 +62,9 @@
</span><span class="cx">     virtual FloatSize presentationSize() const = 0;
</span><span class="cx">     virtual void offsetTimestampsBy(const MediaTime&amp;) = 0;
</span><span class="cx">     virtual void setTimestamps(const MediaTime&amp;, const MediaTime&amp;) = 0;
</span><ins>+    virtual bool isDivisable() const = 0;
+    enum DivideFlags { BeforePresentationTime, AfterPresentationTime };
+    virtual std::pair&lt;RefPtr&lt;MediaSample&gt;, RefPtr&lt;MediaSample&gt;&gt; divide(const MediaTime&amp; presentationTime) = 0;
</ins><span class="cx"> 
</span><span class="cx">     enum SampleFlags {
</span><span class="cx">         None = 0,
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcfCoreMediaSoftLinkcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.cpp (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.cpp        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.cpp        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -109,6 +109,9 @@
</span><span class="cx"> SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMAudioFormatDescriptionGetStreamBasicDescription, const AudioStreamBasicDescription *, (CMAudioFormatDescriptionRef desc), (desc))
</span><span class="cx"> SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer, OSStatus, (CMSampleBufferRef sbuf, size_t *bufferListSizeNeededOut, AudioBufferList *bufferListOut, size_t bufferListSize, CFAllocatorRef bbufStructAllocator, CFAllocatorRef bbufMemoryAllocator, uint32_t flags, CMBlockBufferRef *blockBufferOut), (sbuf, bufferListSizeNeededOut, bufferListOut, bufferListSize, bbufStructAllocator, bbufMemoryAllocator, flags, blockBufferOut))
</span><span class="cx"> SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMSampleBufferGetNumSamples, CMItemCount, (CMSampleBufferRef sbuf), (sbuf))
</span><ins>+SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMSampleBufferCallBlockForEachSample, OSStatus, (CMSampleBufferRef sbuf, OSStatus (^handler)(CMSampleBufferRef, CMItemCount)), (sbuf, handler))
+SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMSampleBufferCopySampleBufferForRange, OSStatus, (CFAllocatorRef allocator, CMSampleBufferRef sbuf, CFRange sampleRange, CMSampleBufferRef* sBufOut), (allocator, sbuf, sampleRange, sBufOut))
+SOFT_LINK_FUNCTION_FOR_SOURCE(WebCore, CoreMedia, CMSampleBufferGetSampleSizeArray, OSStatus, (CMSampleBufferRef sbuf, CMItemCount sizeArrayEntries, size_t* sizeArrayOut, CMItemCount* sizeArrayEntriesNeededOut), (sbuf, sizeArrayEntries, sizeArrayOut, sizeArrayEntriesNeededOut))
</ins><span class="cx"> #endif // PLATFORM(COCOA)
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(IOS)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcfCoreMediaSoftLinkh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.h (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.h        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/cf/CoreMediaSoftLink.h        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -184,6 +184,12 @@
</span><span class="cx"> #define CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer softLink_CoreMedia_CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer
</span><span class="cx"> SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, CoreMedia, CMSampleBufferGetNumSamples, CMItemCount, (CMSampleBufferRef sbuf), (sbuf))
</span><span class="cx"> #define CMSampleBufferGetNumSamples softLink_CoreMedia_CMSampleBufferGetNumSamples
</span><ins>+SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, CoreMedia, CMSampleBufferCallBlockForEachSample, OSStatus, (CMSampleBufferRef sbuf, OSStatus (^handler)(CMSampleBufferRef, CMItemCount)), (sbuf, handler))
+#define CMSampleBufferCallBlockForEachSample softLink_CoreMedia_CMSampleBufferCallBlockForEachSample
+SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, CoreMedia, CMSampleBufferCopySampleBufferForRange, OSStatus, (CFAllocatorRef allocator, CMSampleBufferRef sbuf, CFRange sampleRange, CMSampleBufferRef* sBufOut), (allocator, sbuf, sampleRange, sBufOut))
+#define CMSampleBufferCopySampleBufferForRange softLink_CoreMedia_CMSampleBufferCopySampleBufferForRange
+SOFT_LINK_FUNCTION_FOR_HEADER(WebCore, CoreMedia, CMSampleBufferGetSampleSizeArray, OSStatus, (CMSampleBufferRef sbuf, CMItemCount sizeArrayEntries, size_t* sizeArrayOut, CMItemCount* sizeArrayEntriesNeededOut), (sbuf, sizeArrayEntries, sizeArrayOut, sizeArrayEntriesNeededOut))
+#define CMSampleBufferGetSampleSizeArray softLink_CoreMedia_CMSampleBufferGetSampleSizeArray
</ins><span class="cx"> 
</span><span class="cx"> #endif // PLATFORM(COCOA)
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPlatformTimeRangescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -193,6 +193,9 @@
</span><span class="cx">     MediaTime closestDelta = MediaTime::positiveInfiniteTime();
</span><span class="cx">     MediaTime closestTime = MediaTime::zeroTime();
</span><span class="cx">     unsigned count = length();
</span><ins>+    if (!count)
+        return MediaTime::invalidTime();
+
</ins><span class="cx">     bool ignoreInvalid;
</span><span class="cx"> 
</span><span class="cx">     for (unsigned ndx = 0; ndx &lt; count; ndx++) {
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationMediaSampleAVFObjCh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/MediaSampleAVFObjC.h (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/MediaSampleAVFObjC.h        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/MediaSampleAVFObjC.h        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -33,6 +33,7 @@
</span><span class="cx"> class MediaSampleAVFObjC final : public MediaSample {
</span><span class="cx"> public:
</span><span class="cx">     static Ref&lt;MediaSampleAVFObjC&gt; create(CMSampleBufferRef sample, int trackID) { return adoptRef(*new MediaSampleAVFObjC(sample, trackID)); }
</span><ins>+    static Ref&lt;MediaSampleAVFObjC&gt; create(CMSampleBufferRef sample, AtomicString trackID) { return adoptRef(*new MediaSampleAVFObjC(sample, trackID)); }
</ins><span class="cx">     static Ref&lt;MediaSampleAVFObjC&gt; create(CMSampleBufferRef sample) { return adoptRef(*new MediaSampleAVFObjC(sample)); }
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -40,6 +41,11 @@
</span><span class="cx">         : m_sample(sample)
</span><span class="cx">     {
</span><span class="cx">     }
</span><ins>+    MediaSampleAVFObjC(CMSampleBufferRef sample, AtomicString trackID)
+        : m_sample(sample)
+        , m_id(trackID)
+    {
+    }
</ins><span class="cx">     MediaSampleAVFObjC(CMSampleBufferRef sample, int trackID)
</span><span class="cx">         : m_sample(sample)
</span><span class="cx">         , m_id(String::format(&quot;%d&quot;, trackID))
</span><span class="lines">@@ -62,6 +68,8 @@
</span><span class="cx">     void dump(PrintStream&amp;) const override;
</span><span class="cx">     void offsetTimestampsBy(const MediaTime&amp;) override;
</span><span class="cx">     void setTimestamps(const MediaTime&amp;, const MediaTime&amp;) override;
</span><ins>+    bool isDivisable() const override;
+    std::pair&lt;RefPtr&lt;MediaSample&gt;, RefPtr&lt;MediaSample&gt;&gt; divide(const MediaTime&amp; presentationTime) override;
</ins><span class="cx"> 
</span><span class="cx">     RetainPtr&lt;CMSampleBufferRef&gt; m_sample;
</span><span class="cx">     AtomicString m_id;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaSampleAVFObjCmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.mm        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -142,4 +142,51 @@
</span><span class="cx">     m_sample = adoptCF(newSample);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool MediaSampleAVFObjC::isDivisable() const
+{
+    if (CMSampleBufferGetNumSamples(m_sample.get()) == 1)
+        return false;
+
+    if (CMSampleBufferGetSampleSizeArray(m_sample.get(), 0, nullptr, nullptr) == kCMSampleBufferError_BufferHasNoSampleSizes)
+        return false;
+
+    return true;
</ins><span class="cx"> }
</span><ins>+
+std::pair&lt;RefPtr&lt;MediaSample&gt;, RefPtr&lt;MediaSample&gt;&gt; MediaSampleAVFObjC::divide(const MediaTime&amp; presentationTime)
+{
+    if (!isDivisable())
+        return { nullptr, nullptr };
+
+    CFIndex samplesBeforePresentationTime = 0;
+
+    CMSampleBufferCallBlockForEachSample(m_sample.get(), [&amp;] (CMSampleBufferRef sampleBuffer, CMItemCount) -&gt; OSStatus {
+        if (toMediaTime(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) &gt;= presentationTime)
+            return 1;
+        ++samplesBeforePresentationTime;
+        return noErr;
+    });
+
+    if (!samplesBeforePresentationTime)
+        return { nullptr, this };
+
+    CMItemCount sampleCount = CMSampleBufferGetNumSamples(m_sample.get());
+    if (samplesBeforePresentationTime &gt;= sampleCount)
+        return { this, nullptr };
+
+    CMSampleBufferRef rawSampleBefore = nullptr;
+    CFRange rangeBefore = CFRangeMake(0, samplesBeforePresentationTime);
+    if (CMSampleBufferCopySampleBufferForRange(kCFAllocatorDefault, m_sample.get(), rangeBefore, &amp;rawSampleBefore) != noErr)
+        return { nullptr, nullptr };
+    RetainPtr&lt;CMSampleBufferRef&gt; sampleBefore = adoptCF(rawSampleBefore);
+
+    CMSampleBufferRef rawSampleAfter = nullptr;
+    CFRange rangeAfter = CFRangeMake(samplesBeforePresentationTime, sampleCount - samplesBeforePresentationTime);
+    if (CMSampleBufferCopySampleBufferForRange(kCFAllocatorDefault, m_sample.get(), rangeAfter, &amp;rawSampleAfter) != noErr)
+        return { nullptr, nullptr };
+    RetainPtr&lt;CMSampleBufferRef&gt; sampleAfter = adoptCF(rawSampleAfter);
+
+    return { MediaSampleAVFObjC::create(sampleBefore.get(), m_id), MediaSampleAVFObjC::create(sampleAfter.get(), m_id) };
+}
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformmockmediasourceMockSourceBufferPrivatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp (206000 => 206001)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp        2016-09-15 22:38:17 UTC (rev 206000)
+++ trunk/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp        2016-09-15 23:13:56 UTC (rev 206001)
</span><span class="lines">@@ -66,7 +66,10 @@
</span><span class="cx">     void dump(PrintStream&amp;) const override;
</span><span class="cx">     void offsetTimestampsBy(const MediaTime&amp; offset) override { m_box.offsetTimestampsBy(offset); }
</span><span class="cx">     void setTimestamps(const MediaTime&amp; presentationTimestamp, const MediaTime&amp; decodeTimestamp) override { m_box.setTimestamps(presentationTimestamp, decodeTimestamp); }
</span><ins>+    bool isDivisable() const override { return false; }
+    std::pair&lt;RefPtr&lt;MediaSample&gt;, RefPtr&lt;MediaSample&gt;&gt; divide(const MediaTime&amp;) override { return {nullptr, nullptr}; }
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     unsigned generation() const { return m_box.generation(); }
</span><span class="cx"> 
</span><span class="cx">     MockSampleBox m_box;
</span></span></pre>
</div>
</div>

</body>
</html>