Title: [171316] trunk
Revision
171316
Author
[email protected]
Date
2014-07-21 13:47:03 -0700 (Mon, 21 Jul 2014)

Log Message

[MSE] YouTube video decode error when variant-switching
https://bugs.webkit.org/show_bug.cgi?id=135128

Reviewed by Brent Fulgham.

Source/WebCore:
Test: media/media-source/media-source-overlapping-decodetime.html

When variant-switching, the situation can arise where an existing sample with a presentation
timestamp of N and a decode timestamp of M, and a new sample with a presentation timestamp > N
and the same decode timestamp of M, will keep the new sample from being added to the SampleMap.
This can result in a decode error when samples depending on that new, missing sample are enqueued.

The MSE spec is silent on the issue of overlapping decode timestamps. However, it guarantees that
presentation timestamps are non-overlapping. So instead of using just the decode timestamp as a key
for storing the samples in decode order, use both the decode timestamp and the presentation timestamp.
That ensures that samples with different presentation times but equal decode times are both inserted
into the decode queue, and in the correct order.

* Modules/mediasource/SampleMap.cpp:
(WebCore::SampleIsRandomAccess::operator()): Update the parameter type to match the new KeyType.
(WebCore::SampleMap::addSample): Pass both decodeTime and presentationTime as the key to decodeOrder.
(WebCore::SampleMap::removeSample): Ditto.
(WebCore::DecodeOrderSampleMap::findSampleWithDecodeKey): Renamed from findSampleWithDecodeTime.
(WebCore::DecodeOrderSampleMap::reverseFindSampleWithDecodeKey): renamed from reverseFindSampleWithDecodeTime.
(WebCore::DecodeOrderSampleMap::findSyncSamplePriorToPresentationTime): Use renamed version of above.
(WebCore::DecodeOrderSampleMap::findSyncSampleAfterPresentationTime): Ditto.
(WebCore::DecodeOrderSampleMap::findDependentSamples): Ditto.
(WebCore::DecodeOrderSampleMap::findSampleWithDecodeTime): Deleted.
(WebCore::DecodeOrderSampleMap::reverseFindSampleWithDecodeTime): Deleted.
* Modules/mediasource/SampleMap.h:
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::removeCodedFrames): Ditto.
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Ditto.
(WebCore::SourceBuffer::reenqueueMediaForTime): Ditto.

LayoutTests:
* media/media-source/media-source-overlapping-decodetime-expected.txt: Added.
* media/media-source/media-source-overlapping-decodetime.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (171315 => 171316)


--- trunk/LayoutTests/ChangeLog	2014-07-21 19:26:15 UTC (rev 171315)
+++ trunk/LayoutTests/ChangeLog	2014-07-21 20:47:03 UTC (rev 171316)
@@ -1,3 +1,13 @@
+2014-07-21  Jer Noble  <[email protected]>
+
+        [MSE] YouTube video decode error when variant-switching
+        https://bugs.webkit.org/show_bug.cgi?id=135128
+
+        Reviewed by Brent Fulgham.
+
+        * media/media-source/media-source-overlapping-decodetime-expected.txt: Added.
+        * media/media-source/media-source-overlapping-decodetime.html: Added.
+
 2014-07-21  Alexey Proskuryakov  <[email protected]>
 
         fast/canvas/canvas-putImageData-zero-alpha.html is flaky

Added: trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime-expected.txt (0 => 171316)


--- trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime-expected.txt	2014-07-21 20:47:03 UTC (rev 171316)
@@ -0,0 +1,20 @@
+
+RUN(video.src = ""
+EVENT(sourceopen)
+RUN(sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(updateend)
+RUN(sourceBuffer.appendBuffer(samples))
+EVENT(updateend)
+RUN(sourceBuffer.appendBuffer(samples))
+EVENT(updateend)
+EXPECTED (bufferedSamples.length == '7') OK
+{PTS({0/1000, 0.000000}), DTS({0/1000, 0.000000}), duration({1000/1000, 1.000000}), flags(1), generation(0)}
+{PTS({1000/1000, 1.000000}), DTS({1000/1000, 1.000000}), duration({1000/1000, 1.000000}), flags(0), generation(0)}
+{PTS({2000/1000, 2.000000}), DTS({2000/1000, 2.000000}), duration({1000/1000, 1.000000}), flags(0), generation(0)}
+{PTS({3000/1000, 3.000000}), DTS({3000/1000, 3.000000}), duration({1000/1000, 1.000000}), flags(1), generation(0)}
+{PTS({4000/1000, 4.000000}), DTS({3000/1000, 3.000000}), duration({1000/1000, 1.000000}), flags(1), generation(1)}
+{PTS({5000/1000, 5.000000}), DTS({4000/1000, 4.000000}), duration({1000/1000, 1.000000}), flags(0), generation(1)}
+{PTS({6000/1000, 6.000000}), DTS({5000/1000, 5.000000}), duration({1000/1000, 1.000000}), flags(0), generation(1)}
+END OF TEST
+

Added: trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime.html (0 => 171316)


--- trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-overlapping-decodetime.html	2014-07-21 20:47:03 UTC (rev 171316)
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>mock-media-source</title>
+    <script src=""
+    <script src=""
+    <script>
+    var source;
+    var sourceBuffer;
+    var initSegment;
+    var samples;
+    var bufferedSamples;
+
+    if (window.internals)
+        internals.initializeMockMediaSource();
+
+    function runTest()
+    {
+        findMediaElement();
+
+        source = new MediaSource();
+        waitForEventOn(source, 'sourceopen', sourceOpen);
+        run('video.src = ""
+    }
+
+    function sourceOpen()
+    {
+        run('sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock")');
+        waitForEventOn(sourceBuffer, 'updateend', loadOrderedSamples, false, true);
+        initSegment = makeAInit(8, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+    }
+
+    function loadOrderedSamples()
+    {
+       samples = concatenateSamples([
+            makeASample(0, 0, 1, 1, SAMPLE_FLAG.SYNC, 0),
+            makeASample(1, 1, 1, 1, SAMPLE_FLAG.NONE, 0),
+            makeASample(2, 2, 1, 1, SAMPLE_FLAG.NONE, 0),
+            makeASample(3, 3, 1, 1, SAMPLE_FLAG.SYNC, 0),
+            makeASample(4, 4, 1, 1, SAMPLE_FLAG.NONE, 0),
+            makeASample(5, 5, 1, 1, SAMPLE_FLAG.NONE, 0),
+        ]);
+        waitForEventOn(sourceBuffer, 'updateend', loadMoreOrderedSamples, false, true);
+        run('sourceBuffer.appendBuffer(samples)');
+    }
+
+    function loadMoreOrderedSamples()
+    {
+        samples = concatenateSamples([
+            makeASample(4, 3, 1, 1, SAMPLE_FLAG.SYNC, 1),
+            makeASample(5, 4, 1, 1, SAMPLE_FLAG.NONE, 1),
+            makeASample(6, 5, 1, 1, SAMPLE_FLAG.NONE, 1),
+        ]);
+        waitForEventOn(sourceBuffer, 'updateend', samplesAdded, false, true);
+        run('sourceBuffer.appendBuffer(samples)');
+    }
+
+     function samplesAdded()
+    {
+        bufferedSamples = internals.bufferedSamplesForTrackID(sourceBuffer, 1);
+        testExpected("bufferedSamples.length", 7);
+        bufferedSamples.forEach(consoleWrite);
+
+        endTest();
+    }
+    </script>
+</head>
+<body _onload_="runTest()">
+    <video></video>
+</body>

Modified: trunk/Source/WebCore/ChangeLog (171315 => 171316)


--- trunk/Source/WebCore/ChangeLog	2014-07-21 19:26:15 UTC (rev 171315)
+++ trunk/Source/WebCore/ChangeLog	2014-07-21 20:47:03 UTC (rev 171316)
@@ -1,3 +1,40 @@
+2014-07-21  Jer Noble  <[email protected]>
+
+        [MSE] YouTube video decode error when variant-switching
+        https://bugs.webkit.org/show_bug.cgi?id=135128
+
+        Reviewed by Brent Fulgham.
+
+        Test: media/media-source/media-source-overlapping-decodetime.html
+
+        When variant-switching, the situation can arise where an existing sample with a presentation
+        timestamp of N and a decode timestamp of M, and a new sample with a presentation timestamp > N
+        and the same decode timestamp of M, will keep the new sample from being added to the SampleMap.
+        This can result in a decode error when samples depending on that new, missing sample are enqueued.
+
+        The MSE spec is silent on the issue of overlapping decode timestamps. However, it guarantees that
+        presentation timestamps are non-overlapping. So instead of using just the decode timestamp as a key
+        for storing the samples in decode order, use both the decode timestamp and the presentation timestamp.
+        That ensures that samples with different presentation times but equal decode times are both inserted
+        into the decode queue, and in the correct order.
+
+        * Modules/mediasource/SampleMap.cpp:
+        (WebCore::SampleIsRandomAccess::operator()): Update the parameter type to match the new KeyType.
+        (WebCore::SampleMap::addSample): Pass both decodeTime and presentationTime as the key to decodeOrder.
+        (WebCore::SampleMap::removeSample): Ditto.
+        (WebCore::DecodeOrderSampleMap::findSampleWithDecodeKey): Renamed from findSampleWithDecodeTime.
+        (WebCore::DecodeOrderSampleMap::reverseFindSampleWithDecodeKey): renamed from reverseFindSampleWithDecodeTime.
+        (WebCore::DecodeOrderSampleMap::findSyncSamplePriorToPresentationTime): Use renamed version of above.
+        (WebCore::DecodeOrderSampleMap::findSyncSampleAfterPresentationTime): Ditto.
+        (WebCore::DecodeOrderSampleMap::findDependentSamples): Ditto.
+        (WebCore::DecodeOrderSampleMap::findSampleWithDecodeTime): Deleted.
+        (WebCore::DecodeOrderSampleMap::reverseFindSampleWithDecodeTime): Deleted.
+        * Modules/mediasource/SampleMap.h:
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::removeCodedFrames): Ditto.
+        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Ditto.
+        (WebCore::SourceBuffer::reenqueueMediaForTime): Ditto.
+
 2014-07-21  Andy Estes  <[email protected]>
 
         [iOS] Handle QuickLook ResourceLoaders in the web process

Modified: trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp (171315 => 171316)


--- trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp	2014-07-21 19:26:15 UTC (rev 171315)
+++ trunk/Source/WebCore/Modules/mediasource/SampleMap.cpp	2014-07-21 20:47:03 UTC (rev 171316)
@@ -66,7 +66,7 @@
 
 class SampleIsRandomAccess {
 public:
-    bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value)
+    bool operator()(DecodeOrderSampleMap::MapType::value_type& value)
     {
         return value.second->flags() == MediaSample::IsSync;
     }
@@ -112,9 +112,12 @@
 {
     RefPtr<MediaSample> sample = prpSample;
     ASSERT(sample);
+
     presentationOrder().m_samples.insert(PresentationOrderSampleMap::MapType::value_type(sample->presentationTime(), sample));
-    decodeOrder().m_samples.insert(DecodeOrderSampleMap::MapType::value_type(sample->decodeTime(), sample));
 
+    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), sample->presentationTime());
+    decodeOrder().m_samples.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, sample));
+
     m_totalSize += sample->sizeInBytes();
 }
 
