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