Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: ae9751dfb31afc7a17c1b0f36da7c5a145831793
      
https://github.com/WebKit/WebKit/commit/ae9751dfb31afc7a17c1b0f36da7c5a145831793
  Author: Jean-Yves Avenard <[email protected]>
  Date:   2026-06-04 (Thu, 04 Jun 2026)

  Changed paths:
    A 
LayoutTests/media/media-source/media-source-seek-readystate-no-stall-expected.txt
    A LayoutTests/media/media-source/media-source-seek-readystate-no-stall.html
    M 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm
    M Source/WebCore/testing/Internals.cpp
    M Source/WebCore/testing/Internals.h
    M Source/WebCore/testing/Internals.idl

  Log Message:
  -----------
  [MSE] Playback stalls after rapid seeks when readyState transitions mid-seek
https://bugs.webkit.org/show_bug.cgi?id=316273
rdar://177990720

Reviewed by Eric Carlson.

A seek is a multi-step operation that owns the renderer's playback rate
from prepareToSeek (rate=0) through maybeCompleteSeek (rate=m_rate) until
completeSeek finally clears m_seeking on the main thread. The renderer-
side work runs on the MediaSource queue; completeSeek hops back to the
main thread once the finishSeek IPC round-trip resolves. During that
trailing window the renderer is already playing at m_rate, but
MediaPlayerPrivate still considers itself mid-seek.

If the MediaSource fires monitorSourceBuffers in that window and
readyState transitions (HAVE_METADATA -> HAVE_ENOUGH_DATA is the common
case on the BBC stream, where the source buffer is evicted and refilled
during a rapid rewind), updateStateFromReadyState runs on the main
thread, observes shouldBePlaying()==false (because seeking() is still
true), and calls stall(). That stall() is dispatched to the renderer
queue and lands after maybeCompleteSeek's setSynchronizerRate(m_rate),
overriding it back to 0. completeSeek then fires 'seeked' on a renderer
that is paused at the OS level. Nothing reissues setSynchronizerRate(1)
until the user manually pauses and plays again.

The race is more visible when MediaContainment is on -- finishSeek
crosses an IPC boundary, widening the window in which the rogue stall
can dispatch -- but the underlying invariant violation exists in both
modes: state-reactive code on the main thread must not flip the
renderer's rate while the seek path is still mid-flight.

Treat the seek path as the sole owner of rate and readyState propagation
between prepareToSeek and completeSeek. updateStateFromReadyState
returns early while seeking(); completeSeek re-invokes it so any
transition that was suppressed mid-seek (rate and readyStateChanged) is
applied exactly once when the seek lands. m_readyState itself is still
updated synchronously, so readers see the current value during the
seek -- only the side-effecting notification is deferred. This also
coalesces the burst of intermediate waiting/canplay/playing events the
BBC player observed across each rewind.

A new internals.effectiveRate(HTMLMediaElement) exposes the renderer's
effective playback rate so the regression test can assert the bug
directly.

* 
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::completeSeek):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::updateStateFromReadyState):
* Source/WebCore/testing/Internals.cpp:
(WebCore::Internals::effectiveRate):
* Source/WebCore/testing/Internals.h:
* Source/WebCore/testing/Internals.idl:
* LayoutTests/media/media-source/media-source-seek-readystate-no-stall.html: 
Added.
* 
LayoutTests/media/media-source/media-source-seek-readystate-no-stall-expected.txt:
 Added.

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



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

Reply via email to