@@ -122,8 +125,10 @@
 {
     ASSERT(sample);
     presentationOrder().m_samples.erase(sample->presentationTime());
-    decodeOrder().m_samples.erase(sample->decodeTime());
 
+    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), sample->presentationTime());
+    decodeOrder().m_samples.erase(decodeKey);
+
     m_totalSize -= sample->sizeInBytes();
 }
 
@@ -148,9 +153,9 @@
     return m_samples.lower_bound(time);
 }
 
-DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSampleWithDecodeTime(const MediaTime& time)
+DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSampleWithDecodeKey(const KeyType& key)
 {
-    return m_samples.find(time);
+    return m_samples.find(key);
 }
 
 PresentationOrderSampleMap::reverse_iterator PresentationOrderSampleMap::reverseFindSampleContainingPresentationTime(const MediaTime& time)
@@ -166,9 +171,9 @@
     return std::lower_bound(rbegin(), rend(), time, SampleIsGreaterThanMediaTimeComparator<MapType>());
 }
 
-DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::reverseFindSampleWithDecodeTime(const MediaTime& time)
+DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::reverseFindSampleWithDecodeKey(const KeyType& key)
 {
-    DecodeOrderSampleMap::iterator found = findSampleWithDecodeTime(time);
+    DecodeOrderSampleMap::iterator found = findSampleWithDecodeKey(key);
     if (found == end())
         return rend();
     return --reverse_iterator(found);
@@ -180,7 +185,8 @@
     if (reverseCurrentSamplePTS == m_presentationOrder.rend())
         return rend();
 
-    reverse_iterator reverseCurrentSampleDTS = reverseFindSampleWithDecodeTime(reverseCurrentSamplePTS->second->decodeTime());
+    const RefPtr<MediaSample>& sample = reverseCurrentSamplePTS->second;
+    reverse_iterator reverseCurrentSampleDTS = reverseFindSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
 
     reverse_iterator foundSample = findSyncSamplePriorToDecodeIterator(reverseCurrentSampleDTS);
     if (foundSample == rend())
@@ -201,7 +207,8 @@
     if (currentSamplePTS == m_presentationOrder.end())
         return end();
 
-    iterator currentSampleDTS = findSampleWithDecodeTime(currentSamplePTS->second->decodeTime());
+    const RefPtr<MediaSample>& sample = currentSamplePTS->second;
+    iterator currentSampleDTS = findSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
     
     MediaTime upperBound = time + threshold;
     iterator foundSample = std::find_if(currentSampleDTS, end(), SampleIsRandomAccess());
@@ -234,7 +241,7 @@
 DecodeOrderSampleMap::reverse_iterator_range DecodeOrderSampleMap::findDependentSamples(MediaSample* sample)
 {
     ASSERT(sample);
-    reverse_iterator currentDecodeIter = reverseFindSampleWithDecodeTime(sample->decodeTime());
+    reverse_iterator currentDecodeIter = reverseFindSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
     reverse_iterator nextSyncSample = findSyncSamplePriorToDecodeIterator(currentDecodeIter);
     return reverse_iterator_range(currentDecodeIter, nextSyncSample);
 }

Modified: trunk/Source/WebCore/Modules/mediasource/SampleMap.h (171315 => 171316)


--- trunk/Source/WebCore/Modules/mediasource/SampleMap.h	2014-07-21 19:26:15 UTC (rev 171315)
+++ trunk/Source/WebCore/Modules/mediasource/SampleMap.h	2014-07-21 20:47:03 UTC (rev 171316)
@@ -65,7 +65,8 @@
 class DecodeOrderSampleMap {
     friend class SampleMap;
 public:
-    typedef std::map<MediaTime, RefPtr<MediaSample>> MapType;
+    typedef std::pair<MediaTime, MediaTime> KeyType;
+    typedef std::map<KeyType, RefPtr<MediaSample>> MapType;
     typedef MapType::iterator iterator;
     typedef MapType::reverse_iterator reverse_iterator;
     typedef std::pair<reverse_iterator, reverse_iterator> reverse_iterator_range;
@@ -75,8 +76,8 @@
     reverse_iterator rbegin() { return m_samples.rbegin(); }
     reverse_iterator rend() { return m_samples.rend(); }
 
-    iterator findSampleWithDecodeTime(const MediaTime&);
-    reverse_iterator reverseFindSampleWithDecodeTime(const MediaTime&);
+    iterator findSampleWithDecodeKey(const KeyType&);
+    reverse_iterator reverseFindSampleWithDecodeKey(const KeyType&);
     reverse_iterator findSyncSamplePriorToPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
     reverse_iterator findSyncSamplePriorToDecodeIterator(reverse_iterator);
     iterator findSyncSampleAfterPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (171315 => 171316)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-07-21 19:26:15 UTC (rev 171315)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-07-21 20:47:03 UTC (rev 171316)
@@ -562,7 +562,8 @@
         // and the next sync sample frame are removed. But we must start from the first sample in decode order, not
         // presentation order.
         PresentationOrderSampleMap::iterator minDecodeTimeIter = std::min_element(removePresentationStart, removePresentationEnd, decodeTimeComparator);
-        DecodeOrderSampleMap::iterator removeDecodeStart = trackBuffer.samples.decodeOrder().findSampleWithDecodeTime(minDecodeTimeIter->second->decodeTime());
+        DecodeOrderSampleMap::KeyType decodeKey(minDecodeTimeIter->second->decodeTime(), minDecodeTimeIter->second->presentationTime());
+        DecodeOrderSampleMap::iterator removeDecodeStart = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
 
         DecodeOrderSampleMap::MapType erasedSamples(removeDecodeStart, removeDecodeEnd);
 
@@ -1157,8 +1158,8 @@
 
             // Otherwise: Remove all coded frames between the coded frames removed in the previous step
             // and the next random access point after those removed frames.
-            auto firstDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeTime(erasedSamples.decodeOrder().begin()->first);
-            auto lastDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeTime(erasedSamples.decodeOrder().rbegin()->first);
+            auto firstDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().begin()->first);
+            auto lastDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().rbegin()->first);
             auto nextSyncIter = trackBuffer.samples.decodeOrder().findSyncSampleAfterDecodeIterator(lastDecodeIter);
             dependentSamples.insert(firstDecodeIter, nextSyncIter);
 
@@ -1186,8 +1187,10 @@
         // Otherwise:
         // Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
         trackBuffer.samples.addSample(sample);
-        trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeTimestamp, sample));
 
+        DecodeOrderSampleMap::KeyType decodeKey(decodeTimestamp, presentationTimestamp);
+        trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, sample));
+
         // 1.18 Set last decode timestamp for track buffer to decode timestamp.
         trackBuffer.lastDecodeTimestamp = decodeTimestamp;
 
@@ -1380,8 +1383,8 @@
     }
 
     // Seach backward for the previous sync sample.
-    MediaTime currentSampleDecodeTime = currentSamplePTSIterator->second->decodeTime();
-    auto currentSampleDTSIterator = trackBuffer.samples.decodeOrder().findSampleWithDecodeTime(currentSampleDecodeTime);
+    DecodeOrderSampleMap::KeyType decodeKey(currentSamplePTSIterator->second->decodeTime(), currentSamplePTSIterator->second->presentationTime());
+    auto currentSampleDTSIterator = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
     ASSERT(currentSampleDTSIterator != trackBuffer.samples.decodeOrder().end());
 
     auto reverseCurrentSampleIter = --DecodeOrderSampleMap::reverse_iterator(currentSampleDTSIterator);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to