feat(linux): pace capture at exact fractional NTSC framerates#5282
feat(linux): pace capture at exact fractional NTSC framerates#5282djadjka wants to merge 4 commits into
Conversation
When a client requests a fractional refresh rate via x-nv-video[0].clientRefreshRateX100 (e.g. 11988 for an Xbox whose display pipeline runs at 120/1.001 Hz), the encoder time base is already set to the exact rational since LizardByte#4019, but the Linux capture loops still paced at the integer maxFPS. The resulting 0.1% delivery surplus accumulates one extra frame every ~8 s, which the client pacer keeps correcting (periodic frame queue oscillation / micro-judder). Apply the same exact-rational handling to the kmsgrab, wlgrab, x11grab and CUDA capture pacing, mirroring the Windows implementation from LizardByte#4019. Tested on an AMD (RDNA4) + KDE Wayland host with KMS capture against a moonlight-xbox client at 4K 119.88: the standing frame-queue oscillation on the client disappears.
|
Would be possible to implement it to the new pipewire based capture (xdg portal, kwin grab) too? Or is it enough there that the compositor sets the max fps? |
Double-checked: both PipeWire paths (xdg-portal Compositor pacing isn't involved there: the format is deliberately negotiated as |
ReenigneArcher
left a comment
There was a problem hiding this comment.
Let's make a reusable function instead of copying the same code to so many different places.
Extract video::capture_frame_interval() and use it in all Linux capture pacers, including the PipeWire path which carried its own copy of the same math.
ReenigneArcher
left a comment
There was a problem hiding this comment.
Perhaps a few other areas to update?
Address review feedback: extract video::framerate_to_rational() as the
single place implementing the "exact rational when framerateX100 is set,
integer framerate otherwise" pattern, and rebuild capture_frame_interval()
on top of it. Migrate the call sites that re-implemented the pattern:
avcodec and NVENC encoder setup become branchless, the PipeWire and
wlroots capture pacers no longer re-derive the rational for logging, and
the Windows strict frame rate reuses the helper inside its sentinel
branch (the {0,0} sentinel must stay to keep the refresh-rate matching
heuristic). Add unit tests for the new helper and the capture frame
interval, including the integer fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|



Description
#4019 added support for the
x-nv-video[0].clientRefreshRateX100RTSP parameter, butimplemented the capture-side handling only for the Windows backend
(
platform/windows/display_base.cpp); on Linux only the encoder time base got the exactrational. The Linux capture loops still pace at the integer
maxFPS, so for a client thatrequests e.g. 11988 (Xbox, whose display pipeline runs at 120/1.001 Hz) the host delivers
120.00 fps while the client drains 119.88 fps. The 0.1% surplus accumulates one extra frame
every ~8 s, which the client-side pacer keeps correcting — visible as a periodic frame-queue
oscillation / micro-judder that no client setting can remove.
This PR mirrors the Windows handling in the four Linux capture pacers (kmsgrab, wlgrab,
x11grab, CUDA): when
framerateX100is present, the frame interval is derived from theexact rational returned by
video::framerateX100_to_rational().Tested on an AMD RDNA4 + KDE Wayland host (KMS capture, Vulkan encoder) against a
moonlight-xbox client at 4K 119.88 Hz: the standing frame-queue oscillation on
the client disappears (queue settles flat instead of breathing by one frame every ~8 s).
Issues Fixed or Closed
Completes the Linux side of #4019.
Type of Change
Checklist
AI Usage