Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: beb0b2162e807bbe1178b79e8ad7c058c5f9a2d9
https://github.com/WebKit/WebKit/commit/beb0b2162e807bbe1178b79e8ad7c058c5f9a2d9
Author: Jean-Yves Avenard <[email protected]>
Date: 2026-06-19 (Fri, 19 Jun 2026)
Changed paths:
M Source/WebCore/SourcesCocoa.txt
M Source/WebCore/WebCore.xcodeproj/project.pbxproj
M Source/WebCore/platform/SharedTimebase.cpp
M Source/WebCore/platform/SharedTimebase.h
M Source/WebCore/platform/graphics/AudioVideoRenderer.h
M Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.h
M Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm
A Source/WebCore/platform/graphics/cocoa/PeriodicSharedTimer.h
A Source/WebCore/platform/graphics/cocoa/PeriodicSharedTimer.mm
M Source/WebKit/GPUProcess/media/RemoteAudioVideoRendererProxyManager.cpp
M Source/WebKit/GPUProcess/media/RemoteAudioVideoRendererProxyManager.h
M Source/WebKit/WebProcess/GPU/media/AudioVideoRendererRemote.cpp
M Source/WebKit/WebProcess/GPU/media/AudioVideoRendererRemote.h
M Source/WebKit/WebProcess/GPU/media/RemoteAudioVideoRendererState.h
M Tools/TestWebKitAPI/Tests/WebCore/SharedTimebaseTests.cpp
Log Message:
-----------
Have AudioVideoRenderer take and update a SharedTimebase and reduce
noticeable drift
https://bugs.webkit.org/show_bug.cgi?id=317117
rdar://problem/179688028
Reviewed by Youenn Fablet.
The WebContent process reads currentTime() during script execution and
requestAnimationFrame callbacks, but the authoritative time lives in
the GPU process on the AVSampleBufferRenderSynchronizer's CMTimebase.
Previously the SharedTimebase was owned by
RemoteAudioVideoRendererProxyManager, one layer removed from the
renderer. Snapshots were refreshed both from a periodic time observer
on the synchronizer and from state-change callbacks, but every refresh
went through the main thread. While the main thread was blocked the
snapshot went stale, and any state change that happened in an AVF
thread (most notably the synchronizer rate-changed callback transitioning
to 0) had to wait for the main-thread hop before the reader saw it. The
reader meanwhile kept extrapolating off the prior snapshot and its
computed currentTime drifted from the renderer's actual state.
Move SharedTimebase ownership onto AudioVideoRendererAVFObjC itself and
publish from inside the renderer. AudioVideoRendererRemote hands the
read-only handle to the WebContent process at construction; reads on
that side go through SharedTimebaseReader against shared memory.
Publishing is centralized through a single helper:
AudioVideoRendererAVFObjC::publishSnapshot(currentTime, playbackRate)
which takes m_publishLock (SequenceLocked is multi-reader, single-writer
— writers must be externally synchronized), applies the same floor /
ceiling clamps for every publish path, advances the high-water-mark,
and stores the snapshot. Three writers feed it:
- Main thread on state changes via updateSharedTimebase().
- A 100ms wall-clock dispatch timer on a dedicated serial queue.
Independent of the synchronizer (which doesn't tick periodic
observers when paused) and of the main runloop, so a blocked main
thread or an AVF rate-lie window can't keep the snapshot stale for
longer than one tick.
- The rate-changed listener on rate→0, publishing synchronously from
the AVF thread before the main-thread hop, so the reader stops
extrapolating off the prior rate≠0 snapshot in the interim.
What this does not — and cannot — fix:
- AVSampleBufferRenderSynchronizer / CMTimebase can return a smaller
time at a later host time. Observed during play() resumption when
FigAudioQueueRenderPipeline re-anchors the timebase via
CMTimebaseSetRateAndAnchorTime(rate=1, mediaTime=t, sourceTime=ts)
with sourceTime in the future of "now": the linear mapping
f(host) = anchor + rate × (host − sourceTime) is replaced, and
f(now_pre) > f(now_post) even though host time is monotonic.
- CMTimebaseGetEffectiveRate likewise lies briefly — the synchronizer
publishes a non-zero rate before its timebase starts advancing
(handled separately by the startup gate observer), and time can
advance for a window even after the effective rate reports 0.
We can't make AVF stop doing this. To keep the writer honest we
maintain m_publishedTimeFloor (under m_publishLock): the high-water-mark
of every published currentTime, reset downward only on seek. Every
publishSnapshot call clamps up to this floor before storing, so a
backward step from the synchronizer is suppressed before it reaches
the shared memory.
On the reader, the analogous m_lastReturnedTime clamp catches anything
that survives — extrapolation jitter, a writer-side occurence we haven't
found yet — we log those for now.
The reader stays forward-monotonic without ever propagating a regression to
callers.
Other simplifications enabled by the new ownership:
- SharedTimebaseReader::create no longer takes a maxExtrapolation
parameter; the reader extrapolates directly from the last snapshot
and is bounded by the 100ms publish cadence.
- RemoteAudioVideoRendererProxyManager no longer owns the timebase
or pumps periodic time updates; its remaining time observer fires
only at the metricsAdvanceUpdate cadence needed by
videoPlaybackQualityMetrics.
* Source/WebCore/SourcesCocoa.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/platform/SharedTimebase.cpp:
(WebCore::SharedTimebaseReader::create):
(WebCore::SharedTimebaseReader::SharedTimebaseReader):
(WebCore::SharedTimebaseReader::currentTime const):
(WebCore::SharedTimebaseReader::resetForTimeDiscontinuity):
* Source/WebCore/platform/SharedTimebase.h:
* Source/WebCore/platform/graphics/AudioVideoRenderer.h:
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.h:
(WebCore::AudioVideoRendererAVFObjC::create): Deleted.
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm:
(WebCore::AudioVideoRendererAVFObjC::create):
(WebCore::AudioVideoRendererAVFObjC::AudioVideoRendererAVFObjC):
(WebCore::AudioVideoRendererAVFObjC::~AudioVideoRendererAVFObjC):
(WebCore::AudioVideoRendererAVFObjC::pause):
(WebCore::AudioVideoRendererAVFObjC::currentTime const):
(WebCore::AudioVideoRendererAVFObjC::effectiveRate const):
(WebCore::AudioVideoRendererAVFObjC::updateSharedTimebase):
(WebCore::AudioVideoRendererAVFObjC::publishSnapshot):
(WebCore::AudioVideoRendererAVFObjC::notifyTimeReachedAndStall):
(WebCore::AudioVideoRendererAVFObjC::cancelTimeReachedAction):
(WebCore::AudioVideoRendererAVFObjC::performTaskAtTime):
(WebCore::AudioVideoRendererAVFObjC::setTimeObserver):
(WebCore::AudioVideoRendererAVFObjC::prepareToSeek):
(WebCore::AudioVideoRendererAVFObjC::notifyEffectiveRateChanged):
(WebCore::AudioVideoRendererAVFObjC::handleEffectiveRateChanged):
(WebCore::AudioVideoRendererAVFObjC::releaseStartupGateAndForwardRate):
(WebCore::AudioVideoRendererAVFObjC::clampTimeToLastSeekTime const):
(WebCore::AudioVideoRendererAVFObjC::maybeCompleteSeek):
(WebCore::AudioVideoRendererAVFObjC::setSynchronizerRate):
(WebCore::AudioVideoRendererAVFObjC::periodicSharedTimerFired):
* Source/WebCore/platform/graphics/cocoa/PeriodicSharedTimer.h: Added.
(WebCore::PeriodicSharedTimer::WTF_GUARDED_BY_LOCK):
* Source/WebCore/platform/graphics/cocoa/PeriodicSharedTimer.mm: Added.
(WebCore::PeriodicSharedTimer::singleton):
(WebCore::PeriodicSharedTimer::PeriodicSharedTimer):
(WebCore::PeriodicSharedTimer::timerFired):
(WebCore::PeriodicSharedTimer::addClient):
(WebCore::PeriodicSharedTimer::removeClient):
* Source/WebKit/GPUProcess/media/RemoteAudioVideoRendererProxyManager.cpp:
(WebKit::RemoteAudioVideoRendererProxyManager::publishAndSend):
(WebKit::RemoteAudioVideoRendererProxyManager::createRenderer):
(WebKit::RemoteAudioVideoRendererProxyManager::create):
(WebKit::RemoteAudioVideoRendererProxyManager::installTimeObserver):
(WebKit::RemoteAudioVideoRendererProxyManager::play):
(WebKit::RemoteAudioVideoRendererProxyManager::pause):
(WebKit::RemoteAudioVideoRendererProxyManager::setRate):
(WebKit::RemoteAudioVideoRendererProxyManager::stall):
(WebKit::RemoteAudioVideoRendererProxyManager::setVideoPlaybackMetricsUpdateInterval):
(WebKit::RemoteAudioVideoRendererProxyManager::updateContextSharedTimebase):
Deleted.
* Source/WebKit/GPUProcess/media/RemoteAudioVideoRendererProxyManager.h:
* Source/WebKit/WebProcess/GPU/media/AudioVideoRendererRemote.cpp:
(WebKit::AudioVideoRendererRemote::AudioVideoRendererRemote):
* Source/WebKit/WebProcess/GPU/media/AudioVideoRendererRemote.h:
* Source/WebKit/WebProcess/GPU/media/RemoteAudioVideoRendererState.h:
* Tools/TestWebKitAPI/Tests/WebCore/SharedTimebaseTests.cpp:
(TestWebKitAPI::TEST(SharedTimebase, Basic)):
Canonical link: https://commits.webkit.org/315527@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications