Skip to content

[MSE][Cocoa] Silence spurious UpcomingPTSExpectation warnings from#64270

Merged
webkit-commit-queue merged 1 commit into
WebKit:mainfrom
jyavenard:eng/UpcomingPTSExpectation
May 7, 2026
Merged

[MSE][Cocoa] Silence spurious UpcomingPTSExpectation warnings from#64270
webkit-commit-queue merged 1 commit into
WebKit:mainfrom
jyavenard:eng/UpcomingPTSExpectation

Conversation

@jyavenard
Copy link
Copy Markdown
Member

@jyavenard jyavenard commented May 5, 2026

62987e2

[MSE][Cocoa] Silence spurious UpcomingPTSExpectation warnings from
AVSampleBufferVideoRenderer on B-frame content
https://bugs.webkit.org/show_bug.cgi?id=314077
rdar://176260662

Reviewed by Jer Noble.

AVSampleBufferVideoRenderer's _enqueueSingleSampleBuffer: compares each
incoming sample's PTS against a floor that WebKit publishes via
-[AVSampleBufferDisplayLayer expectMinimumUpcomingSampleBufferPresentationTime:]
and logs "UpcomingPTSExpectation is enabled, but enqueuePTS:X is smaller
than expectedMinimumUpcomingPTS:Y" whenever a later sample arrives with
a lower PTS. The log is informational only — samples are still accepted
— but on B-frame streams the message fires every frame pair: a 5-minute
Twitch capture produced 6197 lines, burying other signal during stall
investigations.

The floor came from TrackBuffer::minimumEnqueuedPresentationTime(),
recomputed by TrackBuffer::updateMinimumUpcomingPresentationTime() as a
sliding-window minimum over m_decodeQueue. When the parser feeds samples
one at a time (typical Twitch behaviour, and the common case during
post-changeType recovery) the decode queue is near-empty after each pop
and the scan can only see the current sample's own PTS. In decode order
that's a P-frame at the top of a GOP triplet, with two B-frames at lower
PTS arriving next — and the floor we publish is therefore always too
high by the B-frame reorder distance.

Two coordinated changes:

1. TrackBuffer now tracks the max observed reorder depth. On every
    addSample() we count samples in decode order since the most recent
    new-max PTS; the running maximum is stored in
    m_maxObservedReorderDepth, seeded to a conservative default of 3 to
    cover typical IPBB/IPBBB patterns and growing on deeper reorder.
    updateMinimumUpcomingPresentationTime() refuses to publish a value
    until the queue contains at least m_maxObservedReorderDepth + 1
    samples past the head when m_hasOutOfOrderFrames is set — that's the
    point at which the sliding-window scan has actually seen every
    B-frame that could come before the head's PTS. A new
    setInitialReorderDepth(size_t) hook is exposed so a future change
    can seed the depth from the codec config (H.264 max_num_reorder_frames
    / H.265 sps_max_num_reorder_pics); today it is unused and the
    mechanism is purely empirical.

2. Propagate the "no lookahead available" state cleanly down to the
    renderer instead of masking it. AudioVideoRendererAVFObjC::enqueueSample()
    no longer falls back to sample->presentationTime() when the caller
    passes std::nullopt; VideoMediaSampleRenderer::enqueueSample() now
    takes std::optional<MediaTime> and stores the optional in
    m_compressedSampleQueue. decodeNextSampleIfNeeded() only calls
    -expectMinimumUpcomingSampleBufferPresentationTime: when the stored
    optional holds a value. Without a lookahead it falls through and
    keeps decoding, which was the previous unintended behaviour anyway
    (the floor we were publishing was wrong).

Net effect: zero warnings in steady-state Twitch playback, and none
during ad/content transitions once the first handful of samples have
established the reorder depth.

No tests added; manual verification via
log stream --predicate='(process="Safari" or process="com.apple.WebKit.GPU.Development")'
with a Twitch session shows the "UpcomingPTSExpectation is enabled"
message count drop from ~6000 / 5 min to near zero.

