Skip to main content

pixelflux/encoders/
codec.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! What every encoder backend shares about a codec: its name and wire identity, the
8//! quantizer domain the session-wide quality index maps onto, the level a stream of a
9//! given geometry advertises, and the bitstream reads that label a frame on the wire.
10
11/// The picture codec of a capture session, and of a virtual-camera uplink. `Jpeg` is the
12/// striped still-image mode; the rest are video codecs, which stream as whole frames except
13/// H.264, whose software path may also stripe. The discriminants are the codec ids the
14/// Python API speaks (`VirtualCamera.CODEC_*`) and the video wire ids.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16#[repr(u32)]
17pub enum Codec {
18    Jpeg = 0,
19    H264 = 1,
20    Vp8 = 2,
21    Vp9 = 3,
22    Av1 = 4,
23    H265 = 5,
24}
25
26/// The hardware encoder family a session's quantizer curve is read for
27/// (`Codec::hardware_quantizer`).
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum Hardware {
30    Nvenc,
31    Vaapi,
32    VaapiIntel,
33}
34
35use super::reference::{REFERENCE_FRAMES, Reference};
36
37/// Wire tag of a JPEG stripe: `u8 reserved`, `u16 frame id`, `u16 stripe Y`, JPEG data.
38pub const WIRE_JPEG: u8 = 0x03;
39/// Wire tag of an encoded video frame or stripe: a type byte whose low nibble is the
40/// frame kind and whose high nibble is the codec's [`Codec::wire_id`], then `u16 frame
41/// id`, `u16 stripe Y`, `u16 width`, `u16 height`, `u16 reference` (the frame it predicts
42/// from, its own id where it names none), and the codec's own bitstream.
43pub const WIRE_VIDEO: u8 = 0x04;
44/// Frame kinds in the low nibble of the video type byte: a decode entry point, an
45/// intra-coded picture that is not one, and a predicted picture.
46pub const FRAME_KEY: u8 = 0x01;
47pub const FRAME_INTRA: u8 = 0x02;
48pub const FRAME_DELTA: u8 = 0x00;
49/// A bit ORed into a delta frame's kind: the session keeps the frame as a long-term reference and
50/// it predicts from one every client holds (`ReferenceWindow::settle`), so every client can
51/// decode it.
52pub const FRAME_ANCHOR: u8 = 0x08;
53/// Bytes of the two stripe headers.
54pub const JPEG_HEADER_LEN: usize = 6;
55pub const VIDEO_HEADER_LEN: usize = 12;
56
57impl Codec {
58    /// The video codecs, in wire-id order.
59    pub const VIDEO: [Codec; 5] = [Codec::H264, Codec::Vp8, Codec::Vp9, Codec::Av1, Codec::H265];
60
61    /// The codec's numeric id, as the Python API and the video wire carry it.
62    pub const fn id(self) -> u32 {
63        self as u32
64    }
65
66    /// The codec a numeric id names.
67    pub fn from_id(id: u32) -> Option<Codec> {
68        [
69            Codec::Jpeg,
70            Codec::H264,
71            Codec::Vp8,
72            Codec::Vp9,
73            Codec::Av1,
74            Codec::H265,
75        ]
76        .into_iter()
77        .find(|c| c.id() == id)
78    }
79
80    /// The codec a settings name selects; `None` for a name no backend serves.
81    pub fn parse(name: &str) -> Option<Codec> {
82        match name.trim().to_ascii_lowercase().as_str() {
83            "jpeg" | "mjpeg" => Some(Codec::Jpeg),
84            "h264" | "avc" => Some(Codec::H264),
85            "h265" | "hevc" => Some(Codec::H265),
86            "vp8" => Some(Codec::Vp8),
87            "vp9" => Some(Codec::Vp9),
88            "av1" => Some(Codec::Av1),
89            _ => None,
90        }
91    }
92
93    /// The settings and log name of the codec.
94    pub fn name(self) -> &'static str {
95        match self {
96            Codec::Jpeg => "jpeg",
97            Codec::H264 => "h264",
98            Codec::H265 => "h265",
99            Codec::Vp8 => "vp8",
100            Codec::Vp9 => "vp9",
101            Codec::Av1 => "av1",
102        }
103    }
104
105    /// The name a stream line prints.
106    pub fn display(self) -> &'static str {
107        match self {
108            Codec::Jpeg => "JPEG",
109            Codec::H264 => "H264",
110            Codec::H265 => "H265",
111            Codec::Vp8 => "VP8",
112            Codec::Vp9 => "VP9",
113            Codec::Av1 => "AV1",
114        }
115    }
116
117    /// Whether the session is a video stream rather than striped stills.
118    pub fn is_video(self) -> bool {
119        self != Codec::Jpeg
120    }
121
122    /// Whether a frame of this codec may be cut into independently encoded stripes: the
123    /// still-image mode always is, H.264 is on its software path, and every other codec
124    /// streams whole frames.
125    pub fn stripes(self) -> bool {
126        matches!(self, Codec::Jpeg | Codec::H264)
127    }
128
129    /// The codec's identity in the high nibble of the video type byte: its id.
130    pub fn wire_id(self) -> u8 {
131        self.id() as u8
132    }
133
134    /// The video codec a wire id names.
135    pub fn from_wire_id(id: u8) -> Option<Codec> {
136        Codec::from_id(id as u32).filter(|c| c.is_video())
137    }
138
139    /// The codec tag the Python `StripeFrame` carries: the codec id.
140    pub fn data_type(self) -> i32 {
141        self.id() as i32
142    }
143
144    /// Whether frames depend on earlier ones, so a dropped frame forces a wait for a key
145    /// frame.
146    pub fn is_inter_coded(self) -> bool {
147        self.is_video()
148    }
149
150    /// Whether the codec can carry 4:4:4 chroma at all (H.264 High 4:4:4, H.265 RExt, VP9
151    /// profile 1); whether a given backend does is negotiated by that backend.
152    pub fn fullcolor(self) -> bool {
153        matches!(self, Codec::H264 | Codec::H265 | Codec::Vp9)
154    }
155
156    /// Whether the codec has a 10-bit profile a session here can ask for (H.265 Main 10 and
157    /// its 4:4:4, VP9 profiles 2 and 3, AV1); whether a given backend does is negotiated by
158    /// that backend.
159    pub fn high_bit_depth(self) -> bool {
160        matches!(self, Codec::H265 | Codec::Vp9 | Codec::Av1)
161    }
162
163    /// Whether a forced key frame of this codec repeats the stream's parameter sets, so
164    /// a joining decoder can start there. Every backend is configured to, so the answer
165    /// is the same for all of them.
166    pub fn keyframes_self_contained(self) -> bool {
167        self.is_video()
168    }
169
170    /// The top of the codec's quantizer domain: H.26x QP, the VP8 quantizer index, or
171    /// the VP9 / AV1 `base_q_idx`. The value a backend programs never exceeds it.
172    pub fn quantizer_max(self) -> u32 {
173        match self {
174            Codec::Jpeg => 100,
175            Codec::H264 | Codec::H265 => 51,
176            Codec::Vp8 => 127,
177            Codec::Vp9 | Codec::Av1 => 255,
178        }
179    }
180
181    /// The quantizer, in this codec's own domain, that a session quality index (`video_crf`,
182    /// on the H.26x QP scale) selects.
183    ///
184    /// H.26x takes the index as its QP. The VP8, VP9, and AV1 quantizer indices come from
185    /// tables measured on the software encoders against libx264 (ultrafast, zerolatency, four
186    /// threads) at the same index, every encoder at the settings pixelflux gives it — VP8 at
187    /// libvpx speed 16, VP9 at speed 8 with screen tuning, AV1 at SVT-AV1 preset 11 in its
188    /// real-time mode — interpolated in between. An AV1 point is the quantizer whose SSIM
189    /// matches on a scrolling text desktop. A VP8 or VP9 point is the one whose PSNR matches,
190    /// the finest of a text desktop held still, the same scrolled, and a gradient wallpaper,
191    /// which agree within two libvpx levels for VP9; VP8 reaches 43 dB at its finest, so
192    /// below index 25 it has nothing left to match. Each table ends at the codec's maximum
193    /// quantizer at index 51, since past its last matched point the encoder cannot degrade
194    /// as far as x264 does and there is nothing left to match. A hardware session reads
195    /// `hardware_quantizer`. An AV1 index of zero is lossless, so its floor is one; the
196    /// SVT-AV1 session floors its level at ten, below which its real-time mode miscodes.
197    pub fn quantizer(self, crf: i32) -> u32 {
198        let crf = crf.clamp(0, 51) as u32;
199        match self {
200            Codec::Jpeg => crf,
201            Codec::H264 | Codec::H265 => crf,
202            Codec::Vp8 => interpolate(&VP8_QINDEX, crf),
203            Codec::Vp9 => interpolate(&VP9_QINDEX, crf),
204            Codec::Av1 => interpolate(&AV1_QINDEX, crf).max(1),
205        }
206    }
207
208    /// The quantizer a CBR QP clamp (`video_min_qp` / `video_max_qp`, on the H.26x scale)
209    /// becomes in this codec's domain; zero stays zero, meaning "the encoder's default".
210    pub fn quantizer_bound(self, qp: i32) -> u32 {
211        if qp <= 0 { 0 } else { self.quantizer(qp) }
212    }
213
214    /// `quantizer` for a hardware session, matched to libx264 the same way. H.264 is measured on
215    /// NVENC (Pascal and Ada) and on AMD's VA-API, which agree within a step, each coding a
216    /// quantizer about three finer than the index to match x264; AMD's VA-API H.265 follows
217    /// the same curve within a step, where NVENC's H.265 is within two steps of the index from
218    /// 22 up and keeps it. Intel's VA-API H.265 matches x264 on a still frame at that curve but
219    /// refines a moving picture well past it for fewer bytes (Arc and Alder Lake-N), so it reads
220    /// one two steps coarser from index 25 up; below 20 its still frames of text fall 3 to 4 dB
221    /// short of x264's at that curve, so there it reads one finer. AV1 has a curve per backend:
222    /// NVENC's is measured on Ada, and VA-API's on Intel (Arc) with the in-loop filters its
223    /// constant-quantizer frames carry, on a
224    /// text desktop and a gradient wallpaper, taking the finer match of the two. VA-API's VP9
225    /// is matched the same way on Intel (Arc), finer than libvpx at the same index on text.
226    /// VP8 keeps the software table. NVENC refuses an AV1 index of zero, so the floor stays one.
227    pub fn hardware_quantizer(self, hw: Hardware, crf: i32) -> u32 {
228        let crf = crf.clamp(0, 51) as u32;
229        match (self, hw) {
230            (Codec::H264, _) | (Codec::H265, Hardware::Vaapi) => {
231                interpolate(&H26X_HARDWARE_QP, crf)
232            }
233            (Codec::H265, Hardware::VaapiIntel) => interpolate(&H265_INTEL_QP, crf),
234            (Codec::Vp9, Hardware::Vaapi | Hardware::VaapiIntel) => {
235                interpolate(&VP9_VAAPI_QINDEX, crf)
236            }
237            (Codec::Av1, Hardware::Nvenc) => interpolate(&AV1_NVENC_QINDEX, crf).max(1),
238            (Codec::Av1, Hardware::Vaapi | Hardware::VaapiIntel) => {
239                interpolate(&AV1_VAAPI_QINDEX, crf).max(1)
240            }
241            _ => self.quantizer(crf as i32),
242        }
243    }
244
245    /// `quantizer_bound` for a hardware session.
246    pub fn hardware_quantizer_bound(self, hw: Hardware, qp: i32) -> u32 {
247        if qp <= 0 {
248            0
249        } else {
250            self.hardware_quantizer(hw, qp)
251        }
252    }
253
254    /// The session quality index whose quantizer (`quantizer`) is nearest `q`, a quantizer of
255    /// this codec's domain: what a rate-controlled frame's quantizer amounts to on the H.26x
256    /// scale the quality settings use.
257    pub fn quality_index(self, q: u32) -> u32 {
258        nearest_index(q, |crf| self.quantizer(crf))
259    }
260
261    /// `quality_index` for a hardware session.
262    pub fn hardware_quality_index(self, hw: Hardware, q: u32) -> u32 {
263        nearest_index(q, |crf| self.hardware_quantizer(hw, crf))
264    }
265}
266
267/// The quality index in `0..=51` whose quantizer is nearest `q`, the lowest on a tie.
268fn nearest_index(q: u32, quantizer: impl Fn(i32) -> u32) -> u32 {
269    (0..=51)
270        .min_by_key(|&crf| (quantizer(crf) as i64 - q as i64).abs())
271        .unwrap_or(0) as u32
272}
273
274/// Session quality index → VP8 quantizer index (0..=127) breakpoints.
275const VP8_QINDEX: [(u32, u32); 10] = [
276    (0, 0),
277    (15, 1),
278    (20, 3),
279    (25, 5),
280    (30, 24),
281    (35, 49),
282    (40, 73),
283    (45, 106),
284    (50, 127),
285    (51, 127),
286];
287/// Session quality index → VP9 `base_q_idx` (0..=255) breakpoints.
288const VP9_QINDEX: [(u32, u32); 12] = [
289    (0, 0),
290    (5, 1),
291    (10, 6),
292    (15, 18),
293    (20, 32),
294    (25, 66),
295    (30, 108),
296    (35, 140),
297    (40, 176),
298    (45, 208),
299    (50, 236),
300    (51, 255),
301];
302/// Session quality index → VA-API VP9 `base_q_idx` breakpoints.
303const VP9_VAAPI_QINDEX: [(u32, u32); 12] = [
304    (0, 0),
305    (5, 2),
306    (10, 5),
307    (15, 11),
308    (20, 19),
309    (25, 48),
310    (30, 88),
311    (35, 124),
312    (40, 163),
313    (45, 200),
314    (50, 228),
315    (51, 240),
316];
317/// Session quality index → AV1 `base_q_idx` (0..=255) breakpoints.
318const AV1_QINDEX: [(u32, u32); 10] = [
319    (0, 0),
320    (10, 12),
321    (15, 43),
322    (20, 88),
323    (25, 134),
324    (30, 168),
325    (35, 190),
326    (40, 215),
327    (45, 253),
328    (51, 255),
329];
330/// Session quality index → hardware H.264 QP breakpoints, and VA-API H.265's.
331const H26X_HARDWARE_QP: [(u32, u32); 11] = [
332    (0, 0),
333    (10, 9),
334    (15, 14),
335    (20, 17),
336    (25, 22),
337    (30, 27),
338    (35, 32),
339    (40, 37),
340    (45, 43),
341    (50, 47),
342    (51, 48),
343];
344/// Session quality index → Intel VA-API H.265 QP breakpoints.
345const H265_INTEL_QP: [(u32, u32); 11] = [
346    (0, 0),
347    (10, 8),
348    (15, 12),
349    (20, 17),
350    (25, 24),
351    (30, 29),
352    (35, 34),
353    (40, 39),
354    (45, 45),
355    (50, 49),
356    (51, 50),
357];
358/// Session quality index → VA-API AV1 `base_q_idx` breakpoints.
359const AV1_VAAPI_QINDEX: [(u32, u32); 12] = [
360    (0, 1),
361    (5, 3),
362    (10, 10),
363    (15, 26),
364    (20, 40),
365    (25, 80),
366    (30, 106),
367    (35, 136),
368    (40, 171),
369    (45, 205),
370    (50, 236),
371    (51, 244),
372];
373/// Session quality index → NVENC AV1 `base_q_idx` breakpoints.
374const AV1_NVENC_QINDEX: [(u32, u32); 12] = [
375    (0, 1),
376    (5, 20),
377    (10, 26),
378    (15, 44),
379    (20, 66),
380    (25, 105),
381    (30, 135),
382    (35, 163),
383    (40, 195),
384    (45, 219),
385    (50, 241),
386    (51, 244),
387];
388
389/// Piecewise-linear lookup of `x` in ascending `(x, y)` breakpoints, clamped at both ends.
390fn interpolate(points: &[(u32, u32)], x: u32) -> u32 {
391    let (first, last) = (points[0], points[points.len() - 1]);
392    if x <= first.0 {
393        return first.1;
394    }
395    if x >= last.0 {
396        return last.1;
397    }
398    for pair in points.windows(2) {
399        let ((x0, y0), (x1, y1)) = (pair[0], pair[1]);
400        if x >= x0 && x <= x1 {
401            let span = (x1 - x0) as i64;
402            let along = (x - x0) as i64;
403            let delta = y1 as i64 - y0 as i64;
404            return (y0 as i64 + (delta * along + span / 2) / span) as u32;
405        }
406    }
407    last.1
408}
409
410/// `base_q_idx` of each libvpx / libaom quantizer level 0..=63, as those libraries map
411/// their `--min-q` / `--max-q` / `--cq-level` arguments.
412pub const VPX_QINDEX: [u8; 64] = [
413    0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
414    96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168,
415    172, 176, 180, 184, 188, 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244,
416    249, 255,
417];
418/// VP8 quantizer index of each libvpx VP8 quantizer level 0..=63.
419pub const VP8_LEVEL_QINDEX: [u8; 64] = [
420    0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 12, 13, 15, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30,
421    31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 64, 67, 70, 73, 76, 79, 82, 85,
422    88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127,
423];
424
425/// The libvpx / SVT-AV1 quantizer level (0..=63) closest to a quantizer index of
426/// `codec`'s domain: the software encoders take the level, the hardware ones the index.
427pub fn vpx_level(codec: Codec, qindex: u32) -> u32 {
428    let table: &[u8; 64] = if codec == Codec::Vp8 {
429        &VP8_LEVEL_QINDEX
430    } else {
431        &VPX_QINDEX
432    };
433    let target = qindex.min(codec.quantizer_max()) as i64;
434    let mut best = 0usize;
435    for (level, &q) in table.iter().enumerate() {
436        if (q as i64 - target).abs() < (table[best] as i64 - target).abs() {
437            best = level;
438        }
439    }
440    best as u32
441}
442
443/// Append the header of one encoded video frame or stripe: the tag, the codec and frame kind,
444/// then the frame id, the stripe's top row, its width and height, and the id of the frame it
445/// predicts from, all big-endian u16. A frame that predicts from none, or whose encoder does
446/// not say, names itself there.
447#[allow(clippy::too_many_arguments)]
448pub fn push_video_header(
449    out: &mut Vec<u8>,
450    codec: Codec,
451    frame_type: u8,
452    frame_id: u16,
453    y_start: u16,
454    width: u16,
455    height: u16,
456    reference: Reference,
457) {
458    out.push(WIRE_VIDEO);
459    out.push((frame_type & 0x0f) | (codec.wire_id() << 4));
460    out.extend_from_slice(&frame_id.to_be_bytes());
461    out.extend_from_slice(&y_start.to_be_bytes());
462    out.extend_from_slice(&width.to_be_bytes());
463    out.extend_from_slice(&height.to_be_bytes());
464    let reference = match reference {
465        Reference::Frame(id) => id,
466        Reference::None | Reference::Untracked => frame_id,
467    };
468    out.extend_from_slice(&reference.to_be_bytes());
469}
470
471/// Append the header of one JPEG stripe.
472pub fn push_jpeg_header(out: &mut Vec<u8>, frame_id: u16, y_start: u16) {
473    out.push(WIRE_JPEG);
474    out.push(0);
475    out.extend_from_slice(&frame_id.to_be_bytes());
476    out.extend_from_slice(&y_start.to_be_bytes());
477}
478
479/// The codec and frame kind a video type byte carries.
480pub fn parse_video_type(type_byte: u8) -> Option<(Codec, u8)> {
481    Codec::from_wire_id(type_byte >> 4).map(|c| (c, type_byte & 0x0f & !FRAME_ANCHOR))
482}
483
484/// The frame kind of a key/delta answer an encoder reports without a bitstream parse.
485pub fn frame_type_from_key(key: bool) -> u8 {
486    if key { FRAME_KEY } else { FRAME_DELTA }
487}
488
489/// Lowest H.264 level whose Annex-A Table A-1 limits admit a `width` x `height` stream at
490/// `fps` carrying up to `bitrate_bps` (0 where no rate is declared), as level_idc (41 = 4.1,
491/// 52 = 5.2, 62 = 6.2).
492///
493/// A frame is charged two ways: **MaxFS**, its size in macroblocks, and **MaxMBPS**, that
494/// size times the frame rate; a declared rate is charged against **MaxBR** at the High
495/// profile's factor of 1.25, which every session here encodes with (4:4:4 allows more), since
496/// an encoder that validates the level against the rate refuses a CBR target past it.
497/// Advertising the lowest fitting level asks the least of a decoder, so clients that gate
498/// hardware decode on the level accept the widest range of streams. One shared ladder keeps
499/// every backend agreeing on the same geometry; it starts at 4.1 and a caller needing more
500/// raises the floor itself. Above 6.2's limits there is no higher level, so it returns 62 as
501/// a best effort.
502pub fn h264_level(width: u32, height: u32, fps: u32, bitrate_bps: u64) -> u32 {
503    let mbs = (width as u64).div_ceil(16) * (height as u64).div_ceil(16);
504    let mbps = mbs * fps.max(1) as u64;
505    const LEVELS: [(u32, u64, u64, u64); 8] = [
506        (41, 8192, 245760, 50_000_000),
507        (42, 8704, 522240, 50_000_000),
508        (50, 22080, 589824, 135_000_000),
509        (51, 36864, 983040, 240_000_000),
510        (52, 36864, 2073600, 240_000_000),
511        (60, 139264, 4177920, 240_000_000),
512        (61, 139264, 8355840, 480_000_000),
513        (62, 139264, 16711680, 800_000_000),
514    ];
515    for &(level, max_fs, max_mbps, max_br) in &LEVELS {
516        if mbs <= max_fs && mbps <= max_mbps && bitrate_bps <= max_br / 4 * 5 {
517            return level;
518        }
519    }
520    62
521}
522
523/// The decoded picture buffer, in frames, an H.264 stream at `level` may declare for a
524/// `width` x `height` picture: the level's MaxDpbMbs over the picture's macroblocks, at most
525/// `REFERENCE_FRAMES`.
526pub fn h264_dpb_frames(level: u32, width: u32, height: u32) -> u32 {
527    let mbs = (width as u64).div_ceil(16) * (height as u64).div_ceil(16);
528    let max_dpb_mbs: u64 = match level {
529        41 => 32768,
530        42 => 34816,
531        50 => 110400,
532        51 | 52 => 184320,
533        _ => 696320,
534    };
535    ((max_dpb_mbs / mbs.max(1)) as u32).clamp(1, REFERENCE_FRAMES)
536}
537
538/// The level an x264 session declares so its decoded picture buffer holds `REFERENCE_FRAMES`:
539/// the lowest from `level` up to 5.2 whose buffer admits them at `width` x `height`, else 5.2,
540/// or `level` itself above 5.2.
541#[cfg(any(feature = "gpl", test))]
542pub fn h264_reference_level(level: u32, width: u32, height: u32) -> u32 {
543    if level > 52 {
544        return level;
545    }
546    [41, 42, 50, 51, 52]
547        .into_iter()
548        .filter(|&l| l >= level)
549        .find(|&l| h264_dpb_frames(l, width, height) == REFERENCE_FRAMES)
550        .unwrap_or(52)
551}
552
553/// Lowest H.265 level whose Annex-A Tables A.8 and A.9 limits admit a `width` x `height`
554/// stream at `fps` carrying up to `bitrate_bps` (0 where no rate is declared) at the High
555/// tier or the Main one, as general_level_idc (123 = 4.1, 156 = 5.2, 186 = 6.2).
556///
557/// A frame is charged by **MaxLumaPs**, its luma samples, and **MaxLumaSr**, those times the
558/// frame rate; a declared rate by the tier's **MaxBR**, which an encoder that validates the
559/// level against the rate refuses a CBR target past. The ladder starts at 4.1 like the H.264
560/// one and answers 6.2 for anything beyond its limits.
561pub fn h265_level(width: u32, height: u32, fps: u32, bitrate_bps: u64, high_tier: bool) -> u32 {
562    let ps = width as u64 * height as u64;
563    let sr = ps * fps.max(1) as u64;
564    const LEVELS: [(u32, u64, u64, u64, u64); 7] = [
565        (123, 2_228_224, 133_693_440, 20_000_000, 50_000_000),
566        (150, 8_912_896, 267_386_880, 25_000_000, 100_000_000),
567        (153, 8_912_896, 534_773_760, 40_000_000, 160_000_000),
568        (156, 8_912_896, 1_069_547_520, 60_000_000, 240_000_000),
569        (180, 35_651_584, 1_069_547_520, 60_000_000, 240_000_000),
570        (183, 35_651_584, 2_139_095_040, 120_000_000, 480_000_000),
571        (186, 35_651_584, 4_278_190_080, 240_000_000, 800_000_000),
572    ];
573    for &(level, max_ps, max_sr, main_br, high_br) in &LEVELS {
574        if ps <= max_ps && sr <= max_sr && bitrate_bps <= if high_tier { high_br } else { main_br }
575        {
576            return level;
577        }
578    }
579    186
580}
581
582/// The reference frames an H.265 stream at `level` may hold for a `width` x `height`
583/// picture: the decoded picture buffer A.4.2 sizes (six pictures at the level's full
584/// MaxLumaPs, more for a smaller picture) less the picture being decoded, at most
585/// `REFERENCE_FRAMES`.
586pub fn h265_dpb_frames(level: u32, width: u32, height: u32) -> u32 {
587    let max_luma_ps: u64 = match level {
588        ..=123 => 2_228_224,
589        150..=156 => 8_912_896,
590        _ => 35_651_584,
591    };
592    let picture = width as u64 * height as u64;
593    let pictures = if picture <= max_luma_ps / 4 {
594        16
595    } else if picture <= max_luma_ps / 2 {
596        12
597    } else if picture <= max_luma_ps * 3 / 4 {
598        8
599    } else {
600        6
601    };
602    (pictures - 1).min(REFERENCE_FRAMES)
603}
604
605/// The tier an H.265 session declares at `level` (general_level_idc): High (1) from level 4.0
606/// up, where Annex A defines one and every decoder of those levels takes it, so a CBR target
607/// above the level's Main-tier MaxBR is not refused by an encoder that validates the two
608/// together; Main (0) below, where no High tier exists.
609pub fn h265_tier(level: u32) -> u32 {
610    if level >= 120 { 1 } else { 0 }
611}
612
613/// Lowest AV1 level whose Annex A limits admit a `width` x `height` stream at `fps` carrying
614/// up to `bitrate_bps` (0 where no rate is declared), as seq_level_idx (8 = 4.0, 13 = 5.1,
615/// 19 = 6.3).
616///
617/// A frame is charged by **MaxPicSize** and the per-axis **MaxHSize** / **MaxVSize**, its
618/// rate by **MaxDisplayRate**, and a declared bitrate by the Main tier's **MaxBitrate**, the
619/// tier every hardware encoder declares, which an encoder that validates the level against
620/// the rate refuses a CBR target past. MaxPicSize is its own limit, far below the product of
621/// the axis maxima -- no level admits a picture that is both its widest and its tallest -- so
622/// it is carried per level rather than derived. The ladder starts at 4.0 and answers 6.3 for
623/// anything beyond its limits.
624pub fn av1_level(width: u32, height: u32, fps: u32, bitrate_bps: u64) -> u32 {
625    let ps = width as u64 * height as u64;
626    let rate = ps * fps.max(1) as u64;
627    const LEVELS: [(u32, u64, u32, u32, u64, u64); 10] = [
628        (8, 2_359_296, 6144, 3456, 70_778_880, 12_000_000),
629        (9, 2_359_296, 6144, 3456, 141_557_760, 20_000_000),
630        (12, 8_912_896, 8192, 4352, 267_386_880, 30_000_000),
631        (13, 8_912_896, 8192, 4352, 534_773_760, 40_000_000),
632        (14, 8_912_896, 8192, 4352, 1_069_547_520, 60_000_000),
633        (15, 8_912_896, 8192, 4352, 1_069_547_520, 60_000_000),
634        (16, 35_651_584, 16384, 8704, 1_069_547_520, 60_000_000),
635        (17, 35_651_584, 16384, 8704, 2_139_095_040, 100_000_000),
636        (18, 35_651_584, 16384, 8704, 4_278_190_080, 160_000_000),
637        (19, 35_651_584, 16384, 8704, 4_278_190_080, 160_000_000),
638    ];
639    for &(level, max_ps, max_w, max_h, max_rate, max_br) in &LEVELS {
640        if ps <= max_ps
641            && width <= max_w
642            && height <= max_h
643            && rate <= max_rate
644            && bitrate_bps <= max_br
645        {
646            return level;
647        }
648    }
649    19
650}
651
652/// Wire frame kind of one encoded H.264 access unit, read from the bitstream itself:
653/// IDR = key, intra-only non-IDR = intra, predicted = delta. The stripe header labels
654/// real decode entry points, so the NAL and slice types the encoder actually produced
655/// decide, never what was requested of it. An access unit whose slices cannot be parsed
656/// is labeled delta: a false delta costs one skipped entry point, a false key breaks the
657/// decoder.
658pub fn h264_frame_type(au: &[u8]) -> u8 {
659    let mut intra = false;
660    for nal in annexb_nals(au) {
661        match nal[0] & 0x1f {
662            5 => return FRAME_KEY,
663            1 => match h264_slice_is_intra(&nal[1..]) {
664                Some(true) => intra = true,
665                _ => return FRAME_DELTA,
666            },
667            _ => {}
668        }
669    }
670    if intra { FRAME_INTRA } else { FRAME_DELTA }
671}
672
673/// Wire frame kind of one encoded H.265 access unit: an IRAP picture (BLA, IDR, or CRA,
674/// which every backend here emits as an IDR with its parameter sets) is a key, anything
675/// else a delta.
676pub fn h265_frame_type(au: &[u8]) -> u8 {
677    for nal in annexb_nals(au) {
678        let nal_type = (nal[0] >> 1) & 0x3f;
679        if (16..=23).contains(&nal_type) {
680            return FRAME_KEY;
681        }
682    }
683    FRAME_DELTA
684}
685
686/// Whether a VP8 frame is a key frame: the frame tag's lowest bit is clear.
687pub fn vp8_is_key(frame: &[u8]) -> bool {
688    frame.first().is_some_and(|b| b & 1 == 0)
689}
690
691/// Whether a VP9 frame is a key frame, read from its uncompressed header: not a shown existing
692/// frame, and `frame_type` KEY_FRAME. Profile 3 carries one reserved bit ahead of those two.
693pub fn vp9_is_key(frame: &[u8]) -> bool {
694    let Some(&b) = frame.first() else {
695        return false;
696    };
697    if b >> 6 != 0b10 {
698        return false;
699    }
700    let profile = ((b >> 5) & 1) | (((b >> 4) & 1) << 1);
701    let (show_existing, frame_type) = if profile == 3 {
702        (b & 0x04, b & 0x02)
703    } else {
704        (b & 0x08, b & 0x04)
705    };
706    show_existing == 0 && frame_type == 0
707}
708
709/// The `base_q_idx` of a VP9 frame, read from its uncompressed header past the color
710/// configuration, the frame size, the references, and the loop filter; None for a shown
711/// existing frame or a header cut short.
712pub fn vp9_base_q_idx(frame: &[u8]) -> Option<u32> {
713    let mut pos = 0usize;
714    let mut bits = |n: usize| -> Option<u32> {
715        let mut value = 0u32;
716        for _ in 0..n {
717            let byte = *frame.get(pos / 8)?;
718            value = (value << 1) | ((byte >> (7 - pos % 8)) & 1) as u32;
719            pos += 1;
720        }
721        Some(value)
722    };
723    if bits(2)? != 2 {
724        return None;
725    }
726    let profile = bits(1)? | (bits(1)? << 1);
727    if profile == 3 {
728        bits(1)?;
729    }
730    if bits(1)? == 1 {
731        return None;
732    }
733    let key = bits(1)? == 0;
734    let show_frame = bits(1)? == 1;
735    let error_resilient = bits(1)? == 1;
736    let color_config = |bits: &mut dyn FnMut(usize) -> Option<u32>| -> Option<()> {
737        if profile >= 2 {
738            bits(1)?;
739        }
740        if bits(3)? != 7 {
741            bits(1)?;
742            if profile & 1 == 1 {
743                bits(3)?;
744            }
745        } else if profile & 1 == 1 {
746            bits(1)?;
747        }
748        Some(())
749    };
750    let sizes = |bits: &mut dyn FnMut(usize) -> Option<u32>| -> Option<()> {
751        bits(32)?;
752        if bits(1)? == 1 {
753            bits(32)?;
754        }
755        Some(())
756    };
757    if key {
758        bits(24)?;
759        color_config(&mut bits)?;
760        sizes(&mut bits)?;
761    } else {
762        let intra_only = !show_frame && bits(1)? == 1;
763        if !error_resilient {
764            bits(2)?;
765        }
766        if intra_only {
767            bits(24)?;
768            if profile > 0 {
769                color_config(&mut bits)?;
770            }
771            bits(8)?;
772            sizes(&mut bits)?;
773        } else {
774            bits(8)?;
775            bits(12)?;
776            let mut found = false;
777            for _ in 0..3 {
778                if bits(1)? == 1 {
779                    found = true;
780                    break;
781                }
782            }
783            if !found {
784                bits(32)?;
785            }
786            if bits(1)? == 1 {
787                bits(32)?;
788            }
789            bits(1)?;
790            if bits(1)? == 0 {
791                bits(2)?;
792            }
793        }
794    }
795    if !error_resilient {
796        bits(2)?;
797    }
798    bits(2)?;
799    bits(9)?;
800    if bits(1)? == 1 && bits(1)? == 1 {
801        for _ in 0..6 {
802            if bits(1)? == 1 {
803                bits(7)?;
804            }
805        }
806    }
807    bits(8)
808}
809
810/// Whether an AV1 temporal unit opens with a key frame: the first frame header or frame
811/// OBU's `frame_type`, read past any temporal delimiter and sequence header. A show
812/// existing frame, or a unit without a frame, is a delta.
813pub fn av1_is_key(tu: &[u8]) -> bool {
814    let mut pos = 0usize;
815    while pos < tu.len() {
816        let header = tu[pos];
817        let obu_type = (header >> 3) & 0x0f;
818        let has_extension = header & 0x04 != 0;
819        let has_size = header & 0x02 != 0;
820        let mut i = pos + 1 + has_extension as usize;
821        let size = if has_size {
822            let mut value = 0u64;
823            let mut shift = 0u32;
824            loop {
825                if i >= tu.len() || shift > 56 {
826                    return false;
827                }
828                let byte = tu[i];
829                i += 1;
830                value |= ((byte & 0x7f) as u64) << shift;
831                if byte & 0x80 == 0 {
832                    break;
833                }
834                shift += 7;
835            }
836            value as usize
837        } else {
838            tu.len().saturating_sub(i)
839        };
840        if matches!(obu_type, 3 | 6) {
841            let Some(&first) = tu.get(i) else {
842                return false;
843            };
844            let show_existing_frame = first & 0x80 != 0;
845            let frame_type = (first >> 5) & 0x03;
846            return !show_existing_frame && frame_type == 0;
847        }
848        pos = i.saturating_add(size);
849    }
850    false
851}
852
853/// The NAL units of an Annex-B stream, each without its start code and never empty.
854pub(crate) fn annexb_nals(au: &[u8]) -> impl Iterator<Item = &[u8]> {
855    let mut starts = Vec::new();
856    let mut i = 0usize;
857    while i + 2 < au.len() {
858        if au[i] == 0 && au[i + 1] == 0 && au[i + 2] == 1 {
859            starts.push(i + 3);
860            i += 3;
861        } else {
862            i += 1;
863        }
864    }
865    let mut nals = Vec::with_capacity(starts.len());
866    for (n, &start) in starts.iter().enumerate() {
867        let mut end = starts.get(n + 1).map_or(au.len(), |&next| next - 3);
868        while end > start && au[end - 1] == 0 {
869            end -= 1;
870        }
871        if end > start {
872            nals.push(&au[start..end]);
873        }
874    }
875    nals.into_iter()
876}
877
878/// Whether a non-IDR H.264 slice header opens an I/SI slice. Reads `first_mb_in_slice`
879/// and `slice_type` (both ue(v)) from the start of the slice RBSP, undoing emulation
880/// prevention bytes; `None` when the header is truncated or malformed.
881fn h264_slice_is_intra(payload: &[u8]) -> Option<bool> {
882    let mut rbsp = [0u8; 12];
883    let mut n = 0usize;
884    let mut zeros = 0usize;
885    for &b in payload {
886        if n == rbsp.len() {
887            break;
888        }
889        if zeros >= 2 && b == 3 {
890            zeros = 0;
891            continue;
892        }
893        zeros = if b == 0 { zeros + 1 } else { 0 };
894        rbsp[n] = b;
895        n += 1;
896    }
897    let mut pos = 0usize;
898    let bit = |p: &mut usize| -> Option<u8> {
899        if *p / 8 >= n {
900            return None;
901        }
902        let b = (rbsp[*p / 8] >> (7 - *p % 8)) & 1;
903        *p += 1;
904        Some(b)
905    };
906    let ue = |p: &mut usize| -> Option<u32> {
907        let mut leading = 0u32;
908        while bit(p)? == 0 {
909            leading += 1;
910            if leading > 24 {
911                return None;
912            }
913        }
914        let mut val = 0u32;
915        for _ in 0..leading {
916            val = (val << 1) | bit(p)? as u32;
917        }
918        Some((1u32 << leading) - 1 + val)
919    };
920    ue(&mut pos)?;
921    let slice_type = ue(&mut pos)?;
922    if slice_type > 9 {
923        return None;
924    }
925    Some(matches!(slice_type % 5, 2 | 4))
926}
927
928#[cfg(test)]
929mod tests {
930    use super::*;
931
932    /// High tier exists from level 4.0 up and is declared there; the ladder never answers below
933    /// 4.1, so every level it names carries one.
934    #[test]
935    fn hevc_tier_follows_the_level() {
936        assert_eq!(h265_tier(93), 0);
937        assert_eq!(h265_tier(120), 1);
938        assert_eq!(h265_tier(h265_level(1280, 720, 30, 0, true)), 1);
939        assert_eq!(h265_tier(h265_level(3840, 2160, 60, 0, true)), 1);
940    }
941
942    /// Names round-trip through the parser, and the aliases land on the same codec.
943    #[test]
944    fn names_round_trip() {
945        for codec in [
946            Codec::Jpeg,
947            Codec::H264,
948            Codec::H265,
949            Codec::Vp8,
950            Codec::Vp9,
951            Codec::Av1,
952        ] {
953            assert_eq!(Codec::parse(codec.name()), Some(codec));
954        }
955        assert_eq!(Codec::parse("HEVC"), Some(Codec::H265));
956        assert_eq!(Codec::parse(" avc "), Some(Codec::H264));
957        assert_eq!(Codec::parse("mjpeg"), Some(Codec::Jpeg));
958        assert_eq!(Codec::parse("h266"), None);
959    }
960
961    /// Every video codec has a distinct wire id that reads back, and the type byte packs
962    /// the frame kind beside it.
963    #[test]
964    fn wire_ids_are_distinct_and_packed() {
965        let mut seen = std::collections::HashSet::new();
966        for codec in Codec::VIDEO {
967            assert!(seen.insert(codec.wire_id()));
968            assert_eq!(Codec::from_wire_id(codec.wire_id()), Some(codec));
969            let mut out = Vec::new();
970            push_video_header(
971                &mut out,
972                codec,
973                FRAME_KEY,
974                0x1234,
975                7,
976                1920,
977                1080,
978                Reference::None,
979            );
980            assert_eq!(out.len(), VIDEO_HEADER_LEN);
981            assert_eq!(out[0], WIRE_VIDEO);
982            assert_eq!(parse_video_type(out[1]), Some((codec, FRAME_KEY)));
983            assert_eq!(
984                &out[2..],
985                &[0x12, 0x34, 0, 7, 0x07, 0x80, 0x04, 0x38, 0x12, 0x34]
986            );
987            out.clear();
988            push_video_header(
989                &mut out,
990                codec,
991                FRAME_DELTA,
992                0x1235,
993                7,
994                1920,
995                1080,
996                Reference::Frame(0x1230),
997            );
998            assert_eq!(
999                &out[10..],
1000                &[0x12, 0x30],
1001                "a predicted frame names its reference"
1002            );
1003        }
1004        assert_eq!(
1005            Codec::from_wire_id(0),
1006            None,
1007            "JPEG never rides the video tag"
1008        );
1009        assert_eq!(Codec::from_wire_id(9), None);
1010        for id in 0..6 {
1011            assert_eq!(Codec::from_id(id).map(|c| c.id()), Some(id));
1012        }
1013        assert_eq!(Codec::from_id(6), None);
1014        assert_eq!(Codec::H264.data_type(), 1);
1015        assert_eq!(Codec::Jpeg.data_type(), 0);
1016        let mut jpeg = Vec::new();
1017        push_jpeg_header(&mut jpeg, 3, 64);
1018        assert_eq!(jpeg, [WIRE_JPEG, 0, 0, 3, 0, 64]);
1019    }
1020
1021    /// Only the striped codecs stripe; every other codec is a whole-frame stream.
1022    #[test]
1023    fn striping_and_chroma_by_codec() {
1024        assert!(Codec::Jpeg.stripes() && Codec::H264.stripes());
1025        for codec in [Codec::H265, Codec::Vp8, Codec::Vp9, Codec::Av1] {
1026            assert!(!codec.stripes(), "{codec:?}");
1027            assert!(codec.is_video());
1028        }
1029        assert!(Codec::H264.fullcolor() && Codec::H265.fullcolor() && Codec::Vp9.fullcolor());
1030        assert!(!Codec::Vp8.fullcolor() && !Codec::Av1.fullcolor());
1031    }
1032
1033    /// The quantizer maps are monotonic, span their domain, clamp the index, and keep AV1
1034    /// off the zero NVENC refuses; H.26x takes the index as it is in software, and hardware
1035    /// H.264, and VA-API's H.265, a quantizer about three finer.
1036    #[test]
1037    fn quantizer_maps_are_monotonic_and_bounded() {
1038        for codec in Codec::VIDEO {
1039            let mut last = 0;
1040            for crf in 0..=51 {
1041                let q = codec.quantizer(crf);
1042                assert!(q >= last, "{codec:?} crf {crf}: {q} < {last}");
1043                for hw in [Hardware::Nvenc, Hardware::Vaapi, Hardware::VaapiIntel] {
1044                    let h = codec.hardware_quantizer(hw, crf);
1045                    assert!(h >= codec.hardware_quantizer(hw, crf.max(1) - 1));
1046                    assert!(h <= codec.quantizer_max());
1047                }
1048                assert!(q <= codec.quantizer_max());
1049                last = q;
1050            }
1051            assert_eq!(codec.quantizer(51), codec.quantizer_max());
1052            assert_eq!(codec.quantizer(99), codec.quantizer(51));
1053            assert_eq!(codec.quantizer(-4), codec.quantizer(0));
1054            assert_eq!(codec.quantizer_bound(0), 0);
1055            assert_eq!(codec.quantizer_bound(30), codec.quantizer(30));
1056        }
1057        assert_eq!(Codec::H264.quantizer(25), 25);
1058        assert_eq!(Codec::H265.quantizer(25), 25);
1059        assert_eq!(Codec::Av1.quantizer(0), 1);
1060        assert_eq!(Codec::H264.hardware_quantizer(Hardware::Nvenc, 25), 22);
1061        assert_eq!(Codec::H264.hardware_quantizer(Hardware::Vaapi, 25), 22);
1062        assert_eq!(Codec::H265.hardware_quantizer(Hardware::Nvenc, 25), 25);
1063        assert_eq!(Codec::H265.hardware_quantizer(Hardware::Vaapi, 25), 22);
1064        assert_eq!(Codec::Av1.hardware_quantizer(Hardware::Vaapi, 25), 80);
1065        assert_eq!(Codec::H265.hardware_quantizer(Hardware::VaapiIntel, 25), 24);
1066        assert_eq!(Codec::H264.hardware_quantizer(Hardware::VaapiIntel, 25), 22);
1067        assert_eq!(Codec::Av1.hardware_quantizer(Hardware::VaapiIntel, 25), 80);
1068        assert_eq!(Codec::Av1.hardware_quantizer(Hardware::Nvenc, 0), 1);
1069        assert_eq!(Codec::Vp9.quantizer(25), 66);
1070        assert_eq!(Codec::Vp9.hardware_quantizer(Hardware::Vaapi, 25), 48);
1071        assert_eq!(Codec::Vp9.hardware_quantizer(Hardware::VaapiIntel, 25), 48);
1072        assert_eq!(
1073            Codec::Vp8.hardware_quantizer(Hardware::Vaapi, 25),
1074            Codec::Vp8.quantizer(25)
1075        );
1076        assert_eq!(Codec::H264.hardware_quantizer_bound(Hardware::Nvenc, 0), 0);
1077        assert_eq!(Codec::H264.hardware_quality_index(Hardware::Nvenc, 22), 25);
1078        assert_eq!(interpolate(&[(0, 0), (10, 100)], 5), 50);
1079        assert_eq!(interpolate(&[(0, 0), (10, 100)], 3), 30);
1080    }
1081
1082    /// The libvpx quantizer level found for an index is the closest table entry, at both
1083    /// ends of both tables.
1084    #[test]
1085    fn vpx_levels_invert_the_tables() {
1086        assert_eq!(vpx_level(Codec::Vp9, 0), 0);
1087        assert_eq!(vpx_level(Codec::Vp9, 255), 63);
1088        assert_eq!(vpx_level(Codec::Vp9, 104), 26);
1089        assert_eq!(vpx_level(Codec::Av1, 249), 62);
1090        assert_eq!(vpx_level(Codec::Vp8, 0), 0);
1091        assert_eq!(vpx_level(Codec::Vp8, 127), 63);
1092        assert_eq!(
1093            vpx_level(Codec::Vp8, 52),
1094            36,
1095            "a tie resolves to the lower level"
1096        );
1097        for level in 0..64u32 {
1098            assert_eq!(
1099                vpx_level(Codec::Vp9, VPX_QINDEX[level as usize] as u32),
1100                level
1101            );
1102            assert_eq!(
1103                vpx_level(Codec::Vp8, VP8_LEVEL_QINDEX[level as usize] as u32),
1104                level
1105            );
1106        }
1107    }
1108
1109    /// The decoded picture buffer follows the level and the picture: the reference frames a
1110    /// session asks for where the level admits them, fewer where it does not.
1111    #[test]
1112    fn the_reference_level_holds_every_reference_up_to_5_2() {
1113        assert_eq!(h264_reference_level(41, 1280, 720), 41);
1114        assert_eq!(h264_reference_level(42, 1920, 1080), 50);
1115        assert_eq!(h264_dpb_frames(50, 1920, 1080), REFERENCE_FRAMES);
1116        assert_eq!(h264_reference_level(51, 2560, 1440), 51);
1117        assert_eq!(h264_reference_level(52, 3840, 2160), 52);
1118        assert_eq!(h264_dpb_frames(52, 3840, 2160), 5);
1119        assert_eq!(h264_reference_level(60, 7680, 4320), 60);
1120    }
1121
1122    #[test]
1123    fn dpb_follows_the_level() {
1124        assert_eq!(h264_dpb_frames(41, 1280, 720), REFERENCE_FRAMES);
1125        assert_eq!(h264_dpb_frames(42, 1920, 1080), 4);
1126        assert_eq!(h264_dpb_frames(50, 2560, 1440), 7);
1127        assert_eq!(h264_dpb_frames(52, 3840, 2160), 5);
1128        assert_eq!(h264_dpb_frames(62, 7680, 4320), 5);
1129        assert_eq!(h265_dpb_frames(123, 1920, 1080), 5);
1130        assert_eq!(h265_dpb_frames(123, 1280, 720), REFERENCE_FRAMES);
1131        assert_eq!(h265_dpb_frames(156, 3840, 2160), 5);
1132        assert_eq!(h265_dpb_frames(180, 3840, 2160), REFERENCE_FRAMES);
1133    }
1134
1135    /// The level ladders start at their floors, step through the standard limits, and
1136    /// saturate rather than fail past the top.
1137    #[test]
1138    fn level_ladders() {
1139        assert_eq!(h264_level(1920, 1080, 60, 0), 42);
1140        assert_eq!(h264_level(1280, 720, 30, 0), 41);
1141        assert_eq!(h264_level(3840, 2160, 60, 0), 52);
1142        assert_eq!(h264_level(7680, 4320, 60, 0), 61);
1143        assert_eq!(h264_level(16384, 16384, 240, 0), 62);
1144
1145        assert_eq!(h265_level(1920, 1080, 60, 0, true), 123);
1146        assert_eq!(h265_level(1920, 1080, 65, 0, true), 150);
1147        assert_eq!(h265_level(3840, 2160, 60, 0, true), 153);
1148        assert_eq!(h265_level(3840, 2160, 120, 0, true), 156);
1149        assert_eq!(h265_level(7680, 4320, 30, 0, true), 180);
1150        assert_eq!(h265_level(7680, 4320, 60, 0, true), 183);
1151        assert_eq!(h265_level(7680, 4320, 120, 0, true), 186);
1152        assert_eq!(h265_level(16384, 16384, 240, 0, true), 186);
1153
1154        assert_eq!(av1_level(1280, 720, 30, 0), 8);
1155        assert_eq!(av1_level(1920, 1080, 60, 0), 9);
1156        assert_eq!(av1_level(3840, 2160, 60, 0), 13);
1157        assert_eq!(av1_level(3840, 2160, 120, 0), 14);
1158        assert_eq!(
1159            av1_level(4096, 2304, 30, 0),
1160            16,
1161            "larger than 5.x's MaxPicSize"
1162        );
1163        assert_eq!(av1_level(7680, 4320, 60, 0), 17);
1164        assert_eq!(av1_level(7680, 4320, 120, 0), 18);
1165        // Both axes fit 5.x, the picture does not: MaxPicSize is not the product of the axes.
1166        assert_eq!(av1_level(8192, 4352, 60, 0), 17);
1167        assert_eq!(av1_level(8192, 4352, 240, 0), 19);
1168    }
1169
1170    /// A declared bitrate raises a level the picture alone would not: past 4.2's 62.5 Mbit/s
1171    /// (High profile) for H.264 at 1080p60, past the tier's ceiling for HEVC, past the Main
1172    /// tier's for AV1; a rate within the ceiling leaves the level where the picture put it.
1173    #[test]
1174    fn level_ladders_carry_the_bitrate() {
1175        assert_eq!(h264_level(1920, 1080, 60, 62_500_000), 42);
1176        assert_eq!(h264_level(1920, 1080, 60, 100_000_000), 50);
1177        assert_eq!(h264_level(1920, 1080, 60, 300_000_000), 51);
1178        assert_eq!(h264_level(1920, 1080, 60, 1_000_000_000), 62);
1179
1180        assert_eq!(h265_level(1920, 1080, 60, 50_000_000, true), 123);
1181        assert_eq!(h265_level(1920, 1080, 60, 60_000_000, true), 150);
1182        assert_eq!(h265_level(1920, 1080, 60, 60_000_000, false), 156);
1183        assert_eq!(h265_level(1920, 1080, 60, 20_000_000, false), 123);
1184        assert_eq!(h265_level(3840, 2160, 60, 200_000_000, true), 156);
1185
1186        assert_eq!(av1_level(1920, 1080, 60, 20_000_000), 9);
1187        assert_eq!(av1_level(1920, 1080, 60, 25_000_000), 12);
1188        assert_eq!(av1_level(3840, 2160, 60, 45_000_000), 14);
1189        assert_eq!(av1_level(1280, 720, 30, 100_000_000), 17);
1190        assert_eq!(av1_level(1280, 720, 30, 1_000_000_000), 19);
1191    }
1192
1193    /// H.264 labels follow the NAL and slice types: an IDR is a key, an I slice in a
1194    /// non-IDR is intra, a P slice is delta, and a truncated slice is delta.
1195    #[test]
1196    fn h264_labels_from_the_bitstream() {
1197        let idr = [
1198            0, 0, 0, 1, 0x67, 0x64, 0, 0x1f, 0, 0, 1, 0x65, 0x88, 0x84, 0,
1199        ];
1200        assert_eq!(h264_frame_type(&idr), FRAME_KEY);
1201        // slice_type 7 (I, all slices) encoded ue: first_mb=0 -> '1', slice_type 7 -> '0001000'
1202        let intra = [0, 0, 1, 0x41, 0b1000_1000, 0b0000_0000];
1203        assert_eq!(h264_frame_type(&intra), FRAME_INTRA);
1204        // slice_type 0 (P): first_mb '1', slice_type '1'
1205        let p = [0, 0, 1, 0x41, 0b1100_0000];
1206        assert_eq!(h264_frame_type(&p), FRAME_DELTA);
1207        assert_eq!(h264_frame_type(&[0, 0, 1, 0x41]), FRAME_DELTA);
1208        assert_eq!(h264_frame_type(&[]), FRAME_DELTA);
1209    }
1210
1211    /// H.265 labels follow the NAL type: an IDR (19) or CRA (21) is a key, a TRAIL_R (1)
1212    /// picture behind parameter sets is a delta, and a stream of parameter sets alone is
1213    /// a delta.
1214    #[test]
1215    fn h265_labels_from_the_bitstream() {
1216        let idr = [0, 0, 0, 1, 0x40, 0x01, 0x0c, 0, 0, 1, 0x26, 0x01, 0xaf];
1217        assert_eq!(h265_frame_type(&idr), FRAME_KEY);
1218        let cra = [0, 0, 1, 0x2a, 0x01, 0xaf];
1219        assert_eq!(h265_frame_type(&cra), FRAME_KEY);
1220        let trail = [0, 0, 1, 0x02, 0x01, 0xd0];
1221        assert_eq!(h265_frame_type(&trail), FRAME_DELTA);
1222        let headers_only = [0, 0, 1, 0x40, 0x01, 0, 0, 1, 0x42, 0x01];
1223        assert_eq!(h265_frame_type(&headers_only), FRAME_DELTA);
1224    }
1225
1226    /// VP8 keys are read from the frame tag, VP9 keys from the uncompressed header in every
1227    /// profile, with a shown existing frame never a key.
1228    #[test]
1229    fn vpx_labels_from_the_bitstream() {
1230        assert!(vp8_is_key(&[0x50, 0x42, 0x00]));
1231        assert!(!vp8_is_key(&[0x51, 0x42, 0x00]));
1232        assert!(!vp8_is_key(&[]));
1233        assert!(vp9_is_key(&[0x82, 0x49, 0x83]));
1234        assert!(!vp9_is_key(&[0x86, 0x00]));
1235        assert!(!vp9_is_key(&[0x8a, 0x00]));
1236        assert!(vp9_is_key(&[0xb0, 0x00]), "profile 3");
1237        assert!(!vp9_is_key(&[0xb2, 0x00]), "profile 3 inter");
1238        assert!(!vp9_is_key(&[0x42, 0x00]), "bad frame marker");
1239    }
1240
1241    /// AV1 labels come from the first frame's header past the temporal delimiter and
1242    /// sequence header: frame_type 0 is a key, 1 (inter) or a shown existing frame a
1243    /// delta, and a unit that ends before a frame, or a malformed size, is a delta.
1244    #[test]
1245    fn av1_labels_from_the_temporal_unit() {
1246        // TD (type 2, has_size, size 0), SEQ (type 1, size 1), FRAME (type 6, size 2) key.
1247        let key = [0x12, 0x00, 0x0a, 0x01, 0x00, 0x32, 0x02, 0x10, 0x00];
1248        assert!(av1_is_key(&key));
1249        let inter = [0x12, 0x00, 0x32, 0x02, 0x30, 0x00];
1250        assert!(!av1_is_key(&inter));
1251        let existing = [0x32, 0x01, 0x80];
1252        assert!(!av1_is_key(&existing));
1253        let frame_header_only = [0x1a, 0x01, 0x00];
1254        assert!(av1_is_key(&frame_header_only));
1255        assert!(!av1_is_key(&[0x12, 0x00]));
1256        assert!(!av1_is_key(&[
1257            0x32, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1258        ]));
1259        assert!(!av1_is_key(&[]));
1260    }
1261}