nx_nack.nx source
↩ module page · 27 lines · 1834 B
1// nx_nack.nx -- SOVEREIGN NACK (negative-acknowledgement selective retransmit), the loss-recovery rung
2// that partners FEC + the jitter buffer. BENCHMARK: WebRTC's rtx/NACK -- when a frame is lost and FEC
3// can't cover it, the receiver ASKS for it again, but ONLY if a retransmit can still arrive before its
4// playout deadline (otherwise the request is wasted bandwidth). Built from scratch, pure integer, no third
5// party, transport-agnostic. The DECISION is the whole game: request iff (time-to-deadline >= one RTT +
6// a frame), rate-limited to one request per RTT (a retransmit can't have arrived sooner), capped at
7// NACK_MAX_RETRIES (give up, let concealment handle it -- no NACK storm). Sender keeps a small history
8// ring so it can honor requests. license_tier: ORIGINAL
9
10const NACK_MAX_RETRIES: i64 = 3
11
12// receiver: should we request retransmit of `seq` at time `now`? deadline = when it must be played out.
13func nack_should_request(seq: i64, now: i64, anchor_play: i64, anchor_seq: i64, frame_us: i64,
14 rtt_us: i64, retries: i64, last_nack: i64) -> i64 {
15 let deadline: i64 = anchor_play + (seq - anchor_seq) * frame_us
16 let ttl: i64 = deadline - now
17 if ttl < rtt_us + frame_us { return 0 } // a retransmit can't arrive in time -> don't waste it
18 if retries >= NACK_MAX_RETRIES { return 0 } // give up after N tries (conceal instead)
19 if retries > 0 { if now - last_nack < rtt_us { return 0 } } // one request per RTT max (no storm)
20 return 1
21}
22
23// sender history ring (seq -> present): put on send, query on NACK.
24func nack_hist_put(hist_seq: *i64, H: i64, seq: i64) -> i64 { hist_seq[seq % H] = seq; return 0 }
25func nack_hist_has(hist_seq: *i64, H: i64, seq: i64) -> i64 { if hist_seq[seq % H] == seq { return 1 } return 0 }
26
27func main() -> i64 { return 0 }