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