⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 284711 in webkit


Ignore:
Timestamp:
Oct 22, 2021, 1:22:52 PM (5 years ago)
Author:
aboya@igalia.com
Message:

[MSE][GStreamer] Honor MP4 edit lists, bis
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.

This is a reworked version that avoids using gst_sample_set_buffer()
which is not available on GStreamer 1.14, and fixes an issue where
frames that would get a negative DTS were not being enqueued properly.

  • Modules/mediasource/MediaSource.cpp:

(WebCore::MediaSource::currentTimeFudgeFactor):

  • platform/graphics/SourceBufferPrivate.h:

(WebCore::SourceBufferPrivate::timeFudgeFactor const):

  • platform/graphics/gstreamer/GStreamerCommon.h:

(WebCore::toGstClockTime):

  • platform/graphics/gstreamer/MediaSampleGStreamer.cpp:

(WebCore::MediaSampleGStreamer::MediaSampleGStreamer):

  • platform/graphics/gstreamer/mse/AppendPipeline.cpp:

(WebCore::bufferTimeToStreamTime):
(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:
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r284709 r284711  
     12021-10-22  Alicia Boya García  <aboya@igalia.com>
     2
     3        [MSE][GStreamer] Honor MP4 edit lists, bis
     4        https://bugs.webkit.org/show_bug.cgi?id=231019
     5
     6        Reviewed by Xabier Rodriguez-Calvar.
     7
     8        Update expectations for mediasource-remove.html in the GStreamer
     9        ports, as a couple subtests get fixed.
     10
     11        * platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt:
     12
    1132021-10-22  Chris Dumez  <cdumez@apple.com>
    214
  • trunk/LayoutTests/platform/glib/imported/w3c/web-platform-tests/media-source/mediasource-remove-expected.txt

    r284087 r284711  
    1212PASS Test remove with a start at the duration.
    1313PASS Test remove transitioning readyState from 'ended' to 'open'.
    14 FAIL Test removing all appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
    15 FAIL Test removing beginning of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
    16 FAIL Test removing the middle of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
    17 FAIL Test removing the end of appended data. assert_equals: Initial buffered range. expected "{ [0.095, 6.548) }" but got "{ [0.000, 6.548) }"
     14PASS Test removing all appended data.
     15PASS Test removing beginning of appended data.
     16FAIL 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) }"
     17FAIL Test removing the end of appended data. assert_equals: Buffered ranges after remove(). expected "{ [0.095, 1.022) }" but got "{ [0.095, 0.995) }"
    1818
  • trunk/Source/WebCore/ChangeLog

    r284694 r284711  
     12021-10-22  Alicia Boya García  <aboya@igalia.com>
     2
     3        [MSE][GStreamer] Honor MP4 edit lists, bis
     4        https://bugs.webkit.org/show_bug.cgi?id=231019
     5
     6        Reviewed by Xabier Rodriguez-Calvar.
     7
     8        This patch takes into consideration the GstSegment attached to a
     9        sample to offset the PTS and DTS. This ensures accurate timestamps are
     10        obtained for MP4 files containing edit lists (commonly necessary for
     11        files containing video with B frames to have PTS starting at zero).
     12
     13        Before this was implemented, a workaround was in place based on a
     14        heuristic (DTS = 0 && PTS > 0 && PTS < 0.1). The workaround is
     15        preserved for the sake of content without proper edit lists, but
     16        any edit list takes preference.
     17
     18        The time fudge factor has been modified from 0.083 seconds up to
     19        0.100 seconds to accomodate the size of the empty edit in test.mp4
     20        used by Web Platform Tests.
     21
     22        This test fixes improves expectation results and fixes two subtests in
     23        imported/w3c/web-platform-tests/media-source/mediasource-remove.html.
     24
     25        This is a reworked version that avoids using gst_sample_set_buffer()
     26        which is not available on GStreamer 1.14, and fixes an issue where
     27        frames that would get a negative DTS were not being enqueued properly.
     28
     29        * Modules/mediasource/MediaSource.cpp:
     30        (WebCore::MediaSource::currentTimeFudgeFactor):
     31        * platform/graphics/SourceBufferPrivate.h:
     32        (WebCore::SourceBufferPrivate::timeFudgeFactor const):
     33        * platform/graphics/gstreamer/GStreamerCommon.h:
     34        (WebCore::toGstClockTime):
     35        * platform/graphics/gstreamer/MediaSampleGStreamer.cpp:
     36        (WebCore::MediaSampleGStreamer::MediaSampleGStreamer):
     37        * platform/graphics/gstreamer/mse/AppendPipeline.cpp:
     38        (WebCore::bufferTimeToStreamTime):
     39        (WebCore::AppendPipeline::appsinkNewSample):
     40
    1412021-10-22  Alex Christensen  <achristensen@webkit.org>
    242
  • trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp

    r284087 r284711  
    324324const MediaTime& MediaSource::currentTimeFudgeFactor()
    325325{
    326     // Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
    327     static NeverDestroyed<MediaTime> fudgeFactor(2002, 24000);
     326    // Allow hasCurrentTime() to be off by as much as 100ms.
     327    static NeverDestroyed<MediaTime> fudgeFactor(1, 10);
    328328    return fudgeFactor;
    329329}
  • trunk/Source/WebCore/platform/graphics/SourceBufferPrivate.h

    r284087 r284711  
    151151    // The following method should never be called directly and be overridden instead.
    152152    WEBCORE_EXPORT virtual void append(Vector<unsigned char>&&);
    153     virtual MediaTime timeFudgeFactor() const { return {2002, 24000}; }
     153    virtual MediaTime timeFudgeFactor() const { return { 1, 10 }; }
    154154    virtual bool isActive() const { return false; }
    155155    virtual bool isSeeking() const { return false; }
  • trunk/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h

    r281440 r284711  
    8181inline GstClockTime toGstClockTime(const MediaTime &mediaTime)
    8282{
     83    if (mediaTime.isInvalid())
     84        return GST_CLOCK_TIME_NONE;
     85    if (mediaTime < MediaTime::zeroTime())
     86        return 0;
    8387    return static_cast<GstClockTime>(toGstUnsigned64Time(mediaTime));
    8488}
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaSampleGStreamer.cpp

    r284368 r284711  
    7373
    7474    m_size = gst_buffer_get_size(buffer);
    75     m_sample = sample;
     75    m_sample = adoptGRef(gst_sample_new(buffer, gst_sample_get_caps(sample.get()), nullptr,
     76        gst_sample_get_info(sample.get()) ? gst_structure_copy(gst_sample_get_info(sample.get())) : nullptr));
    7677
    7778    if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
  • trunk/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp

    r284156 r284711  
    375375}
    376376
     377static MediaTime bufferTimeToStreamTime(const GstSegment* segment, GstClockTime bufferTime)
     378{
     379    if (bufferTime == GST_CLOCK_TIME_NONE)
     380        return MediaTime::invalidTime();
     381
     382    guint64 streamTime;
     383    int sign = gst_segment_to_stream_time_full(segment, GST_FORMAT_TIME, bufferTime, &streamTime);
     384    if (!sign) {
     385        GST_ERROR("Couldn't map buffer time %" GST_TIME_FORMAT " to segment %" GST_PTR_FORMAT, GST_TIME_ARGS(bufferTime), segment);
     386        return MediaTime::invalidTime();
     387    }
     388    return MediaTime(sign * streamTime, GST_SECOND);
     389}
     390
    377391void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr<GstSample>&& sample)
    378392{
    379393    ASSERT(isMainThread());
    380394
    381     if (UNLIKELY(!gst_sample_get_buffer(sample.get()))) {
     395    GstBuffer* buffer = gst_sample_get_buffer(sample.get());
     396    if (UNLIKELY(!buffer)) {
    382397        GST_WARNING("Received sample without buffer from appsink.");
    383398        return;
    384399    }
    385400
    386     if (!GST_BUFFER_PTS_IS_VALID(gst_sample_get_buffer(sample.get()))) {
     401    if (!GST_BUFFER_PTS_IS_VALID(buffer)) {
    387402        // When demuxing Vorbis, matroskademux creates several PTS-less frames with header information. We don't need those.
    388403        GST_DEBUG("Ignoring sample without PTS: %" GST_PTR_FORMAT, gst_sample_get_buffer(sample.get()));
     
    390405    }
    391406
     407    GstSegment* segment = gst_sample_get_segment(sample.get());
    392408    auto mediaSample = MediaSampleGStreamer::create(WTFMove(sample), track.presentationSize, track.trackId);
     409
     410    if (segment && (segment->time || segment->start)) {
     411        // MP4 has the concept of edit lists, where some buffer time needs to be offsetted, often very slightly,
     412        // to get exact timestamps.
     413        MediaTime pts = bufferTimeToStreamTime(segment, GST_BUFFER_PTS(buffer));
     414        MediaTime dts = bufferTimeToStreamTime(segment, GST_BUFFER_DTS(buffer));
     415        GST_TRACE_OBJECT(track.appsinkPad.get(), "Mapped buffer to segment, PTS %" GST_TIME_FORMAT " -> %s DTS %" GST_TIME_FORMAT " -> %s",
     416            GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), pts.toString().utf8().data(), GST_TIME_ARGS(GST_BUFFER_DTS(buffer)), dts.toString().utf8().data());
     417        mediaSample->setTimestamps(pts, dts);
     418    } else if (!GST_BUFFER_DTS(buffer) && GST_BUFFER_PTS(buffer) > 0 && GST_BUFFER_PTS(buffer) <= 100'000'000) {
     419        // Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected
     420        // results for applications, we extend the duration of this first sample to the left so that it starts at zero.
     421        // This is relevant for files that should have an edit list but don't, or when using GStreamer < 1.16, where
     422        // edit lists are not parsed in push-mode.
     423
     424        GST_DEBUG("Extending first sample of track '%s' to make it start at PTS=0 %" GST_PTR_FORMAT, track.trackId.string().utf8().data(), buffer);
     425        mediaSample->extendToTheBeginning();
     426    }
    393427
    394428    GST_TRACE("append: trackId=%s PTS=%s DTS=%s DUR=%s presentationSize=%.0fx%.0f",
     
    398432        mediaSample->duration().toString().utf8().data(),
    399433        mediaSample->presentationSize().width(), mediaSample->presentationSize().height());
    400 
    401     // Hack, rework when GStreamer >= 1.16 becomes a requirement:
    402     // We're not applying edit lists. GStreamer < 1.16 doesn't emit the correct segments to do so.
    403     // GStreamer fix in https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/commit/c2a0da8096009f0f99943f78dc18066965be60f9
    404     // Also, in order to apply them we would need to convert the timestamps to stream time, which we're not currently
    405     // doing for consistency between GStreamer versions.
    406     //
    407     // In consequence, the timestamps we're handling here are unedited track time. In track time, the first sample is
    408     // guaranteed to have DTS == 0, but in the case of streams with B-frames, often PTS > 0. Edit lists fix this by
    409     // offsetting all timestamps by that amount in movie time, but we can't do that if we don't have access to them.
    410     // (We could assume the track PTS of the sample with track DTS = 0 is the offset, but we don't have any guarantee
    411     // we will get appended that sample first, or ever).
    412     //
    413     // Because a track presentation time starting at some close to zero, but not exactly zero time can cause unexpected
    414     // results for applications, we extend the duration of this first sample to the left so that it starts at zero.
    415     if (mediaSample->decodeTime() == MediaTime::zeroTime() && mediaSample->presentationTime() > MediaTime::zeroTime() && mediaSample->presentationTime() <= MediaTime(1, 10)) {
    416         GST_DEBUG("Extending first sample to make it start at PTS=0");
    417         mediaSample->extendToTheBeginning();
    418     }
    419434
    420435    m_sourceBufferPrivate.didReceiveSample(mediaSample.get());
Note: See TracChangeset for help on using the changeset viewer.