Skip to main content

av_scenechange/data/
frame.rs

1use std::sync::Arc;
2
3use v_frame::{frame::Frame, math::Fixed, pixel::Pixel, plane::Plane};
4
5use crate::data::motion::{RefMEStats, ReferenceFramesSet};
6
7pub const MAX_PLANES: usize = 3;
8
9pub const ALLOWED_REF_FRAMES: &[RefType] = &[RefType::LAST_FRAME];
10pub const INTER_REFS_PER_FRAME: usize = 7;
11
12#[derive(Debug, Clone)]
13pub struct FrameState<T: Pixel> {
14    pub input: Arc<Frame<T>>,
15    pub input_hres: Arc<Plane<T>>, // half-resolution version of input luma
16    pub input_qres: Arc<Plane<T>>, // quarter-resolution version of input luma
17    pub frame_me_stats: RefMEStats,
18}
19
20impl<T: Pixel> FrameState<T> {
21    /// Similar to [`FrameState::new_with_frame`], but takes an `me_stats`
22    /// and `rec` to enable reusing the same underlying allocations to create
23    /// a `FrameState`
24    ///
25    /// This function primarily exists for [`estimate_inter_costs`], and so
26    /// it does not create hres or qres versions of `frame` as downscaling is
27    /// somewhat expensive and are not needed for [`estimate_inter_costs`].
28    pub fn new_with_frame_and_me_stats_and_rec(frame: Arc<Frame<T>>, me_stats: RefMEStats) -> Self {
29        let hres = Plane::new(0, 0, 0, 0, 0, 0);
30        let qres = Plane::new(0, 0, 0, 0, 0, 0);
31
32        Self {
33            input: frame,
34            input_hres: Arc::new(hres),
35            input_qres: Arc::new(qres),
36            frame_me_stats: me_stats,
37            // enc_stats: Default::default(),
38        }
39    }
40}
41
42// LAST_FRAME through ALTREF_FRAME correspond to slots 0-6.
43#[allow(non_camel_case_types)]
44#[allow(dead_code)]
45#[derive(PartialEq, Eq, PartialOrd, Copy, Clone, Debug)]
46pub enum RefType {
47    INTRA_FRAME = 0,
48    LAST_FRAME = 1,
49    LAST2_FRAME = 2,
50    LAST3_FRAME = 3,
51    GOLDEN_FRAME = 4,
52    BWDREF_FRAME = 5,
53    ALTREF2_FRAME = 6,
54    ALTREF_FRAME = 7,
55    NONE_FRAME = 8,
56}
57
58impl RefType {
59    /// convert to a ref list index, 0-6 (`INTER_REFS_PER_FRAME`)
60    ///
61    /// # Panics
62    ///
63    /// - If the ref type is a None or Intra frame
64    pub fn to_index(self) -> usize {
65        use self::RefType::*;
66
67        match self {
68            NONE_FRAME => {
69                panic!("Tried to get slot of NONE_FRAME");
70            }
71            INTRA_FRAME => {
72                panic!("Tried to get slot of INTRA_FRAME");
73            }
74            _ => (self as usize) - 1,
75        }
76    }
77}
78
79// Frame Invariants are invariant inside a frame
80#[allow(dead_code)]
81#[derive(Debug, Clone)]
82pub struct FrameInvariants<T: Pixel> {
83    pub w_in_b: usize,
84    pub h_in_b: usize,
85    pub ref_frames: [u8; INTER_REFS_PER_FRAME],
86    pub rec_buffer: ReferenceFramesSet<T>,
87}
88
89impl<T: Pixel> FrameInvariants<T> {
90    pub fn new(width: usize, height: usize) -> Self {
91        let w_in_b = 2 * width.align_power_of_two_and_shift(3); // MiCols, ((width+7)/8)<<3 >> MI_SIZE_LOG2
92        let h_in_b = 2 * height.align_power_of_two_and_shift(3); // MiRows, ((height+7)/8)<<3 >> MI_SIZE_LOG2
93
94        Self {
95            w_in_b,
96            h_in_b,
97            ref_frames: [0; INTER_REFS_PER_FRAME],
98            rec_buffer: ReferenceFramesSet::new(),
99        }
100    }
101
102    pub fn new_key_frame(width: usize, height: usize) -> Self {
103        Self::new(width, height)
104    }
105
106    /// Returns the created `FrameInvariants`, or `None` if this should be
107    /// a placeholder frame.
108    pub(crate) fn new_inter_frame(
109        previous_coded_fi: &Self,
110        output_frameno_in_gop: u64,
111    ) -> Option<Self> {
112        let mut fi = previous_coded_fi.clone();
113        let idx_in_group_output = get_idx_in_group_output(output_frameno_in_gop);
114        let order_hint = get_order_hint(output_frameno_in_gop, idx_in_group_output);
115
116        // this is the slot that the current frame is going to be saved into
117        let slot_idx = get_slot_idx(0, order_hint);
118
119        // level 0 has no forward references
120        // default to last P frame
121        //
122        // calculations done relative to the slot_idx for this frame.
123        // the last four frames can be found by subtracting from the current
124        //
125        // add 4 to prevent underflow
126        // TODO: maybe use order_hint here like in get_slot_idx?
127        // this is the previous P frame
128        fi.ref_frames = [(slot_idx + 4 - 1) as u8 % 4; INTER_REFS_PER_FRAME];
129
130        Some(fi)
131    }
132}
133
134// All the stuff below is ripped from InterCfg but assumes
135// reordering and multiref are off, so pyramid depth is always 0
136const fn get_slot_idx(level: u64, order_hint: u32) -> u32 {
137    // Frames with level == 0 are stored in slots 0..4, and frames with higher
138    //  values of level in slots 4..8
139    if level == 0 {
140        order_hint & 3
141    } else {
142        // This only works with pyramid_depth <= 4.
143        3 + level as u32
144    }
145}
146
147/// Get the index of an output frame in its re-ordering group given the output
148///  frame number of the frame in the current keyframe gop.
149/// When re-ordering is disabled, this always returns 0.
150fn get_idx_in_group_output(output_frameno_in_gop: u64) -> u64 {
151    // The first frame in the GOP should be a keyframe and is not re-ordered,
152    // so we should not be calling this function on it.
153    debug_assert!(output_frameno_in_gop > 0);
154
155    output_frameno_in_gop - 1
156}
157
158/// Get the order-hint of an output frame given the output frame number of the
159///  frame in the current keyframe gop and the index of that output frame
160///  in its re-ordering gorup.
161fn get_order_hint(output_frameno_in_gop: u64, idx_in_group_output: u64) -> u32 {
162    // The first frame in the GOP should be a keyframe, but currently this
163    //  function only handles inter frames.
164    // We could return 0 for keyframes if keyframe support is needed.
165    debug_assert!(output_frameno_in_gop > 0);
166
167    // Which P-frame group in the current gop is this output frame in?
168    // Subtract 1 because the first frame in the gop is always a keyframe.
169    let group_idx = output_frameno_in_gop - 1;
170    // Get the offset to the corresponding input frame.
171    let offset = idx_in_group_output + 1;
172    // Construct the final order hint relative to the start of the group.
173    (group_idx + offset) as u32
174}