From ffa00d9b0d410929a63b5fd8f20b052805868c64 Mon Sep 17 00:00:00 2001 From: Eugene Mutavchi Date: Fri, 27 Jun 2025 18:32:29 +0000 Subject: [PATCH] [MSE][GStreamer] don't push samples while seeking MediaSource::waitForTarget completes asynchronously. Even when the target time is already buffered, it still enqueues a task to compute seek time on next event loop cycle. This can lead to SourceBuffer providing media data for incorrect time, after GStreamer seek already flushed the source. For example this happens if TrackQueue::LowLevelHandler callback was posted just before the seek. The proposed change implements SourceBufferPrivate::isSeeking() that returns true until MSE seek is completed, effectivly blocking SourceBufferPrivate::provideMediaData during the seek. --- .../mse/MediaPlayerPrivateGStreamerMSE.cpp | 1 + .../mse/MediaSourcePrivateGStreamer.cpp | 6 ++++++ .../gstreamer/mse/MediaSourcePrivateGStreamer.h | 2 ++ .../mse/SourceBufferPrivateGStreamer.cpp | 17 +++++++++++++++++ .../mse/SourceBufferPrivateGStreamer.h | 5 +++++ 5 files changed, 31 insertions(+) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp index 40f8a494a6c5..54a386742332 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaPlayerPrivateGStreamerMSE.cpp @@ -269,6 +269,7 @@ bool MediaPlayerPrivateGStreamerMSE::doSeek(const SeekTarget& target, float rate // This will also add support for fastSeek once done (see webkit.org/b/260607) if (!m_mediaSourcePrivate) return false; + m_mediaSourcePrivate->willSeek(); m_mediaSourcePrivate->waitForTarget(target)->whenSettled(RunLoop::current(), [this, weakThis = ThreadSafeWeakPtr { *this }](auto&& result) { RefPtr self = weakThis.get(); if (!self || !result) diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.cpp index 31f380833006..cf0f4d96ba5e 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.cpp @@ -205,6 +205,12 @@ TrackID MediaSourcePrivateGStreamer::registerTrackId(TrackID preferredId) return assignedId; } +void MediaSourcePrivateGStreamer::willSeek() +{ + for (auto* sourceBuffer : m_activeSourceBuffers) + downcast(sourceBuffer)->willSeek(); +} + bool MediaSourcePrivateGStreamer::unregisterTrackId(TrackID trackId) { ASSERT(isMainThread()); diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.h index b639e29a8933..b5df57af8031 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/MediaSourcePrivateGStreamer.h @@ -77,6 +77,8 @@ class MediaSourcePrivateGStreamer final : public MediaSourcePrivate TrackID registerTrackId(TrackID); bool unregisterTrackId(TrackID); + void willSeek(); + #if !RELEASE_LOG_DISABLED const Logger& logger() const final { return m_logger; } ASCIILiteral logClassName() const override { return "MediaSourcePrivateGStreamer"_s; } diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp index 9ba00d6ad38c..e8a1392f00b0 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp @@ -403,6 +403,23 @@ size_t SourceBufferPrivateGStreamer::platformEvictionThreshold() const return evictionThreshold; } +void SourceBufferPrivateGStreamer::willSeek() +{ + ALWAYS_LOG(LOGIDENTIFIER); + m_seeking = true; +} + +bool SourceBufferPrivateGStreamer::isSeeking() const +{ + return m_seeking; +} + +void SourceBufferPrivateGStreamer::seekToTime(const MediaTime& time) +{ + m_seeking = false; + SourceBufferPrivate::seekToTime(time); +} + #undef GST_CAT_DEFAULT } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h index f6cf62245dbf..e4bc07fe2f2a 100644 --- a/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h +++ b/Source/WebCore/platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.h @@ -96,6 +96,10 @@ class SourceBufferPrivateGStreamer final : public SourceBufferPrivate, public Ca size_t platformMaximumBufferSize() const override; size_t platformEvictionThreshold() const final; + void willSeek(); + bool isSeeking() const final; + void seekToTime(const MediaTime&) final; + private: friend class AppendPipeline; @@ -109,6 +113,7 @@ class SourceBufferPrivateGStreamer final : public SourceBufferPrivate, public Ca std::unique_ptr m_appendPipeline; StdUnorderedMap> m_tracks; std::optional m_appendPromise; + bool m_seeking { false }; #if !RELEASE_LOG_DISABLED Ref m_logger;