Skip to main content

pixelflux/encoders/
sps.rs

1//! The color a stream declares, read from an H.264 sequence parameter set and written into one.
2//!
3//! An encoder that converts RGB itself decides the matrix and the range, and the stream is the
4//! only place a decoder learns which it was: with no `video_signal_type` in the VUI a browser
5//! guesses from the frame size, so one session is colored one way at 1080p and another at
6//! 800x600. Devices differ in whether they say: a Raspberry Pi with firmware from August 2024
7//! or later converts and declares it, and the same board on an older firmware converts to full
8//! range BT.601 and declares nothing at all.
9//!
10//! So this reads first. A stream that declares its color is left alone, whatever it declares,
11//! because the device knows what it did and we do not. Only a stream that declares nothing is
12//! written into, and then only with a value the caller can justify.
13//!
14//! The VUI sits at the end of the SPS, ahead of the trailing bits, and the fields after the
15//! insertion point are copied as opaque bits rather than re-encoded: a writer that re-emits what
16//! it does not understand is a writer that corrupts streams on devices it was never run against.
17//!
18//! The same VUI bounds reordering. Without a `bitstream_restriction` a decoder has to assume the
19//! stream may reorder as deep as its level's decoded picture buffer, and Chromium's hardware H.264
20//! decoder (VA-API, D3D11, VideoToolbox) holds that many pictures back before it outputs one, up to
21//! sixteen at a small size. A stream whose pictures leave in the order they are shown is written a
22//! bound of zero. That write reads through the timing and HRD parameters to reach the restriction,
23//! still copying them as they came, and the read has to land on the stop bit, so a field it
24//! stepped over wrongly refuses the write instead of corrupting the set.
25//!
26//! x264's and NVENC's `frame_num` are widened here too (`WideFrameNum`), the one write that
27//! reaches past the set into every slice header.
28
29/// What a stream says about the color it carries.
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct ColorSignal {
32    pub full_range: bool,
33    pub primaries: u8,
34    pub transfer: u8,
35    pub matrix: u8,
36}
37
38impl ColorSignal {
39    /// BT.601 at full range, which is what a VideoCore firmware older than August 2024 converts
40    /// RGB with, both by measurement against a decoded chart and by the driver author's account.
41    pub const BT601_FULL: Self = Self {
42        full_range: true,
43        primaries: 6,
44        transfer: 6,
45        matrix: 6,
46    };
47
48    /// BT.709 at limited range, the crate's own convention for 4:2:0.
49    pub const BT709_LIMITED: Self = Self {
50        full_range: false,
51        primaries: 1,
52        transfer: 1,
53        matrix: 1,
54    };
55}
56
57struct Reader<'a> {
58    bits: &'a [u8],
59    pos: usize,
60}
61
62impl<'a> Reader<'a> {
63    fn new(bits: &'a [u8]) -> Self {
64        Self { bits, pos: 0 }
65    }
66
67    fn bit(&mut self) -> Result<u32, String> {
68        let byte = self
69            .bits
70            .get(self.pos >> 3)
71            .ok_or("the SPS ends mid-field")?;
72        let value = (byte >> (7 - (self.pos & 7))) & 1;
73        self.pos += 1;
74        Ok(value as u32)
75    }
76
77    fn bits(&mut self, count: usize) -> Result<u32, String> {
78        let mut value = 0;
79        for _ in 0..count {
80            value = (value << 1) | self.bit()?;
81        }
82        Ok(value)
83    }
84
85    /// Exp-Golomb, the coding every length in an SPS is written with.
86    fn ue(&mut self) -> Result<u32, String> {
87        let mut zeros = 0;
88        while self.bit()? == 0 {
89            zeros += 1;
90            if zeros > 31 {
91                return Err("an exp-Golomb code longer than the field it names".into());
92            }
93        }
94        if zeros == 0 {
95            return Ok(0);
96        }
97        Ok((1 << zeros) - 1 + self.bits(zeros)?)
98    }
99
100    fn se(&mut self) -> Result<i32, String> {
101        let value = self.ue()?;
102        Ok(if value.is_multiple_of(2) {
103            -((value / 2) as i32)
104        } else {
105            value.div_ceil(2) as i32
106        })
107    }
108}
109
110struct Writer {
111    bytes: Vec<u8>,
112    pos: usize,
113}
114
115impl Writer {
116    fn new() -> Self {
117        Self {
118            bytes: Vec::new(),
119            pos: 0,
120        }
121    }
122
123    fn bit(&mut self, value: u32) {
124        if self.pos.is_multiple_of(8) {
125            self.bytes.push(0);
126        }
127        if value & 1 != 0 {
128            let index = self.pos >> 3;
129            self.bytes[index] |= 1 << (7 - (self.pos & 7));
130        }
131        self.pos += 1;
132    }
133
134    fn bits(&mut self, value: u32, count: usize) {
135        for shift in (0..count).rev() {
136            self.bit((value >> shift) & 1);
137        }
138    }
139
140    /// Copy bits `[from, to)` of `source` verbatim, which is how everything this code does not
141    /// interpret survives an insertion that shifts it.
142    fn copy_from(&mut self, source: &[u8], from: usize, to: usize) {
143        for index in from..to {
144            let byte = source[index >> 3];
145            self.bit(((byte >> (7 - (index & 7))) & 1) as u32);
146        }
147    }
148
149    /// Exp-Golomb, as `Reader::ue` reads it.
150    fn ue(&mut self, value: u32) {
151        let coded = value as u64 + 1;
152        let length = 64 - coded.leading_zeros() as usize;
153        for _ in 1..length {
154            self.bit(0);
155        }
156        for shift in (0..length).rev() {
157            self.bit(((coded >> shift) & 1) as u32);
158        }
159    }
160
161    fn trailing_bits(&mut self) {
162        self.bit(1);
163        while !self.pos.is_multiple_of(8) {
164            self.bit(0);
165        }
166    }
167}
168
169/// Where the video signal type sits in a VUI, and what it says today.
170struct Located {
171    /// The bit the block starts at: the `video_signal_type_present_flag` itself.
172    start: usize,
173    /// The bit after the block, where the rest of the VUI resumes.
174    end: usize,
175    /// Present when the VUI exists at all; a VUI has to be created otherwise.
176    vui_present: bool,
177    /// The bit the `vui_parameters_present_flag` sits at, for a stream that carries no VUI.
178    vui_flag: usize,
179    declared: Option<ColorSignal>,
180    pic_order_cnt_type: u32,
181    max_num_ref_frames: u32,
182    /// `log2_max_frame_num_minus4` as coded.
183    frame_num: Coded,
184    /// `separate_colour_plane_flag`, which puts a field ahead of `frame_num` in every slice.
185    separate_planes: bool,
186}
187
188fn unescape(nal: &[u8]) -> Vec<u8> {
189    let mut out = Vec::with_capacity(nal.len());
190    let mut index = 0;
191    while index < nal.len() {
192        if index + 2 < nal.len() && nal[index] == 0 && nal[index + 1] == 0 && nal[index + 2] == 3 {
193            out.push(0);
194            out.push(0);
195            index += 3;
196        } else {
197            out.push(nal[index]);
198            index += 1;
199        }
200    }
201    out
202}
203
204fn escape(rbsp: &[u8]) -> Vec<u8> {
205    let mut out = Vec::with_capacity(rbsp.len() + 4);
206    let mut zeros = 0;
207    for &byte in rbsp {
208        if zeros >= 2 && byte <= 3 {
209            out.push(3);
210            zeros = 0;
211        }
212        out.push(byte);
213        zeros = if byte == 0 { zeros + 1 } else { 0 };
214    }
215    // A unit ending in zeros (a slice's cabac_zero_words) ends escaped, so the next start code
216    // cannot be read into it.
217    if rbsp.last() == Some(&0) {
218        out.push(3);
219    }
220    out
221}
222
223/// The last bit that carries meaning: the RBSP stop bit, with alignment zeros after it.
224fn stop_bit(rbsp: &[u8]) -> Result<usize, String> {
225    for index in (0..rbsp.len() * 8).rev() {
226        let byte = rbsp[index >> 3];
227        if (byte >> (7 - (index & 7))) & 1 == 1 {
228            return Ok(index);
229        }
230    }
231    Err("the SPS carries no stop bit".into())
232}
233
234fn locate(rbsp: &[u8]) -> Result<Located, String> {
235    let mut r = Reader::new(rbsp);
236    let profile = r.bits(8)?;
237    r.bits(8)?;
238    r.bits(8)?;
239    r.ue()?;
240    let mut separate_planes = false;
241    if matches!(
242        profile,
243        100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
244    ) {
245        let chroma = r.ue()?;
246        if chroma == 3 {
247            separate_planes = r.bit()? == 1;
248        }
249        r.ue()?;
250        r.ue()?;
251        r.bit()?;
252        if r.bit()? == 1 {
253            // Scaling lists are the one part an insertion cannot step over blindly, so a stream
254            // carrying them is refused rather than guessed at.
255            return Err("the SPS carries scaling lists".into());
256        }
257    }
258    let frame_num_at = r.pos;
259    let frame_num = (r.ue()?, frame_num_at, r.pos);
260    let poc_type = r.ue()?;
261    if poc_type == 0 {
262        r.ue()?;
263    } else if poc_type == 1 {
264        r.bit()?;
265        r.se()?;
266        r.se()?;
267        let cycle = r.ue()?;
268        for _ in 0..cycle {
269            r.se()?;
270        }
271    }
272    let max_num_ref_frames = r.ue()?;
273    r.bit()?;
274    r.ue()?;
275    r.ue()?;
276    if r.bit()? == 0 {
277        r.bit()?;
278    }
279    r.bit()?;
280    if r.bit()? == 1 {
281        r.ue()?;
282        r.ue()?;
283        r.ue()?;
284        r.ue()?;
285    }
286    let vui_flag = r.pos;
287    if r.bit()? == 0 {
288        return Ok(Located {
289            start: vui_flag,
290            end: vui_flag,
291            vui_present: false,
292            vui_flag,
293            declared: None,
294            pic_order_cnt_type: poc_type,
295            max_num_ref_frames,
296            frame_num,
297            separate_planes,
298        });
299    }
300    if r.bit()? == 1 && r.bits(8)? == 255 {
301        r.bits(16)?;
302        r.bits(16)?;
303    }
304    if r.bit()? == 1 {
305        r.bit()?;
306    }
307    let start = r.pos;
308    let declared = if r.bit()? == 1 {
309        r.bits(3)?;
310        let full_range = r.bit()? == 1;
311        if r.bit()? == 1 {
312            Some(ColorSignal {
313                full_range,
314                primaries: r.bits(8)? as u8,
315                transfer: r.bits(8)? as u8,
316                matrix: r.bits(8)? as u8,
317            })
318        } else {
319            // A range without a matrix says half of what a decoder needs, and the half it leaves
320            // out is the one that colors the picture, so this counts as undeclared.
321            None
322        }
323    } else {
324        None
325    };
326    Ok(Located {
327        start,
328        end: r.pos,
329        vui_present: true,
330        vui_flag,
331        declared,
332        pic_order_cnt_type: poc_type,
333        max_num_ref_frames,
334        frame_num,
335        separate_planes,
336    })
337}
338
339/// A field's value, with the first bit its code spans and the bit after it.
340type Coded = (u32, usize, usize);
341
342/// Where a VUI's bitstream restriction sits, and the reordering it declares.
343struct Restriction {
344    /// The bit the `bitstream_restriction_flag` sits at, or the VUI's own flag where the SPS
345    /// carries no VUI.
346    flag: usize,
347    /// `max_num_reorder_frames` and `max_dec_frame_buffering` with the bits each code spans, or
348    /// None where the stream declares no restriction.
349    declared: Option<(Coded, Coded)>,
350}
351
352/// The restriction of the VUI `at` located, read through what follows its video signal type.
353/// The read has to end on the stop bit, the check that no field was stepped over wrongly.
354fn restriction(rbsp: &[u8], at: &Located) -> Result<Restriction, String> {
355    let last = stop_bit(rbsp)?;
356    if !at.vui_present {
357        return if at.vui_flag + 1 == last {
358            Ok(Restriction {
359                flag: at.vui_flag,
360                declared: None,
361            })
362        } else {
363            Err("the SPS does not end after its VUI flag".into())
364        };
365    }
366    let mut r = Reader {
367        bits: rbsp,
368        pos: at.end,
369    };
370    if r.bit()? == 1 {
371        r.ue()?;
372        r.ue()?;
373    }
374    if r.bit()? == 1 {
375        r.bits(32)?;
376        r.bits(32)?;
377        r.bit()?;
378    }
379    let nal_hrd = r.bit()? == 1;
380    if nal_hrd {
381        hrd(&mut r)?;
382    }
383    let vcl_hrd = r.bit()? == 1;
384    if vcl_hrd {
385        hrd(&mut r)?;
386    }
387    if nal_hrd || vcl_hrd {
388        r.bit()?;
389    }
390    r.bit()?;
391    let flag = r.pos;
392    let declared = if r.bit()? == 1 {
393        r.bit()?;
394        for _ in 0..4 {
395            r.ue()?;
396        }
397        let at = r.pos;
398        let reorder = (r.ue()?, at, r.pos);
399        let at = r.pos;
400        Some((reorder, (r.ue()?, at, r.pos)))
401    } else {
402        None
403    };
404    if r.pos != last {
405        return Err("the VUI does not end where the SPS does".into());
406    }
407    Ok(Restriction { flag, declared })
408}
409
410/// Step over one `hrd_parameters` structure.
411fn hrd(r: &mut Reader) -> Result<(), String> {
412    let schedules = r.ue()? + 1;
413    if schedules > 32 {
414        return Err("an HRD with more schedules than the syntax allows".into());
415    }
416    r.bits(8)?;
417    for _ in 0..schedules {
418        r.ue()?;
419        r.ue()?;
420        r.bit()?;
421    }
422    r.bits(20)?;
423    Ok(())
424}
425
426/// What the stream says about its color, or `None` when it says nothing a decoder can use.
427pub fn read_color(nal: &[u8]) -> Option<ColorSignal> {
428    let rbsp = unescape(nal.get(1..)?);
429    locate(&rbsp).ok()?.declared
430}
431
432/// The same SPS with `signal` declared in its VUI, replacing whatever it declared before.
433pub fn write_color(nal: &[u8], signal: ColorSignal) -> Result<Vec<u8>, String> {
434    let header = *nal.first().ok_or("an empty NAL unit")?;
435    let rbsp = unescape(&nal[1..]);
436    let at = locate(&rbsp)?;
437    let last = stop_bit(&rbsp)?;
438
439    let mut w = Writer::new();
440    if at.vui_present {
441        w.copy_from(&rbsp, 0, at.start);
442        write_signal(&mut w, signal);
443        w.copy_from(&rbsp, at.end, last);
444    } else {
445        w.copy_from(&rbsp, 0, at.vui_flag);
446        w.bit(1);
447        w.bit(0);
448        w.bit(0);
449        write_signal(&mut w, signal);
450        // The rest of a VUI this stream never had: chroma location, timing, both HRDs, picture
451        // structure, and bitstream restriction, each absent.
452        for _ in 0..6 {
453            w.bit(0);
454        }
455    }
456    w.trailing_bits();
457
458    let mut out = Vec::with_capacity(w.bytes.len() + 8);
459    out.push(header);
460    out.extend_from_slice(&escape(&w.bytes));
461    Ok(out)
462}
463
464fn write_signal(w: &mut Writer, signal: ColorSignal) {
465    w.bit(1);
466    w.bits(5, 3);
467    w.bit(u32::from(signal.full_range));
468    w.bit(1);
469    w.bits(signal.primaries as u32, 8);
470    w.bits(signal.transfer as u32, 8);
471    w.bits(signal.matrix as u32, 8);
472}
473
474/// The same SPS bounding reordering at zero, or None where it already does: a restriction
475/// written where the VUI carries none (a VUI created where there is none), with every other
476/// restriction field at the value a decoder infers in its absence and `max_dec_frame_buffering`
477/// at `max_num_ref_frames`; a declared `max_num_reorder_frames` above zero rewritten to zero.
478/// Only a stream that cannot reorder takes it: `in_order` where the session proves its pictures
479/// leave in the order they are shown, else a stream at picture order count type 2, whose
480/// output order is its decoding order.
481pub fn bound_reorder(nal: &[u8], in_order: bool) -> Result<Option<Vec<u8>>, String> {
482    let header = *nal.first().ok_or("an empty NAL unit")?;
483    let rbsp = unescape(&nal[1..]);
484    let at = locate(&rbsp)?;
485    let bound = restriction(&rbsp, &at)?;
486    if let Some(((0, _, _), (buffering, ..))) = bound.declared
487        && buffering >= at.max_num_ref_frames
488    {
489        return Ok(None);
490    }
491    if !in_order && at.pic_order_cnt_type != 2 {
492        return Err(format!(
493            "picture order count type {} may reorder",
494            at.pic_order_cnt_type
495        ));
496    }
497    let last = stop_bit(&rbsp)?;
498    let mut w = Writer::new();
499    match bound.declared {
500        Some(((_, reorder_at, _), (buffering, _, buffering_end))) => {
501            w.copy_from(&rbsp, 0, reorder_at);
502            w.ue(0);
503            w.ue(buffering.max(at.max_num_ref_frames));
504            w.copy_from(&rbsp, buffering_end, last);
505        }
506        None => {
507            w.copy_from(&rbsp, 0, bound.flag);
508            if !at.vui_present {
509                w.bit(1);
510                for _ in 0..8 {
511                    w.bit(0);
512                }
513            }
514            w.bit(1);
515            w.bit(1);
516            w.ue(2);
517            w.ue(1);
518            w.ue(15);
519            w.ue(15);
520            w.ue(0);
521            w.ue(at.max_num_ref_frames);
522        }
523    }
524    w.trailing_bits();
525    let mut out = Vec::with_capacity(w.bytes.len() + 8);
526    out.push(header);
527    out.extend_from_slice(&escape(&w.bytes));
528    Ok(Some(out))
529}
530
531/// Where each H.264 sequence parameter set sits in an Annex B access unit, as `(start, end)` of
532/// the NAL unit itself, the start code excluded. A set repeats with every key frame when the
533/// encoder is asked for headers on each one, and a stream whose first set alone is written says
534/// one thing to the client that connected first and another to the one that joined later.
535pub fn sequence_parameter_sets(unit: &[u8]) -> Vec<(usize, usize)> {
536    let mut starts = Vec::new();
537    let mut index = 0;
538    while index + 3 < unit.len() {
539        if unit[index] == 0 && unit[index + 1] == 0 && unit[index + 2] == 1 {
540            starts.push(index + 3);
541            index += 3;
542        } else {
543            index += 1;
544        }
545    }
546    let mut sets = Vec::new();
547    for (position, &start) in starts.iter().enumerate() {
548        if unit[start] & 0x1f != 7 {
549            continue;
550        }
551        let mut end = starts.get(position + 1).map_or(unit.len(), |next| next - 3);
552        if end > start && unit[end - 1] == 0 {
553            end -= 1;
554        }
555        sets.push((start, end));
556    }
557    sets
558}
559
560/// A device's H.264 stream held to a reorder bound of zero: each sequence parameter set it
561/// sends is rewritten once and matched by its bytes after that, since a device repeats the
562/// same set with every key frame.
563pub struct NoReorder {
564    /// What the stream is named as in the log.
565    source: &'static str,
566    in_order: bool,
567    /// The last set seen and what replaces it, None where it is sent as it came.
568    last: Option<(Vec<u8>, Option<Vec<u8>>)>,
569    /// Whether the last line logged said the sets are rewritten, so each outcome is said once.
570    logged: Option<bool>,
571}
572
573impl NoReorder {
574    pub fn new(source: &'static str, in_order: bool) -> Self {
575        Self {
576            source,
577            in_order,
578            last: None,
579            logged: None,
580        }
581    }
582
583    /// The set that replaces `sps`, or None where it goes out as it came.
584    pub fn set(&mut self, sps: &[u8]) -> Option<Vec<u8>> {
585        if let Some((was, now)) = &self.last
586            && was == sps
587        {
588            return now.clone();
589        }
590        let now = match bound_reorder(sps, self.in_order) {
591            Ok(bounded) => {
592                if bounded.is_some() && self.logged != Some(true) {
593                    self.logged = Some(true);
594                    println!(
595                        "[pixelflux] {} declares no bound on reordering; its sequence parameter sets are written a bound of zero.",
596                        self.source
597                    );
598                }
599                bounded
600            }
601            Err(e) => {
602                if self.logged != Some(false) {
603                    self.logged = Some(false);
604                    eprintln!(
605                        "[pixelflux] {}'s sequence parameter set was left as it came, without a reorder bound of zero: {e}",
606                        self.source
607                    );
608                }
609                None
610            }
611        };
612        self.last = Some((sps.to_vec(), now.clone()));
613        now
614    }
615
616    /// `unit` with every sequence parameter set in it replaced, or None where none is.
617    pub fn apply(&mut self, unit: &[u8]) -> Option<Vec<u8>> {
618        let mut out: Option<Vec<u8>> = None;
619        let mut copied = 0;
620        for (start, end) in sequence_parameter_sets(unit) {
621            if let Some(set) = self.set(&unit[start..end]) {
622                let out = out.get_or_insert_with(|| Vec::with_capacity(unit.len() + 16));
623                out.extend_from_slice(&unit[copied..start]);
624                out.extend_from_slice(&set);
625                copied = end;
626            }
627        }
628        let mut out = out?;
629        out.extend_from_slice(&unit[copied..]);
630        Some(out)
631    }
632}
633
634/// The bits `WideFrameNum` adds to `frame_num`: a whole byte, so everything after the field in a
635/// slice moves by one byte and keeps the alignment its entropy coding and trailing bits rest on.
636const WIDER_FRAME_NUM: u32 = 8;
637
638/// An x264 or NVENC stream with `frame_num` widened by a byte, in its sequence parameter sets and
639/// every slice header.
640///
641/// x264 sizes the counter to its decoded picture buffer, sixteen values for eight references,
642/// NVENC to 256, and a loss covering the frame where it wraps costs a key frame
643/// (`ReferenceWindow`): past a gap across the wrap, FFmpeg's decoder drops about a range of
644/// pictures. Widened, the wrap comes once in 4096 or 65536 frames. The field's low bits stay the
645/// encoder's, and the byte ahead of them counts its wraps since the key frame.
646#[derive(Default)]
647pub struct WideFrameNum {
648    /// `log2_max_frame_num` as the encoder wrote the last set, None where that set went out as it
649    /// came.
650    narrow: Option<u32>,
651    /// The widened `frame_num` of the last slice.
652    frame_num: u32,
653    /// A slice could not be widened, and every set since goes out as it came.
654    failed: bool,
655}
656
657impl WideFrameNum {
658    /// Append `payload`, one Annex B NAL unit with its start code, to `out`: a sequence
659    /// parameter set widened, and a slice under a widened one. False where a slice could not be,
660    /// which leaves its picture unreadable; the caller then codes a key frame, whose set goes out
661    /// as it came.
662    pub fn push(&mut self, payload: &[u8], out: &mut Vec<u8>) -> bool {
663        let code = payload
664            .iter()
665            .position(|&b| b != 0)
666            .map_or(payload.len(), |one| one + 1);
667        self.push_unit(&payload[..code], &payload[code..], out)
668    }
669
670    /// `push` for a NAL unit apart from the start code it goes out behind.
671    pub fn push_unit(&mut self, start: &[u8], nal: &[u8], out: &mut Vec<u8>) -> bool {
672        let widened = match nal.first().map(|h| h & 0x1f) {
673            Some(7) => {
674                let wide = if self.failed { None } else { widen_sps(nal) };
675                self.narrow = wide.as_ref().map(|w| w.0);
676                wide.map(|w| w.1)
677            }
678            Some(kind @ (1 | 5)) => match self.narrow {
679                Some(narrow) => match widen_slice(nal, narrow, kind == 5, self.frame_num) {
680                    Some((frame_num, slice)) => {
681                        self.frame_num = frame_num;
682                        Some(slice)
683                    }
684                    None => {
685                        self.failed = true;
686                        self.narrow = None;
687                        out.extend_from_slice(start);
688                        out.extend_from_slice(nal);
689                        return false;
690                    }
691                },
692                None => None,
693            },
694            _ => None,
695        };
696        out.extend_from_slice(start);
697        out.extend_from_slice(widened.as_deref().unwrap_or(nal));
698        true
699    }
700}
701
702/// The set with `frame_num` `WIDER_FRAME_NUM` bits wider, and the `log2_max_frame_num` it had;
703/// None where the wider field would pass sixteen bits or the set cannot be read.
704fn widen_sps(nal: &[u8]) -> Option<(u32, Vec<u8>)> {
705    let rbsp = unescape(nal.get(1..)?);
706    let at = locate(&rbsp).ok()?;
707    let (minus4, from, to) = at.frame_num;
708    if minus4 + 4 + WIDER_FRAME_NUM > 16 || at.separate_planes {
709        return None;
710    }
711    let last = stop_bit(&rbsp).ok()?;
712    let mut w = Writer::new();
713    w.copy_from(&rbsp, 0, from);
714    w.ue(minus4 + WIDER_FRAME_NUM);
715    w.copy_from(&rbsp, to, last);
716    w.trailing_bits();
717    let mut out = Vec::with_capacity(w.bytes.len() + 8);
718    out.push(nal[0]);
719    out.extend_from_slice(&escape(&w.bytes));
720    Some((minus4 + 4, out))
721}
722
723/// A slice under a widened set, and its widened `frame_num`: the encoder's `narrow` bits numbered
724/// on from `last`, the widened value of the slice before it, with the byte above them inserted
725/// ahead.
726fn widen_slice(nal: &[u8], narrow: u32, idr: bool, last: u32) -> Option<(u32, Vec<u8>)> {
727    let mut rbsp = unescape(nal.get(1..)?);
728    let mut r = Reader::new(&rbsp);
729    // first_mb_in_slice, slice_type, pic_parameter_set_id
730    for _ in 0..3 {
731        r.ue().ok()?;
732    }
733    let at = r.pos;
734    let low = r.bits(narrow as usize).ok()?;
735    let range = 1 << narrow;
736    let frame_num = if idr {
737        low
738    } else {
739        (last + (low + range - last % range) % range) % (range << WIDER_FRAME_NUM)
740    };
741    let high = (frame_num >> narrow) as u8;
742    let (byte, bit) = (at / 8, at % 8);
743    let kept = rbsp[byte];
744    rbsp.splice(
745        byte..=byte,
746        [
747            (kept & !(0xff >> bit)) | (high >> bit),
748            (((high as u16) << (8 - bit)) as u8) | (kept & (0xff >> bit)),
749        ],
750    );
751    let mut out = Vec::with_capacity(nal.len() + 4);
752    out.push(nal[0]);
753    out.extend_from_slice(&escape(&rbsp));
754    Some((frame_num, out))
755}
756
757/// The sequence parameter set read an H.264 session makes of its own key frames, and the
758/// reference checks of every backend share.
759mod dpb {
760    struct Bits<'a> {
761        rbsp: &'a [u8],
762        pos: usize,
763    }
764
765    impl Bits<'_> {
766        fn bits(&mut self, n: u32) -> u32 {
767            (0..n).fold(0, |acc, _| {
768                let bit = self
769                    .rbsp
770                    .get(self.pos / 8)
771                    .map_or(0, |b| (b >> (7 - self.pos % 8)) & 1);
772                self.pos += 1;
773                (acc << 1) | bit as u32
774            })
775        }
776
777        fn ue(&mut self) -> u32 {
778            let mut zeros = 0;
779            while self.bits(1) == 0 && zeros < 32 {
780                zeros += 1;
781            }
782            (1 << zeros) - 1 + self.bits(zeros)
783        }
784
785        fn se(&mut self) -> i32 {
786            let k = self.ue() as i32;
787            if k % 2 == 1 { (k + 1) / 2 } else { -(k / 2) }
788        }
789    }
790
791    /// `(log2_max_frame_num, max_num_ref_frames, chroma_format_idc)` of the first SPS in an
792    /// Annex-B H.264 stream.
793    fn h264_sps(stream: &[u8]) -> Option<(u32, u32, u32)> {
794        let nal = crate::encoders::codec::annexb_nals(stream).find(|n| n[0] & 0x1f == 7)?;
795        let mut rbsp = Vec::with_capacity(nal.len());
796        let mut zeros = 0;
797        for &b in &nal[1..] {
798            if zeros >= 2 && b == 3 {
799                zeros = 0;
800                continue;
801            }
802            zeros = if b == 0 { zeros + 1 } else { 0 };
803            rbsp.push(b);
804        }
805        let mut r = Bits {
806            rbsp: &rbsp,
807            pos: 0,
808        };
809        let profile = r.bits(8);
810        r.bits(16);
811        r.ue();
812        let mut chroma = 1;
813        if matches!(
814            profile,
815            100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
816        ) {
817            chroma = r.ue();
818            if chroma == 3 {
819                r.bits(1);
820            }
821            r.ue();
822            r.ue();
823            r.bits(1);
824            if r.bits(1) == 1 {
825                for list in 0..(if chroma == 3 { 12 } else { 8 }) {
826                    if r.bits(1) == 1 {
827                        let (mut last, mut next) = (8i32, 8i32);
828                        for _ in 0..(if list < 6 { 16 } else { 64 }) {
829                            if next != 0 {
830                                next = (last + r.se()).rem_euclid(256);
831                            }
832                            if next != 0 {
833                                last = next;
834                            }
835                        }
836                    }
837                }
838            }
839        }
840        let log2_max_frame_num = r.ue() + 4;
841        match r.ue() {
842            0 => {
843                r.ue();
844            }
845            1 => {
846                r.bits(1);
847                r.se();
848                r.se();
849                for _ in 0..r.ue() {
850                    r.se();
851                }
852            }
853            _ => {}
854        }
855        Some((log2_max_frame_num, r.ue(), chroma))
856    }
857
858    /// How many values `frame_num` takes before it wraps, from the first SPS of an Annex-B
859    /// H.264 stream.
860    pub fn h264_frame_num_range(stream: &[u8]) -> Option<u32> {
861        h264_sps(stream).map(|(log2, _, _)| 1 << log2)
862    }
863
864    /// `max_num_ref_frames` of the first SPS in an Annex-B H.264 stream.
865    #[cfg(test)]
866    pub fn h264_max_num_ref_frames(stream: &[u8]) -> Option<u32> {
867        h264_sps(stream).map(|(_, refs, _)| refs)
868    }
869
870    /// `chroma_format_idc` of the first SPS in an Annex-B H.264 stream: 3 for 4:4:4.
871    #[cfg(test)]
872    pub fn h264_chroma_format_idc(stream: &[u8]) -> Option<u32> {
873        h264_sps(stream).map(|(_, _, chroma)| chroma)
874    }
875}
876
877pub use dpb::h264_frame_num_range;
878#[cfg(test)]
879pub use dpb::{h264_chroma_format_idc, h264_max_num_ref_frames};
880
881/// The reordering the first SPS of an Annex B H.264 stream bounds, as `(max_num_reorder_frames,
882/// max_dec_frame_buffering)`, or None where it declares no bound or cannot be read.
883#[cfg(test)]
884pub fn h264_reorder(stream: &[u8]) -> Option<(u32, u32)> {
885    let nal = crate::encoders::codec::annexb_nals(stream).find(|n| n[0] & 0x1f == 7)?;
886    let rbsp = unescape(&nal[1..]);
887    let bound = restriction(&rbsp, &locate(&rbsp).ok()?).ok()?;
888    bound
889        .declared
890        .map(|((reorder, ..), (buffering, ..))| (reorder, buffering))
891}
892
893/// The timing the first SPS of an Annex B H.264 stream declares, as `(num_units_in_tick,
894/// time_scale)`, or None where it declares none or cannot be read.
895#[cfg(test)]
896pub fn h264_timing(stream: &[u8]) -> Option<(u32, u32)> {
897    let nal = crate::encoders::codec::annexb_nals(stream).find(|n| n[0] & 0x1f == 7)?;
898    let rbsp = unescape(&nal[1..]);
899    let at = locate(&rbsp).ok()?;
900    if !at.vui_present {
901        return None;
902    }
903    let mut r = Reader {
904        bits: &rbsp,
905        pos: at.end,
906    };
907    if r.bit().ok()? == 1 {
908        r.ue().ok()?;
909        r.ue().ok()?;
910    }
911    if r.bit().ok()? == 0 {
912        return None;
913    }
914    Some((r.bits(32).ok()?, r.bits(32).ok()?))
915}
916
917/// Sequence parameter sets devices wrote, and the edits the tests make of them.
918#[cfg(test)]
919pub(crate) mod fixtures {
920    use super::*;
921
922    /// An SPS a Raspberry Pi 4 produced at 1280x720: a VUI with timing and no color at all.
923    pub const PI4_SPS: &[u8] = &[
924        0x27, 0x64, 0x00, 0x28, 0xac, 0x2b, 0x40, 0x28, 0x02, 0xdd, 0x08, 0x00, 0x00, 0x03, 0x00,
925        0x08, 0x00, 0x00, 0x03, 0x01, 0xe7, 0x15, 0x00, 0x01, 0xe8, 0x48, 0x00, 0x02, 0xfa, 0xf3,
926        0x7b, 0xdc, 0x03, 0xc4, 0x89, 0xa8,
927    ];
928
929    /// The SPS a Radeon Pro VII's VCE firmware writes for 1920x1080 under Mesa 24.0.5, whatever
930    /// the session packed: color and timing in the VUI, no bitstream restriction, picture order
931    /// count type 0, two reference frames.
932    pub const VCE_SPS: &[u8] = &[
933        0x67, 0x64, 0x40, 0x2a, 0xac, 0x26, 0xc0, 0x78, 0x02, 0x27, 0xe5, 0xc0, 0x5a, 0x20, 0x00,
934        0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x0f, 0x10, 0x80,
935    ];
936
937    /// The restriction an SPS declares, or None where it declares none.
938    pub fn reorder_of(nal: &[u8]) -> Option<(u32, u32)> {
939        let rbsp = unescape(&nal[1..]);
940        let bound =
941            restriction(&rbsp, &locate(&rbsp).expect("located")).expect("read to the stop bit");
942        bound
943            .declared
944            .map(|((reorder, ..), (buffering, ..))| (reorder, buffering))
945    }
946
947    /// `nal` rebuilt with its bitstream restriction taken out, or its whole VUI.
948    pub fn without(nal: &[u8], whole_vui: bool) -> Vec<u8> {
949        let rbsp = unescape(&nal[1..]);
950        let at = locate(&rbsp).expect("located");
951        let bound = restriction(&rbsp, &at).expect("read");
952        let mut w = Writer::new();
953        w.copy_from(&rbsp, 0, if whole_vui { at.vui_flag } else { bound.flag });
954        w.bit(0);
955        w.trailing_bits();
956        let mut out = vec![nal[0]];
957        out.extend_from_slice(&escape(&w.bytes));
958        out
959    }
960
961    /// Assert the first SPS of the Annex B `stream` bounds reordering at zero in a buffer no
962    /// smaller than its references: the set a hardware decoder outputs each picture at once
963    /// for, and one Chromium's parser accepts.
964    pub fn assert_no_reorder(stream: &[u8], what: &str) {
965        let refs = h264_max_num_ref_frames(stream).unwrap_or_else(|| panic!("{what}: no SPS"));
966        match h264_reorder(stream) {
967            Some((0, buffering)) => {
968                assert!(
969                    buffering >= refs,
970                    "{what}: a buffer of {buffering} below its {refs} references"
971                )
972            }
973            other => panic!("{what}: the SPS bounds reordering at {other:?}, not zero"),
974        }
975    }
976
977    /// `nal` rebuilt declaring a reorder depth of `reorder` in a buffer of `buffering`.
978    pub fn with_depth(nal: &[u8], reorder: u32, buffering: u32) -> Vec<u8> {
979        let rbsp = unescape(&nal[1..]);
980        let bound = restriction(&rbsp, &locate(&rbsp).expect("located")).expect("read");
981        let ((_, at, _), _) = bound.declared.expect("a restriction to rewrite");
982        let mut w = Writer::new();
983        w.copy_from(&rbsp, 0, at);
984        w.ue(reorder);
985        w.ue(buffering);
986        w.trailing_bits();
987        let mut out = vec![nal[0]];
988        out.extend_from_slice(&escape(&w.bytes));
989        out
990    }
991}
992
993#[cfg(test)]
994mod tests {
995    use super::fixtures::*;
996    use super::*;
997    use crate::RustCaptureSettings;
998
999    #[test]
1000    fn a_stream_that_declares_nothing_reads_as_nothing() {
1001        assert_eq!(read_color(PI4_SPS), None);
1002    }
1003
1004    #[test]
1005    fn what_is_written_reads_back() {
1006        for signal in [ColorSignal::BT601_FULL, ColorSignal::BT709_LIMITED] {
1007            let patched = write_color(PI4_SPS, signal).expect("the SPS takes a color");
1008            assert_eq!(
1009                read_color(&patched),
1010                Some(signal),
1011                "what was written did not read back"
1012            );
1013        }
1014    }
1015
1016    /// The fields after the insertion point are what the encoder said about timing and delay, and
1017    /// a stream whose insertion trod on them decodes at the wrong rate rather than failing.
1018    #[test]
1019    fn the_bits_after_the_insertion_survive_it() {
1020        let patched = write_color(PI4_SPS, ColorSignal::BT601_FULL).expect("patched");
1021        let (before, after) = (unescape(&PI4_SPS[1..]), unescape(&patched[1..]));
1022        let at_before = locate(&before).expect("located");
1023        let at_after = locate(&after).expect("located");
1024        let tail_before = stop_bit(&before).expect("stop") - at_before.end;
1025        let tail_after = stop_bit(&after).expect("stop") - at_after.end;
1026        assert_eq!(tail_before, tail_after, "the tail changed length");
1027        for index in 0..tail_before {
1028            let bit = |data: &[u8], at: usize| (data[at >> 3] >> (7 - (at & 7))) & 1;
1029            assert_eq!(
1030                bit(&before, at_before.end + index),
1031                bit(&after, at_after.end + index),
1032                "bit {index} of the tail changed"
1033            );
1034        }
1035    }
1036
1037    #[test]
1038    fn a_declared_color_reads_back_as_declared() {
1039        let patched = write_color(PI4_SPS, ColorSignal::BT709_LIMITED).expect("patched");
1040        assert_eq!(read_color(&patched), Some(ColorSignal::BT709_LIMITED));
1041        let again = write_color(&patched, ColorSignal::BT601_FULL).expect("rewritten");
1042        assert_eq!(
1043            read_color(&again),
1044            Some(ColorSignal::BT601_FULL),
1045            "a rewrite did not replace"
1046        );
1047        assert_eq!(
1048            again.len(),
1049            patched.len(),
1050            "replacing a color changed the length"
1051        );
1052    }
1053
1054    /// Emulation prevention is not decoration: a byte pair of zeros followed by a small byte is
1055    /// a start code to a decoder unless it is escaped.
1056    #[test]
1057    fn escaping_round_trips() {
1058        let raw = vec![0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00];
1059        assert_eq!(unescape(&escape(&raw)), raw);
1060        assert!(escape(&raw).len() > raw.len(), "nothing was escaped");
1061    }
1062
1063    /// Whether the first `bits` bits of two sets' payloads agree.
1064    fn same_prefix(a: &[u8], b: &[u8], bits: usize) -> bool {
1065        let (a, b) = (unescape(&a[1..]), unescape(&b[1..]));
1066        (0..bits).all(|i| (a[i >> 3] >> (7 - (i & 7))) & 1 == (b[i >> 3] >> (7 - (i & 7))) & 1)
1067    }
1068
1069    #[test]
1070    fn exp_golomb_writes_what_it_reads() {
1071        let mut w = Writer::new();
1072        let values = [0, 1, 2, 3, 7, 15, 16, 255, 65_534, u32::MAX - 1];
1073        for v in values {
1074            w.ue(v);
1075        }
1076        w.trailing_bits();
1077        let mut r = Reader::new(&w.bytes);
1078        for v in values {
1079            assert_eq!(r.ue().expect("read"), v);
1080        }
1081    }
1082
1083    /// A VUI without a restriction gets one saying no picture waits for a later one, and
1084    /// everything before it, the color and the timing included, is copied as it came.
1085    #[test]
1086    fn a_set_without_a_restriction_is_bounded_at_zero() {
1087        assert_eq!(
1088            reorder_of(VCE_SPS),
1089            None,
1090            "the fixture declares no restriction"
1091        );
1092        let bounded = bound_reorder(VCE_SPS, true)
1093            .expect("writable")
1094            .expect("a change");
1095        assert_eq!(
1096            reorder_of(&bounded),
1097            Some((0, 2)),
1098            "zero, in a buffer of the set's two references"
1099        );
1100        assert_eq!(read_color(&bounded), read_color(VCE_SPS), "the color moved");
1101        let flag = restriction(
1102            &unescape(&VCE_SPS[1..]),
1103            &locate(&unescape(&VCE_SPS[1..])).unwrap(),
1104        )
1105        .unwrap()
1106        .flag;
1107        assert!(
1108            same_prefix(VCE_SPS, &bounded, flag),
1109            "a bit before the restriction changed"
1110        );
1111        assert_eq!(
1112            bound_reorder(&bounded, true),
1113            Ok(None),
1114            "a bounded set is left as it came"
1115        );
1116    }
1117
1118    /// A stream whose session cannot vouch for its order is bounded only where the set itself
1119    /// rules reordering out: picture order count type 2 has output order equal decoding order.
1120    #[test]
1121    fn only_an_order_the_stream_proves_is_bounded() {
1122        assert!(bound_reorder(VCE_SPS, false).is_err(), "type 0 may reorder");
1123        let pi4 = without(PI4_SPS, false);
1124        assert_eq!(locate(&unescape(&pi4[1..])).unwrap().pic_order_cnt_type, 2);
1125        let bounded = bound_reorder(&pi4, false)
1126            .expect("type 2 cannot reorder")
1127            .expect("a change");
1128        assert_eq!(reorder_of(&bounded), Some((0, 1)));
1129    }
1130
1131    /// The Raspberry Pi 4 bounds its own stream, and the color written into it keeps the bound.
1132    #[test]
1133    fn a_bounded_set_is_left_as_it_came() {
1134        assert_eq!(reorder_of(PI4_SPS), Some((0, 1)));
1135        assert_eq!(bound_reorder(PI4_SPS, true), Ok(None));
1136        let colored = write_color(PI4_SPS, ColorSignal::BT601_FULL).expect("colored");
1137        assert_eq!(
1138            reorder_of(&colored),
1139            Some((0, 1)),
1140            "the color write lost the restriction"
1141        );
1142        assert_eq!(bound_reorder(&colored, false), Ok(None));
1143    }
1144
1145    /// The restriction sits after both HRDs, so a set carrying them is read through them, and
1146    /// they come out bit for bit.
1147    #[test]
1148    fn hrd_parameters_are_read_through_and_kept() {
1149        let bare = without(PI4_SPS, false);
1150        assert_eq!(reorder_of(&bare), None);
1151        let bounded = bound_reorder(&bare, true)
1152            .expect("writable")
1153            .expect("a change");
1154        assert_eq!(reorder_of(&bounded), Some((0, 1)));
1155        let flag = restriction(
1156            &unescape(&bare[1..]),
1157            &locate(&unescape(&bare[1..])).unwrap(),
1158        )
1159        .unwrap()
1160        .flag;
1161        assert!(
1162            same_prefix(&bare, &bounded, flag),
1163            "the HRD parameters changed"
1164        );
1165    }
1166
1167    /// A set without a VUI gets one carrying the restriction alone: no color is claimed.
1168    #[test]
1169    fn a_set_without_a_vui_takes_one_with_the_restriction_alone() {
1170        let bare = without(VCE_SPS, true);
1171        assert!(!locate(&unescape(&bare[1..])).unwrap().vui_present);
1172        let bounded = bound_reorder(&bare, true)
1173            .expect("writable")
1174            .expect("a change");
1175        assert_eq!(reorder_of(&bounded), Some((0, 2)));
1176        assert_eq!(read_color(&bounded), None, "a color nobody declared");
1177        let colored = write_color(&bounded, ColorSignal::BT709_LIMITED).expect("colored after");
1178        assert_eq!(
1179            reorder_of(&colored),
1180            Some((0, 2)),
1181            "a color written later keeps the bound"
1182        );
1183    }
1184
1185    /// A set declaring a depth above zero is rewritten to zero, and a buffer below the
1186    /// reference count, which Chromium refuses the set for, is raised to it.
1187    #[test]
1188    fn a_declared_depth_is_rewritten() {
1189        let deep = with_depth(PI4_SPS, 3, 3);
1190        assert_eq!(reorder_of(&deep), Some((3, 3)));
1191        let bounded = bound_reorder(&deep, true)
1192            .expect("writable")
1193            .expect("a change");
1194        assert_eq!(
1195            reorder_of(&bounded),
1196            Some((0, 3)),
1197            "the declared buffer is kept"
1198        );
1199        let short = with_depth(&bound_reorder(VCE_SPS, true).unwrap().unwrap(), 0, 1);
1200        assert_eq!(reorder_of(&short), Some((0, 1)));
1201        let raised = bound_reorder(&short, true)
1202            .expect("writable")
1203            .expect("a change");
1204        assert_eq!(
1205            reorder_of(&raised),
1206            Some((0, 2)),
1207            "a buffer below the two references"
1208        );
1209    }
1210
1211    /// Anything the reader cannot account for bit by bit leaves the set as it came.
1212    #[test]
1213    fn a_set_the_reader_doubts_is_refused() {
1214        let rbsp = unescape(&VCE_SPS[1..]);
1215        let last = stop_bit(&rbsp).unwrap();
1216        let mut w = Writer::new();
1217        w.copy_from(&rbsp, 0, last);
1218        w.bit(1);
1219        w.trailing_bits();
1220        let mut trailing = vec![VCE_SPS[0]];
1221        trailing.extend_from_slice(&escape(&w.bytes));
1222        assert!(
1223            bound_reorder(&trailing, true).is_err(),
1224            "a bit after the VUI went unnoticed"
1225        );
1226        let mut scaled = unescape(&PI4_SPS[1..]);
1227        scaled[3] |= 0x01;
1228        let mut scaled_nal = vec![PI4_SPS[0]];
1229        scaled_nal.extend_from_slice(&escape(&scaled));
1230        assert!(
1231            bound_reorder(&scaled_nal, true).is_err(),
1232            "scaling lists are refused"
1233        );
1234        assert!(
1235            bound_reorder(&PI4_SPS[..10], true).is_err(),
1236            "a truncated set"
1237        );
1238    }
1239
1240    /// The sets of a real stream, stripped of their bound three ways and bounded again,
1241    /// decode to the same pictures as the stream as it came, through a decoder that is not
1242    /// ours; the unit's other NAL units pass through untouched.
1243    #[test]
1244    fn a_bounded_stream_decodes_as_it_came() {
1245        use crate::encoders::oh264::Openh264Encoder;
1246        use crate::webcam::decode::{Decoder as _, VideoDecoder};
1247        let (w, h) = (160usize, 96usize);
1248        let s = RustCaptureSettings {
1249            width: w as i32,
1250            height: h as i32,
1251            target_fps: 30.0,
1252            omit_stripe_headers: true,
1253            ..Default::default()
1254        };
1255        let mut enc = Openh264Encoder::new(&s).expect("openh264 init");
1256        let frames: Vec<Vec<u8>> = (0..6)
1257            .map(|t| {
1258                let pixels: Vec<u8> = (0..w * h)
1259                    .flat_map(|i| {
1260                        let v = ((i % w + 3 * t) * 5 + (i / w) * 3) as u8;
1261                        [v, v.wrapping_mul(3), v.wrapping_add(t as u8), 255]
1262                    })
1263                    .collect();
1264                enc.encode_host_argb(&pixels, w * 4, t as u64, t == 0, false)
1265                    .expect("encode")
1266            })
1267            .collect();
1268        let sets = sequence_parameter_sets(&frames[0]);
1269        assert_eq!(sets.len(), 1, "the key frame carries one set");
1270        let (start, end) = sets[0];
1271        let original = &frames[0][start..end];
1272        let refs = locate(&unescape(&original[1..]))
1273            .unwrap()
1274            .max_num_ref_frames;
1275        let decode = |stream: &[Vec<u8>]| -> Vec<Vec<u8>> {
1276            let mut dec = VideoDecoder::new(crate::encoders::codec::Codec::H264).expect("decoder");
1277            stream
1278                .iter()
1279                .map(|f| {
1280                    assert!(dec.decode(f).expect("decodes"), "no picture");
1281                    let p = dec.frame().expect("frame");
1282                    let rows =
1283                        |plane: &[u8], stride: usize, width: usize, height: usize| -> Vec<u8> {
1284                            (0..height)
1285                                .flat_map(|y| plane[y * stride..y * stride + width].to_vec())
1286                                .collect()
1287                        };
1288                    let mut out = rows(p.y, p.y_stride, p.width, p.height);
1289                    out.extend(rows(
1290                        p.u,
1291                        p.uv_stride,
1292                        p.width.div_ceil(2),
1293                        p.height.div_ceil(2),
1294                    ));
1295                    out.extend(rows(
1296                        p.v,
1297                        p.uv_stride,
1298                        p.width.div_ceil(2),
1299                        p.height.div_ceil(2),
1300                    ));
1301                    out
1302                })
1303                .collect()
1304        };
1305        let expected = decode(&frames);
1306        let mut variants = vec![
1307            (without(original, true), refs),
1308            (without(original, false), refs),
1309        ];
1310        if reorder_of(original).is_some() {
1311            variants.push((with_depth(original, 2, refs.max(2)), refs.max(2)));
1312        }
1313        for (variant, buffering) in variants {
1314            let mut stripped = frames.clone();
1315            stripped[0] = [&frames[0][..start], &variant[..], &frames[0][end..]].concat();
1316            let mut holder = NoReorder::new("The test encoder", true);
1317            let bounded = holder.apply(&stripped[0]).expect("a set to bound");
1318            assert_eq!(h264_reorder(&bounded), Some((0, buffering)));
1319            assert_eq!(
1320                holder.apply(&stripped[0]).as_ref(),
1321                Some(&bounded),
1322                "the second key frame differs"
1323            );
1324            assert_eq!(
1325                holder.apply(&frames[1]),
1326                None,
1327                "a unit without a set was touched"
1328            );
1329            assert!(
1330                bounded.starts_with(&frames[0][..start]),
1331                "what precedes the set changed"
1332            );
1333            assert!(
1334                bounded.ends_with(&frames[0][end..]),
1335                "the slices after the set changed"
1336            );
1337            let mut stream = stripped;
1338            stream[0] = bounded;
1339            assert_eq!(
1340                decode(&stream),
1341                expected,
1342                "the bounded stream decodes to other pictures"
1343            );
1344        }
1345    }
1346
1347    /// A slice NAL unit with its start code: `first_mb_in_slice`, `slice_type`, picture
1348    /// parameter set 0, `frame_num` `low` in `narrow` bits, then `tail` and the stop bit.
1349    fn slice(first_mb: u32, slice_type: u32, narrow: u32, low: u32, tail: &[u8]) -> Vec<u8> {
1350        let mut w = Writer::new();
1351        w.ue(first_mb);
1352        w.ue(slice_type);
1353        w.ue(0);
1354        w.bits(low, narrow as usize);
1355        for &byte in tail {
1356            w.bits(byte as u32, 8);
1357        }
1358        w.trailing_bits();
1359        let idr = slice_type == 7;
1360        [
1361            &[0, 0, 0, 1, if idr { 0x65 } else { 0x41 }][..],
1362            &escape(&w.bytes),
1363        ]
1364        .concat()
1365    }
1366
1367    /// A baseline set as x264 writes one for eight references: `frame_num` in `log2` bits,
1368    /// picture order count type 2, 1280x720, and a VUI with a bitstream restriction alone.
1369    fn x264_like_sps(log2: u32) -> Vec<u8> {
1370        let mut w = Writer::new();
1371        w.bits(66, 8);
1372        w.bits(0xc0, 8);
1373        w.bits(31, 8);
1374        w.ue(0);
1375        w.ue(log2 - 4);
1376        w.ue(2);
1377        w.ue(8);
1378        w.bit(0);
1379        w.ue(79);
1380        w.ue(44);
1381        w.bit(1);
1382        w.bit(1);
1383        w.bit(0);
1384        w.bit(1);
1385        for _ in 0..8 {
1386            w.bit(0);
1387        }
1388        w.bit(1);
1389        w.bit(1);
1390        for v in [2, 1, 16, 16, 0, 8] {
1391            w.ue(v);
1392        }
1393        w.trailing_bits();
1394        [&[0x67][..], &escape(&w.bytes)].concat()
1395    }
1396
1397    /// The slice's fields as a decoder reads them under a set of `log2` bits of `frame_num`, and
1398    /// the bytes after them up to the stop bit.
1399    fn read_slice(nal: &[u8], log2: u32) -> (u32, u32, u32, u32, Vec<u8>) {
1400        let code = nal.iter().position(|&b| b != 0).unwrap() + 1;
1401        let rbsp = unescape(&nal[code + 1..]);
1402        let mut r = Reader::new(&rbsp);
1403        let fields = (
1404            r.ue().unwrap(),
1405            r.ue().unwrap(),
1406            r.ue().unwrap(),
1407            r.bits(log2 as usize).unwrap(),
1408        );
1409        let stop = stop_bit(&rbsp).unwrap();
1410        assert_eq!((stop - r.pos) % 8, 0, "the tail is not whole bytes");
1411        let tail = (0..(stop - r.pos) / 8)
1412            .map(|_| r.bits(8).unwrap() as u8)
1413            .collect();
1414        assert!(
1415            rbsp.len() * 8 - stop <= 8,
1416            "the stop bit is not in the last byte"
1417        );
1418        (fields.0, fields.1, fields.2, fields.3, tail)
1419    }
1420
1421    /// Whether an escaped payload carries no start code or reserved sequence.
1422    fn escaped(nal: &[u8]) -> bool {
1423        let code = nal.iter().position(|&b| b != 0).unwrap() + 1;
1424        !nal[code..]
1425            .windows(3)
1426            .any(|w| w[0] == 0 && w[1] == 0 && w[2] <= 2)
1427            && nal.last() != Some(&0)
1428    }
1429
1430    /// The widened set declares eight bits more of `frame_num` and every other field as it came:
1431    /// the reference count, the color, the timing, and the bound on reordering after it.
1432    #[test]
1433    fn a_widened_set_changes_its_frame_num_alone() {
1434        for sps in [PI4_SPS.to_vec(), VCE_SPS.to_vec(), x264_like_sps(4)] {
1435            let (narrow, wide) = widen_sps(&sps).expect("the set widens");
1436            let rbsp = unescape(&sps[1..]);
1437            let at = locate(&rbsp).unwrap();
1438            assert_eq!(narrow, at.frame_num.0 + 4);
1439            let stream = |nal: &[u8]| [&[0, 0, 0, 1][..], nal].concat();
1440            assert_eq!(
1441                h264_frame_num_range(&stream(&wide)),
1442                h264_frame_num_range(&stream(&sps)).map(|range| range << 8)
1443            );
1444            assert_eq!(
1445                h264_max_num_ref_frames(&stream(&wide)),
1446                h264_max_num_ref_frames(&stream(&sps))
1447            );
1448            assert_eq!(read_color(&wide), read_color(&sps));
1449            assert_eq!(h264_timing(&stream(&wide)), h264_timing(&stream(&sps)));
1450            assert_eq!(reorder_of(&wide), reorder_of(&sps));
1451            let wide_rbsp = unescape(&wide[1..]);
1452            let wide_at = locate(&wide_rbsp).unwrap();
1453            let bit = |data: &[u8], at: usize| (data[at >> 3] >> (7 - (at & 7))) & 1;
1454            let tail = stop_bit(&rbsp).unwrap() - at.frame_num.2;
1455            assert_eq!(tail, stop_bit(&wide_rbsp).unwrap() - wide_at.frame_num.2);
1456            assert!(
1457                (0..tail)
1458                    .all(|i| bit(&rbsp, at.frame_num.2 + i)
1459                        == bit(&wide_rbsp, wide_at.frame_num.2 + i)),
1460                "a bit after the field changed"
1461            );
1462        }
1463        assert!(
1464            widen_sps(&x264_like_sps(9)).is_none(),
1465            "a field past sixteen bits"
1466        );
1467    }
1468
1469    /// Wherever `frame_num` falls in its byte, the widened slice reads back with the same fields,
1470    /// the wider count, and every byte after it, escaped where the inserted byte made zeros.
1471    #[test]
1472    fn a_widened_slice_keeps_every_bit_after_its_frame_num() {
1473        let mut offsets = [false; 8];
1474        for first_mb in [0, 1, 2, 3, 6, 7, 14, 15, 300, 8159] {
1475            for slice_type in [0, 5, 2, 7] {
1476                let mut r = Writer::new();
1477                r.ue(first_mb);
1478                r.ue(slice_type);
1479                r.ue(0);
1480                offsets[r.pos % 8] = true;
1481                // Widened counts with a high byte of 5, and of 0 ahead of zeros that then need
1482                // escaping.
1483                for (tail, wide) in [
1484                    (&[0xde, 0xad, 0xbe, 0xef][..], 0x59u32),
1485                    (&[0x00, 0x00, 0x01, 0x80][..], 9),
1486                    (&[0x00, 0x00][..], 0),
1487                ] {
1488                    let nal = slice(first_mb, slice_type, 4, wide & 15, tail);
1489                    let last = (wide + 4095) % 4096;
1490                    let (frame_num, widened) =
1491                        widen_slice(&nal[4..], 4, slice_type == 7, last).expect("widens");
1492                    let widened = [&nal[..4], &widened].concat();
1493                    let expected = if slice_type == 7 { wide & 15 } else { wide };
1494                    assert_eq!(frame_num, expected);
1495                    assert_eq!(
1496                        read_slice(&widened, 12),
1497                        (first_mb, slice_type, 0, expected, tail.to_vec()),
1498                        "first_mb {first_mb}, slice_type {slice_type}"
1499                    );
1500                    assert!(escaped(&widened), "{widened:02x?}");
1501                }
1502            }
1503        }
1504        // Three exp-Golomb codes are odd lengths, so the field starts at an odd bit, each of them.
1505        assert_eq!(
1506            offsets,
1507            [false, true, false, true, false, true, false, true]
1508        );
1509    }
1510
1511    /// The count runs on across x264's wraps, stays with a picture's every slice, restarts at a
1512    /// key frame, and wraps at 4096; a set that cannot widen leaves its slices as they came.
1513    #[test]
1514    fn the_widened_count_runs_on_across_x264s_wrap() {
1515        let mut wide = WideFrameNum::default();
1516        let mut out = Vec::new();
1517        assert!(wide.push(&[&[0, 0, 0, 1][..], &x264_like_sps(4)].concat(), &mut out));
1518        assert_eq!(h264_frame_num_range(&out), Some(4096));
1519        let mut seen = Vec::new();
1520        for frame in 0..4100u32 {
1521            for first_mb in [0, 1800] {
1522                let nal = slice(
1523                    first_mb,
1524                    if frame == 0 { 7 } else { 5 },
1525                    4,
1526                    frame % 16,
1527                    &[0x5a],
1528                );
1529                out.clear();
1530                assert!(wide.push(&nal, &mut out));
1531                let (_, _, _, frame_num, _) = read_slice(&out, 12);
1532                seen.push(frame_num);
1533            }
1534        }
1535        let expected: Vec<u32> = (0..4100u32).flat_map(|f| [f % 4096; 2]).collect();
1536        assert_eq!(seen, expected);
1537        out.clear();
1538        assert!(wide.push(&slice(0, 7, 4, 0, &[0x5a]), &mut out));
1539        assert_eq!(read_slice(&out, 12).3, 0, "a key frame restarts the count");
1540
1541        let mut narrow = WideFrameNum::default();
1542        let sps = [&[0, 0, 0, 1][..], &x264_like_sps(9)].concat();
1543        let p = slice(0, 5, 9, 300, &[0x5a]);
1544        out.clear();
1545        assert!(narrow.push(&sps, &mut out));
1546        assert!(narrow.push(&p, &mut out));
1547        assert_eq!(
1548            out,
1549            [&sps[..], &p[..]].concat(),
1550            "they went out as they came"
1551        );
1552    }
1553
1554    #[test]
1555    fn a_unit_ending_in_zeros_ends_escaped() {
1556        assert_eq!(escape(&[1, 0, 0]), [1, 0, 0, 3]);
1557        assert_eq!(unescape(&escape(&[1, 0, 0])), [1, 0, 0]);
1558    }
1559}