Skip to main content

pixelflux/encoders/
reference.rs

1//! Which frame a delivered frame predicts from, kept true across the frames a client reports
2//! lost.
3
4use std::collections::VecDeque;
5
6/// How many frames back a prediction may reach: the decoded picture buffer every session asks
7/// for, where its level admits it.
8pub const REFERENCE_FRAMES: u32 = 8;
9
10/// The most long-term frames a window keeps as anchors, out of the decoded picture buffer, so a
11/// loss older than every recent frame still finds one before it.
12pub const ANCHORS: usize = 2;
13
14/// Two anchors take a frame every `ANCHOR_EVERY` frames after a key frame by turns, the lost or
15/// older one first, so one is always twelve to twenty-three frames old.
16const ANCHOR_EVERY: u64 = 12;
17
18/// One anchor takes a frame every `ANCHOR_ALONE_EVERY` frames after a key frame: a loss just
19/// before it is reported once it has moved on, which a longer period makes rarer.
20const ANCHOR_ALONE_EVERY: u64 = 48;
21
22/// An anchor predicts from the newest frame every consumer was sent (`settle`) only within an
23/// anchor period and `SETTLE_SLACK` frames of it, a consumer that took the last anchor. One
24/// further behind, for which every anchor would predict from far back and cost every consumer
25/// the bits, is predicted past on its own report instead (from the pinned anchor, where it comes
26/// to that). The frame where H.264's `frame_num` wraps reaches back however far: a decoder that
27/// misses it cannot be predicted past.
28const SETTLE_SLACK: u64 = 4;
29
30/// Of two anchors, the one marked predicts from the frame every consumer was sent (`settle`) only
31/// where that is one of the `SETTLE_RECENT` newest recent frames, which the buffer keeps beside two
32/// anchors on every device (`nvenc::anchor_count`). Further back the anchor costs the moving
33/// picture: NVENC's HEVC on a 1080p page scrolling at 8 Mbit/s, every consumer's acknowledgement
34/// late, averaged 49.3 dB from two frames back, 49.0 from three, 38.5 from four and 27.4 from the
35/// other anchor a period back (the frame sent to all of them past the recent ones), against 49.7.
36/// The anchor predicts from the frame before it then, and a consumer that loses frames is predicted
37/// past from the pinned anchor on its own report.
38const SETTLE_RECENT: usize = 2;
39
40/// The schedule keeps an anchor every consumer was sent until they all hold it, for up to
41/// `PENDING_PERIODS` anchor periods: a consumer slower to report than a period, as one on a
42/// narrow link is, would otherwise never see an anchor pinned past the key frame.
43const PENDING_PERIODS: u64 = 4;
44
45/// A lone anchor every consumer holds is not marked over while the newest frame they were all
46/// sent runs more than `HOLD_LAG` frames ahead of the newest they all hold, as when a consumer's
47/// link goes dark: a consumer reporting its loss once it is back is predicted past only from an
48/// anchor it holds. Past `HOLD_MAX` frames it is marked over all the same, a consumer that far
49/// behind taking a key frame.
50const HOLD_LAG: u64 = ANCHOR_ALONE_EVERY / 2;
51const HOLD_MAX: u64 = 8 * ANCHOR_ALONE_EVERY;
52
53/// What the consumers say of a frame, applied in the order they said it ahead of an encode.
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum ReferenceReport {
56    /// A consumer lost the frame (`invalidate_reference`).
57    Lost(u16),
58    /// Every consumer holds the frame (`ReferenceWindow::acknowledge`).
59    Held(u16),
60    /// Every consumer was sent the frame, which one may yet lose (`ReferenceWindow::acknowledge`).
61    Sent(u16),
62    /// The consumers' sender let the frame go unsent to hold the stream to a rate, as it was handed
63    /// over (`invalidate_reference(withheld=True)`): lost to every consumer.
64    Withheld(u16),
65}
66
67/// The frame a delivered frame predicts from.
68#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
69pub enum Reference {
70    /// A session that cannot say which frame it predicted from, or leave a lost one out.
71    #[default]
72    Untracked,
73    /// A frame that decodes on its own.
74    None,
75    /// The id of the frame predicted from.
76    Frame(u16),
77}
78
79impl Reference {
80    /// The wire form: the frame id, -1 for a frame that decodes on its own, -2 where the
81    /// session does not track its references.
82    pub fn frame_id(self) -> i32 {
83        match self {
84            Reference::Untracked => -2,
85            Reference::None => -1,
86            Reference::Frame(id) => i32::from(id),
87        }
88    }
89}
90
91/// What an invalidation asks of the encoder.
92#[derive(Debug, PartialEq, Eq)]
93pub enum Invalidation {
94    /// The frame predates the last key frame, was already removed, or was never a reference.
95    Ignored,
96    /// Forget the frame carrying this timestamp and every frame after it.
97    Forget(u64),
98    /// The frame has left the window, so every reference still held predicts through it: the
99    /// next frame has to be a key frame.
100    KeyFrame,
101}
102
103/// The reference frames a session's decoder holds, in encode order, with the ones a client
104/// reported lost marked.
105///
106/// An encoder told to forget a frame leaves it and every frame after it out of its predictions,
107/// predicts the next frame from the newest frame before them, and codes a key frame when none is
108/// left. The window mirrors that, so the reference a frame carries on the wire is the one its
109/// bitstream uses, and it asks for the key frame where the encoder would. Frames are addressed
110/// by the capture's frame id, which wraps; the timestamp the encoder is told to forget is the
111/// session's own count of encoded frames, which does not.
112///
113/// An H.264 session also names how many values its `frame_num` takes before wrapping
114/// (`set_frame_num_range`). A decoder that never receives the frame carrying `frame_num` 0 sees
115/// a gap across that wrap, and FFmpeg's H.264 decoder, which the browsers decode with on Linux,
116/// derives the picture order past such a gap a wrap short and drops the pictures after it until
117/// that order passes the last one shown (13 of a range of 16 after two frames lost), so a
118/// loss covering that frame is answered with a key frame rather than a prediction past it.
119///
120/// A window `with_anchors` also keeps long-term frames out of the buffer: the key frame is the
121/// first anchor and frames after it are marked on a schedule, and a loss older than every recent
122/// frame is predicted past from the newest anchor before it.
123///
124/// Where the consumers say which frames every one of them holds, or was sent (`acknowledge`),
125/// several of them share the stream and each recovers on its own: the newest anchor all of them
126/// hold is pinned, the schedule marking the other, so a loss of any depth is predicted past from a
127/// frame every consumer has; an anchor predicts from a frame they were all sent where that is near
128/// (`settle`), so each can decode it; and in H.264 the frame where `frame_num` wraps is an anchor
129/// too.
130pub struct ReferenceWindow {
131    frames: VecDeque<(u16, u64, bool)>,
132    capacity: usize,
133    lends: bool,
134    anchors: Vec<Option<(u16, u64, bool)>>,
135    recent: VecDeque<(u16, u64, bool)>,
136    next_pts: u64,
137    age: KeyAge,
138    key_pts: u64,
139    frame_num_range: u64,
140    acknowledged: bool,
141    common: VecDeque<u64>,
142    sent: VecDeque<u64>,
143}
144
145/// How far a frame id lies behind the newest frame recorded, in capture frames counted across
146/// the id's wrap, and whether it went out at or after the last key frame. A session can run far
147/// longer than the id takes to wrap between key frames, so the ids alone cannot order a frame
148/// against the key.
149#[derive(Default)]
150struct KeyAge {
151    newest: u16,
152    since_key: u64,
153}
154
155impl KeyAge {
156    fn record(&mut self, frame_id: u16, key: bool) {
157        self.since_key = if key {
158            0
159        } else {
160            self.since_key + u64::from(frame_id.wrapping_sub(self.newest))
161        };
162        self.newest = frame_id;
163    }
164
165    /// How many frames `frame_id` went out before the newest one; `None` for a frame before the
166    /// last key frame or one never sent.
167    fn behind(&self, frame_id: u16) -> Option<u64> {
168        let behind = u64::from(self.newest.wrapping_sub(frame_id));
169        (behind < 0x8000 && behind <= self.since_key).then_some(behind)
170    }
171}
172
173impl ReferenceWindow {
174    pub fn new(capacity: u32) -> Self {
175        Self {
176            frames: VecDeque::new(),
177            capacity: capacity.max(1) as usize,
178            lends: false,
179            anchors: Vec::new(),
180            recent: VecDeque::new(),
181            next_pts: 0,
182            age: KeyAge::default(),
183            key_pts: 0,
184            frame_num_range: 0,
185            acknowledged: false,
186            common: VecDeque::new(),
187            sent: VecDeque::new(),
188        }
189    }
190
191    /// A window over a decoded picture buffer of `capacity` frames, `count` of them anchors (one
192    /// or two).
193    pub fn with_anchors(capacity: u32, count: usize) -> Self {
194        let mut w = Self::new(capacity);
195        w.anchors = vec![None; count.clamp(1, ANCHORS)];
196        w.set_capacity(capacity);
197        w
198    }
199
200    /// An anchor holding no frame, or a lost one, lends its share of the buffer to the recent
201    /// frames, as NVENC's AV1 encoder does with a long-term slot it holds nothing in.
202    pub fn lend_free_anchors(&mut self) {
203        self.lends = true;
204    }
205
206    /// The recent frames the buffer holds: its share beside the anchors, and that of each anchor
207    /// lending its own (`lend_free_anchors`).
208    fn recent_capacity(&self) -> usize {
209        let free = self
210            .anchors
211            .iter()
212            .filter(|a| a.is_none_or(|a| a.2))
213            .count();
214        self.capacity + if self.lends { free } else { 0 }
215    }
216
217    /// Whether the window keeps anchors.
218    pub fn anchored(&self) -> bool {
219        !self.anchors.is_empty()
220    }
221
222    /// How many anchors the window keeps.
223    pub fn anchor_count(&self) -> usize {
224        self.anchors.len()
225    }
226
227    /// The anchor the next frame is marked into, if any: the first for a key frame or while none
228    /// is held, and on the schedule after it the one that is empty, lost, or older, never the one
229    /// pinned (`acknowledge`) where there are two, nor one every consumer was sent and is yet to
230    /// hold (`PENDING_PERIODS`), nor a lone one they all hold while frames they were all sent go
231    /// unheld (`HOLD_LAG`) or while it would predict from the one it marks over (`reaches_back`).
232    /// Acknowledged, an H.264 frame where `frame_num` wraps is an anchor too.
233    pub fn plan_anchor(&self, key: bool) -> Option<u8> {
234        if self.anchors.is_empty() {
235            return None;
236        }
237        if key || !self.has_reference() || self.anchors.iter().all(Option::is_none) {
238            return Some(0);
239        }
240        let wraps = self.wraps_next();
241        if !(self.next_pts - self.key_pts).is_multiple_of(self.anchor_period()) && !wraps {
242            return None;
243        }
244        if self.anchors.len() == 1 && self.acknowledged && !wraps && self.reaches_back() {
245            return None;
246        }
247        if self.anchors.len() == 1
248            && self.acknowledged
249            && !wraps
250            && let Some((_, pts, false)) = self.anchors[0]
251            && self.is_common(pts)
252        {
253            let held = self
254                .common
255                .back()
256                .map_or(self.key_pts, |&c| c.max(self.key_pts));
257            let sent = self.sent.back().map_or(held, |&s| s.max(held));
258            if sent - held > HOLD_LAG && self.next_pts - pts < HOLD_MAX {
259                return None;
260            }
261        }
262        let rank = |a: &Option<(u16, u64, bool)>| {
263            a.map_or((0, 0), |(_, pts, lost)| (u8::from(!lost), pts))
264        };
265        let pinned = self.pinned_anchor();
266        let pending = |a: &Option<(u16, u64, bool)>| {
267            a.is_some_and(|(_, pts, lost)| {
268                !lost
269                    && self.is_shared(pts)
270                    && !self.is_common(pts)
271                    && self.next_pts - pts < PENDING_PERIODS * self.anchor_period()
272            })
273        };
274        (0..self.anchors.len())
275            .filter(|&i| self.anchors.len() == 1 || Some(i) != pinned)
276            .filter(|&i| wraps || !pending(&self.anchors[i]))
277            .min_by_key(|&i| rank(&self.anchors[i]))
278            .map(|i| i as u8)
279    }
280
281    /// Whether an anchor marked now would predict from an older one (`settle`): no recent frame
282    /// held is one every consumer was sent, as while one of them lags, and the newest anchor is
283    /// under `HOLD_MAX` frames old. A lone anchor so marked costs every consumer the bits of a
284    /// prediction from an anchor period back, and the one it would mark over serves the one
285    /// lagging as well.
286    fn reaches_back(&self) -> bool {
287        self.newest_valid()
288            .is_some_and(|(_, pts)| !self.is_shared(pts))
289            && !self.frames.iter().any(|f| !f.2 && self.is_shared(f.1))
290            && self
291                .anchor_frames()
292                .filter(|a| !a.2)
293                .map(|a| a.1)
294                .max()
295                .is_some_and(|pts| self.next_pts - pts < HOLD_MAX)
296    }
297
298    /// How many frames apart the schedule marks anchors.
299    fn anchor_period(&self) -> u64 {
300        if self.anchors.len() == 1 {
301            ANCHOR_ALONE_EVERY
302        } else {
303            ANCHOR_EVERY
304        }
305    }
306
307    /// Whether the next frame carries H.264 `frame_num` 0, the consumers acknowledging.
308    fn wraps_next(&self) -> bool {
309        self.acknowledging()
310            && self.frame_num_range > 0
311            && (self.next_pts - self.key_pts).is_multiple_of(self.frame_num_range)
312    }
313
314    /// The consumer says which frames its clients hold (`acknowledge`) from the first frame on.
315    pub fn set_acknowledged(&mut self) {
316        self.acknowledged = true;
317    }
318
319    /// Note that every consumer holds `frame_id`, or where not `held` was sent it; false for a
320    /// frame not remembered, one a consumer reported lost, or one from before the last key frame.
321    pub fn acknowledge(&mut self, frame_id: u16, held: bool) -> bool {
322        self.acknowledged = true;
323        let Some(&(_, pts, lost)) = self.recent.iter().rev().find(|f| f.0 == frame_id) else {
324            return false;
325        };
326        if lost || pts < self.key_pts {
327            return false;
328        }
329        let noted = if held {
330            &mut self.common
331        } else {
332            &mut self.sent
333        };
334        if let Err(at) = noted.binary_search(&pts) {
335            noted.insert(at, pts);
336        }
337        while noted.len() > RECENT_FRAMES {
338            noted.pop_front();
339        }
340        true
341    }
342
343    /// Whether the consumers acknowledge what they hold or were sent (`set_acknowledged`).
344    pub fn acknowledging(&self) -> bool {
345        self.acknowledged
346    }
347
348    /// Whether every consumer holds the frame at `pts`: the key frame, which each is sent before
349    /// any other, one acknowledged, or, acknowledged, an H.264 frame a consumer never leaves out
350    /// (`kept`), so the anchor at the `frame_num` wrap stays pinned until a newer one is held.
351    fn is_common(&self, pts: u64) -> bool {
352        pts == self.key_pts
353            || (self.acknowledged && self.kept(pts))
354            || self.common.binary_search(&pts).is_ok()
355    }
356
357    /// Whether the frame at `pts` is one a consumer sends every client of an H.264 stream it
358    /// sends any of (pixelflux's consumers do): where `frame_num` wraps, which a decoder cannot
359    /// be predicted past without.
360    fn kept(&self, pts: u64) -> bool {
361        self.frame_num_range > 0
362            && pts > self.key_pts
363            && (pts - self.key_pts).is_multiple_of(self.frame_num_range)
364    }
365
366    /// Whether every consumer was sent the frame at `pts`, as every one holds a common one.
367    fn is_shared(&self, pts: u64) -> bool {
368        self.is_common(pts) || self.sent.binary_search(&pts).is_ok()
369    }
370
371    /// Whether every consumer was sent the frame the next one predicts from, asked before that
372    /// one is recorded: a lone anchor marked over the one it predicts from lets that one go.
373    pub fn predicts_from_shared(&self) -> bool {
374        self.newest_valid()
375            .is_some_and(|(_, pts)| self.is_shared(pts))
376    }
377
378    /// The anchor holding the newest frame every consumer holds, which the schedule leaves.
379    fn pinned_anchor(&self) -> Option<usize> {
380        if !self.acknowledged {
381            return None;
382        }
383        self.anchors
384            .iter()
385            .enumerate()
386            .filter_map(|(i, a)| a.filter(|f| !f.2 && self.is_common(f.1)).map(|f| (i, f.1)))
387            .max_by_key(|&(_, pts)| pts)
388            .map(|(i, _)| i)
389    }
390
391    /// Before an anchor: where the newest frame held is not one every consumer was sent, forget
392    /// the frames after the newest that is, so the anchor predicts from it and every consumer can
393    /// decode it. The invalidation that does it, for the encoder; None where none is needed, no
394    /// frame held is one they were all sent, or that one lies further back than `SETTLE_SLACK`
395    /// or, of two anchors, `SETTLE_RECENT` allows.
396    pub fn settle(&mut self) -> Option<Invalidation> {
397        if !self.acknowledging() {
398            return None;
399        }
400        // The frame at a `frame_num` wrap predicts from one every consumer holds, not one it was
401        // only sent: a client that lost that one could not be predicted past the wrap.
402        let wraps = self.wraps_next();
403        let usable = |w: &Self, pts: u64| {
404            if wraps {
405                w.is_common(pts)
406            } else {
407                w.is_shared(pts)
408            }
409        };
410        let (_, newest) = self.newest_valid()?;
411        if usable(self, newest) {
412            return None;
413        }
414        let shared = self
415            .held()
416            .filter(|f| !f.2 && usable(self, f.1))
417            .map(|f| f.1)
418            .max()?;
419        if !wraps && self.next_pts - shared > self.anchor_period() + SETTLE_SLACK {
420            return None;
421        }
422        if self.anchors.len() > 1
423            && !wraps
424            && !self
425                .frames
426                .iter()
427                .rev()
428                .filter(|f| !f.2)
429                .take(SETTLE_RECENT)
430                .any(|f| f.1 == shared)
431        {
432            return None;
433        }
434        let &(first, _, _) = self.recent.iter().find(|f| f.1 > shared && !f.2)?;
435        Some(self.invalidate(first))
436    }
437
438    /// Before an H.264 frame: where the newest frame held is a recent one further back than the
439    /// buffer holds recent frames, forget the frames after the newest anchor every consumer was
440    /// sent (acknowledged: the pinned one, a newer one they were all sent, or the one at the
441    /// `frame_num` wrap, which a consumer never leaves out), else every recent one, so the frame
442    /// predicts from an anchor. A decoder that fills a `frame_num` gap with pictures of its own
443    /// (FFmpeg's, which Chromium decodes in software with, and Firefox and WebKit on Linux) has let
444    /// that recent frame go by then for a client that missed the frames since (anchors among them,
445    /// which the recent frames leave out), and fails a frame that names it (Chromium's does). The
446    /// invalidation, for the encoder; None where none is needed.
447    pub fn forget_stale(&mut self) -> Option<Invalidation> {
448        if self.frame_num_range == 0 || self.anchors.is_empty() {
449            return None;
450        }
451        let (_, newest) = self.newest_valid()?;
452        let recent = self.frames.iter().any(|f| f.1 == newest && !f.2);
453        if !recent || self.next_pts - newest <= self.capacity as u64 {
454            return None;
455        }
456        let target = self
457            .anchor_frames()
458            .filter(|a| !a.2 && (!self.acknowledged || self.is_shared(a.1)))
459            .map(|a| a.1)
460            .max();
461        let first = match target {
462            Some(anchor) => self.recent.iter().find(|f| f.1 > anchor && !f.2)?.0,
463            None => self.frames.iter().find(|f| !f.2)?.0,
464        };
465        Some(self.invalidate(first))
466    }
467
468    /// The anchor the next frame predicts from, where the newest frame held is an anchor; the
469    /// encoder names it, since NVENC's H.264 fallback takes the first anchor otherwise.
470    pub fn predicting_anchor(&self) -> Option<u8> {
471        let (_, pts) = self.newest_valid()?;
472        if self.frames.iter().any(|f| f.1 == pts && !f.2) {
473            return None;
474        }
475        self.anchors
476            .iter()
477            .position(|a| a.is_some_and(|a| a.1 == pts && !a.2))
478            .map(|i| i as u8)
479    }
480
481    /// The anchor holding the frame at `pts`, where one does, lost or not: an encoder that writes
482    /// its own headers lists it long-term under that index while the decoder holds it.
483    pub fn anchor_slot(&self, pts: u64) -> Option<u8> {
484        self.anchors
485            .iter()
486            .position(|a| a.is_some_and(|a| a.1 == pts))
487            .map(|i| i as u8)
488    }
489
490    /// How many values the stream's `frame_num` takes before it wraps; 0 for a codec without
491    /// that counter. A range other than the one before is logged (`frame_num_range_line`).
492    pub fn set_frame_num_range(&mut self, range: u32) {
493        if let Some(line) = frame_num_range_line(self.frame_num_range, u64::from(range)) {
494            println!("{line}");
495        }
496        self.frame_num_range = u64::from(range);
497    }
498
499    /// Forget every frame held, for a stream the session restarts with a key frame of its own.
500    pub fn reset(&mut self) {
501        self.frames.clear();
502        self.anchors.iter_mut().for_each(|a| *a = None);
503        self.recent.clear();
504        self.common.clear();
505        self.sent.clear();
506    }
507
508    /// The decoded picture buffer the session has now; frames past it are let go. The anchors
509    /// take their share of it.
510    pub fn set_capacity(&mut self, capacity: u32) {
511        self.capacity = capacity.saturating_sub(self.anchors.len() as u32).max(1) as usize;
512        while self.frames.len() > self.recent_capacity() {
513            self.frames.pop_front();
514        }
515    }
516
517    /// The timestamp the next frame is encoded with.
518    pub fn next_pts(&self) -> u64 {
519        self.next_pts
520    }
521
522    /// The anchors held, in no order.
523    fn anchor_frames(&self) -> impl Iterator<Item = (u16, u64, bool)> + '_ {
524        self.anchors.iter().flatten().copied()
525    }
526
527    /// Whether a frame not coded as a key frame has a reference left to predict from.
528    pub fn has_reference(&self) -> bool {
529        self.frames
530            .iter()
531            .copied()
532            .chain(self.anchor_frames())
533            .any(|f| !f.2)
534    }
535
536    /// The newest frame the client still has, as its id and timestamp: the one the next frame
537    /// predicts from.
538    pub fn newest_valid(&self) -> Option<(u16, u64)> {
539        let held = self.frames.iter().copied().chain(self.anchor_frames());
540        held.filter(|f| !f.2)
541            .max_by_key(|f| f.1)
542            .map(|f| (f.0, f.1))
543    }
544
545    /// The frames the decoder holds, oldest first: each frame's id, timestamp, and whether
546    /// a client reported it lost.
547    pub fn held(&self) -> impl Iterator<Item = (u16, u64, bool)> + '_ {
548        // The anchors, newest first so the oldest pops, merged into the recent frames.
549        let mut anchors: Vec<_> = self.anchor_frames().collect();
550        anchors.sort_by_key(|f| std::cmp::Reverse(f.1));
551        let mut frames = self.frames.iter().copied().peekable();
552        std::iter::from_fn(move || {
553            if anchors
554                .last()
555                .is_some_and(|a| frames.peek().is_none_or(|f| a.1 < f.1))
556            {
557                anchors.pop()
558            } else {
559                frames.next()
560            }
561        })
562    }
563
564    /// The timestamp of the last key frame, where the counts a codec keeps restart.
565    pub fn key_pts(&self) -> u64 {
566        self.key_pts
567    }
568
569    /// Record the frame just encoded with `next_pts` and answer what it predicted from.
570    pub fn record(&mut self, frame_id: u16, key: bool) -> Reference {
571        self.record_marked(frame_id, key, None)
572    }
573
574    /// Record the frame just encoded with `next_pts`, marked into `anchor` where the session
575    /// marked it (`plan_anchor`), and answer what it predicted from.
576    pub fn record_marked(&mut self, frame_id: u16, key: bool, anchor: Option<u8>) -> Reference {
577        let pts = self.next_pts;
578        self.next_pts += 1;
579        self.age.record(frame_id, key);
580        let reference = if key {
581            self.frames.clear();
582            self.anchors.iter_mut().for_each(|a| *a = None);
583            self.common.clear();
584            self.sent.clear();
585            self.key_pts = pts;
586            Reference::None
587        } else {
588            self.newest_valid()
589                .map_or(Reference::None, |(id, _)| Reference::Frame(id))
590        };
591        match anchor.and_then(|slot| self.anchors.get_mut(slot as usize)) {
592            Some(slot) => *slot = Some((frame_id, pts, false)),
593            None => self.frames.push_back((frame_id, pts, false)),
594        }
595        while self.frames.len() > self.recent_capacity() {
596            self.frames.pop_front();
597        }
598        self.recent.push_back((frame_id, pts, false));
599        if self.recent.len() > RECENT_FRAMES {
600            self.recent.pop_front();
601        }
602        reference
603    }
604
605    /// Take back the frame just recorded, which the session codes again in its place under the
606    /// same id: it is left out of the predictions as a frame the client lost is, and `forget`
607    /// tells the encoder its timestamp. False, with the window as it was, where it was a key
608    /// frame, nothing older is left to predict from, it carries an H.264 `frame_num` 0, which
609    /// the decoder has to see (`set_frame_num_range`), or `forget` refuses.
610    pub fn retract(&mut self, forget: impl FnOnce(u64) -> bool) -> bool {
611        let Some(pts) = self.next_pts.checked_sub(1) else {
612            return false;
613        };
614        let older = self
615            .frames
616            .iter()
617            .copied()
618            .chain(self.anchor_frames())
619            .any(|f| !f.2 && f.1 < pts);
620        let wraps =
621            self.frame_num_range > 0 && (pts - self.key_pts).is_multiple_of(self.frame_num_range);
622        if !older || wraps || !forget(pts) {
623            return false;
624        }
625        let held = self
626            .frames
627            .iter_mut()
628            .chain(self.anchors.iter_mut().flatten());
629        for f in held.chain(self.recent.iter_mut()).filter(|f| f.1 == pts) {
630            f.2 = true;
631        }
632        true
633    }
634
635    /// Leave `frame_id` and every frame encoded after it out of the references. A report for
636    /// a picture already removed by an earlier invalidation leaves the recovered chain intact.
637    /// The bounded history dates those reports across capture-id gaps and wrap; a loss older
638    /// than the history keeps the conservative key-frame fallback.
639    pub fn invalidate(&mut self, frame_id: u16) -> Invalidation {
640        if self
641            .recent
642            .iter()
643            .rev()
644            .find(|f| f.0 == frame_id)
645            .is_some_and(|f| f.2)
646        {
647            return Invalidation::Ignored;
648        }
649        let action = if self.anchored() {
650            self.invalidate_anchored(frame_id)
651        } else {
652            self.invalidate_recent(frame_id)
653        };
654        let cut = match action {
655            Invalidation::Forget(pts) => pts,
656            Invalidation::KeyFrame => self.key_pts,
657            Invalidation::Ignored => return action,
658        };
659        for f in self.recent.iter_mut().filter(|f| f.1 >= cut) {
660            f.2 = true;
661        }
662        action
663    }
664
665    /// Invalidate a session with only recent references.
666    fn invalidate_recent(&mut self, frame_id: u16) -> Invalidation {
667        let Some(&(oldest, _, _)) = self.frames.front() else {
668            return Invalidation::Ignored;
669        };
670        // The newest with the id: a retracted frame shares it with the one sent in its place.
671        match self.frames.iter().rposition(|f| f.0 == frame_id) {
672            Some(at) => {
673                let pts = self.frames[at].1;
674                let wraps = self.frame_num_range > 0
675                    && self
676                        .frames
677                        .iter()
678                        .skip(at)
679                        .any(|f| (f.1 - self.key_pts).is_multiple_of(self.frame_num_range));
680                for f in self.frames.iter_mut().skip(if wraps { 0 } else { at }) {
681                    f.2 = true;
682                }
683                if wraps {
684                    Invalidation::KeyFrame
685                } else {
686                    Invalidation::Forget(pts)
687                }
688            }
689            None if self
690                .age
691                .behind(frame_id)
692                .is_some_and(|b| b > u64::from(self.age.newest.wrapping_sub(oldest))) =>
693            {
694                for f in self.frames.iter_mut() {
695                    f.2 = true;
696                }
697                Invalidation::KeyFrame
698            }
699            None => Invalidation::Ignored,
700        }
701    }
702
703    /// `invalidate` where anchors are kept: a loss older than every recent frame is dated by
704    /// the frames remembered and predicted past from an anchor before it, where one is left.
705    fn invalidate_anchored(&mut self, frame_id: u16) -> Invalidation {
706        let remembered = self
707            .recent
708            .iter()
709            .rev()
710            .find(|r| r.0 == frame_id)
711            .map(|r| r.1);
712        let lost_pts = match remembered {
713            Some(pts) if pts >= self.key_pts => pts,
714            Some(_) => return Invalidation::Ignored,
715            None => {
716                // Older than every frame remembered, so everything held predicts through it; a
717                // frame before the key frame, or one never sent, was never a reference.
718                let Some(&(oldest, _, _)) = self.recent.front() else {
719                    return Invalidation::Ignored;
720                };
721                if !self
722                    .age
723                    .behind(frame_id)
724                    .is_some_and(|b| b > u64::from(self.age.newest.wrapping_sub(oldest)))
725                {
726                    return Invalidation::Ignored;
727                }
728                self.key_pts
729            }
730        };
731        let newest = self.next_pts.saturating_sub(1);
732        // The first frame at or after the loss that carries `frame_num` 0, if one was sent.
733        let range = self.frame_num_range;
734        let wraps =
735            range > 0 && (lost_pts - self.key_pts).div_ceil(range) * range <= newest - self.key_pts;
736        let cut = if wraps { 0 } else { lost_pts };
737        for f in self.frames.iter_mut().filter(|f| f.1 >= cut) {
738            f.2 = true;
739        }
740        for a in self.anchors.iter_mut().flatten().filter(|a| a.1 >= cut) {
741            a.2 = true;
742        }
743        if wraps || !self.has_reference() {
744            Invalidation::KeyFrame
745        } else {
746            Invalidation::Forget(lost_pts)
747        }
748    }
749}
750
751/// The buffers a VP8 or VP9 encoder predicts from, with the frame each holds and whether a
752/// client reported it lost.
753///
754/// Some buffers are spent on anchors older than the last few frames, so a loss up to twelve
755/// frames deep still finds a frame older than it. VP8 has three, LAST, GOLDEN, and ALTREF: every
756/// frame lands in LAST, and every twelfth in GOLDEN and ALTREF by turns, so one anchor is always
757/// twelve to twenty-three frames old. VP9 has eight: the last four frames by turns in the first
758/// four, and every fourth frame by turns in the other four, the oldest twelve to fifteen frames
759/// back. A frame predicts from the newest buffer whose frame the client still has, the first
760/// such buffer when others are as new, and a VP8 frame coded from an anchor rather than LAST
761/// refreshes all three, since it is the newest picture both sides hold. The frame ids wrap; the
762/// timestamps are the session's own count of encoded frames and do not, and the recent ones are
763/// kept so a lost frame no buffer holds still dates the buffers coded after it.
764pub struct ReferenceSlots {
765    slots: [Option<(u16, u64, bool)>; 8],
766    count: usize,
767    recent: VecDeque<(u16, u64)>,
768    next_pts: u64,
769    age: KeyAge,
770    key_pts: u64,
771    acknowledged: bool,
772    common: VecDeque<u64>,
773    sent: VecDeque<u64>,
774}
775
776/// How many recent frames are remembered by id, about eight seconds at 60 frames a second: a
777/// consumer whose clients hold a pinned anchor may report a run lost that long ago and have it
778/// predicted past (`acknowledge`); the anchors alone reach twenty-three back.
779const RECENT_FRAMES: usize = 512;
780
781/// VP8's GOLDEN and ALTREF each take every `ANCHOR_PERIOD`th frame, half a period apart.
782const ANCHOR_PERIOD: u64 = 24;
783
784/// VP9 keeps the last `RING` frames, and every `RING`th frame in as many buffers again.
785const RING: u64 = 4;
786
787/// The buffers a frame refreshes, as a bit per slot: LAST, GOLDEN, ALTREF for VP8, the eight in
788/// order for VP9.
789#[derive(Clone, Copy, Debug, PartialEq, Eq)]
790pub struct SlotRefresh(pub u8);
791
792impl SlotRefresh {
793    pub const LAST: u8 = 1;
794    pub const GOLDEN: u8 = 2;
795    pub const ALTREF: u8 = 4;
796    pub const ALL: u8 = 7;
797
798    pub fn refreshes(self, slot: u8) -> bool {
799        self.0 & slot != 0
800    }
801}
802
803/// How the next frame predicts and which buffers it refreshes.
804#[derive(Clone, Copy, Debug, PartialEq, Eq)]
805pub struct SlotPlan {
806    /// The buffer predicted from, as a `SlotRefresh` bit; zero for a key frame.
807    pub predict_from: u8,
808    pub refresh: SlotRefresh,
809}
810
811impl SlotPlan {
812    /// A VP8 key frame: it predicts from nothing and re-anchors every buffer.
813    pub const KEY: Self = Self {
814        predict_from: 0,
815        refresh: SlotRefresh(SlotRefresh::ALL),
816    };
817}
818
819impl Default for ReferenceSlots {
820    fn default() -> Self {
821        Self::new()
822    }
823}
824
825impl ReferenceSlots {
826    /// VP8's three buffers.
827    pub fn new() -> Self {
828        Self::with(3)
829    }
830
831    /// VP9's eight buffers.
832    pub fn vp9() -> Self {
833        Self::with(8)
834    }
835
836    fn with(count: usize) -> Self {
837        Self {
838            slots: [None; 8],
839            count,
840            recent: VecDeque::new(),
841            next_pts: 0,
842            age: KeyAge::default(),
843            key_pts: 0,
844            acknowledged: false,
845            common: VecDeque::new(),
846            sent: VecDeque::new(),
847        }
848    }
849
850    /// The timestamp the next frame is encoded with.
851    pub fn next_pts(&self) -> u64 {
852        self.next_pts
853    }
854
855    /// Note that every consumer holds `frame_id`, or where not `held` was sent it, as
856    /// `ReferenceWindow::acknowledge` does: the anchor buffer holding the newest frame they all
857    /// hold is then pinned, and one they were all sent is kept until they hold it
858    /// (`PENDING_PERIODS`). False for a frame not remembered or from before the key frame.
859    pub fn acknowledge(&mut self, frame_id: u16, held: bool) -> bool {
860        self.acknowledged = true;
861        let Some(&(_, pts)) = self.recent.iter().rev().find(|f| f.0 == frame_id) else {
862            return false;
863        };
864        if pts < self.key_pts {
865            return false;
866        }
867        let noted = if held {
868            &mut self.common
869        } else {
870            &mut self.sent
871        };
872        if let Err(at) = noted.binary_search(&pts) {
873            noted.insert(at, pts);
874        }
875        while noted.len() > RECENT_FRAMES {
876            noted.pop_front();
877        }
878        true
879    }
880
881    fn is_common(&self, pts: u64) -> bool {
882        pts == self.key_pts || self.common.binary_search(&pts).is_ok()
883    }
884
885    fn is_shared(&self, pts: u64) -> bool {
886        self.is_common(pts) || self.sent.binary_search(&pts).is_ok()
887    }
888
889    /// The buffers holding anchors: VP8's GOLDEN and ALTREF, VP9's last `RING`.
890    fn anchor_slots(&self) -> std::ops::Range<usize> {
891        if self.count > 3 {
892            RING as usize..self.count
893        } else {
894            1..3
895        }
896    }
897
898    /// How many frames apart an acknowledged session's schedule marks an anchor.
899    fn anchor_every(&self) -> u64 {
900        if self.count > 3 {
901            RING
902        } else {
903            ANCHOR_PERIOD / 2
904        }
905    }
906
907    /// The anchor buffer an acknowledged session refreshes next, if any: an empty or lost one
908    /// first, then the oldest, never the one holding the newest frame every consumer holds, nor
909    /// one holding a frame they were all sent and do not hold yet, marked within
910    /// `PENDING_PERIODS` anchor periods.
911    fn anchor_target(&self) -> Option<usize> {
912        let pinned = self
913            .anchor_slots()
914            .filter_map(|i| {
915                self.slots[i]
916                    .filter(|f| !f.2 && self.is_common(f.1))
917                    .map(|f| (i, f.1))
918            })
919            .max_by_key(|&(_, pts)| pts)
920            .map(|(i, _)| i);
921        self.anchor_slots()
922            .filter(|&i| Some(i) != pinned)
923            .filter(|&i| {
924                !self.slots[i].is_some_and(|(_, pts, lost)| {
925                    !lost
926                        && self.is_shared(pts)
927                        && !self.is_common(pts)
928                        && self.next_pts - pts < PENDING_PERIODS * self.anchor_every()
929                })
930            })
931            .min_by_key(|&i| self.slots[i].map_or((0, 0), |(_, pts, lost)| (u8::from(!lost), pts)))
932    }
933
934    /// Whether a frame coded under `plan` is an anchor on the schedule predicting from a buffer
935    /// holding a frame every consumer was sent, so each can decode it (`FRAME_ANCHOR`). One
936    /// predicting past a loss is not flagged: a consumer it reaches takes it as the end of its
937    /// run, while a flagged one would wait for room behind the consumer's queue.
938    pub fn shared_anchor(&self, plan: SlotPlan) -> bool {
939        plan.predict_from != 0
940            && (self.next_pts - self.key_pts).is_multiple_of(self.anchor_every())
941            && self.anchor_slots().any(|i| plan.refresh.refreshes(1 << i))
942            && self
943                .slot(plan.predict_from)
944                .is_some_and(|(_, pts, lost)| !lost && self.is_shared(pts))
945    }
946
947    /// Whether a frame not coded as a key frame has a buffer left to predict from.
948    pub fn has_reference(&self) -> bool {
949        self.slots[..self.count]
950            .iter()
951            .any(|s| s.is_some_and(|(_, _, lost)| !lost))
952    }
953
954    /// What each buffer holds: the frame's id and timestamp and whether it was reported lost.
955    pub fn slot(&self, slot: u8) -> Option<(u16, u64, bool)> {
956        self.slots[slot.trailing_zeros() as usize]
957    }
958
959    /// The buffer the next frame predicts from (the newest one the client still has) and the
960    /// buffers it refreshes; a key frame refreshes all of them and predicts from none.
961    pub fn plan(&self, key: bool) -> SlotPlan {
962        if key || !self.has_reference() {
963            return SlotPlan {
964                predict_from: 0,
965                refresh: SlotRefresh(((1u16 << self.count) - 1) as u8),
966            };
967        }
968        let mut newest = 0;
969        for i in 0..self.count {
970            if let Some((_, pts, false)) = self.slots[i]
971                && self.slots[newest].is_none_or(|(_, held, lost)| lost || pts > held)
972            {
973                newest = i;
974            }
975        }
976        let predict_from = 1 << newest;
977        let since_key = self.next_pts - self.key_pts;
978        if self.acknowledged {
979            // A frame predicting past a loss from an anchor, the newest picture every consumer
980            // can be given, is an anchor too, as on the schedule.
981            let recent = if self.count > 3 {
982                1 << (since_key % RING)
983            } else {
984                SlotRefresh::LAST
985            };
986            let anchor = since_key.is_multiple_of(self.anchor_every())
987                || self.anchor_slots().contains(&newest);
988            let target = if anchor { self.anchor_target() } else { None };
989            return SlotPlan {
990                predict_from,
991                refresh: SlotRefresh(recent | target.map_or(0, |i| 1 << i)),
992            };
993        }
994        let refresh = if self.count > 3 {
995            1 << (since_key % RING)
996                | if since_key.is_multiple_of(RING) {
997                    1 << (RING + since_key / RING % RING)
998                } else {
999                    0
1000                }
1001        } else if predict_from != SlotRefresh::LAST {
1002            SlotRefresh::ALL
1003        } else {
1004            SlotRefresh::LAST
1005                | if since_key.is_multiple_of(ANCHOR_PERIOD) {
1006                    SlotRefresh::GOLDEN
1007                } else {
1008                    0
1009                }
1010                | if since_key % ANCHOR_PERIOD == ANCHOR_PERIOD / 2 {
1011                    SlotRefresh::ALTREF
1012                } else {
1013                    0
1014                }
1015        };
1016        SlotPlan {
1017            predict_from,
1018            refresh: SlotRefresh(refresh),
1019        }
1020    }
1021
1022    /// Record the frame just encoded under `plan` and answer what it predicted from.
1023    pub fn record(&mut self, frame_id: u16, plan: SlotPlan) -> Reference {
1024        let pts = self.next_pts;
1025        self.next_pts += 1;
1026        self.age.record(frame_id, plan.predict_from == 0);
1027        let reference = match plan.predict_from {
1028            0 => {
1029                self.key_pts = pts;
1030                self.common.clear();
1031                self.sent.clear();
1032                Reference::None
1033            }
1034            from => self.slots[from.trailing_zeros() as usize]
1035                .map_or(Reference::None, |(id, _, _)| Reference::Frame(id)),
1036        };
1037        for (i, slot) in self.slots[..self.count].iter_mut().enumerate() {
1038            if plan.predict_from == 0 || plan.refresh.refreshes(1 << i) {
1039                *slot = Some((frame_id, pts, false));
1040            }
1041        }
1042        self.recent.push_back((frame_id, pts));
1043        if self.recent.len() > RECENT_FRAMES {
1044            self.recent.pop_front();
1045        }
1046        reference
1047    }
1048
1049    /// Leave `frame_id` and every frame encoded after it out of the references.
1050    pub fn invalidate(&mut self, frame_id: u16) -> Invalidation {
1051        let lost_pts = match self.recent.iter().find(|r| r.0 == frame_id) {
1052            Some(&(_, pts)) if pts >= self.key_pts => pts,
1053            Some(_) => return Invalidation::Ignored,
1054            None => {
1055                // A frame older than what is remembered was sent before every buffered one,
1056                // so everything held predicts through it; one before the key frame, or a
1057                // newer one, was never a reference.
1058                let Some(&(oldest, _)) = self.recent.front() else {
1059                    return Invalidation::Ignored;
1060                };
1061                if !self
1062                    .age
1063                    .behind(frame_id)
1064                    .is_some_and(|b| b > u64::from(self.age.newest.wrapping_sub(oldest)))
1065                {
1066                    return Invalidation::Ignored;
1067                }
1068                0
1069            }
1070        };
1071        for slot in self.slots[..self.count].iter_mut().flatten() {
1072            if slot.1 >= lost_pts {
1073                slot.2 = true;
1074            }
1075        }
1076        if self.has_reference() {
1077            Invalidation::Forget(lost_pts)
1078        } else {
1079            Invalidation::KeyFrame
1080        }
1081    }
1082}
1083
1084/// The line saying the `frame_num` range an H.264 key frame carries, where it differs from the
1085/// one the stream carried `before` (0 for none yet); `None` where it is the same or unknown (0).
1086///
1087/// The range sets which losses cost a key frame (`ReferenceWindow`), and it is the encoder's
1088/// own sequence parameter set that says it: a VA-API driver codes its own, so the same session
1089/// can carry 65536 values where pixelflux writes the set and 128 where radeonsi does. Said once
1090/// per change, so the log carries what each stream ran on.
1091pub fn frame_num_range_line(before: u64, range: u64) -> Option<String> {
1092    (range > 0 && range != before)
1093        .then(|| format!("[pixelflux] H.264 frame_num range: {range} values."))
1094}
1095
1096#[cfg(test)]
1097mod tests {
1098    use super::*;
1099
1100    #[test]
1101    fn the_frame_num_range_is_said_once_per_change() {
1102        assert_eq!(
1103            frame_num_range_line(0, 256).as_deref(),
1104            Some("[pixelflux] H.264 frame_num range: 256 values.")
1105        );
1106        assert_eq!(frame_num_range_line(256, 256), None, "the same range again");
1107        assert_eq!(
1108            frame_num_range_line(256, 0),
1109            None,
1110            "a set nothing could be read from"
1111        );
1112        assert_eq!(
1113            frame_num_range_line(65536, 128).as_deref(),
1114            Some("[pixelflux] H.264 frame_num range: 128 values.")
1115        );
1116    }
1117
1118    #[test]
1119    fn a_frame_predicts_from_the_newest_one_the_client_still_has() {
1120        let mut w = ReferenceWindow::new(4);
1121        assert!(
1122            !w.has_reference(),
1123            "an empty window has nothing to predict from"
1124        );
1125        assert_eq!(w.record(10, true), Reference::None);
1126        assert_eq!(w.record(11, false), Reference::Frame(10));
1127        assert_eq!(w.record(12, false), Reference::Frame(11));
1128        assert_eq!(w.invalidate(11), Invalidation::Forget(1));
1129        assert!(w.has_reference(), "frame 10 is still there to predict from");
1130        assert_eq!(w.record(13, false), Reference::Frame(10));
1131        assert_eq!(w.record(14, false), Reference::Frame(13));
1132        assert_eq!(w.next_pts(), 5);
1133    }
1134
1135    #[test]
1136    fn losing_every_reference_asks_for_a_key_frame() {
1137        let mut w = ReferenceWindow::new(3);
1138        w.record(0, true);
1139        for id in 1..6u16 {
1140            w.record(id, false);
1141        }
1142        // The window holds 3, 4, and 5; frame 2 has left it, and everything held predicts
1143        // through it.
1144        assert_eq!(w.invalidate(2), Invalidation::KeyFrame);
1145        assert!(!w.has_reference());
1146        assert_eq!(w.record(6, true), Reference::None);
1147        assert!(w.has_reference());
1148        // Forgetting the only frame held leaves nothing either.
1149        assert_eq!(w.invalidate(6), Invalidation::Forget(6));
1150        assert!(!w.has_reference());
1151    }
1152
1153    #[test]
1154    fn a_frame_older_than_the_key_frame_is_ignored() {
1155        let mut w = ReferenceWindow::new(4);
1156        w.record(100, true);
1157        w.record(101, false);
1158        assert_eq!(w.invalidate(99), Invalidation::Ignored);
1159        assert_eq!(
1160            w.invalidate(105),
1161            Invalidation::Ignored,
1162            "a frame never sent is nothing to forget"
1163        );
1164        assert!(w.has_reference());
1165    }
1166
1167    #[test]
1168    fn frame_ids_wrap_and_timestamps_do_not() {
1169        let mut w = ReferenceWindow::new(4);
1170        w.record(65534, true);
1171        w.record(65535, false);
1172        w.record(0, false);
1173        assert_eq!(w.record(1, false), Reference::Frame(0));
1174        assert_eq!(w.invalidate(65535), Invalidation::Forget(1));
1175        assert_eq!(w.record(2, false), Reference::Frame(65534));
1176        assert_eq!(
1177            w.invalidate(65533),
1178            Invalidation::Ignored,
1179            "before the key frame"
1180        );
1181        w.set_capacity(2);
1182        assert_eq!(
1183            w.invalidate(65534),
1184            Invalidation::KeyFrame,
1185            "left the window, and everything held predicts through it"
1186        );
1187    }
1188
1189    #[test]
1190    fn a_loss_covering_the_frame_num_wrap_costs_a_key_frame() {
1191        let mut w = ReferenceWindow::new(8);
1192        w.set_frame_num_range(16);
1193        w.record(0, true);
1194        for id in 1..=17u16 {
1195            w.record(id, false);
1196        }
1197        // Frame 16 carries frame_num 0 again; a loss that leaves it out cannot be predicted past.
1198        assert_eq!(w.invalidate(17), Invalidation::Forget(17));
1199        assert_eq!(w.record(18, false), Reference::Frame(16));
1200        assert_eq!(
1201            w.invalidate(15),
1202            Invalidation::KeyFrame,
1203            "15, 16, and 18 go, and 16 is the wrap"
1204        );
1205        assert!(!w.has_reference());
1206        assert_eq!(w.record(19, true), Reference::None);
1207        assert_eq!(w.record(20, false), Reference::Frame(19));
1208        assert_eq!(
1209            w.invalidate(20),
1210            Invalidation::Forget(20),
1211            "the count restarts at the key frame"
1212        );
1213        let mut w = ReferenceWindow::new(8);
1214        w.record(0, true);
1215        for id in 1..=17u16 {
1216            w.record(id, false);
1217        }
1218        assert_eq!(
1219            w.invalidate(16),
1220            Invalidation::Forget(16),
1221            "a codec without the counter predicts past it"
1222        );
1223    }
1224
1225    #[test]
1226    fn a_frame_coded_again_predicts_past_the_first_attempt() {
1227        let mut w = ReferenceWindow::new(4);
1228        assert!(!w.retract(|_| true), "nothing recorded");
1229        w.record(10, true);
1230        assert!(
1231            !w.retract(|_| panic!("a key frame is coded again as a key frame")),
1232            "nothing older than a key frame"
1233        );
1234        w.record(11, false);
1235        w.record(12, false);
1236        assert!(!w.retract(|_| false), "the encoder refused");
1237        assert_eq!(w.record(13, false), Reference::Frame(12));
1238        let mut told = None;
1239        assert!(w.retract(|pts| told.replace(pts).is_none()));
1240        assert_eq!(told, Some(3));
1241        assert_eq!(
1242            w.record(13, false),
1243            Reference::Frame(12),
1244            "the frame sent in its place predicts from the one before"
1245        );
1246        assert_eq!(w.record(14, false), Reference::Frame(13));
1247        assert_eq!(
1248            w.invalidate(13),
1249            Invalidation::Forget(4),
1250            "a loss of 13 is the frame sent"
1251        );
1252        assert_eq!(w.record(15, false), Reference::Frame(12));
1253
1254        let mut w = ReferenceWindow::with_anchors(5, 2);
1255        let a = w.plan_anchor(true);
1256        w.record_marked(0, true, a);
1257        for id in 1..12u16 {
1258            let a = w.plan_anchor(false);
1259            w.record_marked(id, false, a);
1260        }
1261        let a = w.plan_anchor(false);
1262        assert_eq!(a, Some(1), "frame 12 is an anchor");
1263        w.record_marked(12, false, a);
1264        assert!(w.retract(|pts| pts == 12));
1265        assert_eq!(w.record_marked(12, false, a), Reference::Frame(11));
1266        assert_eq!(w.record(13, false), Reference::Frame(12));
1267        assert_eq!(w.invalidate(12), Invalidation::Forget(13));
1268        assert_eq!(w.record(14, false), Reference::Frame(11));
1269
1270        let mut w = ReferenceWindow::new(8);
1271        w.set_frame_num_range(16);
1272        w.record(0, true);
1273        for id in 1..=15u16 {
1274            w.record(id, false);
1275        }
1276        assert!(w.retract(|_| true));
1277        w.record(15, false);
1278        assert!(
1279            !w.retract(|_| panic!("frame_num 0 has to reach the decoder")),
1280            "the frame sent in place of 15 carries frame_num 0"
1281        );
1282    }
1283
1284    #[test]
1285    fn three_buffers_anchor_older_frames() {
1286        let mut s = ReferenceSlots::new();
1287        assert!(!s.has_reference());
1288        let key = s.plan(true);
1289        assert_eq!(key.predict_from, 0);
1290        assert_eq!(s.record(0, key), Reference::None);
1291        for id in 1..=30u16 {
1292            let plan = s.plan(false);
1293            assert_eq!(plan.predict_from, SlotRefresh::LAST, "frame {id}");
1294            assert_eq!(s.record(id, plan), Reference::Frame(id - 1));
1295        }
1296        // Frames 25 to 30 are reported lost: LAST holds 30, GOLDEN 24, ALTREF 12.
1297        assert_eq!(s.invalidate(25), Invalidation::Forget(25));
1298        let plan = s.plan(false);
1299        assert_eq!(plan.predict_from, SlotRefresh::GOLDEN);
1300        assert_eq!(
1301            plan.refresh,
1302            SlotRefresh(SlotRefresh::ALL),
1303            "a recovery frame re-anchors every buffer"
1304        );
1305        assert_eq!(s.record(31, plan), Reference::Frame(24));
1306        assert_eq!(s.record(32, s.plan(false)), Reference::Frame(31));
1307        // Losing the re-anchoring frame leaves nothing older than it.
1308        assert_eq!(s.invalidate(31), Invalidation::KeyFrame);
1309        assert!(!s.has_reference());
1310        assert_eq!(s.record(33, s.plan(false)), Reference::None);
1311        assert_eq!(
1312            s.invalidate(3),
1313            Invalidation::Ignored,
1314            "before the key frame"
1315        );
1316        assert_eq!(s.next_pts(), 34);
1317    }
1318
1319    #[test]
1320    fn golden_and_altref_follow_their_periods() {
1321        let mut s = ReferenceSlots::new();
1322        s.record(0, s.plan(true));
1323        let mut refreshes = Vec::new();
1324        for id in 1..=48u16 {
1325            let plan = s.plan(false);
1326            refreshes.push(plan.refresh.0);
1327            s.record(id, plan);
1328        }
1329        for (i, &r) in refreshes.iter().enumerate() {
1330            let frame = i + 1;
1331            let want = SlotRefresh::LAST
1332                | if frame % 24 == 0 {
1333                    SlotRefresh::GOLDEN
1334                } else {
1335                    0
1336                }
1337                | if frame % 24 == 12 {
1338                    SlotRefresh::ALTREF
1339                } else {
1340                    0
1341                };
1342            assert_eq!(r, want, "frame {frame}");
1343        }
1344        // A loss of frames 37 to 48 takes the golden of frame 48; the altref of frame 36 is
1345        // older than it, though no buffer holds frame 37 itself.
1346        assert_eq!(s.invalidate(37), Invalidation::Forget(37));
1347        assert_eq!(s.plan(false).predict_from, SlotRefresh::ALTREF);
1348        assert_eq!(s.record(49, s.plan(false)), Reference::Frame(36));
1349        assert_eq!(
1350            s.invalidate(100),
1351            Invalidation::Ignored,
1352            "a frame never sent is nothing to forget"
1353        );
1354    }
1355
1356    #[test]
1357    fn eight_buffers_keep_four_recent_frames_and_every_fourth() {
1358        let mut s = ReferenceSlots::vp9();
1359        let key = s.plan(true);
1360        assert_eq!(
1361            key.refresh,
1362            SlotRefresh(0xff),
1363            "a key frame refreshes all eight"
1364        );
1365        s.record(0, key);
1366        for id in 1..=19u16 {
1367            let plan = s.plan(false);
1368            let n = id as u8;
1369            let want = 1 << (n % 4)
1370                | if n.is_multiple_of(4) {
1371                    1 << (4 + n / 4 % 4)
1372                } else {
1373                    0
1374                };
1375            assert_eq!(plan.refresh.0, want, "frame {id}");
1376            assert_eq!(s.record(id, plan), Reference::Frame(id - 1));
1377        }
1378        let held: Vec<u16> = (0..8).map(|i| s.slot(1 << i).unwrap().0).collect();
1379        assert_eq!(held, [16, 17, 18, 19, 16, 4, 8, 12]);
1380        // Frames 9 to 19 are reported lost: the anchor of frame 8 is the newest older than it.
1381        assert_eq!(s.invalidate(9), Invalidation::Forget(9));
1382        let plan = s.plan(false);
1383        assert_eq!(plan.predict_from, 1 << 6);
1384        assert_eq!(s.record(20, plan), Reference::Frame(8));
1385        assert_eq!(s.record(21, s.plan(false)), Reference::Frame(20));
1386    }
1387
1388    #[test]
1389    fn acknowledged_slots_keep_an_anchor_until_every_consumer_holds_it() {
1390        // VP8, every consumer sent each frame and holding the key frame alone: GOLDEN takes 12 and
1391        // keeps it, ALTREF keeping the key frame they hold, until 12 is four anchor periods old.
1392        let mut w = ReferenceSlots::new();
1393        w.record(0, w.plan(true));
1394        assert!(w.acknowledge(0, true));
1395        let mut marks = Vec::new();
1396        for id in 1..=72u16 {
1397            let plan = w.plan(false);
1398            if plan.refresh.0 & (SlotRefresh::GOLDEN | SlotRefresh::ALTREF) != 0 {
1399                marks.push((id, plan.refresh.0, w.shared_anchor(plan)));
1400            }
1401            w.record(id, plan);
1402            assert!(w.acknowledge(id, false));
1403        }
1404        let golden = SlotRefresh::LAST | SlotRefresh::GOLDEN;
1405        assert_eq!(marks, [(12, golden, true), (60, golden, true)]);
1406        // Once they hold 60 it is pinned, and the schedule takes ALTREF.
1407        assert!(w.acknowledge(60, true));
1408        let plan = w.plan(false);
1409        assert_eq!(plan.refresh.0, SlotRefresh::LAST);
1410        for id in 73..=84u16 {
1411            let plan = w.plan(false);
1412            if id == 84 {
1413                assert_eq!(plan.refresh.0, SlotRefresh::LAST | SlotRefresh::ALTREF);
1414            }
1415            w.record(id, plan);
1416        }
1417        // A frame predicting past a loss from an anchor is an anchor too, unflagged off the
1418        // schedule.
1419        w.invalidate(80);
1420        let plan = w.plan(false);
1421        assert_eq!(plan.predict_from, SlotRefresh::GOLDEN);
1422        assert_eq!(plan.refresh.0, SlotRefresh::LAST | SlotRefresh::ALTREF);
1423        assert!(!w.shared_anchor(plan));
1424    }
1425
1426    /// A loss up to twelve frames deep, at any point of the schedule, still finds a buffer
1427    /// holding a frame older than it.
1428    #[test]
1429    fn a_loss_up_to_twelve_frames_deep_finds_an_anchor() {
1430        for make in [
1431            ReferenceSlots::new as fn() -> ReferenceSlots,
1432            ReferenceSlots::vp9,
1433        ] {
1434            for sent in 1..=60u16 {
1435                for depth in 1..=sent.min(12) {
1436                    let mut s = make();
1437                    s.record(0, s.plan(true));
1438                    for id in 1..=sent {
1439                        let plan = s.plan(false);
1440                        s.record(id, plan);
1441                    }
1442                    let lost = sent + 1 - depth;
1443                    assert!(
1444                        matches!(s.invalidate(lost), Invalidation::Forget(_)),
1445                        "{} buffers, frame {lost} of {sent} lost",
1446                        s.count
1447                    );
1448                    let plan = s.plan(false);
1449                    assert!(
1450                        s.slot(plan.predict_from)
1451                            .is_some_and(|(id, _, lost_too)| id < lost && !lost_too)
1452                    );
1453                }
1454            }
1455        }
1456    }
1457
1458    #[test]
1459    fn the_set_held_leaves_out_what_a_client_lost() {
1460        let held = |w: &ReferenceWindow| w.held().filter(|f| !f.2).map(|f| f.1).collect::<Vec<_>>();
1461        let mut w = ReferenceWindow::new(4);
1462        assert!(
1463            held(&w).is_empty(),
1464            "nothing is held before the first frame"
1465        );
1466        w.record(10, true);
1467        w.record(11, false);
1468        w.record(12, false);
1469        assert_eq!(held(&w), [0, 1, 2]);
1470        assert_eq!(w.invalidate(11), Invalidation::Forget(1));
1471        assert_eq!(held(&w), [0], "11 and every frame after it are out");
1472        // The frame recorded next predicts from the newest one held, which is the set's last.
1473        assert_eq!(w.record(13, false), Reference::Frame(10));
1474        assert_eq!(held(&w), [0, 3]);
1475        // A lost frame still takes its place in the window until it is let go.
1476        w.record(14, false);
1477        assert_eq!(
1478            held(&w),
1479            [3, 4],
1480            "10 left the window of four, behind the two lost ones"
1481        );
1482        assert_eq!(w.invalidate(10), Invalidation::KeyFrame);
1483        assert!(held(&w).is_empty(), "the next frame has to be a key frame");
1484        w.record(15, true);
1485        assert_eq!(held(&w), [5]);
1486    }
1487
1488    /// A loss is answered however long the stream has run since its key frame: the ids wrap
1489    /// every 65536 frames, so past half of that an id no longer orders a frame against the key,
1490    /// and a capture that skips unchanged frames leaves gaps between the ids it encodes.
1491    #[test]
1492    fn a_loss_long_after_the_key_frame_is_still_answered() {
1493        let mut w = ReferenceWindow::new(8);
1494        let mut s = ReferenceSlots::new();
1495        let id = |n: u32| (n * 3) as u16;
1496        w.record(id(0), true);
1497        s.record(id(0), SlotPlan::KEY);
1498        for n in 1..=40_000u32 {
1499            w.record(id(n), false);
1500            let plan = s.plan(false);
1501            s.record(id(n), plan);
1502        }
1503        assert_eq!(w.invalidate(id(39_998)), Invalidation::Forget(39_998));
1504        assert_eq!(s.invalidate(id(39_998)), Invalidation::Forget(39_998));
1505        assert_eq!(
1506            w.invalidate(id(39_900)),
1507            Invalidation::KeyFrame,
1508            "sent since the key, and older than the window"
1509        );
1510        assert_eq!(
1511            w.invalidate(id(39_999) + 1),
1512            Invalidation::Ignored,
1513            "a frame the capture skipped was never sent"
1514        );
1515        let mut w = ReferenceWindow::new(8);
1516        w.record(id(0), false);
1517        for n in 1..=40_000u32 {
1518            w.record(id(n), n == 39_990);
1519        }
1520        assert_eq!(
1521            w.invalidate(id(39_989)),
1522            Invalidation::Ignored,
1523            "before the key frame"
1524        );
1525        assert_eq!(w.invalidate(id(39_995)), Invalidation::Forget(39_995));
1526    }
1527
1528    /// A window over four frames that keeps anchors, fed a key frame and `n` frames after it as
1529    /// its session would mark them.
1530    fn anchored(n: u16) -> ReferenceWindow {
1531        let mut w = ReferenceWindow::with_anchors(4, 2);
1532        for id in 0..=n {
1533            let key = id == 0;
1534            let slot = w.plan_anchor(key);
1535            w.record_marked(id, key, slot);
1536        }
1537        w
1538    }
1539
1540    #[test]
1541    fn anchors_are_marked_every_twelfth_frame_into_the_older_one() {
1542        let mut w = ReferenceWindow::with_anchors(4, 2);
1543        assert_eq!(
1544            w.plan_anchor(true),
1545            Some(0),
1546            "the key frame is the first anchor"
1547        );
1548        assert_eq!(w.record_marked(0, true, Some(0)), Reference::None);
1549        let mut marks = Vec::new();
1550        for id in 1..=48u16 {
1551            let slot = w.plan_anchor(false);
1552            if let Some(slot) = slot {
1553                marks.push((id, slot));
1554            }
1555            assert_eq!(
1556                w.record_marked(id, false, slot),
1557                Reference::Frame(id - 1),
1558                "frame {id}"
1559            );
1560        }
1561        assert_eq!(marks, [(12, 1), (24, 0), (36, 1), (48, 0)]);
1562        // Two of the four frames are anchors, so two recent frames are held beside them.
1563        assert_eq!(w.held().map(|f| f.0).collect::<Vec<_>>(), [36, 46, 47, 48]);
1564    }
1565
1566    #[test]
1567    fn one_anchor_takes_every_forty_eighth_frame() {
1568        let mut w = ReferenceWindow::with_anchors(4, 1);
1569        w.record_marked(0, true, w.plan_anchor(true));
1570        let mut marks = Vec::new();
1571        for id in 1..=100u16 {
1572            let slot = w.plan_anchor(false);
1573            if let Some(slot) = slot {
1574                marks.push((id, slot));
1575            }
1576            w.record_marked(id, false, slot);
1577        }
1578        assert_eq!(marks, [(48, 0), (96, 0)]);
1579        // Three recent frames beside the anchor; a loss before the anchor finds nothing older.
1580        assert_eq!(w.held().map(|f| f.0).collect::<Vec<_>>(), [96, 98, 99, 100]);
1581        assert_eq!(w.invalidate(97), Invalidation::Forget(97));
1582        assert_eq!(
1583            w.record_marked(101, false, w.plan_anchor(false)),
1584            Reference::Frame(96)
1585        );
1586        assert_eq!(w.invalidate(95), Invalidation::KeyFrame);
1587    }
1588
1589    #[test]
1590    fn a_loss_older_than_every_recent_frame_is_predicted_past_from_an_anchor() {
1591        // Held: anchors 0 and 12, recent frames 19 and 20.
1592        let mut w = anchored(20);
1593        assert_eq!(w.invalidate(16), Invalidation::Forget(16));
1594        assert_eq!(
1595            w.record_marked(21, false, w.plan_anchor(false)),
1596            Reference::Frame(12)
1597        );
1598        assert_eq!(
1599            w.record_marked(22, false, w.plan_anchor(false)),
1600            Reference::Frame(21)
1601        );
1602        // A loss before the newer anchor leaves the key frame.
1603        let mut w = anchored(20);
1604        assert_eq!(w.invalidate(10), Invalidation::Forget(10));
1605        assert_eq!(
1606            w.record_marked(21, false, w.plan_anchor(false)),
1607            Reference::Frame(0)
1608        );
1609        // So does a loss of the newer anchor itself.
1610        let mut w = anchored(20);
1611        assert_eq!(w.invalidate(12), Invalidation::Forget(12));
1612        assert_eq!(w.newest_valid(), Some((0, 0)));
1613        // Losing the key frame leaves nothing.
1614        let mut w = anchored(20);
1615        assert_eq!(w.invalidate(0), Invalidation::KeyFrame);
1616        assert!(!w.has_reference());
1617        assert_eq!(
1618            w.plan_anchor(false),
1619            Some(0),
1620            "the next frame is a key frame and the first anchor"
1621        );
1622    }
1623
1624    #[test]
1625    fn a_lost_anchor_is_the_one_marked_next() {
1626        // Anchors 24 and 12; frame 24 is reported lost at frame 30.
1627        let mut w = anchored(30);
1628        assert_eq!(w.invalidate(24), Invalidation::Forget(24));
1629        assert_eq!(
1630            w.record_marked(31, false, w.plan_anchor(false)),
1631            Reference::Frame(12)
1632        );
1633        for id in 32..=35u16 {
1634            w.record_marked(id, false, w.plan_anchor(false));
1635        }
1636        // Frame 36 replaces the lost anchor, keeping frame 12, the one the client still has.
1637        assert_eq!(w.plan_anchor(false), Some(0));
1638    }
1639
1640    #[test]
1641    fn the_wrap_rule_and_unsent_frames_hold_with_anchors() {
1642        let mut w = ReferenceWindow::with_anchors(4, 2);
1643        w.set_frame_num_range(16);
1644        for id in 0..=17u16 {
1645            let key = id == 0;
1646            let slot = w.plan_anchor(key);
1647            w.record_marked(id, key, slot);
1648        }
1649        // Frame 16 carries frame_num 0 again.
1650        assert_eq!(w.invalidate(17), Invalidation::Forget(17));
1651        assert_eq!(
1652            w.invalidate(15),
1653            Invalidation::KeyFrame,
1654            "15 to 17 go, and 16 is the wrap"
1655        );
1656        let mut w = anchored(20);
1657        assert_eq!(
1658            w.invalidate(40),
1659            Invalidation::Ignored,
1660            "a frame never sent is nothing to forget"
1661        );
1662        w.record_marked(21, true, Some(0));
1663        assert_eq!(
1664            w.invalidate(20),
1665            Invalidation::Ignored,
1666            "before the key frame"
1667        );
1668        assert_eq!(
1669            w.invalidate(21),
1670            Invalidation::KeyFrame,
1671            "the key frame was all there was"
1672        );
1673    }
1674
1675    #[test]
1676    fn a_key_frame_the_session_forces_itself_is_marked() {
1677        let mut w = anchored(20);
1678        // A reconfigure that forces a key frame of its own resets the window first.
1679        w.reset();
1680        assert!(!w.has_reference(), "the next frame has to be a key frame");
1681        assert_eq!(w.plan_anchor(false), Some(0));
1682        assert_eq!(w.invalidate(19), Invalidation::Ignored, "nothing is held");
1683        // A key frame nobody marked leaves no anchor, so the next frame takes the first.
1684        let mut w = anchored(20);
1685        w.record_marked(21, true, None);
1686        assert_eq!(w.plan_anchor(false), Some(0));
1687        w.record_marked(22, false, Some(0));
1688        assert_eq!(w.plan_anchor(false), None);
1689    }
1690
1691    #[test]
1692    fn anchors_take_their_share_of_the_buffer() {
1693        let mut w = ReferenceWindow::with_anchors(8, 2);
1694        for id in 0..=30u16 {
1695            let key = id == 0;
1696            let slot = w.plan_anchor(key);
1697            w.record_marked(id, key, slot);
1698        }
1699        assert_eq!(w.held().count(), 8, "six recent frames and two anchors");
1700        w.set_capacity(4);
1701        assert_eq!(w.held().count(), 4, "two recent frames and two anchors");
1702        assert!(!ReferenceWindow::new(4).anchored() && w.anchored());
1703    }
1704
1705    #[test]
1706    fn the_wire_form_names_the_frame() {
1707        assert_eq!(Reference::Untracked.frame_id(), -2);
1708        assert_eq!(Reference::None.frame_id(), -1);
1709        assert_eq!(Reference::Frame(7).frame_id(), 7);
1710    }
1711
1712    /// Different reports from one invalidated interval must not discard its repaired chain.
1713    #[test]
1714    fn covered_losses_preserve_recovered_references() {
1715        for (anchors, lost, last, recovered, later, fresh, earlier) in [
1716            (0, 6u16, 7u16, 14u16, 7u16, 13u16, 5u16),
1717            (1, 80, 87, 98, 81, 97, 40),
1718            (2, 26, 31, 50, 27, 49, 20),
1719        ] {
1720            for report in [later, fresh, earlier] {
1721                let mut w = if anchors == 0 {
1722                    ReferenceWindow::new(4)
1723                } else {
1724                    ReferenceWindow::with_anchors(8, anchors)
1725                };
1726                for id in 0..=last {
1727                    let key = id == 0;
1728                    w.record_marked(id, key, w.plan_anchor(key));
1729                }
1730                assert!(matches!(w.invalidate(lost), Invalidation::Forget(_)));
1731                for id in last + 1..=recovered {
1732                    w.record_marked(id, false, w.plan_anchor(false));
1733                }
1734                let valid = w.newest_valid();
1735                let action = w.invalidate(report);
1736                if report == later {
1737                    assert_eq!(action, Invalidation::Ignored, "{anchors} anchors");
1738                    assert_eq!(w.newest_valid(), valid);
1739                    assert_eq!(
1740                        w.record_marked(recovered + 1, false, w.plan_anchor(false)),
1741                        Reference::Frame(recovered)
1742                    );
1743                } else {
1744                    assert_ne!(action, Invalidation::Ignored, "loss {report} is new");
1745                }
1746            }
1747        }
1748    }
1749
1750    /// Separate recovery intervals remain independent, including the frames between them.
1751    #[test]
1752    fn covered_loss_intervals_do_not_hide_intervening_losses() {
1753        let mut w = ReferenceWindow::new(4);
1754        for id in 0..=7 {
1755            w.record(id, id == 0);
1756        }
1757        assert_eq!(w.invalidate(6), Invalidation::Forget(6));
1758        for id in 8..=13 {
1759            w.record(id, false);
1760        }
1761        assert_eq!(w.invalidate(12), Invalidation::Forget(12));
1762        for id in 14..=18 {
1763            w.record(id, false);
1764        }
1765        for id in [6, 7, 12, 13] {
1766            assert_eq!(w.invalidate(id), Invalidation::Ignored);
1767            assert_eq!(w.newest_valid(), Some((18, 18)));
1768        }
1769        assert_eq!(w.invalidate(10), Invalidation::KeyFrame);
1770        assert_eq!(w.invalidate(13), Invalidation::Ignored);
1771        assert!(!w.has_reference(), "a required key frame stays required");
1772    }
1773
1774    /// The record dates encoded frames rather than assuming consecutive capture ids.
1775    #[test]
1776    fn covered_loss_history_handles_sparse_wrapping_ids_and_expiry() {
1777        let id = |n: u32| (65520 + n * 3) as u16;
1778        let mut w = ReferenceWindow::with_anchors(4, 1);
1779        for n in 0..=87 {
1780            w.record_marked(id(n), n == 0, w.plan_anchor(n == 0));
1781        }
1782        assert_eq!(w.invalidate(id(80)), Invalidation::Forget(80));
1783        for n in 88..=98 {
1784            w.record_marked(id(n), false, w.plan_anchor(false));
1785        }
1786        assert_eq!(w.invalidate(id(81)), Invalidation::Ignored);
1787        assert_eq!(w.newest_valid(), Some((id(98), 98)));
1788        assert_eq!(w.invalidate(id(81) + 1), Invalidation::Ignored);
1789        for n in 99..=(98 + RECENT_FRAMES as u32) {
1790            w.record_marked(id(n), false, w.plan_anchor(false));
1791        }
1792        assert_eq!(w.invalidate(id(81)), Invalidation::KeyFrame);
1793    }
1794
1795    /// Key frames and capture resets must not inherit a prior loss for a reused id.
1796    #[test]
1797    fn covered_loss_history_does_not_outlive_a_reused_frame_id() {
1798        for reset in [false, true] {
1799            let mut w = ReferenceWindow::new(4);
1800            for id in 0..=7 {
1801                w.record(id, id == 0);
1802            }
1803            assert_eq!(w.invalidate(6), Invalidation::Forget(6));
1804            w.record(8, false);
1805            if reset {
1806                w.reset();
1807            }
1808            w.record(6, true);
1809            assert_eq!(w.invalidate(6), Invalidation::Forget(9));
1810            assert!(!w.has_reference());
1811        }
1812    }
1813
1814    /// A covered report before a frame_num wrap is harmless, but a newly lost wrap is not.
1815    #[test]
1816    fn covered_loss_keeps_the_h264_wrap_guard() {
1817        let mut w = ReferenceWindow::with_anchors(8, 1);
1818        w.set_frame_num_range(16);
1819        for id in 0..=14 {
1820            w.record_marked(id, id == 0, w.plan_anchor(id == 0));
1821        }
1822        assert_eq!(w.invalidate(13), Invalidation::Forget(13));
1823        for id in 15..=17 {
1824            w.record_marked(id, false, w.plan_anchor(false));
1825        }
1826        w.set_capacity(4);
1827        assert_eq!(w.invalidate(14), Invalidation::Ignored);
1828        assert_eq!(w.newest_valid(), Some((17, 17)));
1829        assert_eq!(w.invalidate(15), Invalidation::KeyFrame);
1830        assert!(!w.has_reference());
1831    }
1832
1833    /// Feed frames to a window as a session does: plan the anchor, settle it, record it.
1834    fn feed(
1835        w: &mut ReferenceWindow,
1836        ids: std::ops::RangeInclusive<u16>,
1837    ) -> Vec<(u16, Option<u8>, Reference)> {
1838        ids.map(|id| {
1839            let key = id == 0;
1840            let slot = w.plan_anchor(key);
1841            if slot.is_some() && !key {
1842                w.settle();
1843            }
1844            (id, slot, w.record_marked(id, key, slot))
1845        })
1846        .collect()
1847    }
1848
1849    #[test]
1850    fn an_acknowledged_anchor_is_pinned_while_the_schedule_marks_the_other() {
1851        let mut w = ReferenceWindow::with_anchors(4, 2);
1852        feed(&mut w, 0..=0);
1853        assert!(w.acknowledge(0, true));
1854        let marks: Vec<(u16, u8)> = feed(&mut w, 1..=48)
1855            .into_iter()
1856            .filter_map(|(id, slot, _)| slot.map(|s| (id, s)))
1857            .collect();
1858        assert_eq!(
1859            marks,
1860            [(12, 1), (24, 1), (36, 1), (48, 1)],
1861            "the key frame stays in slot 0"
1862        );
1863        assert!(w.acknowledge(48, true));
1864        assert_eq!(
1865            feed(&mut w, 49..=60).last().map(|f| f.1),
1866            Some(Some(0)),
1867            "48 pinned, the schedule takes slot 0 back"
1868        );
1869        assert!(!w.acknowledge(1000, true), "a frame never sent is not held");
1870    }
1871
1872    #[test]
1873    fn an_anchor_predicts_from_the_newest_frame_every_consumer_was_sent() {
1874        let mut w = ReferenceWindow::with_anchors(8, 2);
1875        feed(&mut w, 0..=11);
1876        for id in 0..=10u16 {
1877            assert!(w.acknowledge(id, true));
1878        }
1879        assert_eq!(w.plan_anchor(false), Some(1));
1880        assert_eq!(
1881            w.settle(),
1882            Some(Invalidation::Forget(11)),
1883            "11 is forgotten"
1884        );
1885        assert!(w.predicts_from_shared());
1886        assert_eq!(w.record_marked(12, false, Some(1)), Reference::Frame(10));
1887        assert_eq!(
1888            w.record(13, false),
1889            Reference::Frame(12),
1890            "the stream goes on from the anchor"
1891        );
1892
1893        // Of two anchors, one is not settled further back than the two newest recent frames:
1894        // it predicts from the frame before it.
1895        let mut w = ReferenceWindow::with_anchors(8, 2);
1896        feed(&mut w, 0..=11);
1897        for id in 0..=9u16 {
1898            assert!(w.acknowledge(id, true));
1899        }
1900        assert_eq!(w.settle(), None, "9 is three frames back");
1901        assert_eq!(w.record_marked(12, false, Some(1)), Reference::Frame(11));
1902
1903        // Held by no consumer but the key frame, the other anchor: not settled on it either.
1904        let mut w = ReferenceWindow::with_anchors(4, 2);
1905        feed(&mut w, 0..=11);
1906        assert!(w.acknowledge(0, true));
1907        assert_eq!(w.settle(), None);
1908        assert_eq!(w.record_marked(12, false, Some(1)), Reference::Frame(11));
1909
1910        // A lone anchor is not marked while it would predict from the one it marks over, every
1911        // consumer holding frames since that the window let go: they all hold that one.
1912        let mut w = ReferenceWindow::with_anchors(8, 1);
1913        feed(&mut w, 0..=47);
1914        for id in 0..=30u16 {
1915            assert!(w.acknowledge(id, true));
1916        }
1917        assert_eq!(w.plan_anchor(false), None);
1918        assert_eq!(w.record_marked(48, false, None), Reference::Frame(47));
1919
1920        // Every consumer was sent the newest frame, which none holds yet: nothing to settle, and
1921        // nothing pinned on it.
1922        let mut w = ReferenceWindow::with_anchors(4, 2);
1923        feed(&mut w, 0..=11);
1924        assert!(w.acknowledge(0, true));
1925        assert!(w.acknowledge(11, false));
1926        assert_eq!(w.settle(), None);
1927        assert!(w.predicts_from_shared());
1928        let mut w = ReferenceWindow::with_anchors(4, 2);
1929        feed(&mut w, 0..=11);
1930        assert_eq!(
1931            w.settle(),
1932            None,
1933            "unacknowledged, an anchor is settled by nothing"
1934        );
1935    }
1936
1937    #[test]
1938    fn only_an_anchor_every_consumer_holds_is_pinned() {
1939        // Sent to every consumer as it is marked, 12 is not pinned until all hold it, and the
1940        // schedule keeps it meanwhile: 24 is not marked over it, the key frame they hold staying.
1941        let mut w = ReferenceWindow::with_anchors(4, 2);
1942        feed(&mut w, 0..=0);
1943        assert!(w.acknowledge(0, true));
1944        for id in 1..=24u16 {
1945            feed(&mut w, id..=id);
1946            assert!(w.acknowledge(id, false));
1947        }
1948        let anchors: Vec<u16> = w.anchor_frames().map(|a| a.0).collect();
1949        assert!(anchors.contains(&0) && anchors.contains(&12), "{anchors:?}");
1950        assert!(w.acknowledge(12, true));
1951        assert_eq!(
1952            feed(&mut w, 25..=36).last().map(|f| f.1),
1953            Some(Some(0)),
1954            "12 pinned"
1955        );
1956        // 36, sent to every consumer and never held, is kept for four anchor periods.
1957        let mut marks = Vec::new();
1958        for id in 25..=96u16 {
1959            if id > 36 {
1960                marks.extend(
1961                    feed(&mut w, id..=id)
1962                        .into_iter()
1963                        .filter_map(|f| f.1.map(|s| (id, s))),
1964                );
1965            }
1966            assert!(w.acknowledge(id, false));
1967        }
1968        assert_eq!(marks, [(84, 0)]);
1969    }
1970
1971    #[test]
1972    fn a_lone_anchor_every_consumer_holds_stays_while_one_lags() {
1973        // Every consumer is sent each frame and holds each up to 60, then one holds nothing more,
1974        // as through an outage: the anchor at 48 stays, as one marked over it would be one that
1975        // consumer lacks, until it is HOLD_MAX old.
1976        let mut w = ReferenceWindow::with_anchors(8, 1);
1977        let mut marks = Vec::new();
1978        for id in 0..=480u16 {
1979            marks.extend(
1980                feed(&mut w, id..=id)
1981                    .into_iter()
1982                    .filter_map(|f| f.1.map(|s| (id, s))),
1983            );
1984            assert!(w.acknowledge(id, id <= 60));
1985        }
1986        assert_eq!(
1987            marks,
1988            [(0, 0), (48, 0), (432, 0)],
1989            "past HOLD_MAX it is marked over, the anchor after it kept as one sent is"
1990        );
1991    }
1992
1993    #[test]
1994    fn a_lone_anchor_is_not_marked_from_the_one_it_marks_over() {
1995        // Every consumer holds each frame nine frames late, past the seven recent frames: each
1996        // anchor due would predict from the key frame, so none is marked until that is HOLD_MAX
1997        // old. Held in time, one is marked every period.
1998        for (late, want) in [
1999            (9u16, vec![(0, 0), (384, 0)]),
2000            (0, vec![(0, 0), (48, 0), (96, 0)]),
2001        ] {
2002            let mut w = ReferenceWindow::with_anchors(8, 1);
2003            let mut marks = Vec::new();
2004            for id in 0..=(if late > 0 { 480 } else { 100 }) {
2005                marks.extend(
2006                    feed(&mut w, id..=id)
2007                        .into_iter()
2008                        .filter_map(|f| f.1.map(|s| (id, s))),
2009                );
2010                if id >= late {
2011                    assert!(w.acknowledge(id - late, true));
2012                }
2013            }
2014            assert_eq!(marks, want, "held {late} frames late");
2015        }
2016    }
2017
2018    #[test]
2019    fn a_consumer_further_behind_than_an_anchor_period_is_predicted_past_on_its_report() {
2020        let mut w = ReferenceWindow::with_anchors(4, 2);
2021        for id in 0..=60u16 {
2022            feed(&mut w, id..=id);
2023            assert!(w.acknowledge(id, true));
2024        }
2025        let anchors: Vec<(u16, Reference)> = feed(&mut w, 61..=120)
2026            .into_iter()
2027            .filter(|f| f.1.is_some())
2028            .map(|f| (f.0, f.2))
2029            .collect();
2030        assert_eq!(
2031            anchors,
2032            [
2033                (72, Reference::Frame(71)),
2034                (84, Reference::Frame(83)),
2035                (96, Reference::Frame(95)),
2036                (108, Reference::Frame(107)),
2037                (120, Reference::Frame(119)),
2038            ],
2039            "a consumer holds nothing past 60: of two anchors, each predicts from the frame before"
2040        );
2041        assert_eq!(w.invalidate(121), Invalidation::Ignored, "never sent");
2042        assert!(matches!(w.invalidate(61), Invalidation::Forget(_)));
2043        assert_eq!(
2044            w.predicting_anchor(),
2045            Some(1),
2046            "60, pinned in slot 1, named"
2047        );
2048        assert_eq!(w.record(121, false), Reference::Frame(60));
2049
2050        let mut w = ReferenceWindow::with_anchors(4, 2);
2051        feed(&mut w, 0..=120);
2052        assert_eq!(
2053            w.invalidate(61),
2054            Invalidation::KeyFrame,
2055            "unacknowledged, both anchors are past 61"
2056        );
2057    }
2058
2059    #[test]
2060    fn a_recent_frame_further_back_than_the_buffer_is_not_predicted_from_after_a_gap() {
2061        // 48, the anchor, is reported lost with the frames after it, so 47 is the newest frame
2062        // left. Five frames after the anchor, 47 is seven back, within seven recent, and the
2063        // next frame predicts from it.
2064        let mut w = ReferenceWindow::with_anchors(8, 1);
2065        w.set_frame_num_range(256);
2066        w.set_acknowledged();
2067        for id in 0..=47u16 {
2068            feed(&mut w, id..=id);
2069            assert!(w.acknowledge(id, true));
2070        }
2071        assert_eq!(feed(&mut w, 48..=53)[0].1, Some(0), "48 is the anchor");
2072        assert_eq!(w.invalidate(48), Invalidation::Forget(48));
2073        assert_eq!(w.forget_stale(), None);
2074        assert_eq!(w.record(54, false), Reference::Frame(47));
2075
2076        // Six frames after it, 47 is eight back past seven recent: a decoder filling the gap has
2077        // let it go, and with the one anchor lost the next frame is a key frame.
2078        let mut w = ReferenceWindow::with_anchors(8, 1);
2079        w.set_frame_num_range(256);
2080        w.set_acknowledged();
2081        for id in 0..=47u16 {
2082            feed(&mut w, id..=id);
2083            assert!(w.acknowledge(id, true));
2084        }
2085        feed(&mut w, 48..=54);
2086        assert_eq!(w.invalidate(48), Invalidation::Forget(48));
2087        assert_eq!(w.newest_valid(), Some((47, 47)));
2088        assert_eq!(w.forget_stale(), Some(Invalidation::KeyFrame));
2089
2090        let mut w = ReferenceWindow::with_anchors(8, 2);
2091        w.set_acknowledged();
2092        feed(&mut w, 0..=12);
2093        assert_eq!(w.forget_stale(), None, "only H.264 counts frame_num gaps");
2094    }
2095
2096    #[test]
2097    fn the_frame_where_frame_num_wraps_is_an_anchor_once_acknowledged() {
2098        let mut w = ReferenceWindow::with_anchors(8, 1);
2099        w.set_frame_num_range(16);
2100        feed(&mut w, 0..=15);
2101        assert_eq!(w.plan_anchor(false), None, "16 is off the schedule");
2102        assert!(w.acknowledge(15, true));
2103        assert!(w.plan_anchor(false).is_some(), "frame_num wraps at 16");
2104
2105        // It predicts from the newest frame every consumer holds however far back: the anchor.
2106        let mut w = ReferenceWindow::with_anchors(8, 1);
2107        w.set_frame_num_range(64);
2108        for id in 0..=48u16 {
2109            feed(&mut w, id..=id);
2110            assert!(w.acknowledge(id, true));
2111        }
2112        let at: Vec<(u16, Reference)> = feed(&mut w, 49..=64)
2113            .into_iter()
2114            .filter(|f| f.1.is_some())
2115            .map(|f| (f.0, f.2))
2116            .collect();
2117        assert_eq!(
2118            at.last(),
2119            Some(&(64, Reference::Frame(48))),
2120            "the wrap reaches back to 48, the anchor every consumer holds"
2121        );
2122
2123        // A frame every consumer was only sent, not yet held, is no reference for the wrap.
2124        let mut w = ReferenceWindow::with_anchors(8, 1);
2125        w.set_frame_num_range(16);
2126        w.set_acknowledged();
2127        for id in 0..=15u16 {
2128            feed(&mut w, id..=id);
2129            assert!(w.acknowledge(id, id <= 13));
2130        }
2131        assert!(w.plan_anchor(false).is_some());
2132        assert_eq!(w.settle(), Some(Invalidation::Forget(14)));
2133        assert_eq!(w.record_marked(16, false, Some(0)), Reference::Frame(13));
2134    }
2135}