Title: [172506] trunk/Source/WebCore
Revision
172506
Author
[email protected]
Date
2014-08-12 16:37:34 -0700 (Tue, 12 Aug 2014)

Log Message

[MSE] YouTube will lose audio, video after seeking backwards to an unbuffered range.
https://bugs.webkit.org/show_bug.cgi?id=135855

Reviewed by Eric Carlson.

When seeking into an unbuffered or partially buffered range, we will unconditionally pass samples to the
decode queue even if there exist large gaps between those samples. Subsequently, the decoder will not
notify us that it has become ready for new samples until playback reaches those later samples and the samples
are discarded.

When sending samples to be decoded in provideMediaData(), stop if there exists a large gap in the sample timeline.
Do this by tracking the last enqueued decode end time, and look to see if the next sample's decode time indicates
a gap of greater than 1 second.

* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::TrackBuffer::TrackBuffer): Initialize lastEnqueuedDecodeEndTime.
(WebCore::SourceBuffer::seekToTime): Set needsReenqueueing, in case samples do not yet exist in the trackBuffer
    sufficient to re-enqueue for the destination time, so that re-enqueueing will occur after the next append.
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Check against lastEnqueuedDecodeEndTime, rather than
    lastEnqueuedPresentationTime before adding samples to the decodeQueue.
(WebCore::SourceBuffer::provideMediaData): Stop when we reach a large gap between samples.
(WebCore::SourceBuffer::reenqueueMediaForTime): Set or clear lastEnqueuedDecodeEndTime based on whether we
    have appended any non-displaying samples after flushing.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (172505 => 172506)


--- trunk/Source/WebCore/ChangeLog	2014-08-12 23:15:00 UTC (rev 172505)
+++ trunk/Source/WebCore/ChangeLog	2014-08-12 23:37:34 UTC (rev 172506)
@@ -1,3 +1,29 @@
+2014-08-12  Jer Noble  <[email protected]>
+
+        [MSE] YouTube will lose audio, video after seeking backwards to an unbuffered range.
+        https://bugs.webkit.org/show_bug.cgi?id=135855
+
+        Reviewed by Eric Carlson.
+
+        When seeking into an unbuffered or partially buffered range, we will unconditionally pass samples to the
+        decode queue even if there exist large gaps between those samples. Subsequently, the decoder will not
+        notify us that it has become ready for new samples until playback reaches those later samples and the samples
+        are discarded.
+
+        When sending samples to be decoded in provideMediaData(), stop if there exists a large gap in the sample timeline.
+        Do this by tracking the last enqueued decode end time, and look to see if the next sample's decode time indicates
+        a gap of greater than 1 second.
+
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::TrackBuffer::TrackBuffer): Initialize lastEnqueuedDecodeEndTime.
+        (WebCore::SourceBuffer::seekToTime): Set needsReenqueueing, in case samples do not yet exist in the trackBuffer
+            sufficient to re-enqueue for the destination time, so that re-enqueueing will occur after the next append.
+        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Check against lastEnqueuedDecodeEndTime, rather than
+            lastEnqueuedPresentationTime before adding samples to the decodeQueue.
+        (WebCore::SourceBuffer::provideMediaData): Stop when we reach a large gap between samples.
+        (WebCore::SourceBuffer::reenqueueMediaForTime): Set or clear lastEnqueuedDecodeEndTime based on whether we
+            have appended any non-displaying samples after flushing.
+
 2014-08-12  Commit Queue  <[email protected]>
 
         Unreviewed, rolling out r172494.

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (172505 => 172506)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-12 23:15:00 UTC (rev 172505)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-12 23:37:34 UTC (rev 172506)
@@ -71,6 +71,7 @@
     MediaTime lastFrameDuration;
     MediaTime highestPresentationTimestamp;
     MediaTime lastEnqueuedPresentationTime;
+    MediaTime lastEnqueuedDecodeEndTime;
     bool needRandomAccessFlag;
     bool enabled;
     bool needsReenqueueing;
@@ -83,6 +84,7 @@
         , lastFrameDuration(MediaTime::invalidTime())
         , highestPresentationTimestamp(MediaTime::invalidTime())
         , lastEnqueuedPresentationTime(MediaTime::invalidTime())
+        , lastEnqueuedDecodeEndTime(MediaTime::invalidTime())
         , needRandomAccessFlag(true)
         , enabled(false)
         , needsReenqueueing(false)
@@ -317,6 +319,7 @@
         TrackBuffer& trackBuffer = trackBufferPair.value;
         const AtomicString& trackID = trackBufferPair.key;
 
+        trackBuffer.needsReenqueueing = true;
         reenqueueMediaForTime(trackBuffer, trackID, time);
     }
 }
@@ -1208,7 +1211,7 @@
         // Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
         trackBuffer.samples.addSample(sample);
 
-        if (trackBuffer.lastEnqueuedPresentationTime.isInvalid() || presentationTimestamp > trackBuffer.lastEnqueuedPresentationTime) {
+        if (trackBuffer.lastEnqueuedDecodeEndTime.isInvalid() || decodeTimestamp >= trackBuffer.lastEnqueuedDecodeEndTime) {
             DecodeOrderSampleMap::KeyType decodeKey(decodeTimestamp, presentationTimestamp);
             trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, sample));
         }
@@ -1383,7 +1386,19 @@
         }
 
         RefPtr<MediaSample> sample = sampleIt->second;
+        // Do not enqueue samples spanning a significant unbuffered gap.
+        // NOTE: one second is somewhat arbitrary. MediaSource::monitorSourceBuffers() is run
+        // on the playbackTimer, which is effectively every 350ms. Allowing > 350ms gap between
+        // enqueued samples allows for situations where we overrun the end of a buffered range
+        // but don't notice for 350s of playback time, and the client can enqueue data for the
+        // new current time without triggering this early return.
+        // FIXME(135867): Make this gap detection logic less arbitrary.
+        MediaTime oneSecond(1, 1);
+        if (trackBuffer.lastEnqueuedDecodeEndTime.isValid() && sample->decodeTime() - trackBuffer.lastEnqueuedDecodeEndTime > oneSecond)
+            break;
+
         trackBuffer.lastEnqueuedPresentationTime = sample->presentationTime();
+        trackBuffer.lastEnqueuedDecodeEndTime = sample->decodeTime() + sample->duration();
         m_private->enqueueSample(sample.release(), trackID);
 #if !LOG_DISABLED
         ++enqueuedSamples;
@@ -1425,8 +1440,13 @@
 
     m_private->flushAndEnqueueNonDisplayingSamples(nonDisplayingSamples, trackID);
 
-    if (!nonDisplayingSamples.isEmpty())
+    if (!nonDisplayingSamples.isEmpty()) {
         trackBuffer.lastEnqueuedPresentationTime = nonDisplayingSamples.last()->presentationTime();
+        trackBuffer.lastEnqueuedDecodeEndTime = nonDisplayingSamples.last()->decodeTime();
+    } else {
+        trackBuffer.lastEnqueuedPresentationTime = MediaTime::invalidTime();
+        trackBuffer.lastEnqueuedDecodeEndTime = MediaTime::invalidTime();
+    }
 
     // Fill the decode queue with the remaining samples.
     trackBuffer.decodeQueue.clear();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to