Title: [283609] trunk
Revision
283609
Author
[email protected]
Date
2021-10-06 02:48:38 -0700 (Wed, 06 Oct 2021)

Log Message

[MSE][GStreamer] Honor MP4 edit lists
https://bugs.webkit.org/show_bug.cgi?id=231019

Reviewed by Xabier Rodriguez-Calvar.

Source/WebCore:

This patch takes into consideration the GstSegment attached to a
sample to offset the PTS and DTS. This ensures accurate timestamps are
obtained for MP4 files containing edit lists (commonly necessary for
files containing video with B frames to have PTS starting at zero).

Before this was implemented, a workaround was in place based on a
heuristic (DTS = 0 && PTS > 0 && PTS < 0.1). The workaround is
preserved for the sake of content without proper edit lists, but
any edit list takes preference.

The time fudge factor has been modified from 0.083 seconds up to
0.100 seconds to accomodate the size of the empty edit in test.mp4
used by Web Platform Tests.

This test fixes improves expectation results and fixes two subtests in
imported/w3c/web-platform-tests/media-source/mediasource-remove.html.

* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::currentTimeFudgeFactor):
* platform/graphics/SourceBufferPrivate.h:
(WebCore::SourceBufferPrivate::timeFudgeFactor const):
* platform/graphics/gstreamer/MediaSampleGStreamer.cpp:
(WebCore::MediaSampleGStreamer::extendToTheBeginning): Deleted.
* platform/graphics/gstreamer/MediaSampleGStreamer.h:
* platform/graphics/gstreamer/mse/AppendPipeline.cpp:
(WebCore::bufferTimeToStreamTimeClamped):
(WebCore::AppendPipeline::appsinkNewSample):

LayoutTests:

Update expectations for mediasource-remove.html in the GStreamer
ports, as a couple subtests get fixed.

* platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (283608 => 283609)


--- trunk/LayoutTests/ChangeLog	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/LayoutTests/ChangeLog	2021-10-06 09:48:38 UTC (rev 283609)
@@ -1,3 +1,15 @@
+2021-10-06  Alicia Boya García  <[email protected]>
+
+        [MSE][GStreamer] Honor MP4 edit lists
+        https://bugs.webkit.org/show_bug.cgi?id=231019
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Update expectations for mediasource-remove.html in the GStreamer
+        ports, as a couple subtests get fixed.
+
+        * platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt:
+
 2021-10-05  John Wilander  <[email protected]>
 
         PCM: Allow measurement of links in nested, cross-site iframes

Modified: trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt (283608 => 283609)


--- trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt	2021-10-06 09:48:38 UTC (rev 283609)
@@ -11,8 +11,8 @@
 PASS Test aborting a remove operation.
 PASS Test remove with a start at the duration.
 PASS Test remove transitioning readyState from 'ended' to 'open'.
-FAIL Test removing all appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
-FAIL Test removing beginning of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
-FAIL Test removing the middle of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
-FAIL Test removing the end of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
+PASS Test removing all appended data.
+PASS Test removing beginning of appended data.
+FAIL Test removing the middle of appended data. assert_equals: Buffered ranges after remove(). expected "{ [0.095, 0.997) [3.298, 6.548) }" but got "{ [0.095, 0.975) [3.298, 6.548) }"
+FAIL Test removing the end of appended data. assert_equals: Buffered ranges after remove(). expected "{ [0.095, 1.022) }" but got "{ [0.095, 0.995) }"
 

Modified: trunk/Source/WebCore/ChangeLog (283608 => 283609)


--- trunk/Source/WebCore/ChangeLog	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/ChangeLog	2021-10-06 09:48:38 UTC (rev 283609)
@@ -1,3 +1,38 @@
+2021-10-06  Alicia Boya García  <[email protected]>
+
+        [MSE][GStreamer] Honor MP4 edit lists
+        https://bugs.webkit.org/show_bug.cgi?id=231019
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        This patch takes into consideration the GstSegment attached to a
+        sample to offset the PTS and DTS. This ensures accurate timestamps are
+        obtained for MP4 files containing edit lists (commonly necessary for
+        files containing video with B frames to have PTS starting at zero).
+
+        Before this was implemented, a workaround was in place based on a
+        heuristic (DTS = 0 && PTS > 0 && PTS < 0.1). The workaround is
+        preserved for the sake of content without proper edit lists, but
+        any edit list takes preference.
+
+        The time fudge factor has been modified from 0.083 seconds up to
+        0.100 seconds to accomodate the size of the empty edit in test.mp4
+        used by Web Platform Tests.
+
+        This test fixes improves expectation results and fixes two subtests in
+        imported/w3c/web-platform-tests/media-source/mediasource-remove.html.
+
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::currentTimeFudgeFactor):
+        * platform/graphics/SourceBufferPrivate.h:
+        (WebCore::SourceBufferPrivate::timeFudgeFactor const):
+        * platform/graphics/gstreamer/MediaSampleGStreamer.cpp:
+        (WebCore::MediaSampleGStreamer::extendToTheBeginning): Deleted.
+        * platform/graphics/gstreamer/MediaSampleGStreamer.h:
+        * platform/graphics/gstreamer/mse/AppendPipeline.cpp:
+        (WebCore::bufferTimeToStreamTimeClamped):
+        (WebCore::AppendPipeline::appsinkNewSample):
+
 2021-10-06  Jer Noble  <[email protected]>
 
         [Build-time perf] Forward declare JS TypedArrays

Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp (283608 => 283609)


--- trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2021-10-06 09:48:38 UTC (rev 283609)
@@ -323,8 +323,8 @@
 
 const MediaTime& MediaSource::currentTimeFudgeFactor()
 {
-    // Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
-    static NeverDestroyed<MediaTime> fudgeFactor(2002, 24000);
+    // Allow hasCurrentTime() to be off by as much as 100ms.
+    static NeverDestroyed<MediaTime> fudgeFactor(1, 10);
     return fudgeFactor;
 }
 

Modified: trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.h (283608 => 283609)


--- trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.h	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.h	2021-10-06 09:48:38 UTC (rev 283609)
@@ -150,7 +150,7 @@
 protected:
     // The following method should never be called directly and be overridden instead.
     WEBCORE_EXPORT virtual void append(Vector<unsigned char>&&);
-    virtual MediaTime timeFudgeFactor() const { return {2002, 24000}; }
+    virtual MediaTime timeFudgeFactor() const { return {1, 10}; }
     virtual bool isActive() const { return false; }
     virtual bool isSeeking() const { return false; }
     virtual MediaTime currentMediaTime() const { return { }; }

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.cpp (283608 => 283609)


--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.cpp	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.cpp	2021-10-06 09:48:38 UTC (rev 283609)
@@ -183,15 +183,6 @@
     return JSC::Uint8ClampedArray::tryCreate(WTFMove(bufferStorage), 0, byteLength);
 }
 
-void MediaSampleGStreamer::extendToTheBeginning()
-{
-    // Only to be used with the first sample, as a hack for lack of support for edit lists.
-    // See AppendPipeline::appsinkNewSample()
-    ASSERT(m_dts == MediaTime::zeroTime());
-    m_duration += m_pts;
-    m_pts = MediaTime::zeroTime();
-}
-
 void MediaSampleGStreamer::setTimestamps(const MediaTime& presentationTime, const MediaTime& decodeTime)
 {
     m_pts = presentationTime;

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h (283608 => 283609)


--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.h	2021-10-06 09:48:38 UTC (rev 283609)
@@ -42,7 +42,6 @@
     static Ref<MediaSampleGStreamer> createFakeSample(GstCaps*, MediaTime pts, MediaTime dts, MediaTime duration, const FloatSize& presentationSize, const AtomString& trackId);
     static Ref<MediaSampleGStreamer> createImageSample(PixelBuffer&&, const IntSize& destinationSize = { }, double frameRate = 1);
 
-    void extendToTheBeginning();
     MediaTime presentationTime() const override { return m_pts; }
     MediaTime decodeTime() const override { return m_dts; }
     MediaTime duration() const override { return m_duration; }

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp (283608 => 283609)


--- trunk/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp	2021-10-06 09:35:17 UTC (rev 283608)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp	2021-10-06 09:48:38 UTC (rev 283609)
@@ -375,21 +375,72 @@
     sourceBufferPrivate().didReceiveAllPendingSamples();
 }
 
