Skip to main content

rav2d/
decoder.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::{Arc, Condvar, Mutex, Once};
3
4use crate::cpu;
5use crate::data::Data;
6use crate::dsp::{DSPContext, PalDSPContext, RefmvsDSPContext};
7use crate::error::Rav2dError;
8use crate::internal::DecoderContext;
9use crate::log::Logger;
10use crate::mem::MemPool;
11use crate::obu;
12use crate::picture::{DefaultPicAllocator, PicAllocator, Picture, ThreadPicture};
13
14pub const MAX_THREADS: u32 = 256;
15pub const MAX_FRAME_DELAY: u32 = 256;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u8)]
19/// Which in-loop filters to apply during decoding.
20#[non_exhaustive]
21#[derive(Default)]
22pub enum InloopFilterType {
23    None = 0,
24    Deblock = 1,
25    Cdef = 2,
26    Restoration = 4,
27    Wiener = 8,
28    Gdf = 16,
29    #[default]
30    All = 31,
31}
32
33impl InloopFilterType {
34    /// Raw in-loop-filter bit word matching dav2d.h's DAV2D_INLOOPFILTER_*
35    /// (DEBLOCK=1<<0, CDEF=1<<1, CCSO=1<<2, WIENER=1<<3, GDF=1<<4). The enum's
36    /// numeric repr already matches these C bits; bit 2 is published as
37    /// `Restoration` but semantically means CCSO per dav2d.h.
38    pub(crate) fn to_flags(self) -> u32 {
39        self as u8 as u32
40    }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44#[repr(u8)]
45/// Which frame types to decode.
46#[non_exhaustive]
47#[derive(Default)]
48pub enum DecodeFrameType {
49    #[default]
50    All = 0,
51    Reference = 1,
52    Intra = 2,
53    Key = 3,
54}
55
56/// Decoder configuration. Use `Settings::default()` for sensible defaults.
57#[derive(Debug, Clone)]
58pub struct Settings {
59    /// Number of worker threads. 0 = auto-detect from CPU count.
60    pub n_threads: u32,
61    /// Maximum frame delay for pipelining. 0 = auto based on thread count.
62    pub max_frame_delay: u32,
63    /// Apply film grain synthesis to decoded output.
64    pub apply_grain: bool,
65    /// Scalability operating point index (0–31).
66    pub operating_point: u32,
67    /// Output all temporal/spatial layers.
68    pub all_layers: bool,
69    /// Maximum frame size in pixels (width × height). 0 = unlimited.
70    pub frame_size_limit: u32,
71    /// Abort on spec-violating bitstreams instead of best-effort.
72    pub strict_std_compliance: bool,
73    /// Output frames not marked for display.
74    pub output_invisible_frames: bool,
75    /// Which in-loop filters to apply.
76    pub inloop_filters: InloopFilterType,
77    /// Which frame types to decode.
78    pub decode_frame_type: DecodeFrameType,
79    /// Bring-up gate: actually run reconstruction (intra only so far) and emit
80    /// pictures. Default off while recon/filters are incomplete; enabled by the
81    /// conformance harness. Will become unconditional once decode is complete.
82    pub run_decode: bool,
83}
84
85impl Default for Settings {
86    fn default() -> Self {
87        Self {
88            n_threads: 0,
89            max_frame_delay: 0,
90            apply_grain: true,
91            operating_point: 0,
92            all_layers: true,
93            frame_size_limit: 0,
94            strict_std_compliance: false,
95            output_invisible_frames: false,
96            inloop_filters: InloopFilterType::All,
97            decode_frame_type: DecodeFrameType::All,
98            run_decode: false,
99        }
100    }
101}
102
103fn get_num_threads(s: &Settings) -> (u32, u32) {
104    #[rustfmt::skip]
105    const FC_LUT: [u8; 49] = [
106        1,
107        2, 2, 2,
108        3, 3, 3, 3, 3,
109        4, 4, 4, 4, 4, 4, 4,
110        5, 5, 5, 5, 5, 5, 5, 5, 5,
111        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
112        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
113    ];
114
115    let n_tc = if s.n_threads > 0 {
116        s.n_threads.clamp(1, MAX_THREADS)
117    } else {
118        (cpu::num_logical_processors() as u32).clamp(1, MAX_THREADS)
119    };
120
121    let n_fc = if s.max_frame_delay > 0 {
122        s.max_frame_delay.min(n_tc)
123    } else if n_tc < 50 {
124        FC_LUT[(n_tc - 1) as usize] as u32
125    } else {
126        8
127    };
128
129    (n_tc, n_fc)
130}
131
132pub fn get_frame_delay(s: &Settings) -> Result<u32, Rav2dError> {
133    if s.n_threads > MAX_THREADS || s.max_frame_delay > MAX_FRAME_DELAY {
134        return Err(Rav2dError::InvalidParam);
135    }
136    let (_, n_fc) = get_num_threads(s);
137    Ok(n_fc)
138}
139
140static INIT_ONCE: Once = Once::new();
141
142fn init_internal() {
143    INIT_ONCE.call_once(|| {
144        cpu::init_cpu();
145    });
146}
147
148struct OutputQueue {
149    pic: ThreadPicture,
150    res: i32,
151}
152
153/// AV2 bitstream decoder.
154///
155/// Feed compressed OBU data with [`send_data`](Self::send_data), then
156/// pull decoded frames with [`get_picture`](Self::get_picture).
157pub struct Decoder {
158    logger: Logger,
159    allocator: Arc<dyn PicAllocator>,
160    inloop_filters: InloopFilterType,
161    decode_frame_type: DecodeFrameType,
162
163    n_tc: u32,
164    n_fc: u32,
165
166    ctx: DecoderContext,
167
168    input: Data,
169    drain: bool,
170    flush: AtomicBool,
171
172    dpb: Vec<OutputQueue>,
173    dpb_in: usize,
174    dpb_out: usize,
175    dpb_sz: usize,
176    /// POC (frame_offset) of the most recently appended output frame, mirroring
177    /// dav2d's `c->dpb_poc`. Used by `queue_flush` at end-of-stream to re-display
178    /// deferred `show_implicit` reference frames in display order.
179    dpb_poc: u8,
180
181    seq_hdr_pool: Arc<MemPool>,
182    frame_hdr_pool: Arc<MemPool>,
183    segmap_pool: Arc<MemPool>,
184    segmap_uv_pool: Arc<MemPool>,
185    refmvs_pool: Arc<MemPool>,
186    ccsomap_pool: Arc<MemPool>,
187    pic_ctx_pool: Arc<MemPool>,
188    cdf_pool: Arc<MemPool>,
189    fgm_pool: Arc<MemPool>,
190    ci_pool: Arc<MemPool>,
191    picture_pool: Arc<MemPool>,
192
193    task_thread: Option<TaskThread>,
194}
195
196struct TaskThread {
197    lock: Mutex<()>,
198    cond: Condvar,
199    cur: u32,
200    n_passes: u32,
201}
202
203impl Decoder {
204    /// Create a new decoder with the given settings.
205    pub fn open(s: &Settings) -> Result<Self, Rav2dError> {
206        init_internal();
207
208        if s.n_threads > MAX_THREADS || s.max_frame_delay > MAX_FRAME_DELAY {
209            return Err(Rav2dError::InvalidParam);
210        }
211        if s.operating_point > 31 {
212            return Err(Rav2dError::InvalidParam);
213        }
214
215        let (n_tc, n_fc) = get_num_threads(s);
216
217        let allocator: Arc<dyn PicAllocator> = Arc::new(DefaultPicAllocator::new());
218        let logger = Logger::with_default();
219
220        let dpb_sz = n_fc as usize + 16;
221        let mut dpb = Vec::with_capacity(dpb_sz);
222        for _ in 0..dpb_sz {
223            dpb.push(OutputQueue {
224                pic: ThreadPicture::new(),
225                res: 0,
226            });
227        }
228
229        let task_thread = if n_tc > 1 {
230            Some(TaskThread {
231                lock: Mutex::new(()),
232                cond: Condvar::new(),
233                cur: n_fc,
234                n_passes: 1 + (n_tc > 1) as u32 + (n_fc > 1) as u32,
235            })
236        } else {
237            None
238        };
239
240        let ctx = DecoderContext {
241            seq_hdr: None,
242            frame_hdr: None,
243            tile: Vec::new(),
244            n_tile_data: 0,
245            n_tiles: 0,
246            refs: Default::default(),
247            cdf: Vec::new(),
248            dsp: Arc::new(std::array::from_fn(|_| DSPContext::default())),
249            pal_dsp: PalDSPContext::default(),
250            refmvs_dsp: RefmvsDSPContext::default(),
251            content_light: None,
252            mastering_display: None,
253            ci: None,
254            fgm: Default::default(),
255            apply_grain: s.apply_grain,
256            operating_point: s.operating_point as i32,
257            operating_point_idc: 0,
258            all_layers: s.all_layers,
259            max_spatial_id: 0,
260            frame_size_limit: s.frame_size_limit,
261            strict_std_compliance: s.strict_std_compliance,
262            output_invisible_frames: s.output_invisible_frames,
263            n_passes: 1,
264            inloop_filters: s.inloop_filters.to_flags(),
265            run_decode: s.run_decode,
266            frame_out: Vec::new(),
267            n_tc,
268        };
269
270        Ok(Self {
271            logger,
272            allocator,
273            inloop_filters: s.inloop_filters,
274            decode_frame_type: s.decode_frame_type,
275            n_tc,
276            n_fc,
277            ctx,
278            input: Data::new(),
279            drain: false,
280            flush: AtomicBool::new(false),
281            dpb,
282            dpb_in: 0,
283            dpb_out: 0,
284            dpb_sz,
285            dpb_poc: 0,
286            seq_hdr_pool: Arc::new(MemPool::new()),
287            frame_hdr_pool: Arc::new(MemPool::new()),
288            segmap_pool: Arc::new(MemPool::new()),
289            segmap_uv_pool: Arc::new(MemPool::new()),
290            refmvs_pool: Arc::new(MemPool::new()),
291            ccsomap_pool: Arc::new(MemPool::new()),
292            pic_ctx_pool: Arc::new(MemPool::new()),
293            cdf_pool: Arc::new(MemPool::new()),
294            fgm_pool: Arc::new(MemPool::new()),
295            ci_pool: Arc::new(MemPool::new()),
296            picture_pool: Arc::new(MemPool::new()),
297            task_thread,
298        })
299    }
300
301    /// Feed compressed data to the decoder. Pass `None` to signal end-of-stream.
302    ///
303    /// Returns `Err(Again)` if the decoder hasn't consumed previous data yet;
304    /// call `get_picture` to drain output before sending more.
305    pub fn send_data(&mut self, data: Option<Data>) -> Result<(), Rav2dError> {
306        match data {
307            None => {
308                self.drain = true;
309                Ok(())
310            }
311            Some(d) => {
312                if self.drain {
313                    return Err(Rav2dError::Eof);
314                }
315                if d.is_empty() || d.len() > usize::MAX / 2 {
316                    return Err(Rav2dError::InvalidParam);
317                }
318                if self.input.has_data() {
319                    return Err(Rav2dError::Again);
320                }
321                self.input = d;
322                Ok(())
323            }
324        }
325    }
326
327    /// Retrieve a decoded picture from the output queue.
328    ///
329    /// Returns `Err(Again)` when no picture is available yet (send more data).
330    /// Returns `Err(Eof)` when the stream has been fully drained.
331    pub fn get_picture(&mut self) -> Result<Picture, Rav2dError> {
332        self.gen_picture()?;
333
334        if self.drain {
335            self.queue_flush();
336        }
337
338        self.output_image()
339    }
340
341    fn output_picture_ready(&self) -> bool {
342        if self.dpb_out == self.dpb_in {
343            return false;
344        }
345        true
346    }
347
348    fn gen_picture(&mut self) -> Result<(), Rav2dError> {
349        if self.output_picture_ready() {
350            return Ok(());
351        }
352
353        while !self.input.is_empty() {
354            let data = match self.input.data() {
355                Some(d) => d,
356                None => break,
357            };
358            match obu::parse_obus(&mut self.ctx, data) {
359                Ok(consumed) => {
360                    assert!(consumed <= self.input.len());
361                    self.input.consume(consumed);
362                    if self.input.is_empty() {
363                        self.input.unref();
364                    }
365                    // Frames reconstructed during parsing: enqueue all of them in
366                    // decode order (a single parse_obus call may decode several).
367                    let frames: Vec<_> = self.ctx.frame_out.drain(..).collect();
368                    for pic in frames {
369                        // Mirror dav2d queue_append: track the POC of the most
370                        // recently queued frame so end-of-stream queue_flush can
371                        // re-display deferred show_implicit frames in order.
372                        if let Some(fh) = pic.frame_hdr.as_ref() {
373                            self.dpb_poc = fh.frame_offset;
374                        }
375                        self.dpb[self.dpb_in].pic.p = pic;
376                        self.dpb_in += 1;
377                        if self.dpb_in == self.dpb_sz {
378                            self.dpb_in = 0;
379                        }
380                    }
381                }
382                Err(_e) => {
383                    self.input.unref();
384                    return Err(Rav2dError::InvalidData);
385                }
386            }
387
388            if self.output_picture_ready() {
389                break;
390            }
391        }
392
393        Ok(())
394    }
395
396    fn output_image(&mut self) -> Result<Picture, Rav2dError> {
397        if self.dpb_in == self.dpb_out {
398            if !self.drain {
399                return Err(Rav2dError::Again);
400            }
401            self.drain = false;
402            return Err(Rav2dError::Eof);
403        }
404
405        let q = &mut self.dpb[self.dpb_out];
406        let mut pic = Picture::new();
407        std::mem::swap(&mut pic, &mut q.pic.p);
408        q.pic.unref();
409
410        self.dpb_out += 1;
411        if self.dpb_out == self.dpb_sz {
412            self.dpb_out = 0;
413        }
414
415        // Film grain is display-only: it must not feed inter prediction, so it is
416        // applied to a fresh output copy here (the DPB/reference copy stays
417        // ungrained). Mirrors dav2d's `dav2d_apply_grain` in `output_image`.
418        // The grain synthesis + base copy are parallelised across `n_tc` threads
419        // (`n_tc == 1` keeps the byte-identical sequential path).
420        if self.ctx.apply_grain && crate::decode::picture_has_grain(&pic) {
421            let grained = crate::decode::apply_grain_to_picture_mt(&pic, self.n_tc);
422            pic.unref();
423            return Ok(grained);
424        }
425
426        Ok(pic)
427    }
428
429    /// End-of-stream display flush, mirroring dav2d `queue_flush` (lib.c).
430    ///
431    /// Frames coded with `show_implicit` are not displayed at decode time; they
432    /// are held in the reference store and emitted in display order once the
433    /// stream drains. This re-queues each such reference whose POC is later than
434    /// the last-displayed POC (`dpb_poc`), smallest-first, exactly once per slot.
435    fn queue_flush(&mut self) {
436        let nb = match self.ctx.seq_hdr.as_ref() {
437            Some(s) => s.order_hint_n_bits as i32,
438            None => return,
439        };
440        let mut mask: u32 = 0;
441        loop {
442            let mut cand: Option<(usize, u8)> = None; // (slot, poc)
443            for n in 0..8 {
444                if mask & (1 << n) != 0 {
445                    continue;
446                }
447                let r = &self.ctx.refs[n];
448                let pic = match r.p.pic.as_ref() {
449                    Some(p) if p.has_data() => p,
450                    _ => continue,
451                };
452                let hdr = match r.p.frame_hdr.as_ref() {
453                    Some(h) => h,
454                    None => continue,
455                };
456                if hdr.show_implicit == 0 {
457                    continue;
458                }
459                let ipoc = pic
460                    .frame_hdr
461                    .as_ref()
462                    .map(|h| h.frame_offset)
463                    .unwrap_or(hdr.frame_offset);
464                if crate::env::get_poc_diff(nb, ipoc as i32, self.dpb_poc as i32) > 0
465                    && (cand.is_none()
466                        || crate::env::get_poc_diff(nb, ipoc as i32, cand.unwrap().1 as i32) < 0)
467                {
468                    cand = Some((n, ipoc));
469                }
470            }
471            let (slot, ipoc) = match cand {
472                Some(c) => c,
473                None => break,
474            };
475            // Append a fresh, independently-owned copy of the stored picture.
476            let pic = self.ctx.refs[slot].p.pic.as_ref().unwrap().clone();
477            self.dpb[self.dpb_in].pic.p = crate::decode::clone_picture_mt(&pic, self.n_tc);
478            self.dpb_in += 1;
479            if self.dpb_in == self.dpb_sz {
480                self.dpb_in = 0;
481            }
482            self.dpb_poc = ipoc;
483            mask |= 1 << slot;
484        }
485    }
486
487    /// Reset the decoder state, discarding all buffered data and references.
488    pub fn flush(&mut self) {
489        self.input.unref();
490
491        for q in &mut self.dpb {
492            if q.pic.p.has_data() {
493                q.pic.unref();
494            }
495        }
496        self.dpb_in = 0;
497        self.dpb_out = 0;
498        self.drain = false;
499
500        for r in &mut self.ctx.refs {
501            r.segmap = None;
502            r.refmvs = None;
503            r.ccsomap = None;
504            r.p.frame_hdr = None;
505            r.refpoc = [0; 7];
506        }
507
508        self.ctx.frame_hdr = None;
509        self.ctx.seq_hdr = None;
510        self.ctx.tile.clear();
511        self.ctx.n_tile_data = 0;
512        self.ctx.n_tiles = 0;
513
514        self.flush.store(false, Ordering::Release);
515    }
516
517    pub fn n_threads(&self) -> u32 {
518        self.n_tc
519    }
520
521    pub fn n_frame_contexts(&self) -> u32 {
522        self.n_fc
523    }
524}
525
526impl Drop for Decoder {
527    fn drop(&mut self) {
528        self.flush();
529
530        self.seq_hdr_pool.end();
531        self.frame_hdr_pool.end();
532        self.segmap_pool.end();
533        self.segmap_uv_pool.end();
534        self.refmvs_pool.end();
535        self.ccsomap_pool.end();
536        self.pic_ctx_pool.end();
537        self.cdf_pool.end();
538        self.fgm_pool.end();
539        self.ci_pool.end();
540        self.picture_pool.end();
541    }
542}
543
544pub fn version() -> &'static str {
545    "0.1.0"
546}
547
548pub fn version_api() -> u32 {
549    1 << 8
550}
551
552#[cfg(test)]
553mod tests {
554    use super::*;
555
556    #[test]
557    fn test_default_settings() {
558        let s = Settings::default();
559        assert_eq!(s.n_threads, 0);
560        assert_eq!(s.max_frame_delay, 0);
561        assert!(s.apply_grain);
562        assert_eq!(s.operating_point, 0);
563        assert!(s.all_layers);
564    }
565
566    #[test]
567    fn test_get_num_threads() {
568        let mut s = Settings::default();
569        s.n_threads = 4;
570        let (n_tc, n_fc) = get_num_threads(&s);
571        assert_eq!(n_tc, 4);
572        assert_eq!(n_fc, 2);
573    }
574
575    #[test]
576    fn test_get_num_threads_single() {
577        let mut s = Settings::default();
578        s.n_threads = 1;
579        let (n_tc, n_fc) = get_num_threads(&s);
580        assert_eq!(n_tc, 1);
581        assert_eq!(n_fc, 1);
582    }
583
584    #[test]
585    fn test_get_num_threads_many() {
586        let mut s = Settings::default();
587        s.n_threads = 49;
588        let (n_tc, n_fc) = get_num_threads(&s);
589        assert_eq!(n_tc, 49);
590        assert_eq!(n_fc, 7);
591    }
592
593    #[test]
594    fn test_get_num_threads_over_50() {
595        let mut s = Settings::default();
596        s.n_threads = 100;
597        let (n_tc, n_fc) = get_num_threads(&s);
598        assert_eq!(n_tc, 100);
599        assert_eq!(n_fc, 8);
600    }
601
602    #[test]
603    fn test_get_frame_delay() {
604        let mut s = Settings::default();
605        s.n_threads = 8;
606        assert_eq!(get_frame_delay(&s).unwrap(), 3);
607    }
608
609    #[test]
610    fn test_get_frame_delay_invalid() {
611        let mut s = Settings::default();
612        s.n_threads = MAX_THREADS + 1;
613        assert_eq!(get_frame_delay(&s), Err(Rav2dError::InvalidParam));
614    }
615
616    #[test]
617    fn test_decoder_open() {
618        let s = Settings::default();
619        let decoder = Decoder::open(&s);
620        assert!(decoder.is_ok());
621        let d = decoder.unwrap();
622        assert!(d.n_threads() >= 1);
623    }
624
625    #[test]
626    fn test_decoder_open_single_thread() {
627        let mut s = Settings::default();
628        s.n_threads = 1;
629        let d = Decoder::open(&s).unwrap();
630        assert_eq!(d.n_threads(), 1);
631        assert_eq!(d.n_frame_contexts(), 1);
632    }
633
634    #[test]
635    fn test_decoder_open_invalid() {
636        let mut s = Settings::default();
637        s.operating_point = 32;
638        assert!(Decoder::open(&s).is_err());
639    }
640
641    #[test]
642    fn test_send_data_drain() {
643        let s = Settings::default();
644        let mut d = Decoder::open(&s).unwrap();
645        assert!(d.send_data(None).is_ok());
646        assert!(d.drain);
647    }
648
649    #[test]
650    fn test_send_data_after_drain() {
651        let s = Settings::default();
652        let mut d = Decoder::open(&s).unwrap();
653        d.send_data(None).unwrap();
654        let data = Data::wrap(vec![1, 2, 3]);
655        assert_eq!(d.send_data(Some(data)), Err(Rav2dError::Eof));
656    }
657
658    #[test]
659    fn test_send_data_empty() {
660        let s = Settings::default();
661        let mut d = Decoder::open(&s).unwrap();
662        let data = Data::new();
663        assert_eq!(d.send_data(Some(data)), Err(Rav2dError::InvalidParam));
664    }
665
666    #[test]
667    fn test_send_data_double() {
668        let s = Settings::default();
669        let mut d = Decoder::open(&s).unwrap();
670        d.send_data(Some(Data::wrap(vec![1, 2, 3]))).unwrap();
671        assert_eq!(
672            d.send_data(Some(Data::wrap(vec![4, 5, 6]))),
673            Err(Rav2dError::Again)
674        );
675    }
676
677    #[test]
678    fn test_get_picture_no_data() {
679        let s = Settings::default();
680        let mut d = Decoder::open(&s).unwrap();
681        assert_eq!(d.get_picture().err(), Some(Rav2dError::Again));
682    }
683
684    #[test]
685    fn test_flush() {
686        let s = Settings::default();
687        let mut d = Decoder::open(&s).unwrap();
688        d.send_data(Some(Data::wrap(vec![1, 2, 3]))).unwrap();
689        d.flush();
690        assert!(d.input.is_empty());
691        assert!(!d.drain);
692    }
693
694    #[test]
695    fn test_version() {
696        assert!(!version().is_empty());
697    }
698
699    #[test]
700    fn test_version_api() {
701        assert!(version_api() > 0);
702    }
703
704    #[test]
705    fn test_decoder_drop() {
706        let s = Settings::default();
707        let d = Decoder::open(&s).unwrap();
708        drop(d);
709    }
710}