1use std::collections::VecDeque;
16
17mod error;
18pub use error::{Error, Result};
19
20mod adapt;
21mod bits;
22mod block;
23mod decode;
24#[allow(dead_code, unused_imports)] mod encode;
26mod geom_tables;
27mod inter;
28mod loopfilter;
29mod mv;
30mod predict;
31mod prob;
32mod prob_tables;
33pub mod prof;
36mod quant;
37mod scan_tables;
38mod token;
39mod transform;
40pub use bits::{BitReader, BoolDecoder};
41pub use encode::prof as encode_prof;
45pub use encode::{ref_hist_take, set_modemap_std, set_snap_pool};
46pub use encode::{EncodedPacket, Vp9Encoder, Vp9EncoderConfig};
47
48pub(crate) const FRAME_MARKER: u32 = 2;
49pub(crate) const SYNC_CODE: u32 = 0x49_8342;
50pub(crate) const CS_RGB: u32 = 7;
51
52#[derive(Debug, Clone, Default, PartialEq, Eq)]
54pub struct FrameHeader {
55 pub profile: u32,
56 pub show_existing_frame: bool,
57 pub frame_to_show: u32,
58 pub key_frame: bool,
59 pub show_frame: bool,
60 pub error_resilient: bool,
61 pub intra_only: bool,
62 pub bit_depth: u32,
63 pub color_space: u32,
64 pub subsampling_x: u32,
65 pub subsampling_y: u32,
66 pub width: u32,
67 pub height: u32,
68 pub lossless: bool,
69 pub base_q_idx: u32,
70 pub delta_q_y_dc: i32,
71 pub delta_q_uv_dc: i32,
72 pub delta_q_uv_ac: i32,
73 pub loop_filter_level: u32,
74 pub loop_filter_sharpness: u32,
75 pub lf_delta_enabled: bool,
76 pub lf_ref_deltas: [i32; 4],
78 pub lf_mode_deltas: [i32; 2],
80 pub lf_ref_delta_updated: [bool; 4],
83 pub lf_mode_delta_updated: [bool; 2],
84 pub tile_cols_log2: u32,
85 pub tile_rows_log2: u32,
86 pub header_size: u32,
88 pub uncompressed_bytes: usize,
90 pub sized: bool,
92 pub reset_frame_context: u32,
95 pub refresh_frame_flags: u32,
97 pub ref_frame_idx: [usize; 3],
99 pub ref_sign_bias: [bool; 3],
101 pub allow_high_precision_mv: bool,
103 pub interp_filter: u32,
105 pub refresh_frame_context: bool,
106 pub frame_parallel_decoding_mode: bool,
107 pub frame_context_idx: u32,
108 pub seg_enabled: bool,
110 pub seg_update_map: bool,
111 pub seg_update_data: bool,
112 pub seg_temporal_update: bool,
113 pub seg_abs_delta: bool,
114 pub seg_tree_probs: [u8; 7],
115 pub seg_pred_probs: [u8; 3],
116 pub seg_feature_enabled: [[bool; 4]; 8],
117 pub seg_feature_data: [[i32; 4]; 8],
118}
119
120pub fn parse_uncompressed_header(
125 r: &mut BitReader,
126 ref_dims: &[(u32, u32); 8],
127) -> Result<FrameHeader> {
128 let mut h = FrameHeader::default();
129 if r.f(2)? != FRAME_MARKER {
130 return Err(Error::invalid("vp9: bad frame marker"));
131 }
132 let profile_low = r.f1()?;
133 let profile_high = r.f1()?;
134 h.profile = (profile_high << 1) | profile_low;
135 if h.profile == 3 {
136 r.f1()?; }
138
139 h.show_existing_frame = r.f1()? == 1;
140 if h.show_existing_frame {
141 h.frame_to_show = r.f(3)?;
142 return Ok(h);
143 }
144
145 h.key_frame = r.f1()? == 0;
146 h.show_frame = r.f1()? == 1;
147 h.error_resilient = r.f1()? == 1;
148
149 if h.key_frame {
150 frame_sync_code(r)?;
151 color_config(r, &mut h)?;
152 frame_size(r, &mut h)?;
153 render_size(r)?;
154 h.sized = true;
155 } else {
156 h.intra_only = if !h.show_frame { r.f1()? == 1 } else { false };
157 h.reset_frame_context = if !h.error_resilient { r.f(2)? } else { 0 };
158 if h.intra_only {
159 frame_sync_code(r)?;
160 if h.profile > 0 {
161 color_config(r, &mut h)?;
162 } else {
163 h.bit_depth = 8;
164 h.color_space = 1; h.subsampling_x = 1;
166 h.subsampling_y = 1;
167 }
168 h.refresh_frame_flags = r.f(8)?;
169 frame_size(r, &mut h)?;
170 render_size(r)?;
171 h.sized = true;
172 } else {
173 h.refresh_frame_flags = r.f(8)?;
176 for i in 0..3 {
177 h.ref_frame_idx[i] = r.f(3)? as usize;
178 h.ref_sign_bias[i] = r.f1()? == 1;
179 }
180 let mut found = false;
182 for i in 0..3 {
183 if r.f1()? == 1 {
184 let (w, hh) = ref_dims[h.ref_frame_idx[i]];
185 h.width = w;
186 h.height = hh;
187 found = true;
188 break;
189 }
190 }
191 if !found {
192 frame_size(r, &mut h)?;
193 }
194 render_size(r)?;
195 h.allow_high_precision_mv = r.f1()? == 1;
196 h.interp_filter = if r.f1()? == 1 {
198 4 } else {
200 const L2F: [u32; 4] = [1, 0, 2, 3];
202 L2F[r.f(2)? as usize]
203 };
204 h.sized = true;
207 }
208 }
209
210 if !h.sized {
211 return Ok(h);
212 }
213
214 if !h.error_resilient {
216 h.refresh_frame_context = r.f1()? == 1;
217 h.frame_parallel_decoding_mode = r.f1()? == 1;
218 } else {
219 h.frame_parallel_decoding_mode = true;
220 }
221 h.frame_context_idx = r.f(2)?;
222 loop_filter_params(r, &mut h)?;
223 quantization_params(r, &mut h)?;
224 segmentation_params(r, &mut h)?;
225 tile_info(r, &mut h)?;
226 h.header_size = r.f(16)?;
227 h.uncompressed_bytes = r.bit_pos().div_ceil(8);
228 Ok(h)
229}
230
231fn loop_filter_params(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
232 h.loop_filter_level = r.f(6)?;
233 h.loop_filter_sharpness = r.f(3)?;
234 h.lf_ref_deltas = [1, 0, -1, -1];
240 h.lf_mode_deltas = [0, 0];
241 h.lf_ref_delta_updated = [false; 4];
242 h.lf_mode_delta_updated = [false; 2];
243 h.lf_delta_enabled = r.f1()? == 1;
244 if h.lf_delta_enabled {
245 if r.f1()? == 1 {
247 for i in 0..4 {
248 if r.f1()? == 1 {
249 h.lf_ref_deltas[i] = r.s(6)?;
250 h.lf_ref_delta_updated[i] = true;
251 }
252 }
253 for i in 0..2 {
254 if r.f1()? == 1 {
255 h.lf_mode_deltas[i] = r.s(6)?;
256 h.lf_mode_delta_updated[i] = true;
257 }
258 }
259 }
260 }
261 Ok(())
262}
263
264fn quantization_params(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
265 h.base_q_idx = r.f(8)?;
266 let dc_y = read_delta_q(r)?;
267 let dc_uv = read_delta_q(r)?;
268 let ac_uv = read_delta_q(r)?;
269 h.delta_q_y_dc = dc_y;
270 h.delta_q_uv_dc = dc_uv;
271 h.delta_q_uv_ac = ac_uv;
272 h.lossless = h.base_q_idx == 0 && dc_y == 0 && dc_uv == 0 && ac_uv == 0;
273 Ok(())
274}
275
276fn read_delta_q(r: &mut BitReader) -> Result<i32> {
277 if r.f1()? == 1 {
278 r.s(4)
279 } else {
280 Ok(0)
281 }
282}
283
284fn segmentation_params(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
285 h.seg_tree_probs = [255; 7];
286 h.seg_pred_probs = [255; 3];
287 h.seg_enabled = r.f1()? == 1;
288 if !h.seg_enabled {
289 return Ok(());
290 }
291 h.seg_update_map = r.f1()? == 1;
292 if h.seg_update_map {
293 for i in 0..7 {
294 h.seg_tree_probs[i] = if r.f1()? == 1 { r.f(8)? as u8 } else { 255 };
295 }
296 h.seg_temporal_update = r.f1()? == 1;
297 if h.seg_temporal_update {
298 for i in 0..3 {
299 h.seg_pred_probs[i] = if r.f1()? == 1 { r.f(8)? as u8 } else { 255 };
300 }
301 }
302 }
303 h.seg_update_data = r.f1()? == 1;
305 if h.seg_update_data {
306 h.seg_abs_delta = r.f1()? == 1;
307 const BITS: [u32; 4] = [8, 6, 2, 0];
308 const SIGNED: [bool; 4] = [true, true, false, false];
309 h.seg_feature_enabled = [[false; 4]; 8];
310 h.seg_feature_data = [[0; 4]; 8];
311 for i in 0..8 {
312 for j in 0..4 {
313 let enabled = r.f1()? == 1;
314 h.seg_feature_enabled[i][j] = enabled;
315 if enabled {
316 let mut data = if BITS[j] > 0 { r.f(BITS[j])? as i32 } else { 0 };
317 if SIGNED[j] && r.f1()? == 1 {
318 data = -data;
319 }
320 h.seg_feature_data[i][j] = data;
321 }
322 }
323 }
324 }
325 Ok(())
326}
327
328fn tile_info(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
329 let mi_cols = (h.width + 7) >> 3;
330 let sb64_cols = (mi_cols + 7) >> 3;
331 let mut min_log2 = 0u32;
333 while (64u32 << min_log2) < sb64_cols {
334 min_log2 += 1;
335 }
336 let mut max_log2 = 1u32;
338 while (sb64_cols >> max_log2) >= 4 {
339 max_log2 += 1;
340 }
341 max_log2 -= 1;
342
343 h.tile_cols_log2 = min_log2;
344 while h.tile_cols_log2 < max_log2 {
345 if r.f1()? == 1 {
346 h.tile_cols_log2 += 1;
347 } else {
348 break;
349 }
350 }
351 h.tile_rows_log2 = r.f1()?;
352 if h.tile_rows_log2 == 1 {
353 h.tile_rows_log2 += r.f1()?;
354 }
355 Ok(())
356}
357
358fn frame_sync_code(r: &mut BitReader) -> Result<()> {
359 if r.f(24)? != SYNC_CODE {
360 return Err(Error::invalid("vp9: bad frame sync code"));
361 }
362 Ok(())
363}
364
365fn color_config(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
366 h.bit_depth = if h.profile >= 2 {
367 if r.f1()? == 1 {
368 12
369 } else {
370 10
371 }
372 } else {
373 8
374 };
375 h.color_space = r.f(3)?;
376 if h.color_space != CS_RGB {
377 r.f1()?; if h.profile == 1 || h.profile == 3 {
379 h.subsampling_x = r.f1()?;
380 h.subsampling_y = r.f1()?;
381 r.f1()?; } else {
383 h.subsampling_x = 1;
384 h.subsampling_y = 1;
385 }
386 } else {
387 if h.profile == 1 || h.profile == 3 {
389 r.f1()?; }
391 }
392 Ok(())
393}
394
395fn frame_size(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
396 h.width = r.f(16)? + 1;
397 h.height = r.f(16)? + 1;
398 Ok(())
399}
400
401fn render_size(r: &mut BitReader) -> Result<()> {
402 if r.f1()? == 1 {
403 r.f(16)?; r.f(16)?; }
406 Ok(())
407}
408
409pub fn consume_compressed_header(data: &[u8], lossless: bool) -> Result<usize> {
415 let mut b = BoolDecoder::new(data)?;
416 let tx_mode = read_tx_mode(&mut b, lossless);
417 if tx_mode == TX_MODE_SELECT {
418 tx_mode_probs(&mut b);
419 }
420 read_coef_probs(&mut b, tx_mode);
421 read_skip_prob(&mut b);
422 Ok(b.bit_pos())
423}
424
425const TX_MODE_SELECT: u32 = 4;
426
427fn read_tx_mode(b: &mut BoolDecoder, lossless: bool) -> u32 {
428 if lossless {
429 0
430 } else {
431 let mut t = b.literal(2);
432 if t == 3 {
433 t += b.literal(1); }
435 t
436 }
437}
438
439fn tx_mode_probs(b: &mut BoolDecoder) {
440 for _ in 0..2 * 1 {
441 diff_update(b); }
443 for _ in 0..2 * 2 {
444 diff_update(b); }
446 for _ in 0..2 * 3 {
447 diff_update(b); }
449}
450
451fn read_coef_probs(b: &mut BoolDecoder, tx_mode: u32) {
456 let max_tx = [0u32, 1, 2, 3, 3][tx_mode as usize];
457 for _tx in 0..=max_tx {
458 if b.read_bool(128) == 1 {
459 for _plane in 0..2 {
460 for _ref in 0..2 {
461 for band in 0..6 {
462 let ctxs = if band == 0 { 3 } else { 6 };
463 for _ctx in 0..ctxs {
464 for _node in 0..3 {
465 diff_update(b);
466 }
467 }
468 }
469 }
470 }
471 }
472 }
473}
474
475fn read_skip_prob(b: &mut BoolDecoder) {
476 for _ in 0..3 {
477 diff_update(b);
478 }
479}
480
481fn diff_update(b: &mut BoolDecoder) {
483 if b.read_bool(252) == 1 {
484 decode_term_subexp(b);
485 }
486}
487
488fn decode_term_subexp(b: &mut BoolDecoder) -> u32 {
489 if b.read_bool(128) == 0 {
490 return b.literal(4);
491 }
492 if b.read_bool(128) == 0 {
493 return b.literal(4) + 16;
494 }
495 if b.read_bool(128) == 0 {
496 return b.literal(5) + 32;
497 }
498 let v = b.literal(7);
499 if v < 65 {
500 return v + 64;
501 }
502 (v << 1) - 1 + b.read_bool(128)
503}
504
505#[derive(Default)]
511pub struct Vp9Decoder {
512 width: u32,
513 height: u32,
514 queue: VecDeque<(Vec<u8>, Option<i64>, bool)>,
515 eof: bool,
516 ref_frames: [Option<std::sync::Arc<decode::RefFrame>>; 8],
518 plane_pool: Vec<[Vec<u16>; 3]>,
527 frame_contexts: [decode::FrameContext; 4],
529 prev_mvs: Option<std::sync::Arc<Vec<mv::MvRef>>>,
531 prev_seg_map: Option<std::sync::Arc<Vec<u8>>>,
533 seg_feature_enabled: [[bool; 4]; 8],
536 seg_feature_data: [[i32; 4]; 8],
537 seg_abs_delta: bool,
538 lf_ref_deltas: [i32; 4],
541 lf_mode_deltas: [i32; 2],
542 last_frame_key: bool,
544 last_show_frame: bool,
545 last_intra_only: bool,
546 last_width: u32,
547 last_height: u32,
548 ss_x: u32,
550 ss_y: u32,
551 bit_depth: u32,
553}
554
555impl Vp9Decoder {
556 pub fn new() -> Vp9Decoder {
559 Vp9Decoder::default()
560 }
561
562 fn ref_dims(&self) -> [(u32, u32); 8] {
564 let mut d = [(0u32, 0u32); 8];
565 for (i, slot) in self.ref_frames.iter().enumerate() {
566 if let Some(rf) = slot {
567 d[i] = (rf.w[0] as u32, rf.h[0] as u32);
568 }
569 }
570 d
571 }
572}
573
574impl Drop for Vp9Decoder {
579 fn drop(&mut self) {
580 prof::dump();
581 }
582}
583
584impl Vp9Decoder {
585 pub fn push(&mut self, data: &[u8], pts: Option<i64>) -> Result<()> {
594 let frames = split_superframe(data);
595 let last_disp = frames.iter().rposition(|f| peek_displayable(f));
596 for (i, f) in frames.into_iter().enumerate() {
597 self.queue.push_back((f, pts, last_disp == Some(i)));
598 }
599 Ok(())
600 }
601
602 pub fn next_frame(&mut self) -> Result<DecodedFrame> {
606 let Some((data, pts, display)) = self.queue.pop_front() else {
607 return if self.eof {
608 Err(Error::Eof)
609 } else {
610 Err(Error::Again)
611 };
612 };
613 let _frame = prof::Scope::new(prof::S::Other);
617 let mut r = BitReader::new(&data);
618 let mut h = prof::dprof!(
619 prof::S::Header,
620 parse_uncompressed_header(&mut r, &self.ref_dims())
621 )?;
622
623 if h.show_existing_frame {
625 let rf = self.ref_frames[h.frame_to_show as usize]
626 .clone()
627 .ok_or_else(|| Error::invalid("vp9: show_existing_frame on empty slot"))?;
628 if !display {
629 return Err(Error::Again);
630 }
631 return Ok(crop_frame(&rf, self.ss_x, self.ss_y, pts));
632 }
633
634 if h.key_frame || h.intra_only {
636 self.ss_x = h.subsampling_x;
637 self.ss_y = h.subsampling_y;
638 self.bit_depth = h.bit_depth;
639 } else {
640 h.subsampling_x = self.ss_x;
641 h.subsampling_y = self.ss_y;
642 h.bit_depth = self.bit_depth;
643 }
644
645 let mut active: [Option<std::sync::Arc<decode::RefFrame>>; 3] = [None, None, None];
647 if !(h.key_frame || h.intra_only) {
648 for i in 0..3 {
649 active[i] = self.ref_frames[h.ref_frame_idx[i]].clone();
650 }
651 }
652
653 let reset_all = h.key_frame || h.intra_only || h.error_resilient;
655 if reset_all {
656 for c in &mut self.frame_contexts {
657 *c = decode::FrameContext::defaults();
658 }
659 }
660
661 if h.key_frame || h.intra_only {
664 self.seg_feature_enabled = [[false; 4]; 8];
665 self.seg_feature_data = [[0; 4]; 8];
666 self.seg_abs_delta = false;
667 }
668 if h.seg_update_data {
669 self.seg_feature_enabled = h.seg_feature_enabled;
670 self.seg_feature_data = h.seg_feature_data;
671 self.seg_abs_delta = h.seg_abs_delta;
672 }
673 h.seg_feature_enabled = self.seg_feature_enabled;
674 h.seg_feature_data = self.seg_feature_data;
675 h.seg_abs_delta = self.seg_abs_delta;
676
677 if reset_all {
681 self.lf_ref_deltas = [1, 0, -1, -1];
682 self.lf_mode_deltas = [0, 0];
683 }
684 for i in 0..4 {
685 if h.lf_ref_delta_updated[i] {
686 self.lf_ref_deltas[i] = h.lf_ref_deltas[i];
687 }
688 }
689 for i in 0..2 {
690 if h.lf_mode_delta_updated[i] {
691 self.lf_mode_deltas[i] = h.lf_mode_deltas[i];
692 }
693 }
694 h.lf_ref_deltas = self.lf_ref_deltas;
695 h.lf_mode_deltas = self.lf_mode_deltas;
696 let ctx_idx = if reset_all {
698 0
699 } else {
700 h.frame_context_idx as usize
701 };
702 let pre_fc = self.frame_contexts[ctx_idx].clone();
703
704 let use_prev_mvs = !h.error_resilient
707 && h.width == self.last_width
708 && h.height == self.last_height
709 && !self.last_intra_only
710 && self.last_show_frame
711 && !self.last_frame_key;
712 let prev_mvs = self.prev_mvs.clone();
713 let prev_seg_map = if h.key_frame
718 || h.intra_only
719 || h.error_resilient
720 || h.width != self.last_width
721 || h.height != self.last_height
722 {
723 None
724 } else {
725 self.prev_seg_map.clone()
726 };
727
728 let recycled = if pool_disabled() {
735 None
736 } else {
737 self.plane_pool.pop()
738 };
739 let decode_res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
740 decode::decode_frame(
741 &h,
742 &data,
743 &active,
744 &pre_fc,
745 self.last_frame_key,
746 prev_mvs,
747 use_prev_mvs,
748 prev_seg_map,
749 recycled,
750 )
751 }));
752 drop(active);
758 let (decoded, out_fc, mvs, seg_map) = match decode_res {
759 Ok(inner) => inner?,
760 Err(_) => return Err(Error::invalid("vp9: decode aborted on malformed input")),
761 };
762 if h.refresh_frame_context {
763 self.frame_contexts[ctx_idx] = out_fc;
764 }
765 self.prev_mvs = Some(mvs);
766 if h.seg_enabled {
777 self.prev_seg_map = Some(seg_map);
778 } else if h.key_frame || h.intra_only || h.error_resilient {
779 self.prev_seg_map = None;
780 }
781 self.last_frame_key = h.key_frame;
782 self.last_show_frame = h.show_frame;
783 self.last_intra_only = h.intra_only;
784 self.last_width = h.width;
785 self.last_height = h.height;
786 let _p = prof::Scope::new(prof::S::RefUpdate);
787 let rf = std::sync::Arc::new(decoded);
788
789 let refresh = if h.key_frame {
791 0xFF
792 } else {
793 h.refresh_frame_flags
794 };
795 for i in 0..8 {
796 if refresh & (1 << i) != 0 {
797 if let Some(old) = self.ref_frames[i].replace(rf.clone()) {
801 if !pool_disabled() && self.plane_pool.len() < 2 {
802 if let Some(inner) = std::sync::Arc::into_inner(old) {
803 self.plane_pool.push(inner.planes);
804 }
805 }
806 }
807 }
808 }
809 self.width = h.width;
810 self.height = h.height;
811
812 if !h.show_frame || !display {
813 return Err(Error::Again);
818 }
819 Ok(crop_frame(&rf, self.ss_x, self.ss_y, pts))
820 }
821
822 pub fn flush(&mut self) {
825 self.eof = true;
826 }
827}
828
829fn peek_displayable(data: &[u8]) -> bool {
835 let mut r = BitReader::new(data);
836 (|| -> Result<bool> {
837 if r.f(2)? != FRAME_MARKER {
838 return Ok(true);
839 }
840 let pl = r.f1()?;
841 let ph = r.f1()?;
842 if (ph << 1) | pl == 3 {
843 r.f1()?; }
845 if r.f1()? == 1 {
846 return Ok(true); }
848 let _key_frame = r.f1()?;
849 Ok(r.f1()? == 1) })()
851 .unwrap_or(true)
852}
853
854fn split_superframe(data: &[u8]) -> Vec<Vec<u8>> {
857 let n = data.len();
858 if n >= 2 {
859 let marker = data[n - 1];
860 if marker & 0xe0 == 0xc0 {
861 let frames = (marker & 0x7) as usize + 1;
862 let mag = ((marker >> 3) & 0x3) as usize + 1;
863 let index_sz = 2 + mag * frames;
864 if n >= index_sz && data[n - index_sz] == marker {
865 let mut out = Vec::with_capacity(frames);
866 let mut off = 0usize;
867 let mut x = n - index_sz + 1;
868 for _ in 0..frames {
869 let mut sz = 0usize;
870 for j in 0..mag {
871 sz |= (data[x] as usize) << (j * 8);
872 x += 1;
873 }
874 if off + sz <= n {
875 out.push(data[off..off + sz].to_vec());
876 }
877 off += sz;
878 }
879 if !out.is_empty() {
880 return out;
881 }
882 }
883 }
884 }
885 vec![data.to_vec()]
886}
887
888fn pool_disabled() -> bool {
890 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
891 *E.get_or_init(|| std::env::var("VP9_NO_PLANE_POOL").is_ok())
892}
893
894#[derive(Debug, Clone)]
901pub struct DecodedFrame {
902 pub width: u32,
903 pub height: u32,
904 pub bit_depth: u32,
906 pub subsampling_x: u32,
907 pub subsampling_y: u32,
908 pub planes: Vec<Vec<u8>>,
910 pub strides: Vec<usize>,
912 pub pts: Option<i64>,
914}
915
916fn crop_frame(rf: &decode::RefFrame, ss_x: u32, ss_y: u32, pts: Option<i64>) -> DecodedFrame {
920 let hbd = rf.bit_depth > 8;
921 let mut planes: Vec<Vec<u8>> = Vec::with_capacity(3);
922 let mut strides: Vec<usize> = Vec::with_capacity(3);
923 for p in 0..3 {
924 let (stride, w, hh) = (rf.stride[p], rf.w[p], rf.h[p]);
925 let mut v = Vec::with_capacity(w * hh * if hbd { 2 } else { 1 });
926 for y in 0..hh {
927 let row = &rf.planes[p][y * stride..y * stride + w];
928 if hbd {
929 for &px in row {
930 v.extend_from_slice(&px.to_le_bytes());
931 }
932 } else {
933 v.extend(row.iter().map(|&px| px as u8));
934 }
935 }
936 planes.push(v);
937 strides.push(if hbd { w * 2 } else { w });
938 }
939 DecodedFrame {
940 width: rf.w[0] as u32,
941 height: rf.h[0] as u32,
942 bit_depth: rf.bit_depth,
943 subsampling_x: ss_x,
944 subsampling_y: ss_y,
945 planes,
946 strides,
947 pts,
948 }
949}
950
951#[cfg(test)]
952mod tests {
953 use super::*;
954
955 #[test]
956 fn parses_real_keyframe_header() {
957 let frame = include_bytes!("testdata/keyframe.vp9");
958 let mut r = BitReader::new(frame);
959 let h = parse_uncompressed_header(&mut r, &[(0, 0); 8]).unwrap();
960 assert_eq!(h.profile, 1);
961 assert!(h.key_frame);
962 assert!(h.show_frame);
963 assert_eq!(h.color_space, CS_RGB);
964 assert_eq!(h.bit_depth, 8);
965 assert_eq!((h.width, h.height), (96, 64));
966 }
967
968 #[test]
972 #[ignore]
973 fn dump_sequence() {
974 let dir = std::env::var("VP9_SEQ_DIR").unwrap();
975 let prefix = std::env::var("VP9_SEQ_PREFIX").unwrap_or_else(|_| "seqfp_f".into());
976 let n: usize = std::env::var("VP9_SEQ_N")
977 .unwrap_or_else(|_| "8".into())
978 .parse()
979 .unwrap();
980 let mut dec = Vp9Decoder::default();
981 for i in 0..n {
982 let data = std::fs::read(format!("{dir}/{prefix}{i}.vp9")).unwrap();
983 dec.push(&data, Some(i as i64)).unwrap();
984 }
985 dec.flush();
986 let mut idx = 0;
987 loop {
988 match dec.next_frame() {
989 Ok(vf) => {
990 let mut buf = Vec::new();
991 for pl in &vf.planes {
992 buf.extend_from_slice(pl);
993 }
994 std::fs::write(format!("{dir}/my_seq_f{idx}.raw"), &buf).unwrap();
995 idx += 1;
996 }
997 Err(Error::Again) => continue,
998 _ => break,
999 }
1000 }
1001 eprintln!("decoded {idx} shown frames");
1002 }
1003
1004 #[test]
1006 #[ignore]
1007 fn dump_keyframe_planes() {
1008 let embedded = include_bytes!("testdata/keyframe.vp9").to_vec();
1009 let frame = match std::env::var("VP9_INPUT") {
1010 Ok(p) => std::fs::read(p).unwrap(),
1011 Err(_) => embedded,
1012 };
1013 let mut r = BitReader::new(&frame);
1014 let h = parse_uncompressed_header(&mut r, &[(0, 0); 8]).unwrap();
1015 let (planes, widths, heights) = decode::decode_intra_frame(&h, &frame).unwrap();
1016 let dir = std::env::var("VP9_DUMP_DIR")
1017 .unwrap_or_else(|_| std::env::temp_dir().to_string_lossy().into_owned());
1018 for (i, p) in planes.iter().enumerate() {
1019 std::fs::write(format!("{dir}/my_plane{i}.raw"), p).unwrap();
1020 }
1021 eprintln!(
1022 "dims {:?} {:?} loop_filter_level={} -> {dir}",
1023 widths, heights, h.loop_filter_level
1024 );
1025 }
1026
1027 #[test]
1032 #[ignore]
1033 fn bench_decode() {
1034 use std::time::Instant;
1035 let dir = std::env::var("VP9_BENCH_DIR").unwrap();
1036 let pre = std::env::var("VP9_BENCH_PREFIX").unwrap_or_else(|_| "bench_f".into());
1037 let n: usize = std::env::var("VP9_BENCH_N").unwrap().parse().unwrap();
1038 let passes: usize = std::env::var("VP9_BENCH_PASSES")
1039 .unwrap_or_else(|_| "5".into())
1040 .parse()
1041 .unwrap();
1042 let packets: Vec<Vec<u8>> = (0..n)
1043 .map(|i| std::fs::read(format!("{dir}/{pre}{i}.vp9")).unwrap())
1044 .collect();
1045
1046 let mut shown = 0usize;
1048 let mut pix = 0u64;
1049 let refdata = std::env::var("VP9_BENCH_REF")
1050 .ok()
1051 .map(|p| std::fs::read(p).unwrap());
1052 {
1053 let mut dec = Vp9Decoder::default();
1054 let mut off = 0usize;
1055 let mut mism = 0usize;
1056 let drain = |dec: &mut Vp9Decoder,
1057 shown: &mut usize,
1058 pix: &mut u64,
1059 off: &mut usize,
1060 mism: &mut usize| loop {
1061 match dec.next_frame() {
1062 Ok(vf) => {
1063 *shown += 1;
1064 for pl in &vf.planes {
1065 *pix += pl.len() as u64;
1066 }
1067 if let Some(rd) = &refdata {
1068 let mut buf = Vec::new();
1069 for pl in &vf.planes {
1070 buf.extend_from_slice(pl);
1071 }
1072 if *off + buf.len() <= rd.len() && rd[*off..*off + buf.len()] != buf[..]
1073 {
1074 *mism += 1;
1075 }
1076 *off += buf.len();
1077 }
1078 }
1079 Err(Error::Again) => break,
1080 _ => break,
1081 }
1082 };
1083 for (i, d) in packets.iter().enumerate() {
1084 dec.push(d, Some(i as i64)).unwrap();
1085 drain(&mut dec, &mut shown, &mut pix, &mut off, &mut mism);
1086 }
1087 dec.flush();
1088 drain(&mut dec, &mut shown, &mut pix, &mut off, &mut mism);
1089 if refdata.is_some() {
1090 eprintln!("[bench] correctness: {} mismatched frames / {shown}", mism);
1091 }
1092 }
1093
1094 let t = Instant::now();
1096 let mut total = 0usize;
1097 for _ in 0..passes {
1098 let mut dec = Vp9Decoder::default();
1099 for (i, d) in packets.iter().enumerate() {
1100 dec.push(d, Some(i as i64)).unwrap();
1101 while dec.next_frame().is_ok() {
1102 total += 1;
1103 }
1104 }
1105 dec.flush();
1106 while dec.next_frame().is_ok() {
1107 total += 1;
1108 }
1109 }
1110 let el = t.elapsed();
1111 let fps = total as f64 / el.as_secs_f64();
1112 let mpix = (pix as f64 * 2.0 / 3.0) * passes as f64 / 1e6 / el.as_secs_f64();
1113 eprintln!(
1114 "[bench] {total} frames in {:.3}s over {passes} passes -> {fps:.1} fps, {mpix:.1} Mpix/s ({shown} frames/pass)",
1115 el.as_secs_f64()
1116 );
1117 }
1118
1119 #[test]
1124 #[ignore]
1125 fn bench_decode_parallel() {
1126 use std::time::Instant;
1127 let dir = std::env::var("VP9_BENCH_DIR").unwrap();
1128 let n: usize = std::env::var("VP9_BENCH_N").unwrap().parse().unwrap();
1129 let passes: usize = std::env::var("VP9_BENCH_PASSES")
1130 .unwrap_or_else(|_| "8".into())
1131 .parse()
1132 .unwrap();
1133 let threads: usize = std::env::var("VP9_BENCH_T")
1134 .unwrap_or_else(|_| "1".into())
1135 .parse()
1136 .unwrap();
1137 let packets: Vec<Vec<u8>> = (0..n)
1138 .map(|i| std::fs::read(format!("{dir}/bench_f{i}.vp9")).unwrap())
1139 .collect();
1140 let decode_all = |packets: &[Vec<u8>]| -> usize {
1141 let mut total = 0;
1142 for _ in 0..passes {
1143 let mut dec = Vp9Decoder::default();
1144 for (i, d) in packets.iter().enumerate() {
1145 dec.push(d, Some(i as i64)).unwrap();
1146 while dec.next_frame().is_ok() {
1147 total += 1;
1148 }
1149 }
1150 dec.flush();
1151 while dec.next_frame().is_ok() {
1152 total += 1;
1153 }
1154 }
1155 total
1156 };
1157 let t = Instant::now();
1158 let total: usize = std::thread::scope(|s| {
1159 let handles: Vec<_> = (0..threads)
1160 .map(|_| s.spawn(|| decode_all(&packets)))
1161 .collect();
1162 handles.into_iter().map(|h| h.join().unwrap()).sum()
1163 });
1164 let el = t.elapsed();
1165 eprintln!(
1166 "[bench-par] threads={threads} {total} frames in {:.3}s -> {:.0} fps aggregate ({:.0} fps/thread)",
1167 el.as_secs_f64(), total as f64 / el.as_secs_f64(), total as f64 / el.as_secs_f64() / threads as f64
1168 );
1169 }
1170
1171 #[test]
1172 fn rejects_bad_marker() {
1173 let mut r = BitReader::new(&[0x00, 0x00]);
1174 assert!(parse_uncompressed_header(&mut r, &[(0, 0); 8]).is_err());
1175 }
1176
1177 #[test]
1182 fn full_header_consumes_compressed_header_exactly() {
1183 let frame = include_bytes!("testdata/keyframe.vp9");
1184 let mut r = BitReader::new(frame);
1185 let h = parse_uncompressed_header(&mut r, &[(0, 0); 8]).unwrap();
1186 assert!(h.sized && h.key_frame);
1187 assert_eq!((h.width, h.height), (96, 64));
1188 assert!(h.base_q_idx > 0); assert!(h.header_size > 0 && (h.header_size as usize) < frame.len());
1190
1191 let start = h.uncompressed_bytes;
1192 let end = start + h.header_size as usize;
1193 let consumed = consume_compressed_header(&frame[start..end], h.lossless).unwrap();
1194 let hs = h.header_size as usize;
1198 assert!(
1199 consumed.div_ceil(8).abs_diff(hs) <= 1,
1200 "consumed {} bytes vs header_size {hs}",
1201 consumed.div_ceil(8),
1202 );
1203 }
1204}