Title: [238086] trunk
Revision
238086
Author
jer.no...@apple.com
Date
2018-11-12 07:12:35 -0800 (Mon, 12 Nov 2018)

Log Message

[MSE] Frame re-ordering can cause iframes to never be enqueued
https://bugs.webkit.org/show_bug.cgi?id=191485

Reviewed by Eric Carlson.

Source/WebCore:

Test: media/media-source/media-source-dropped-iframe.html

Some frame re-ordering techniques result in files where the first frame has a
decode timestamp < 0, but a presentation timestamp >= 0. When appending these
samples to existing content, we can fail to enqueue the first frame because its
DTS overlaps an existing sample, but the presentation timestamp does not.
Rather than try to only enqueue samples whose decode timestamps are > than the
greatest decode end timestamp (minus some fudge factor), allow all frames to be
added to the decode queue if they are strictly ordered greater than the last
enqueued frame.

* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::TrackBuffer::TrackBuffer):
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):
(WebCore::SourceBuffer::provideMediaData):
(WebCore::SourceBuffer::reenqueueMediaForTime):

LayoutTests:

* media/media-source/media-source-dropped-iframe-expected.txt: Added.
* media/media-source/media-source-dropped-iframe.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (238085 => 238086)


--- trunk/LayoutTests/ChangeLog	2018-11-12 14:55:03 UTC (rev 238085)
+++ trunk/LayoutTests/ChangeLog	2018-11-12 15:12:35 UTC (rev 238086)
@@ -1,3 +1,13 @@
+2018-11-12  Jer Noble  <jer.no...@apple.com>
+
+        [MSE] Frame re-ordering can cause iframes to never be enqueued
+        https://bugs.webkit.org/show_bug.cgi?id=191485
+
+        Reviewed by Eric Carlson.
+
+        * media/media-source/media-source-dropped-iframe-expected.txt: Added.
+        * media/media-source/media-source-dropped-iframe.html: Added.
+
 2018-11-11  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Implement a new edit command to change the enclosing list type

Added: trunk/LayoutTests/media/media-source/media-source-dropped-iframe-expected.txt (0 => 238086)


