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