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

Reply via email to