Skip to main content

pito_codecs/vp9/
decoder.rs

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}