1use crate::media::{Frame as Image, Packet, PixelFormat, TimeBase};
2use crate::vp9::error::{Error, Result, ensure};
3use crate::vp9::frame::{Frame, FrameInfo, MiGrid, MiInfo, Pixel, Pools, RowSync};
4use crate::vp9::header::{
5 FrameHeader, HeaderState, KEY_FRAME, RefInfo, Segmentation, parse_compressed,
6 parse_uncompressed, split_superframe,
7};
8use crate::vp9::loopfilter::{LfFrame, PlaneView, filter_superblock};
9use crate::vp9::probs::{Counts, adapt_coef_probs, adapt_noncoef_probs};
10use crate::vp9::tables::*;
11use crate::vp9::tile::{FrameShared, RefUse, Strip, TileDecoder};
12use crate::vp9::{ColorInfo, Picture, Plane};
13use std::collections::VecDeque;
14use std::sync::Arc;
15
16#[derive(Clone, Debug, Default, PartialEq, Eq)]
17pub struct DecodeStats {
18 pub frames: u64,
19 pub shown: u64,
20 pub hidden: u64,
21 pub key_frames: u64,
22 pub intra_only: u64,
23 pub inter_frames: u64,
24 pub show_existing: u64,
25 pub superframes: u64,
26 pub error_resilient: u64,
27 pub no_backward_adaptation: u64,
28 pub size_changes: u64,
29 pub scaled_ref_blocks: u64,
30 pub compound_blocks: u64,
31 pub intra_blocks: u64,
32 pub inter_blocks: u64,
33 pub lossless_frames: u64,
34 pub max_tile_cols: u32,
35 pub max_tile_rows: u32,
36 pub segmentation_frames: u64,
37 pub tx_select_frames: u64,
38 pub switchable_interp_frames: u64,
39 pub high_precision_mv_frames: u64,
40 pub compound_frames: u64,
41 pub bit_depths: [u64; 3],
42 pub profiles: [u64; 4],
43 pub draft_frames: u64,
44}
45
46enum Pending {
47 Ready(Picture),
48 Job(std::sync::mpsc::Receiver<Option<Picture>>),
49 Existing(Arc<Frame>, i64, bool),
50}
51
52pub struct Decoder {
53 st: HeaderState,
54 slots: [Option<Arc<Frame>>; 8],
55 prev_mi: Option<Arc<MiGrid>>,
56 prev_seg_ids: Vec<u8>,
57 last_size: Option<(u32, u32)>,
58 last_show_frame: bool,
59 broken: bool,
60 stats: DecodeStats,
61 threads: usize,
62 bufs: Arc<Pools>,
63 draft: bool,
64 time_base: TimeBase,
65 pending: VecDeque<Pending>,
66 jobs: VecDeque<std::thread::JoinHandle<()>>,
67 pool: Option<rayon::ThreadPool>,
68}
69
70impl Default for Decoder {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76fn tile_offset(i: usize, mis: usize, log2: u32) -> usize {
77 let sbs = (mis + 7) >> 3;
78 (((i * sbs) >> log2) << 3).min(mis)
79}
80
81impl Decoder {
82 pub fn new() -> Self {
83 Self::with_threads(crate::media::default_threads())
84 }
85
86 pub fn with_threads(threads: usize) -> Self {
87 let pool = if threads > 1 {
88 rayon::ThreadPoolBuilder::new()
89 .num_threads(threads)
90 .thread_name(|i| format!("vp9-{i}"))
91 .build()
92 .ok()
93 } else {
94 None
95 };
96 Decoder {
97 st: HeaderState::default(),
98 slots: Default::default(),
99 prev_mi: None,
100 prev_seg_ids: Vec::new(),
101 last_size: None,
102 last_show_frame: false,
103 broken: false,
104 stats: DecodeStats::default(),
105 threads: threads.max(1),
106 bufs: Arc::default(),
107 draft: false,
108 time_base: TimeBase::new(1, 90000),
109 pending: VecDeque::new(),
110 jobs: VecDeque::new(),
111 pool,
112 }
113 }
114
115 pub fn stats(&self) -> DecodeStats {
116 self.stats.clone()
117 }
118
119 pub fn threads(&self) -> usize {
120 if self.frame_threads() {
121 self.threads
122 } else {
123 1
124 }
125 }
126
127 pub fn set_draft(&mut self, on: bool) {
128 self.draft = on;
129 }
130
131 pub fn set_time_base(&mut self, time_base: TimeBase) {
132 self.time_base = time_base;
133 }
134
135 pub fn decode(&mut self, packet: &Packet) -> crate::media::Result<Vec<Image>> {
136 let pics = self.decode_pictures(&packet.data, packet.pts)?;
137 pics.into_iter()
138 .map(|p| image_of(p, self.time_base))
139 .collect()
140 }
141
142 pub fn flush(&mut self) -> crate::media::Result<Vec<Image>> {
143 let time_base = self.time_base;
144 let pics = self.flush_pictures();
145 if std::mem::take(&mut self.broken) {
146 self.reset();
147 return Err(worker_failed().into());
148 }
149 pics.into_iter().map(|p| image_of(p, time_base)).collect()
150 }
151
152 pub fn decode_pictures(&mut self, data: &[u8], pts: i64) -> Result<Vec<Picture>> {
153 let frames = split_superframe(data);
154 if frames.len() > 1 {
155 self.stats.superframes += 1;
156 }
157 for f in frames {
158 if f.is_empty() {
159 continue;
160 }
161 match guarded(|| self.decode_frame(f, pts)) {
162 Some(r) => r?,
163 None => {
164 self.reset();
165 return Err(worker_failed());
166 }
167 }
168 }
169 let out = self.drain(false);
170 if std::mem::take(&mut self.broken) {
171 self.reset();
172 return Err(worker_failed());
173 }
174 Ok(out)
175 }
176
177 pub fn flush_pictures(&mut self) -> Vec<Picture> {
178 let out = self.drain(true);
179 for j in self.jobs.drain(..) {
180 let _ = j.join();
181 }
182 out
183 }
184
185 pub fn reset(&mut self) {
186 self.st = HeaderState::default();
187 self.slots = Default::default();
188 self.prev_mi = None;
189 self.prev_seg_ids = Vec::new();
190 self.last_size = None;
191 self.last_show_frame = false;
192 self.pending.clear();
193 }
194
195 fn frame_threads(&self) -> bool {
196 self.pool.is_some()
197 }
198
199 fn drain(&mut self, all: bool) -> Vec<Picture> {
200 let max_lag = (self.threads / 2).clamp(2, 6);
201 let mut out = Vec::new();
202 loop {
203 let must = all || self.pending.len() > max_lag;
204 let Some(front) = self.pending.front_mut() else {
205 break;
206 };
207 let pic = match front {
208 Pending::Ready(_) => match self.pending.pop_front() {
209 Some(Pending::Ready(p)) => Some(p),
210 _ => None,
211 },
212 Pending::Job(rx) => {
213 let p = if must {
214 rx.recv().unwrap_or(None)
215 } else {
216 match rx.try_recv() {
217 Ok(p) => p,
218 Err(std::sync::mpsc::TryRecvError::Empty) => break,
219 Err(std::sync::mpsc::TryRecvError::Disconnected) => None,
220 }
221 };
222 if p.is_none() {
223 self.broken = true;
224 }
225 self.pending.pop_front();
226 p
227 }
228 Pending::Existing(f, pts, draft) => {
229 if !must && !f.is_complete() {
230 break;
231 }
232 let p = picture_of(f, *pts, *draft);
233 self.pending.pop_front();
234 Some(p)
235 }
236 };
237 out.extend(pic);
238 }
239 out
240 }
241
242 fn decode_frame(&mut self, data: &[u8], pts: i64) -> Result<()> {
243 let refs: [Option<RefInfo>; 8] = std::array::from_fn(|i| {
244 self.slots[i].as_ref().map(|f| RefInfo {
245 width: f.info.width,
246 height: f.info.height,
247 })
248 });
249 let mut h = parse_uncompressed(data, &mut self.st, &refs)?;
250 if h.show_existing_frame {
251 self.stats.show_existing += 1;
252 let f = self.slots[h.frame_to_show_map_idx as usize]
253 .clone()
254 .ok_or_else(|| {
255 Error::MissingReference(format!(
256 "show_existing_frame of empty slot {}",
257 h.frame_to_show_map_idx
258 ))
259 })?;
260 self.stats.shown += 1;
261 self.pending.push_back(Pending::Existing(f, pts, false));
262 return Ok(());
263 }
264 ensure!(
265 h.width <= 16384 && h.height <= 16384,
266 "frame size {}x{} too large",
267 h.width,
268 h.height
269 );
270 let (mi_rows, mi_cols) = (h.mi_rows as usize, h.mi_cols as usize);
271 let size = (h.width, h.height);
272 let size_changed = self.last_size != Some(size);
273 if size_changed && self.last_size.is_some() {
274 self.stats.size_changes += 1;
275 }
276 if self.st.reset_segment_map || size_changed || self.prev_seg_ids.len() != mi_rows * mi_cols
277 {
278 self.prev_seg_ids = vec![0; mi_rows * mi_cols];
279 self.st.reset_segment_map = false;
280 }
281 let use_prev = !size_changed
282 && self.last_show_frame
283 && !h.error_resilient_mode
284 && !h.frame_is_intra
285 && self
286 .prev_mi
287 .as_ref()
288 .is_some_and(|m| m.rows == mi_rows && m.cols == mi_cols);
289 self.last_size = Some(size);
290 self.last_show_frame = h.show_frame;
291
292 let off = h.uncompressed_size;
293 let hs = h.header_size_in_bytes as usize;
294 ensure!(
295 off + hs <= data.len(),
296 "compressed header exceeds frame data"
297 );
298 let mut fc = self.st.contexts[h.frame_context_idx as usize].clone();
299 parse_compressed(&data[off..off + hs], &mut h, &mut fc)?;
300 self.count_frame(&h);
301
302 let mut refs: [Option<RefUse>; 3] = [None, None, None];
303 let ref_frames: Vec<Option<Arc<Frame>>> = (0..3)
304 .map(|i| {
305 if h.frame_is_intra {
306 None
307 } else {
308 self.slots[h.ref_frame_idx[i] as usize].clone()
309 }
310 })
311 .collect();
312 if !h.frame_is_intra {
313 for i in 0..3 {
314 let Some(rf) = &ref_frames[i] else {
315 return Err(Error::MissingReference(format!(
316 "reference slot {} is empty",
317 h.ref_frame_idx[i]
318 )));
319 };
320 let ri = &rf.info;
321 ensure!(
322 ri.bit_depth == h.color.bit_depth
323 && ri.ss_x == h.color.subsampling_x
324 && ri.ss_y == h.color.subsampling_y,
325 "reference frame format differs from the current frame"
326 );
327 let (rw, rh) = (ri.width as i64, ri.height as i64);
328 let (w, hh) = (h.width as i64, h.height as i64);
329 if 2 * w >= rw && 2 * hh >= rh && w <= 16 * rw && hh <= 16 * rh {
330 let x_scale = ((rw << 14) / w) as i32;
331 let y_scale = ((rh << 14) / hh) as i32;
332 refs[i] = Some(RefUse {
333 frame: rf,
334 x_scale,
335 y_scale,
336 x_step: (16 * x_scale) >> 14,
337 y_step: (16 * y_scale) >> 14,
338 });
339 }
340 }
341 }
342 let seg = self.st.seg.clone();
343 let seg_q = segment_quant(&h, &seg);
344 let tile_cols = 1usize << h.tile_cols_log2;
345 let tile_rows = 1usize << h.tile_rows_log2;
346 let mut pos = off + hs;
347 let mut tile_data: Vec<Vec<&[u8]>> = vec![Vec::with_capacity(tile_rows); tile_cols];
348 for tr in 0..tile_rows {
349 for (tc, td) in tile_data.iter_mut().enumerate() {
350 let last = tr == tile_rows - 1 && tc == tile_cols - 1;
351 let sz = if last {
352 data.len() - pos
353 } else {
354 let Some(&b) = data.get(pos..).and_then(|d| d.first_chunk::<4>()) else {
355 return Err(Error::Invalid("tile size beyond frame data".into()));
356 };
357 let s = u32::from_be_bytes(b) as usize;
358 pos += 4;
359 ensure!(s <= data.len() - pos, "tile size {s} beyond frame data");
360 s
361 };
362 td.push(&data[pos..pos + sz]);
363 pos += sz;
364 }
365 }
366 let counting = !h.error_resilient_mode && !h.frame_parallel_decoding_mode;
367 let prev_mi = if use_prev { self.prev_mi.clone() } else { None };
368 let frame = Arc::new(Frame::new(
369 FrameInfo {
370 width: h.width,
371 height: h.height,
372 ss_x: h.color.subsampling_x,
373 ss_y: h.color.subsampling_y,
374 bit_depth: h.color.bit_depth,
375 color_space: h.color.color_space,
376 color_range: h.color.color_range,
377 render_width: h.render_width,
378 render_height: h.render_height,
379 key: h.frame_type == KEY_FRAME,
380 intra_only: h.intra_only,
381 },
382 self.bufs.clone(),
383 ));
384 let mi = Arc::new(MiGrid::new(
385 mi_rows,
386 mi_cols,
387 self.bufs.mi.take_any(mi_rows * mi_cols, MiInfo::default()),
388 ));
389 let sync = Arc::new(RowSync::new(h.sb64_rows as usize, tile_cols));
390 let _unwind = FailOnUnwind(&sync);
391 let draft =
392 self.draft && h.refresh_frame_flags == 0 && h.show_frame && self.st.lf.level > 0;
393 self.stats.draft_frames += draft as u64;
394 let lf = (self.st.lf.level > 0 && !draft).then(|| {
395 LfFrame::new(
396 &self.st.lf,
397 &seg,
398 mi_rows,
399 mi_cols,
400 h.color.subsampling_x,
401 h.color.subsampling_y,
402 h.color.bit_depth,
403 )
404 });
405 let post = self.start_post(PostJob {
406 frame: frame.clone(),
407 mi: mi.clone(),
408 sync: sync.clone(),
409 lf,
410 out: h.show_frame.then_some((pts, draft)),
411 });
412 let frame_ref = &*frame;
413 let shared = FrameShared {
414 h: &h,
415 fc: &fc,
416 seg: &seg,
417 prev_seg_ids: &self.prev_seg_ids,
418 prev_mi: prev_mi.as_deref(),
419 refs,
420 seg_q,
421 counting,
422 dst: frame_ref,
423 mi: &mi,
424 sync: &sync,
425 };
426 let col_bounds: Vec<(usize, usize)> = (0..tile_cols)
427 .map(|i| {
428 (
429 tile_offset(i, mi_cols, h.tile_cols_log2),
430 tile_offset(i + 1, mi_cols, h.tile_cols_log2),
431 )
432 })
433 .collect();
434 let row_bounds: Vec<(usize, usize)> = (0..tile_rows)
435 .map(|i| {
436 (
437 tile_offset(i, mi_rows, h.tile_rows_log2),
438 tile_offset(i + 1, mi_rows, h.tile_rows_log2),
439 )
440 })
441 .collect();
442 let decode_col = |ci: usize| -> (Strip, Option<Error>) {
443 if frame_ref.wide() {
444 decode_strip::<u16>(&shared, col_bounds[ci], &row_bounds, &tile_data[ci])
445 } else {
446 decode_strip::<u8>(&shared, col_bounds[ci], &row_bounds, &tile_data[ci])
447 }
448 };
449 let results: Vec<(Strip, Option<Error>)> = self.run_parallel(tile_cols, &decode_col);
450 let mut counts = Counts::default();
451 let mut error = None;
452 for (s, e) in results {
453 if let Some(e) = e {
454 error.get_or_insert(e);
455 }
456 if counting {
457 counts.add(&s.counts);
458 }
459 self.stats.compound_blocks += s.compound_blocks;
460 self.stats.scaled_ref_blocks += s.scaled_blocks;
461 self.stats.intra_blocks += s.intra_blocks;
462 self.stats.inter_blocks += s.inter_blocks;
463 }
464 if let Some(e) = error {
465 sync.fail();
466 return Err(e);
467 }
468 drop(ref_frames);
469 drop(prev_mi);
470 self.finish_post(post)?;
471 if counting {
472 let saved = &self.st.contexts[h.frame_context_idx as usize];
473 let mut a = saved.clone();
474 a.tx = fc.tx;
475 a.skip = fc.skip;
476 adapt_coef_probs(
477 &mut a,
478 &counts,
479 h.frame_is_intra,
480 self.st.last_frame_type == KEY_FRAME,
481 );
482 if !h.frame_is_intra {
483 a.tx = saved.tx;
484 a.skip = saved.skip;
485 adapt_noncoef_probs(
486 &mut a,
487 &counts,
488 h.interp_filter == SWITCHABLE,
489 h.tx_mode == TX_MODE_SELECT,
490 h.allow_high_precision_mv,
491 );
492 }
493 fc = a;
494 }
495 if h.refresh_frame_context {
496 self.st.contexts[h.frame_context_idx as usize] = fc;
497 }
498 if seg.enabled && seg.update_map {
499 self.prev_seg_ids = mi.seg_ids();
500 }
501 for i in 0..8 {
502 if (h.refresh_frame_flags >> i) & 1 == 1 {
503 self.slots[i] = Some(frame.clone());
504 }
505 }
506 if let Some(old) = self.prev_mi.replace(mi)
507 && let Ok(old) = Arc::try_unwrap(old)
508 {
509 self.bufs.mi.put(old.into_vec());
510 }
511 if h.show_frame {
512 self.stats.shown += 1;
513 } else {
514 self.stats.hidden += 1;
515 }
516 Ok(())
517 }
518
519 fn start_post(&mut self, job: PostJob) -> PostRun {
520 if !self.frame_threads() {
521 return PostRun::Inline(Box::new(job));
522 }
523 while self.jobs.front().is_some_and(|j| j.is_finished()) {
524 let _ = self.jobs.pop_front().map(|j| j.join());
525 }
526 let max_jobs = (self.threads / 3).clamp(2, 6);
527 while self.jobs.len() >= max_jobs {
528 let _ = self.jobs.pop_front().map(|j| j.join());
529 }
530 let shown = job.out.is_some();
531 let (tx, rx) = std::sync::mpsc::channel();
532 let (job_tx, job_rx) = std::sync::mpsc::channel::<PostJob>();
533 let spawned = std::thread::Builder::new()
534 .name("vp9-post".into())
535 .spawn(move || {
536 if let Ok(job) = job_rx.recv() {
537 match guarded(|| job.run()) {
538 Some(Some(p)) => {
539 let _ = tx.send(Some(p));
540 }
541 Some(None) => {}
542 None => {
543 let _ = tx.send(None);
544 }
545 }
546 }
547 });
548 match spawned {
549 Ok(handle) => match job_tx.send(job) {
550 Ok(()) => {
551 self.jobs.push_back(handle);
552 PostRun::Thread(rx, shown)
553 }
554 Err(std::sync::mpsc::SendError(back)) => PostRun::Inline(Box::new(back)),
555 },
556 Err(_) => PostRun::Inline(Box::new(job)),
557 }
558 }
559
560 fn finish_post(&mut self, post: PostRun) -> Result<()> {
561 match post {
562 PostRun::Thread(rx, shown) => {
563 if shown {
564 self.pending.push_back(Pending::Job(rx));
565 }
566 }
567 PostRun::Inline(job) => match guarded(|| job.run()) {
568 Some(Some(p)) => self.pending.push_back(Pending::Ready(p)),
569 Some(None) => {}
570 None => return Err(worker_failed()),
571 },
572 }
573 Ok(())
574 }
575
576 fn count_frame(&mut self, h: &FrameHeader) {
577 let s = &mut self.stats;
578 s.frames += 1;
579 if h.frame_type == KEY_FRAME {
580 s.key_frames += 1;
581 } else if h.intra_only {
582 s.intra_only += 1;
583 } else {
584 s.inter_frames += 1;
585 }
586 s.error_resilient += h.error_resilient_mode as u64;
587 s.no_backward_adaptation +=
588 (h.error_resilient_mode || h.frame_parallel_decoding_mode) as u64;
589 s.lossless_frames += h.lossless as u64;
590 s.max_tile_cols = s.max_tile_cols.max(1 << h.tile_cols_log2);
591 s.max_tile_rows = s.max_tile_rows.max(1 << h.tile_rows_log2);
592 s.segmentation_frames += self.st.seg.enabled as u64;
593 s.tx_select_frames += (h.tx_mode == TX_MODE_SELECT) as u64;
594 s.switchable_interp_frames += (!h.frame_is_intra && h.interp_filter == SWITCHABLE) as u64;
595 s.high_precision_mv_frames += (!h.frame_is_intra && h.allow_high_precision_mv) as u64;
596 s.compound_frames += (!h.frame_is_intra && h.reference_mode != 0) as u64;
597 s.bit_depths[((h.color.bit_depth - 8) >> 1) as usize] += 1;
598 s.profiles[h.profile as usize] += 1;
599 }
600
601 fn run_parallel<T: Send>(&self, n: usize, f: &(dyn Fn(usize) -> T + Sync)) -> Vec<T> {
602 if let Some(pool) = &self.pool
603 && n > 1
604 {
605 use rayon::prelude::*;
606 return pool.install(|| (0..n).into_par_iter().map(f).collect());
607 }
608 (0..n).map(f).collect()
609 }
610}
611
612impl Drop for Decoder {
613 fn drop(&mut self) {
614 for j in self.jobs.drain(..) {
615 let _ = j.join();
616 }
617 }
618}
619
620pub(crate) fn segment_quant(h: &FrameHeader, seg: &Segmentation) -> [[[i32; 2]; 2]; 8] {
621 let bd_idx = ((h.color.bit_depth - 8) >> 1) as usize;
622 let mut seg_q = [[[0i32; 2]; 2]; 8];
623 for (s, q) in seg_q.iter_mut().enumerate() {
624 let qindex = if seg.feature_active(s as u8, SEG_LVL_ALT_Q) {
625 let d = seg.feature_data[s][SEG_LVL_ALT_Q] as i32;
626 (if seg.abs_or_delta_update {
627 d
628 } else {
629 h.base_q_idx as i32 + d
630 })
631 .clamp(0, 255)
632 } else {
633 h.base_q_idx as i32
634 };
635 let dc = |b: i32| DC_QLOOKUP[bd_idx * 256 + b.clamp(0, 255) as usize];
636 let ac = |b: i32| AC_QLOOKUP[bd_idx * 256 + b.clamp(0, 255) as usize];
637 q[0] = [dc(qindex + h.delta_q_y_dc as i32), ac(qindex)];
638 q[1] = [
639 dc(qindex + h.delta_q_uv_dc as i32),
640 ac(qindex + h.delta_q_uv_ac as i32),
641 ];
642 }
643 seg_q
644}
645
646enum PostRun {
647 Thread(std::sync::mpsc::Receiver<Option<Picture>>, bool),
648 Inline(Box<PostJob>),
649}
650
651struct PostJob {
652 frame: Arc<Frame>,
653 mi: Arc<MiGrid>,
654 sync: Arc<RowSync>,
655 lf: Option<LfFrame>,
656 out: Option<(i64, bool)>,
657}
658
659struct PublishGuard<'a>(&'a Frame);
660
661impl Drop for PublishGuard<'_> {
662 fn drop(&mut self) {
663 if !self.0.is_complete() {
664 self.0.publish(self.0.sb_rows());
665 }
666 }
667}
668
669impl PostJob {
670 fn run(self) -> Option<Picture> {
671 let f = &*self.frame;
672 let _guard = PublishGuard(f);
673 let mut pic = self
674 .out
675 .map(|(pts, draft)| PictureBuilder::new(f, pts, draft));
676 let sb_rows = f.sb_rows();
677 for r in 0..sb_rows {
678 if !self.sync.wait(r) {
679 return None;
680 }
681 if let Some(lf) = &self.lf {
682 if f.wide() {
683 filter_row::<u16>(f, &self.mi, lf, r);
684 } else {
685 filter_row::<u8>(f, &self.mi, lf, r);
686 }
687 }
688 if r > 0 {
689 finish(f, r - 1, &mut pic);
690 }
691 }
692 finish(f, sb_rows - 1, &mut pic);
693 pic.map(PictureBuilder::finish)
694 }
695}
696
697fn finish(f: &Frame, r: usize, pic: &mut Option<PictureBuilder>) {
698 if let Some(pic) = pic {
699 pic.add(f, r);
700 }
701 f.finish_rows(r);
702 f.publish(r + 1);
703}
704
705fn filter_row<P: Pixel>(f: &Frame, mi: &MiGrid, lf: &LfFrame, r: usize) {
706 let mut views: [PlaneView<P>; 3] = std::array::from_fn(|p| band_view(f, p, r));
707 for c in 0..lf.mi_cols.div_ceil(8) {
708 filter_superblock(&mut views, mi, lf, r * 8, c * 8);
709 }
710}
711
712fn decode_strip<P: Pixel>(
713 shared: &FrameShared,
714 (cs, ce): (usize, usize),
715 row_bounds: &[(usize, usize)],
716 data: &[&[u8]],
717) -> (Strip, Option<Error>) {
718 let mut td = TileDecoder::<P>::new(shared, cs, ce);
719 let mut err = None;
720 if ce > cs {
721 for (&(rs, re), &d) in row_bounds.iter().zip(data) {
722 match guarded(|| td.decode_tile(d, rs, re)) {
723 Some(true) => {}
724 Some(false) => {
725 err = Some(Error::Invalid("tile data exhausted".into()));
726 break;
727 }
728 None => {
729 err = Some(worker_failed());
730 break;
731 }
732 }
733 }
734 }
735 let e = td.error.take().or(err);
736 (td.strip, e)
737}
738
739fn band_view<P: Pixel>(f: &Frame, p: usize, r: usize) -> PlaneView<'_, P> {
740 let g = f.geo[p];
741 let y0 = (r * g.band_h).saturating_sub(8);
742 let y1 = (r + 1) * g.band_h;
743 let pm = f.plane_mut::<P>(p);
744 PlaneView {
745 data: pm.row(0, y0, (y1 - y0) * g.stride),
746 stride: g.stride,
747 ox: 0,
748 oy: y0,
749 }
750}
751
752struct PictureBuilder {
753 planes: [Plane; 3],
754 info: FrameInfo,
755 pts: i64,
756 draft: bool,
757}
758
759impl PictureBuilder {
760 fn new(f: &Frame, pts: i64, draft: bool) -> Self {
761 let planes = f.geo.map(|g| {
762 if f.info.bit_depth == 8 {
763 Plane::U8(Vec::with_capacity(g.width * g.height))
764 } else {
765 Plane::U16(Vec::with_capacity(g.width * g.height))
766 }
767 });
768 PictureBuilder {
769 planes,
770 info: f.info,
771 pts,
772 draft,
773 }
774 }
775
776 fn add(&mut self, f: &Frame, r: usize) {
777 for p in 0..3 {
778 let g = f.geo[p];
779 let rows = r * g.band_h..((r + 1) * g.band_h).min(g.height);
780 match &mut self.planes[p] {
781 Plane::U8(d) if !f.wide() => {
782 let pm = f.plane_mut::<u8>(p);
783 for y in rows {
784 d.extend_from_slice(pm.row(0, y, g.width));
785 }
786 }
787 Plane::U8(d) => {
788 let pm = f.plane_mut::<u16>(p);
789 for y in rows {
790 d.extend(pm.row(0, y, g.width).iter().map(|&v| v as u8));
791 }
792 }
793 Plane::U16(d) if f.wide() => {
794 let pm = f.plane_mut::<u16>(p);
795 for y in rows {
796 d.extend_from_slice(pm.row(0, y, g.width));
797 }
798 }
799 Plane::U16(d) => {
800 let pm = f.plane_mut::<u8>(p);
801 for y in rows {
802 d.extend(pm.row(0, y, g.width).iter().map(|&v| v as u16));
803 }
804 }
805 }
806 }
807 }
808
809 fn finish(self) -> Picture {
810 let i = self.info;
811 let [y, u, v] = self.planes;
812 let (cw, ch) = (
813 (i.width as usize + i.ss_x as usize) >> i.ss_x as usize,
814 (i.height as usize + i.ss_y as usize) >> i.ss_y as usize,
815 );
816 Picture {
817 width: i.width,
818 height: i.height,
819 chroma_width: cw as u32,
820 chroma_height: ch as u32,
821 bit_depth: i.bit_depth as u32,
822 subsampling_x: i.ss_x,
823 subsampling_y: i.ss_y,
824 y,
825 u,
826 v,
827 y_stride: i.width as usize,
828 uv_stride: cw,
829 pts: self.pts,
830 key: i.key,
831 intra_only: i.intra_only,
832 color: ColorInfo {
833 color_space: i.color_space,
834 full_range: i.color_range,
835 },
836 render_width: i.render_width,
837 render_height: i.render_height,
838 draft: self.draft,
839 }
840 }
841}
842
843fn picture_of(f: &Frame, pts: i64, draft: bool) -> Picture {
844 f.wait(f.sb_rows());
845 let mut b = PictureBuilder::new(f, pts, draft);
846 for r in 0..f.sb_rows() {
847 b.add(f, r);
848 }
849 b.finish()
850}
851
852fn image_of(p: Picture, time_base: TimeBase) -> crate::media::Result<Image> {
853 if !(p.subsampling_x && p.subsampling_y) || !matches!(p.bit_depth, 8 | 10) {
854 return Err(crate::media::Error::Unsupported(format!(
855 "vp9: {}-bit {} as a 4:2:0 frame",
856 p.bit_depth,
857 match (p.subsampling_x, p.subsampling_y) {
858 (false, false) => "4:4:4",
859 (true, false) => "4:2:2",
860 _ => "4:4:0",
861 }
862 )));
863 }
864 let format = if p.bit_depth == 8 {
865 PixelFormat::Yuv420p8
866 } else {
867 PixelFormat::Yuv420p10
868 };
869 let bps = format.bytes_per_sample();
870 let plane = |data: Plane, w: u32, h: u32| crate::media::Plane {
871 data: match data {
872 Plane::U8(v) => v,
873 Plane::U16(v) => v.iter().flat_map(|s| s.to_le_bytes()).collect(),
874 },
875 stride: w as usize * bps,
876 width: w as usize,
877 height: h as usize,
878 };
879 let (cw, ch) = (p.chroma_width, p.chroma_height);
880 Ok(Image {
881 format,
882 width: p.width,
883 height: p.height,
884 pts: p.pts,
885 time_base,
886 planes: [
887 plane(p.y, p.width, p.height),
888 plane(p.u, cw, ch),
889 plane(p.v, cw, ch),
890 ],
891 })
892}
893
894fn guarded<T>(f: impl FnOnce() -> T) -> Option<T> {
895 std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).ok()
896}
897
898fn worker_failed() -> Error {
899 Error::Invalid("a decoding worker failed on this stream".into())
900}
901
902struct FailOnUnwind<'a>(&'a RowSync);
903
904impl Drop for FailOnUnwind<'_> {
905 fn drop(&mut self) {
906 if std::thread::panicking() {
907 self.0.fail();
908 }
909 }
910}