Title: [171995] trunk/Source/WebCore
Revision
171995
Author
[email protected]
Date
2014-08-04 10:26:42 -0700 (Mon, 04 Aug 2014)

Log Message

[MSE] Re-enqueing due to overlapping appended samples can cause stuttering playback
https://bugs.webkit.org/show_bug.cgi?id=135424

Reviewed by Eric Carlson.

If it become necessary to re-enqueue samples (due to appending overlapping samples which cause
existing samples to be removed), the previous behavior was to flush and re-enqueue the new
samples dependencies; i.e., everything up to and including the previous sync sample. This causes
the decoder to visibly stall while it decodes those non-displaying samples, which could be
a second or more worth of encoded video samples, depending on the frequency of sync samples.

Instead, when we are asked to re-enqueue, we will look for the next occurring sync sample.
If found, we can switch over to the replacement samples at that point in the decode queue.
This limits the overhead of a stream switch, and should allow for a visually seamless switch,
at the cost of having to wait for the next sync sample to occur to affect the switch.

* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::seekToTime): Clear the decode queue when seeking.
(WebCore::SourceBuffer::sourceBufferPrivateAppendComplete): Call reenqueueMediaForCurrentTime.
(WebCore::SourceBuffer::reenqueueMediaForCurrentTime): Switch over to the new stream only
    at the next sync sample.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (171994 => 171995)


--- trunk/Source/WebCore/ChangeLog	2014-08-04 17:23:49 UTC (rev 171994)
+++ trunk/Source/WebCore/ChangeLog	2014-08-04 17:26:42 UTC (rev 171995)
@@ -1,3 +1,27 @@
+2014-08-04  Jer Noble  <[email protected]>
+
+        [MSE] Re-enqueing due to overlapping appended samples can cause stuttering playback
+        https://bugs.webkit.org/show_bug.cgi?id=135424
+
+        Reviewed by Eric Carlson.
+
+        If it become necessary to re-enqueue samples (due to appending overlapping samples which cause
+        existing samples to be removed), the previous behavior was to flush and re-enqueue the new
+        samples dependencies; i.e., everything up to and including the previous sync sample. This causes
+        the decoder to visibly stall while it decodes those non-displaying samples, which could be
+        a second or more worth of encoded video samples, depending on the frequency of sync samples.
+
+        Instead, when we are asked to re-enqueue, we will look for the next occurring sync sample.
+        If found, we can switch over to the replacement samples at that point in the decode queue.
+        This limits the overhead of a stream switch, and should allow for a visually seamless switch,
+        at the cost of having to wait for the next sync sample to occur to affect the switch.
+
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::seekToTime): Clear the decode queue when seeking.
+        (WebCore::SourceBuffer::sourceBufferPrivateAppendComplete): Call reenqueueMediaForCurrentTime.
+        (WebCore::SourceBuffer::reenqueueMediaForCurrentTime): Switch over to the new stream only
+            at the next sync sample.
+
 2014-08-04  Chris Fleizach  <[email protected]>
 
         AX: The Dictation command "Replace <phrase> with <phrase>" always capitalizes the replacement string

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (171994 => 171995)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-04 17:23:49 UTC (rev 171994)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-04 17:26:42 UTC (rev 171995)
@@ -317,6 +317,7 @@
         TrackBuffer& trackBuffer = trackBufferPair.value;
         const AtomicString& trackID = trackBufferPair.key;
 
+        trackBuffer.decodeQueue.clear();
         reenqueueMediaForTime(trackBuffer, trackID, time);
     }
 }
@@ -498,14 +499,13 @@
     if (m_source)
         m_source->monitorSourceBuffers();
 
-    MediaTime currentMediaTime = MediaTime::createWithDouble(m_source->currentTime());
     for (auto& trackBufferPair : m_trackBufferMap) {
         TrackBuffer& trackBuffer = trackBufferPair.value;
         const AtomicString& trackID = trackBufferPair.key;
 
         if (trackBuffer.needsReenqueueing) {
-            LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - reenqueuing at time (%s)", this, toString(currentMediaTime).utf8().data());
-            reenqueueMediaForTime(trackBuffer, trackID, currentMediaTime);
+            LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - reenqueuing", this);
+            reenqueueMediaForCurrentTime(trackBuffer, trackID);
         } else
             provideMediaData(trackBuffer, trackID);
     }
@@ -1393,6 +1393,33 @@
     LOG(MediaSource, "SourceBuffer::provideMediaData(%p) - Enqueued %u samples", this, enqueuedSamples);
 }
 
+void SourceBuffer::reenqueueMediaForCurrentTime(TrackBuffer& trackBuffer, AtomicString trackID)
+{
+    if (!trackBuffer.decodeQueue.empty()) {
+        // If the decodeQueue is not empty, attempt to find the next sync sample after the last enqueued presentation time.
+        auto nextSyncSampleIter = trackBuffer.samples.decodeOrder().findSyncSampleAfterPresentationTime(trackBuffer.lastEnqueuedPresentationTime);
+
+        auto decodeEnd = trackBuffer.samples.decodeOrder().end();
+        if (nextSyncSampleIter != decodeEnd) {
+            // If a sync sample is found, remove all existing samples from the decode queue whose decodeTimestamps are
+            // greater-than-or-equal-to the sync sample's decodeTimestamp.
+            auto firstEnqueuedSampleToRemoveIter = trackBuffer.decodeQueue.lower_bound(nextSyncSampleIter->first);
+            trackBuffer.decodeQueue.erase(firstEnqueuedSampleToRemoveIter, trackBuffer.decodeQueue.end());
+
+            // Add the replacement samples, starting from the sync sample, to the decode queue.
+            for (auto iter = nextSyncSampleIter; iter != decodeEnd; ++iter)
+                trackBuffer.decodeQueue.insert(*iter);
+            trackBuffer.needsReenqueueing = false;
+
+            // And provide those replacement samples to the decoder.
+            provideMediaData(trackBuffer, trackID);
+            return;
+        }
+    }
+
+    reenqueueMediaForTime(trackBuffer, trackID, MediaTime::createWithDouble(m_source->currentTime()));
+}
+
 void SourceBuffer::reenqueueMediaForTime(TrackBuffer& trackBuffer, AtomicString trackID, const MediaTime& time)
 {
     // Find the sample which contains the current presentation time.

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h (171994 => 171995)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-08-04 17:23:49 UTC (rev 171994)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-08-04 17:26:42 UTC (rev 171995)
@@ -150,6 +150,7 @@
     bool validateInitializationSegment(const InitializationSegment&);
 
     struct TrackBuffer;
+    void reenqueueMediaForCurrentTime(TrackBuffer&, AtomicString trackID);
     void reenqueueMediaForTime(TrackBuffer&, AtomicString trackID, const MediaTime&);
     void provideMediaData(TrackBuffer&, AtomicString trackID);
     void didDropSample();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to