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}