1#[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum Hardware {
30 Nvenc,
31 Vaapi,
32 VaapiIntel,
33}
34
35use super::reference::{REFERENCE_FRAMES, Reference};
36
37pub const WIRE_JPEG: u8 = 0x03;
39pub const WIRE_VIDEO: u8 = 0x04;
44pub const FRAME_KEY: u8 = 0x01;
47pub const FRAME_INTRA: u8 = 0x02;
48pub const FRAME_DELTA: u8 = 0x00;
49pub const FRAME_ANCHOR: u8 = 0x08;
53pub const JPEG_HEADER_LEN: usize = 6;
55pub const VIDEO_HEADER_LEN: usize = 12;
56
57impl Codec {
58 pub const VIDEO: [Codec; 5] = [Codec::H264, Codec::Vp8, Codec::Vp9, Codec::Av1, Codec::H265];
60
61 pub const fn id(self) -> u32 {
63 self as u32
64 }
65
66 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 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 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 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 pub fn is_video(self) -> bool {
119 self != Codec::Jpeg
120 }
121
122 pub fn stripes(self) -> bool {
126 matches!(self, Codec::Jpeg | Codec::H264)
127 }
128
129 pub fn wire_id(self) -> u8 {
131 self.id() as u8
132 }
133
134 pub fn from_wire_id(id: u8) -> Option<Codec> {
136 Codec::from_id(id as u32).filter(|c| c.is_video())
137 }
138
139 pub fn data_type(self) -> i32 {
141 self.id() as i32
142 }
143
144 pub fn is_inter_coded(self) -> bool {
147 self.is_video()
148 }
149
150 pub fn fullcolor(self) -> bool {
153 matches!(self, Codec::H264 | Codec::H265 | Codec::Vp9)
154 }
155
156 pub fn high_bit_depth(self) -> bool {
160 matches!(self, Codec::H265 | Codec::Vp9 | Codec::Av1)
161 }
162
163 pub fn keyframes_self_contained(self) -> bool {
167 self.is_video()
168 }
169
170 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 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 pub fn quantizer_bound(self, qp: i32) -> u32 {
211 if qp <= 0 { 0 } else { self.quantizer(qp) }
212 }
213
214 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 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 pub fn quality_index(self, q: u32) -> u32 {
258 nearest_index(q, |crf| self.quantizer(crf))
259 }
260
261 pub fn hardware_quality_index(self, hw: Hardware, q: u32) -> u32 {
263 nearest_index(q, |crf| self.hardware_quantizer(hw, crf))
264 }
265}
266
267fn 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
274const 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];
287const 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];
302const 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];
317const 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];
330const 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];
344const 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];
358const 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];
373const 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
389fn 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
410pub 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];
418pub 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
425pub 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#[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
471pub 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
479pub 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
484pub fn frame_type_from_key(key: bool) -> u8 {
486 if key { FRAME_KEY } else { FRAME_DELTA }
487}
488
489pub 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
523pub 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#[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
553pub 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
582pub 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
605pub fn h265_tier(level: u32) -> u32 {
610 if level >= 120 { 1 } else { 0 }
611}
612
613pub 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
652pub 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
673pub 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
686pub fn vp8_is_key(frame: &[u8]) -> bool {
688 frame.first().is_some_and(|b| b & 1 == 0)
689}
690
691pub 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
709pub 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
810pub 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
853pub(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
878fn 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 assert_eq!(av1_level(8192, 4352, 60, 0), 17);
1167 assert_eq!(av1_level(8192, 4352, 240, 0), 19);
1168 }
1169
1170 #[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 #[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 let intra = [0, 0, 1, 0x41, 0b1000_1000, 0b0000_0000];
1203 assert_eq!(h264_frame_type(&intra), FRAME_INTRA);
1204 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 #[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 #[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 #[test]
1245 fn av1_labels_from_the_temporal_unit() {
1246 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}