* Source/WebCore/platform/graphics/TrackBuffer.h:
(WebCore::TrackBuffer::setInitialReorderDepth):
Add setInitialReorderDepth() and the three new members backing the
reorder-depth observation.
* Source/WebCore/platform/graphics/TrackBuffer.cpp:
(WebCore::TrackBuffer::addSample):
Track the running reorder depth alongside m_hasOutOfOrderFrames.
(WebCore::TrackBuffer::updateMinimumUpcomingPresentationTime):
Gate publication on having at least m_maxObservedReorderDepth + 1
samples in the queue when m_hasOutOfOrderFrames is set.
(WebCore::TrackBuffer::clearDecodeQueue):
Reset the observation counters but keep the learned depth.
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm:
(WebCore::AudioVideoRendererAVFObjC::enqueueSample):
Pass the std::optional<MediaTime> through instead of substituting the
sample's own PTS.
* Source/WebCore/platform/graphics/cocoa/VideoMediaSampleRenderer.h:
Change enqueueSample() signature and m_compressedSampleQueue element
type to carry std::optional<MediaTime>.
* Source/WebCore/platform/graphics/cocoa/VideoMediaSampleRenderer.mm:
(WebCore::VideoMediaSampleRenderer::enqueueSample):
Accept std::optional<MediaTime>.
(WebCore::VideoMediaSampleRenderer::decodeNextSampleIfNeeded):
Only publish a floor when the stored optional is present; fall through
and decode otherwise.

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

11fa89f

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows Apple Internal
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win ✅ 🛠 ios-apple
✅ 🧪 bindings ✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ❌ 🧪 win-tests ✅ 🛠 mac-apple
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ❌ 🧪 api-mac ✅ 🧪 api-wpe ✅ 🛠 vision-apple
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🛠 gtk3-libwebrtc
✅ 🧪 api-ios ✅ 🧪 mac-wk1 ✅ 🛠 gtk
✅ 🛠 ios-safer-cpp ✅ 🧪 mac-wk2 ✅ 🧪 gtk-wk2
✅ 🛠 vision ✅ 🧪 mac-AS-debug-wk2 ✅ 🧪 api-gtk
✅ 🛠 🧪 merge ✅ 🛠 vision-sim ✅ 🧪 mac-wk2-stress ✅ 🛠 playstation
✅ 🧪 vision-wk2 ✅ 🧪 mac-intel-wk2
✅ 🛠 tv ✅ 🛠 mac-safer-cpp
✅ 🛠 tv-sim
✅ 🛠 watch
✅ 🛠 watch-sim

@jyavenard jyavenard self-assigned this May 5, 2026
@jyavenard jyavenard added the Media Bugs related to the HTML 5 Media elements. label May 5, 2026
@jyavenard jyavenard requested review from a team, jernoble and youennf May 5, 2026 15:01
@jyavenard jyavenard added the merge-queue Applied to send a pull request to merge-queue label May 7, 2026
AVSampleBufferVideoRenderer on B-frame content
https://bugs.webkit.org/show_bug.cgi?id=314077
rdar://176260662

Reviewed by Jer Noble.

AVSampleBufferVideoRenderer's _enqueueSingleSampleBuffer: compares each
incoming sample's PTS against a floor that WebKit publishes via
-[AVSampleBufferDisplayLayer expectMinimumUpcomingSampleBufferPresentationTime:]
and logs "UpcomingPTSExpectation is enabled, but enqueuePTS:X is smaller
than expectedMinimumUpcomingPTS:Y" whenever a later sample arrives with
a lower PTS. The log is informational only — samples are still accepted
— but on B-frame streams the message fires every frame pair: a 5-minute
Twitch capture produced 6197 lines, burying other signal during stall
investigations.

The floor came from TrackBuffer::minimumEnqueuedPresentationTime(),
recomputed by TrackBuffer::updateMinimumUpcomingPresentationTime() as a
sliding-window minimum over m_decodeQueue. When the parser feeds samples
one at a time (typical Twitch behaviour, and the common case during
post-changeType recovery) the decode queue is near-empty after each pop
and the scan can only see the current sample's own PTS. In decode order
that's a P-frame at the top of a GOP triplet, with two B-frames at lower
PTS arriving next — and the floor we publish is therefore always too
high by the B-frame reorder distance.

Two coordinated changes:

