1use thiserror::Error;
6
7pub mod av1;
8pub mod h264;
9pub mod h265;
10pub mod vp8;
11pub mod vp9;
12
13pub mod stateful;
14pub mod stateless;
15
16use crate::codec::av1::synthesizer::SynthesizerError as AV1SynthesizerError;
17use crate::codec::h264::synthesizer::SynthesizerError as H264SynthesizerError;
18use crate::encoder::stateful::StatefulBackendError;
19use crate::encoder::stateless::StatelessBackendError;
20use crate::FrameLayout;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum RateControl {
25 ConstantBitrate(u64),
27
28 ConstantQuality(u32),
31}
32
33impl RateControl {
34 pub(crate) fn is_same_variant(left: &Self, right: &Self) -> bool {
35 std::mem::discriminant(left) == std::mem::discriminant(right)
36 }
37
38 pub(crate) fn bitrate_target(&self) -> Option<u64> {
39 match self {
40 RateControl::ConstantBitrate(target) => Some(*target),
41 RateControl::ConstantQuality(_) => None,
42 }
43 }
44}
45
46#[derive(Clone)]
47pub enum PredictionStructure {
48 LowDelay { limit: u16 },
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Tunings {
58 pub rate_control: RateControl,
60 pub framerate: u32,
62 pub min_quality: u32,
64 pub max_quality: u32,
66}
67
68impl Default for Tunings {
69 fn default() -> Self {
70 Self {
71 rate_control: RateControl::ConstantBitrate(200_000),
72 framerate: 30,
73 min_quality: 0,
74 max_quality: u32::MAX,
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct FrameMetadata {
82 pub timestamp: u64,
83 pub layout: FrameLayout,
84 pub force_keyframe: bool,
85}
86
87pub struct CodedBitstreamBuffer {
89 pub metadata: FrameMetadata,
91
92 pub bitstream: Vec<u8>,
94}
95
96impl CodedBitstreamBuffer {
97 pub fn new(metadata: FrameMetadata, bitstream: Vec<u8>) -> Self {
98 Self {
99 metadata,
100 bitstream,
101 }
102 }
103}
104
105impl From<CodedBitstreamBuffer> for Vec<u8> {
106 fn from(value: CodedBitstreamBuffer) -> Self {
107 value.bitstream
108 }
109}
110
111#[derive(Error, Debug)]
112pub enum EncodeError {
113 #[error("unsupported")]
114 Unsupported,
115 #[error("invalid internal state. This is likely a bug.")]
116 InvalidInternalState,
117 #[error(transparent)]
118 StatelessBackendError(#[from] StatelessBackendError),
119 #[error(transparent)]
120 StatefulBackendError(#[from] StatefulBackendError),
121 #[error(transparent)]
122 H264SynthesizerError(#[from] H264SynthesizerError),
123 #[error(transparent)]
124 AV1SynthesizerError(#[from] AV1SynthesizerError),
125}
126
127pub type EncodeResult<T> = Result<T, EncodeError>;
128
129pub trait VideoEncoder<Handle> {
131 fn tune(&mut self, tunings: Tunings) -> EncodeResult<()>;
137
138 fn encode(&mut self, meta: FrameMetadata, handle: Handle) -> Result<(), EncodeError>;
144
145 fn drain(&mut self) -> EncodeResult<()>;
153
154 fn poll(&mut self) -> EncodeResult<Option<CodedBitstreamBuffer>>;
160}
161
162pub fn simple_encode_loop<H>(
163 encoder: &mut impl VideoEncoder<H>,
164 frame_producer: &mut impl Iterator<Item = (FrameMetadata, H)>,
165 mut coded_consumer: impl FnMut(CodedBitstreamBuffer),
166) -> EncodeResult<()> {
167 for (meta, handle) in frame_producer.by_ref() {
168 encoder.encode(meta, handle)?;
169 while let Some(coded) = encoder.poll()? {
170 coded_consumer(coded);
171 }
172 }
173
174 encoder.drain()?;
175 while let Some(coded) = encoder.poll()? {
176 coded_consumer(coded);
177 }
178
179 Ok(())
180}
181
182#[cfg(test)]
183pub(crate) mod tests {
184 #[cfg(feature = "v4l2")]
185 use crate::encoder::FrameMetadata;
186 #[cfg(feature = "v4l2")]
187 use crate::utils::UserPtrFrame;
188 #[cfg(feature = "v4l2")]
189 use crate::Fourcc;
190 #[cfg(feature = "v4l2")]
191 use crate::FrameLayout;
192
193 pub fn get_test_frame_t(ts: u64, max_ts: u64) -> f32 {
194 2.0 * std::f32::consts::PI * (ts as f32) / (max_ts as f32)
195 }
196
197 pub fn gen_test_frame<F>(frame_width: usize, frame_height: usize, t: f32, mut set_pix: F)
198 where
199 F: FnMut(usize, usize, [f32; 3]),
200 {
201 let width = frame_width as f32;
202 let height = frame_height as f32;
203 let (sin, cos) = f32::sin_cos(t);
204 let (sin2, cos2) = (sin.powi(2), cos.powi(2));
205
206 let dot_col = height * (1.1 + 2.0 * sin * cos) / 2.2;
208 let dot_row = width * (1.1 + sin) / 2.2;
209 let dot_size2 = (width.min(height) * 0.05).powi(2);
210
211 for frame_row in 0..frame_height {
213 #[allow(clippy::needless_range_loop)]
214 for frame_col in 0..frame_width {
215 let row = frame_row as f32;
216 let col = frame_col as f32;
217
218 let dist = (dot_col - col).powi(2) + (dot_row - row).powi(2);
219
220 let y = if dist < dot_size2 {
221 0.0
222 } else {
223 (row + col) / (width + height)
224 };
225
226 let (u, v) = if dist < dot_size2 {
227 (0.5, 0.5)
228 } else {
229 ((row / width) * sin2, (col / height) * cos2)
230 };
231
232 set_pix(frame_col, frame_row, [y, u, v]);
233 }
234 }
235 }
236
237 pub fn fill_test_frame_nm12(
238 width: usize,
239 height: usize,
240 strides: [usize; 2],
241 t: f32,
242 y_plane: &mut [u8],
243 uv_plane: &mut [u8],
244 ) {
245 gen_test_frame(width, height, t, |col, row, yuv| {
246 const MAX_COMP_VAL: f32 = 0xff as f32;
248
249 let (y, u, v) = (
250 (yuv[0] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
251 (yuv[1] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
252 (yuv[2] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
253 );
254 let y_pos = row * strides[0] + col;
255
256 y_plane[y_pos] = y;
257
258 if col % 2 == 0 && row % 2 == 0 {
260 let u_pos = (row / 2) * strides[1] + col;
261 let v_pos = u_pos + 1;
262
263 uv_plane[u_pos] = u;
264 uv_plane[v_pos] = v;
265 }
266 });
267 }
268
269 pub fn fill_test_frame_nv12(
270 width: usize,
271 height: usize,
272 strides: [usize; 2],
273 offsets: [usize; 2],
274 t: f32,
275 raw: &mut [u8],
276 ) {
277 let (y_plane, uv_plane) = raw.split_at_mut(offsets[1]);
278 let y_plane = &mut y_plane[offsets[0]..];
279
280 fill_test_frame_nm12(width, height, strides, t, y_plane, uv_plane)
281 }
282
283 pub fn fill_test_frame_p010(
284 width: usize,
285 height: usize,
286 strides: [usize; 2],
287 offsets: [usize; 2],
288 t: f32,
289 raw: &mut [u8],
290 ) {
291 gen_test_frame(width, height, t, |col, row, yuv| {
292 const MAX_COMP_VAL: f32 = 0x3ff as f32;
294
295 let (y, u, v) = (
296 (yuv[0] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
297 (yuv[1] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
298 (yuv[2] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
299 );
300 let y_pos = offsets[0] + row * strides[0] + 2 * col;
301
302 raw[y_pos] = ((y << 6) & 0xa0) as u8;
303 raw[y_pos + 1] = (y >> 2) as u8;
304
305 if col % 2 == 0 && row % 2 == 0 {
307 let u_pos = offsets[1] + (row / 2) * strides[1] + 2 * col;
308 let v_pos = u_pos + 2;
309
310 raw[u_pos] = ((u << 6) & 0xa0) as u8;
311 raw[u_pos + 1] = (u >> 2) as u8;
312 raw[v_pos] = ((v << 6) & 0xa0) as u8;
313 raw[v_pos + 1] = (v >> 2) as u8;
314 }
315 });
316 }
317
318 #[cfg(feature = "v4l2")]
319 pub fn userptr_test_frame_generator(
320 frame_count: u64,
321 layout: FrameLayout,
322 buffer_size: usize,
323 ) -> impl Iterator<Item = (FrameMetadata, UserPtrFrame)> {
324 (0..frame_count).map(move |timestamp| {
325 let frame = UserPtrFrame::alloc(layout.clone(), buffer_size);
326
327 let t = get_test_frame_t(timestamp, frame_count);
328
329 if (frame.layout.format.0 == Fourcc::from(b"NM12")
330 || frame.layout.format.0 == Fourcc::from(b"NV12"))
331 && frame.layout.planes.len() == 2
332 && frame.buffers.len() == 2
333 {
334 let y_plane = unsafe {
336 std::slice::from_raw_parts_mut(
337 frame.buffers[frame.layout.planes[0].buffer_index],
338 frame.mem_layout.size(),
339 )
340 };
341 let uv_plane = unsafe {
343 std::slice::from_raw_parts_mut(
344 frame.buffers[frame.layout.planes[1].buffer_index],
345 frame.mem_layout.size(),
346 )
347 };
348
349 fill_test_frame_nm12(
350 frame.layout.size.width as usize,
351 frame.layout.size.height as usize,
352 [frame.layout.planes[0].stride, frame.layout.planes[1].stride],
353 t,
354 y_plane,
355 uv_plane,
356 );
357 } else if frame.layout.format.0 == Fourcc::from(b"NV12")
358 && frame.layout.planes.len() == 1
359 && frame.buffers.len() == 1
360 {
361 let raw = unsafe {
363 std::slice::from_raw_parts_mut(
364 frame.buffers[frame.layout.planes[0].buffer_index]
365 .add(frame.layout.planes[0].offset),
366 frame.mem_layout.size(),
367 )
368 };
369
370 let uv_stride = frame.layout.planes[0].stride;
371 let uv_offset = frame.layout.planes[0].stride * (frame.layout.size.height as usize);
372
373 fill_test_frame_nv12(
374 frame.layout.size.width as usize,
375 frame.layout.size.height as usize,
376 [frame.layout.planes[0].stride, uv_stride],
377 [frame.layout.planes[0].offset, uv_offset],
378 t,
379 raw,
380 );
381 } else {
382 panic!("Unrecognized frame layout used during test");
383 }
384
385 let meta = FrameMetadata {
386 timestamp,
387 layout: frame.layout.clone(),
388 force_keyframe: false,
389 };
390
391 (meta, frame)
392 })
393 }
394}