--- trunk/LayoutTests/media/media-source/media-source-dropped-iframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-dropped-iframe-expected.txt	2018-11-12 15:12:35 UTC (rev 238086)
@@ -0,0 +1,27 @@
+
+EXPECTED (source.readyState == 'closed') OK
+EVENT(sourceopen)
+RUN(sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock"))
+RUN(sourceBuffer.appendBuffer(initSegment))
+EVENT(updateend)
+RUN(sourceBuffer.appendBuffer(samples))
+EVENT(updateend)
+RUN(sourceBuffer.timestampOffset = 3)
+RUN(sourceBuffer.appendBuffer(samples))
+EVENT(updateend)
+Buffered:
+{PTS({0/1 = 0.000000}), DTS({0/1 = 0.000000}), duration({1/1 = 1.000000}), flags(1), generation(1)}
+{PTS({1/1 = 1.000000}), DTS({1/1 = 1.000000}), duration({1/1 = 1.000000}), flags(0), generation(1)}
+{PTS({2/1 = 2.000000}), DTS({2/1 = 2.000000}), duration({1/1 = 1.000000}), flags(0), generation(1)}
+{PTS({3/1 = 3.000000}), DTS({2/1 = 2.000000}), duration({1/1 = 1.000000}), flags(1), generation(2)}
+{PTS({5/1 = 5.000000}), DTS({3/1 = 3.000000}), duration({1/1 = 1.000000}), flags(0), generation(2)}
+{PTS({4/1 = 4.000000}), DTS({4/1 = 4.000000}), duration({1/1 = 1.000000}), flags(0), generation(2)}
+Enqueued:
+{PTS({0/1 = 0.000000}), DTS({0/1 = 0.000000}), duration({1/1 = 1.000000}), flags(1), generation(1)}
+{PTS({1/1 = 1.000000}), DTS({1/1 = 1.000000}), duration({1/1 = 1.000000}), flags(0), generation(1)}
+{PTS({2/1 = 2.000000}), DTS({2/1 = 2.000000}), duration({1/1 = 1.000000}), flags(0), generation(1)}
+{PTS({3/1 = 3.000000}), DTS({2/1 = 2.000000}), duration({1/1 = 1.000000}), flags(1), generation(2)}
+{PTS({5/1 = 5.000000}), DTS({3/1 = 3.000000}), duration({1/1 = 1.000000}), flags(0), generation(2)}
+{PTS({4/1 = 4.000000}), DTS({4/1 = 4.000000}), duration({1/1 = 1.000000}), flags(0), generation(2)}
+END OF TEST
+

Added: trunk/LayoutTests/media/media-source/media-source-dropped-iframe.html (0 => 238086)


--- trunk/LayoutTests/media/media-source/media-source-dropped-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-source/media-source-dropped-iframe.html	2018-11-12 15:12:35 UTC (rev 238086)
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>media-source-dropped-iframe</title>
+    <script src=""
+    <script src=""
+    <script>
+    var source;
+    var sourceBuffer;
+    var initSegment;
+    var samples;
+
+    if (window.internals)
+        internals.initializeMockMediaSource();
+
+    async function runTest() {
+        findMediaElement();
+
+        source = new MediaSource();
+        testExpected('source.readyState', 'closed');
+
+        video.srcObject = source;
+        await waitFor(source, 'sourceopen');
+
+        run('sourceBuffer = source.addSourceBuffer("video/mock; codecs=mock")');
+        initSegment = makeAInit(8, [makeATrack(1, 'mock', TRACK_KIND.VIDEO)]);
+        run('sourceBuffer.appendBuffer(initSegment)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        samples = concatenateSamples([
+            makeASample(0, 0, 1, 1, 1, SAMPLE_FLAG.SYNC, 1),
+            makeASample(1, 1, 1, 1, 1, SAMPLE_FLAG.NONE, 1),
+            makeASample(2, 2, 1, 1, 1, SAMPLE_FLAG.NONE, 1),
+        ]);
+        run('sourceBuffer.appendBuffer(samples)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        run('sourceBuffer.timestampOffset = 3');
+        samples = concatenateSamples([
+            makeASample(0, -1, 1, 1, 1, SAMPLE_FLAG.SYNC, 2),
+            makeASample(2,  0, 1, 1, 1, SAMPLE_FLAG.NONE, 2),
+            makeASample(1,  1, 1, 1, 1, SAMPLE_FLAG.NONE, 2),
+        ]);
+        run('sourceBuffer.appendBuffer(samples)');
+        await waitFor(sourceBuffer, 'updateend');
+
+        bufferedSamples = internals.bufferedSamplesForTrackID(sourceBuffer, 1);
+        enqueuedSamples = internals.enqueuedSamplesForTrackID(sourceBuffer, 1);
+
+        consoleWrite("Buffered:");
+        bufferedSamples.forEach(consoleWrite);
+
+        consoleWrite("Enqueued:");
+        enqueuedSamples.forEach(consoleWrite);
+
+        endTest();
+    }
+    
+    </script>
+</head>
+<body _onload_="runTest().catch(failTest)">
+    <video></video>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (238085 => 238086)


--- trunk/Source/WebCore/ChangeLog	2018-11-12 14:55:03 UTC (rev 238085)
+++ trunk/Source/WebCore/ChangeLog	2018-11-12 15:12:35 UTC (rev 238086)
@@ -1,3 +1,27 @@
+2018-11-12  Jer Noble  <jer.no...@apple.com>
+
+        [MSE] Frame re-ordering can cause iframes to never be enqueued
+        https://bugs.webkit.org/show_bug.cgi?id=191485
+
+        Reviewed by Eric Carlson.
+
+        Test: media/media-source/media-source-dropped-iframe.html
+
+        Some frame re-ordering techniques result in files where the first frame has a
+        decode timestamp < 0, but a presentation timestamp >= 0. When appending these
+        samples to existing content, we can fail to enqueue the first frame because its
+        DTS overlaps an existing sample, but the presentation timestamp does not.
+        Rather than try to only enqueue samples whose decode timestamps are > than the
+        greatest decode end timestamp (minus some fudge factor), allow all frames to be
+        added to the decode queue if they are strictly ordered greater than the last
+        enqueued frame.
+
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::TrackBuffer::TrackBuffer):
+        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):
+        (WebCore::SourceBuffer::provideMediaData):
+        (WebCore::SourceBuffer::reenqueueMediaForTime):
+
 2018-11-12  Yusuke Suzuki  <yusukesuz...@slowstart.org>
 
         IDBTransaction does not use "RefPtr<IDBTransaction> self"

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (238085 => 238086)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2018-11-12 14:55:03 UTC (rev 238085)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2018-11-12 15:12:35 UTC (rev 238086)
@@ -67,7 +67,8 @@
     MediaTime lastFrameDuration;
     MediaTime highestPresentationTimestamp;
     MediaTime lastEnqueuedPresentationTime;