1. TrackBuffer now tracks the max observed reorder depth. On every
    addSample() we count samples in decode order since the most recent
    new-max PTS; the running maximum is stored in
    m_maxObservedReorderDepth, seeded to a conservative default of 3 to
    cover typical IPBB/IPBBB patterns and growing on deeper reorder.
    updateMinimumUpcomingPresentationTime() refuses to publish a value
    until the queue contains at least m_maxObservedReorderDepth + 1
    samples past the head when m_hasOutOfOrderFrames is set — that's the
    point at which the sliding-window scan has actually seen every
    B-frame that could come before the head's PTS. A new
    setInitialReorderDepth(size_t) hook is exposed so a future change
    can seed the depth from the codec config (H.264 max_num_reorder_frames
    / H.265 sps_max_num_reorder_pics); today it is unused and the
    mechanism is purely empirical.

2. Propagate the "no lookahead available" state cleanly down to the
    renderer instead of masking it. AudioVideoRendererAVFObjC::enqueueSample()
    no longer falls back to sample->presentationTime() when the caller
    passes std::nullopt; VideoMediaSampleRenderer::enqueueSample() now
    takes std::optional<MediaTime> and stores the optional in
    m_compressedSampleQueue. decodeNextSampleIfNeeded() only calls
    -expectMinimumUpcomingSampleBufferPresentationTime: when the stored
    optional holds a value. Without a lookahead it falls through and
    keeps decoding, which was the previous unintended behaviour anyway
    (the floor we were publishing was wrong).

Net effect: zero warnings in steady-state Twitch playback, and none
during ad/content transitions once the first handful of samples have
established the reorder depth.

No tests added; manual verification via
log stream --predicate='(process="Safari" or process="com.apple.WebKit.GPU.Development")'
with a Twitch session shows the "UpcomingPTSExpectation is enabled"
message count drop from ~6000 / 5 min to near zero.

* Source/WebCore/platform/graphics/TrackBuffer.h:
(WebCore::TrackBuffer::setInitialReorderDepth):
Add setInitialReorderDepth() and the three new members backing the
reorder-depth observation.
* Source/WebCore/platform/graphics/TrackBuffer.cpp:
(WebCore::TrackBuffer::addSample):
Track the running reorder depth alongside m_hasOutOfOrderFrames.
(WebCore::TrackBuffer::updateMinimumUpcomingPresentationTime):
Gate publication on having at least m_maxObservedReorderDepth + 1
samples in the queue when m_hasOutOfOrderFrames is set.
(WebCore::TrackBuffer::clearDecodeQueue):
Reset the observation counters but keep the learned depth.
* Source/WebCore/platform/graphics/avfoundation/AudioVideoRendererAVFObjC.mm:
(WebCore::AudioVideoRendererAVFObjC::enqueueSample):
Pass the std::optional<MediaTime> through instead of substituting the
sample's own PTS.
* Source/WebCore/platform/graphics/cocoa/VideoMediaSampleRenderer.h:
Change enqueueSample() signature and m_compressedSampleQueue element
type to carry std::optional<MediaTime>.
* Source/WebCore/platform/graphics/cocoa/VideoMediaSampleRenderer.mm:
(WebCore::VideoMediaSampleRenderer::enqueueSample):
Accept std::optional<MediaTime>.
(WebCore::VideoMediaSampleRenderer::decodeNextSampleIfNeeded):
Only publish a floor when the stored optional is present; fall through
and decode otherwise.

Canonical link: https://commits.webkit.org/312763@main
@webkit-commit-queue webkit-commit-queue force-pushed the eng/UpcomingPTSExpectation branch from 11fa89f to 62987e2 Compare May 7, 2026 03:56
@webkit-commit-queue
Copy link
Copy Markdown
Collaborator

Committed 312763@main (62987e2): https://commits.webkit.org/312763@main

Reviewed commits have been landed. Closing PR #64270 and removing active labels.

@webkit-commit-queue webkit-commit-queue merged commit 62987e2 into WebKit:main May 7, 2026
@webkit-commit-queue webkit-commit-queue removed the merge-queue Applied to send a pull request to merge-queue label May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Media Bugs related to the HTML 5 Media elements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants