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();