-    MediaTime lastEnqueuedDecodeEndTime;
+    DecodeOrderSampleMap::KeyType lastEnqueuedDecodeKey;
+    MediaTime lastEnqueuedDecodeDuration;
     MediaTime roundedTimestampOffset;
     uint32_t lastFrameTimescale { 0 };
     bool needRandomAccessFlag { true };
@@ -84,7 +85,8 @@
         , lastFrameDuration(MediaTime::invalidTime())
         , highestPresentationTimestamp(MediaTime::invalidTime())
         , lastEnqueuedPresentationTime(MediaTime::invalidTime())
-        , lastEnqueuedDecodeEndTime(MediaTime::invalidTime())
+        , lastEnqueuedDecodeKey({MediaTime::invalidTime(), MediaTime::invalidTime()})
+        , lastEnqueuedDecodeDuration(MediaTime::invalidTime())
     {
     }
 };
@@ -1748,9 +1750,9 @@
         // queue are "enqueued" (sent to the inner media framework) in `provideMediaData()`.
         //
         // In order to check whether a frame should be added to the decode queue we check whether it starts after the
-        // lastEnqueuedDecodeEndTime or even a bit before that to accomodate files with imprecise timing information.
-        if (trackBuffer.lastEnqueuedDecodeEndTime.isInvalid() || decodeTimestamp >= (trackBuffer.lastEnqueuedDecodeEndTime - contiguousFrameTolerance)) {
-            DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
+        // lastEnqueuedDecodeKey.
+        DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
+        if (trackBuffer.lastEnqueuedDecodeKey.first.isInvalid() || decodeKey > trackBuffer.lastEnqueuedDecodeKey) {
             trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample));
         }
 
@@ -2004,7 +2006,9 @@
         // 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)
+        if (trackBuffer.lastEnqueuedDecodeKey.first.isValid()
+            && trackBuffer.lastEnqueuedDecodeDuration.isValid()
+            && sample->decodeTime() - trackBuffer.lastEnqueuedDecodeKey.first > oneSecond + trackBuffer.lastEnqueuedDecodeDuration)
             break;
 
         // Remove the sample from the decode queue now.
@@ -2011,7 +2015,8 @@
         trackBuffer.decodeQueue.erase(trackBuffer.decodeQueue.begin());
 
         trackBuffer.lastEnqueuedPresentationTime = sample->presentationTime();
-        trackBuffer.lastEnqueuedDecodeEndTime = sample->decodeTime() + sample->duration();
+        trackBuffer.lastEnqueuedDecodeKey = {sample->decodeTime(), sample->presentationTime()};
+        trackBuffer.lastEnqueuedDecodeDuration = sample->duration();
         m_private->enqueueSample(sample.releaseNonNull(), trackID);
 #if !LOG_DISABLED
         ++enqueuedSamples;
@@ -2070,12 +2075,16 @@
     }
 
     if (!trackBuffer.decodeQueue.empty()) {
-        auto& lastSample = trackBuffer.decodeQueue.rbegin()->second;
-        trackBuffer.lastEnqueuedPresentationTime = lastSample->presentationTime();
-        trackBuffer.lastEnqueuedDecodeEndTime = lastSample->decodeTime();
+        auto lastSampleIter = trackBuffer.decodeQueue.rbegin();
+        auto lastSampleDecodeKey = lastSampleIter->first;
+        auto lastSampleDuration = lastSampleIter->second->duration();
+        trackBuffer.lastEnqueuedPresentationTime = lastSampleDecodeKey.second;
+        trackBuffer.lastEnqueuedDecodeKey = lastSampleDecodeKey;
+        trackBuffer.lastEnqueuedDecodeDuration = lastSampleDuration;
     } else {
         trackBuffer.lastEnqueuedPresentationTime = MediaTime::invalidTime();
-        trackBuffer.lastEnqueuedDecodeEndTime = MediaTime::invalidTime();
+        trackBuffer.lastEnqueuedDecodeKey = {MediaTime::invalidTime(), MediaTime::invalidTime()};
+        trackBuffer.lastEnqueuedDecodeDuration = MediaTime::invalidTime();
     }
 
     // Fill the decode queue with the remaining samples.
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to