fix: M2 — eliminate the periodic high-res stream freeze (infinite GOP + single-deadline pacing)
At 5120x1440 the stream froze on a ~2s cadence. Two compounding causes (confirmed by a profiling pass + adversarial review): 1. Periodic IDR every 2s (set_gop(fps*2)). A keyframe at 5K is ~20-40x a P-frame — a recurring multi-millisecond encode+packetize+send spike. Fix: infinite GOP (gop_size=-1), one IDR at stream start, P-frames only; forced-idr makes a client recovery request (RFI via request_keyframe) emit an IDR on demand — the Moonlight/Sunshine low-latency model. 2. Two pacing timers summing on the capture/encode thread: a per-packet thread::sleep pacer (spread a frame's packets across a whole frame interval) PLUS a backstop sleep on top, so every frame cost 1-2x the interval and the big IDR blew through it (the 2->120 oscillation). Fix: delete both; send at line rate and drive cadence from a single absolute deadline. (Proper microburst pacing belongs on a dedicated send thread — a follow-up.) Also: honor the client's fps (pacing clamp 60->240) and add an env-gated (LUMEN_PERF) per-stage timing log (enc/pkt/send µs + unique-vs-reencoded frames + max packet burst) for diagnosing the remaining throughput ceiling. Verified live: freeze gone at 5120x1440. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -158,8 +158,15 @@ impl NvencEncoder {
|
||||
video.set_frame_rate(Some(Rational(fps as i32, 1)));
|
||||
video.set_bit_rate(bitrate_bps as usize);
|
||||
video.set_max_bit_rate(bitrate_bps as usize);
|
||||
video.set_gop(fps.saturating_mul(2).max(1)); // ~2s keyframe interval
|
||||
video.set_max_b_frames(0);
|
||||
// Infinite GOP — NO periodic IDR. A keyframe at 5120x1440 is ~20-40x a P-frame, so a
|
||||
// periodic IDR is a recurring multi-millisecond encode+packetize+send spike — the ~2s
|
||||
// "freeze". NVENC emits one IDR at stream start, then P-frames only; `forced-idr` (below)
|
||||
// turns a client recovery request (RFI, via `request_keyframe`) into an IDR on demand.
|
||||
// This is the Moonlight/Sunshine low-latency model.
|
||||
unsafe {
|
||||
(*video.as_mut_ptr()).gop_size = -1;
|
||||
}
|
||||
|
||||
// For the zero-copy path, take CUDA surfaces: wrap the shared CUcontext in CUDA
|
||||
// hwdevice/hwframes contexts and set `pix_fmt = CUDA` on the raw encoder context
|
||||
@@ -185,6 +192,7 @@ impl NvencEncoder {
|
||||
opts.set("rc", "cbr");
|
||||
opts.set("bf", "0");
|
||||
opts.set("delay", "0");
|
||||
opts.set("forced-idr", "1"); // RFI/request_keyframe → real IDR under the infinite GOP
|
||||
|
||||
let enc = video
|
||||
.open_with(opts)
|
||||
|
||||
Reference in New Issue
Block a user