+static GstClockTime bufferTimeToStreamTimeClamped(const GstSegment* segment, GstClockTime bufferTime)
+{
+    guint64 streamTime;
+    int result = gst_segment_to_stream_time_full(segment, GST_FORMAT_TIME, bufferTime, &streamTime);
+    if (!result) {
+        GST_ERROR("Couldn't map buffer time %" GST_TIME_FORMAT " to segment %" GST_PTR_FORMAT, GST_TIME_ARGS(bufferTime), segment);
+        return bufferTime;
+    }
+    if (result < 0)
+        return 0; // Clamp negative timestamps down to zero.
+    return streamTime;
+}
+
 void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr<GstSample>&& sample)
 {
     ASSERT(isMainThread());
 
-    if (UNLIKELY(!gst_sample_get_buffer(sample.get()))) {
+    GstBuffer* buffer = gst_sample_get_buffer(sample.get());
+    if (UNLIKELY(!buffer)) {
         GST_WARNING("Received sample without buffer from appsink.");
         return;
     }
 
-    if (!GST_BUFFER_PTS_IS_VALID(gst_sample_get_buffer(sample.get()))) {
+    if (!GST_BUFFER_PTS_IS_VALID(buffer)) {
         // When demuxing Vorbis, matroskademux creates several PTS-less frames with header information. We don't need those.
         GST_DEBUG("Ignoring sample without PTS: %" GST_PTR_FORMAT, gst_sample_get_buffer(sample.get()));
         return;
     }
 
+    GstSegment* segment = gst_sample_get_segment(sample.get());
+    bool hasMappedTime = false;
+    GstClockTime pts = GST_BUFFER_PTS(buffer);
+    GstClockTime dts = GST_BUFFER_DTS(buffer);
+    GstClockTime duration = GST_BUFFER_DURATION(buffer);
+    if (segment && (segment->time || segment->start)) {
+        // MP4 has the concept of edit lists, where some buffer time needs to be offsetted, often very slightly,
+        // to get exact timestamps.
+        pts = bufferTimeToStreamTimeClamped(segment, GST_BUFFER_PTS(buffer));
+        dts = bufferTimeToStreamTimeClamped(segment, GST_BUFFER_DTS(buffer));
+        GST_TRACE_OBJECT(track.appsinkPad.get(), "Mapped buffer to segment, PTS %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT " DTS %" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT,
+            GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), GST_TIME_ARGS(pts), GST_TIME_ARGS(GST_BUFFER_DTS(buffer)), GST_TIME_ARGS(dts));
+        hasMappedTime = true;
+    } else if (!dts && pts > 0 && pts <= 100'000'000) {
+        // Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected
+        // results for applications, we extend the duration of this first sample to the left so that it starts at zero.
+        // This is relevant for files that should have an edit list but don't, or when using GStreamer < 1.16, where
+        // edit lists are not parsed in push-mode.
+
+        GST_DEBUG("Extending first sample of track '%s' to make it start at PTS=0 %" GST_PTR_FORMAT, track.trackId.string().utf8().data(), buffer);
+        duration += pts;
+        pts = 0;
+        hasMappedTime = true;
+    }
+
+    if (hasMappedTime) {
+        sample = adoptGRef(gst_sample_make_writable(sample.leakRef()));
+        GRefPtr<GstBuffer> newBuffer = gst_sample_get_buffer(sample.get());
+        // Unset the buffer temporarily to ensure the buffer has refcount of 1 if possible when gst_buffer_make_writable is called, therefore avoiding a copy.
+        gst_sample_set_buffer(sample.get(), nullptr);
+        newBuffer = adoptGRef(gst_buffer_make_writable(newBuffer.leakRef()));
+        GST_BUFFER_PTS(newBuffer.get()) = pts;
+        GST_BUFFER_DTS(newBuffer.get()) = dts;
+        GST_BUFFER_DURATION(newBuffer.get()) = duration;
+        gst_sample_set_buffer(sample.get(), newBuffer.get());
+    }
+
     auto mediaSample = MediaSampleGStreamer::create(WTFMove(sample), track.presentationSize, track.trackId);
 
     GST_TRACE("append: trackId=%s PTS=%s DTS=%s DUR=%s presentationSize=%.0fx%.0f",
@@ -399,25 +450,6 @@
         mediaSample->duration().toString().utf8().data(),
         mediaSample->presentationSize().width(), mediaSample->presentationSize().height());
 
-    // Hack, rework when GStreamer >= 1.16 becomes a requirement:
-    // We're not applying edit lists. GStreamer < 1.16 doesn't emit the correct segments to do so.
-    // GStreamer fix in https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/commit/c2a0da8096009f0f99943f78dc18066965be60f9
-    // Also, in order to apply them we would need to convert the timestamps to stream time, which we're not currently
-    // doing for consistency between GStreamer versions.
-    //
-    // In consequence, the timestamps we're handling here are unedited track time. In track time, the first sample is
-    // guaranteed to have DTS == 0, but in the case of streams with B-frames, often PTS > 0. Edit lists fix this by
-    // offsetting all timestamps by that amount in movie time, but we can't do that if we don't have access to them.
-    // (We could assume the track PTS of the sample with track DTS = 0 is the offset, but we don't have any guarantee
-    // we will get appended that sample first, or ever).
-    //
-    // Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected
-    // results for applications, we extend the duration of this first sample to the left so that it starts at zero.
-    if (mediaSample->decodeTime() == MediaTime::zeroTime() && mediaSample->presentationTime() > MediaTime::zeroTime() && mediaSample->presentationTime() <= MediaTime(1, 10)) {
-        GST_DEBUG("Extending first sample to make it start at PTS=0");
-        mediaSample->extendToTheBeginning();
-    }
-
     m_sourceBufferPrivate.didReceiveSample(mediaSample.get());
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to