forked from unom/punktfunk
feat(recovery): wire LTR-RFI loss recovery into every client
Centralize the client-side loss-range detector in punktfunk-core so every embedder shares one implementation instead of re-deriving the wrapping frame-index arithmetic: - NativeClient::note_frame_index(frame_index) folds each received AU (in receive order) through RfiRecovery::observe, firing a throttled RFI request for the exact lost span [first_missing, frame_index-1] on a forward gap. A host that can RFI (AMD LTR / NVENC) re-references a known-good frame instead of paying a 20-40x IDR spike; the frames_dropped-driven keyframe path stays the backstop for when the recovery frame itself is lost. - Export request_rfi + note_frame_index over the C ABI (Apple client). - Call it from the Android (hw+sw pumps), Apple (StreamPump + Stage2Pipeline via PunktfunkConnection.noteFrameIndex), and Windows in-process pumps. Linux/Deck inherit it through pf-client-core's session pump. - Split the decision into a pure RfiRecovery::observe(frame_index, now) and add 8 unit tests: arming, contiguous runs, exact lost-range, single-frame drop, the 100ms throttle (burst-suppress then re-open), reorder stragglers, and u32 wraparound (contiguous + gap-range). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1597,6 +1597,43 @@ PunktfunkStatus punktfunk_connection_request_mode(const PunktfunkConnection *c,
|
||||
PunktfunkStatus punktfunk_connection_request_keyframe(const PunktfunkConnection *c);
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Ask the host to recover from loss by **reference-frame invalidation** rather than a full IDR:
|
||||
// report the range `[first_frame, last_frame]` of access units the client can no longer trust
|
||||
// (the first missing `frame_index` through the newest received). An RFI-capable host (AMD LTR /
|
||||
// NVENC) re-references a known-good picture before `first_frame` and emits a clean P-frame tagged
|
||||
// `USER_FLAG_RECOVERY_ANCHOR` — no 20-40x IDR spike; a host that can't RFI forces an IDR instead
|
||||
// (same effect as [`punktfunk_connection_request_keyframe`]). Non-blocking, fire-and-forget; the
|
||||
// recovered frame is the only ack, so THROTTLE it exactly like the keyframe request. Prefer this
|
||||
// over the keyframe request on loss so AMD/RFI hosts avoid the spike; keep the keyframe request as
|
||||
// the backstop for when the recovery frame itself is lost.
|
||||
//
|
||||
// # Safety
|
||||
// `c` is a valid connection handle.
|
||||
PunktfunkStatus punktfunk_connection_request_rfi(const PunktfunkConnection *c,
|
||||
uint32_t first_frame,
|
||||
uint32_t last_frame);
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Feed each received frame's `frame_index` (the [`PunktfunkFrame::frame_index`] field, in receive
|
||||
// order) so the client recovers from loss with a cheap reference-frame invalidation instead of a
|
||||
// full IDR. On a forward gap (a `frame_index` jump = the intervening frames were lost and the
|
||||
// following AUs reference a picture that never arrived) this fires a THROTTLED
|
||||
// [`punktfunk_connection_request_rfi`] for the lost range; an RFI-capable host (AMD LTR / NVENC)
|
||||
// then recovers with a clean P-frame instead of a 20-40x IDR spike. Call it for every received
|
||||
// frame — it is cheap and idempotent, and the [`punktfunk_connection_frames_dropped`]-driven
|
||||
// keyframe request stays the backstop. Writes whether a forward gap was detected this call to
|
||||
// `gap_out` (nullable — a client with a post-loss display freeze can use it to re-arm; most
|
||||
// clients pass NULL and ignore it).
|
||||
//
|
||||
// # Safety
|
||||
// `c` is a valid connection handle; `gap_out` is writable or NULL.
|
||||
PunktfunkStatus punktfunk_connection_note_frame_index(const PunktfunkConnection *c,
|
||||
uint32_t frame_index,
|
||||
bool *gap_out);
|
||||
#endif
|
||||
|
||||
#if defined(PUNKTFUNK_FEATURE_QUIC)
|
||||
// Cumulative access units the host→client reassembler dropped as unrecoverable (FEC couldn't
|
||||
// rebuild them). A video loop polls this and calls [`punktfunk_connection_request_keyframe`]
|
||||
|
||||
Reference in New Issue
Block a user