From e360c3bda454abb7a01226c14fd7c76648fc3171 Mon Sep 17 00:00:00 2001 From: Andrzej Surdej Date: Mon, 19 Jan 2026 12:32:39 +0100 Subject: [PATCH] [GST][MSE] Make sure to extend only the very first sample when DTS = 0 and PTS > 0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the absence of an edit list, ensure that WebKit’s AppendPipeline extends only a single sample. This fixes the handling of invalid streams with multiple DTS = 0 samples. Modifying PTS values for all of them results in unexpected behavior, playback issues, PTS reordering, etc. The problem was spotted in a video web application that uses TS -> MSE transmuxing. --- .../platform/graphics/gstreamer/mse/AppendPipeline.cpp | 10 ++++++++-- .../platform/graphics/gstreamer/mse/AppendPipeline.h | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp index cdbd76814e424..1904f5e8cc02a 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.cpp @@ -502,7 +502,7 @@ static MediaTime bufferTimeToStreamTime(const GstSegment* segment, GstClockTime return MediaTime(sign * streamTime, GST_SECOND); } -void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr&& sample) +void AppendPipeline::appsinkNewSample(Track& track, GRefPtr&& sample) { ASSERT(isMainThread()); @@ -529,7 +529,8 @@ void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr&& s GST_TRACE_OBJECT(track.appsinkPad.get(), "Mapped buffer to segment, PTS %" GST_TIME_FORMAT " -> %s DTS %" GST_TIME_FORMAT " -> %s", GST_TIME_ARGS(GST_BUFFER_PTS(buffer)), pts.toString().utf8().data(), GST_TIME_ARGS(GST_BUFFER_DTS(buffer)), dts.toString().utf8().data()); mediaSample->setTimestamps(pts, dts); - } else if (!GST_BUFFER_DTS(buffer) && GST_BUFFER_PTS(buffer) > 0 && GST_BUFFER_PTS(buffer) <= 100'000'000) { + } else if (!GST_BUFFER_DTS(buffer) && GST_BUFFER_PTS(buffer) > 0 && GST_BUFFER_PTS(buffer) <= 100'000'000 && + !track.hasExtendedFirstSample) { // 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 @@ -537,6 +538,7 @@ void AppendPipeline::appsinkNewSample(const Track& track, GRefPtr&& s GST_DEBUG("Extending first sample of track '%" PRIu64 "' to make it start at PTS=0 %" GST_PTR_FORMAT, track.trackId, buffer); mediaSample->extendToTheBeginning(); + track.hasExtendedFirstSample = true; } GST_TRACE_OBJECT(pipeline(), "append: trackId=%" PRIu64 " PTS=%s DTS=%s DUR=%s presentationSize=%.0fx%.0f", @@ -744,6 +746,10 @@ void AppendPipeline::resetParserState() // We can listen again to new requests coming from the streaming thread. m_taskQueue.finishAborting(); + // Reset the flag for extending the first sample, as we're starting fresh with new data. + for (std::unique_ptr& track : m_tracks) + track->hasExtendedFirstSample = false; + #if (!(LOG_DISABLED || defined(GST_DISABLE_GST_DEBUG))) { static unsigned i = 0; diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h index df157dd1fec7e..58bf59a884681 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/AppendPipeline.h @@ -75,12 +75,14 @@ class AppendPipeline { , streamType(streamType) , caps(caps) , presentationSize(presentationSize) + , hasExtendedFirstSample(false) { } TrackID trackId; StreamType streamType; GRefPtr caps; FloatSize presentationSize; + bool hasExtendedFirstSample; // Needed by some formats. To simplify the code, parser can be a GstIdentity when not needed. GRefPtr parser; @@ -117,7 +119,7 @@ class AppendPipeline { static std::tuple, AppendPipeline::StreamType, FloatSize> parseDemuxerSrcPadCaps(GstCaps*); Ref makeWebKitTrack(int trackIndex, TrackID); void appsinkCapsChanged(Track&); - void appsinkNewSample(const Track&, GRefPtr&&); + void appsinkNewSample(Track&, GRefPtr&&); void handleEndOfAppend(); void didReceiveInitializationSegment();