Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 09ea5e2ced877aeeea2d6e369e1aa953f33bcc8f
      
https://github.com/WebKit/WebKit/commit/09ea5e2ced877aeeea2d6e369e1aa953f33bcc8f
  Author: Jean-Yves Avenard <[email protected]>
  Date:   2026-04-24 (Fri, 24 Apr 2026)

  Changed paths:
    M Source/WebCore/platform/graphics/SourceBufferPrivate.cpp
    M Source/WebCore/platform/graphics/SourceBufferPrivate.h
    M Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.h
    M Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm
    M 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h
    M 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm

  Log Message:
  -----------
  media/media-source/media-managedmse-resume-after-stall.html is an 
intermittent failure
https://bugs.webkit.org/show_bug.cgi?id=313098
rdar://175395328

Reviewed by Jer Noble.

The test intermittently failed when the AVSampleBufferRenderSynchronizer
and its associated AVSampleBufferAudioRenderer entered a state in which
the timebase will no longer progress despite being later flushed correctly
and samples being enqueued appropriately.

While the reason for this broken state to occur is yet unexplained,
several pre-condition symptoms were identified that trigger the ultimate
failure.

1- After setting the rate to the AVSampleBufferRenderSynchronizer, reading
the rate attribute may still return the old, stale value. This could cause
the AudioVideoRenderer to incorrectly assume time was progressing when it 
wasn't.
2- The AudioVideoRendererAVFObjC was receiving commands in an unexpected order:
instead of doing play() -> stall() -> flush() -> enqueueSamples() -> play()
we would see instead: play() -> stall() -> play() -> flush() -> enqueueSamples()

To address 1, we stop reading the AVSampleBufferRenderSynchronizer's rate 
attribute
and instead cache the rate last set. It achieves two things: it avoids the
internal sync dispatch the AVSampleBufferRenderSynchronizer makes to its
rate queue, and ensure the value read is always correct.

Condition 2 was due to several races between the 
MediaPlayerPrivateMediaSourceAVFObjC
and SourceBufferPrivateAVFObjC.
The MediaPlayerPrivateMediaSourceAVFObjC sends commands to the 
AudioVideoRenderer
on the main thread, while the SourceBuffer interacts with the renderer's track
on its dedicated queue. The could lead commands reaching the renderer out of
assumed order.

To pre-emptively avoice such race in the MediaPlayer, we now ensure that
any commands in the media player related to the renderer's data flow (seek, 
flush, setRate, play, pause, stall)
only occurs on the MediaSourcePrivate/SourceBufferPrivate's queue.

The primary race and cause for the test to fail was
  1- monitorSourceBuffers() ran first → synchronously fires the readyState 
change → updateStateFromReadyState → play() at time T1
  2- reenqueueMediaIfNeeded() dispatches the reenqueue lambda (which will call 
flush) at time T2

The call to play() which should occur after the flush() could run before.

When a buffered GOP containing the currentTime was being overwritten,
we would mark the track with setNeedsReenqueueing(true) and then notify
the MediaSource that the buffered changed, which triggered a called to
monitorSourceBuffers and in turn call play() as data was now available.
At the same time, we had
```
  // SourceBufferPrivate::reenqueueMediaIfNeeded body
  if (trackBuffer.needsReenqueueing()) {
      DEBUG_LOG_WITH_THIS(...);
      buffer.reenqueueMediaForTime(trackBuffer, trackID, currentTime);
  }
```

Resulting IPC order in the GPU process to become:
1. flushTrack (issued from didReceiveSample on m_dispatcher, before 
appendInternal even returns)
2. …appendBuffer finishes; main thread reacts → updateStateFromReadyState 
dispatches play() to the queue…
3. play() (issued from the queue after m_dispatcher finishes the rest of its 
work)

we now enforce the serialization of the tasks by flushing the tracks early 
before notifying the
MediaSource that the buffered had changed.

Covered by existing tests.

* Source/WebCore/platform/graphics/SourceBufferPrivate.cpp:
(WebCore::SourceBufferPrivate::reenqueueMediaIfNeeded):
(WebCore::SourceBufferPrivate::removeCodedFramesInternal):
(WebCore::SourceBufferPrivate::append):
(WebCore::SourceBufferPrivate::flushTracksThatNeedReenqueueing):
* Source/WebCore/platform/graphics/SourceBufferPrivate.h:
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.h:
(WebCore::AudioVideoRendererAVFObjC::synchronizerRate const):
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm:
(WebCore::AudioVideoRendererAVFObjC::timeIsProgressing const):
(WebCore::AudioVideoRendererAVFObjC::setRate):
(WebCore::AudioVideoRendererAVFObjC::notifyTimeReachedAndStall):
(WebCore::AudioVideoRendererAVFObjC::prepareToSeek):
(WebCore::AudioVideoRendererAVFObjC::updateAllRenderersHaveAvailableSamples):
(WebCore::AudioVideoRendererAVFObjC::setSynchronizerRate):
* 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
* 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::~MediaPlayerPrivateMediaSourceAVFObjC):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::playInternal):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::pauseInternal):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::stall):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::seekInternal):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::continueSeek):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::reenqueueMediaForTimeAndFinishSeek):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::setRateDouble):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::bufferedChanged):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::dispatchToRendererQueue):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::updateStateFromReadyState):

Canonical link: https://commits.webkit.org/311992@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to