1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::fmt;
6
7 pub mod av1;
8 pub mod h264;
9 pub mod h265;
10 pub mod vp8;
11 pub mod vp9;
12
13 pub mod stateful;
14 pub mod stateless;
15
16 use crate::codec::av1::synthesizer::SynthesizerError as AV1SynthesizerError;
17 use crate::codec::h264::synthesizer::SynthesizerError as H264SynthesizerError;
18 use crate::encoder::stateful::StatefulBackendError;
19 use crate::encoder::stateless::StatelessBackendError;
20 use crate::FrameLayout;
21
22 /// Specifies the encoder operation
23 #[derive(Debug, Clone, PartialEq, Eq)]
24 pub enum RateControl {
25 /// The encoder shall maintain the constant bitrate
26 ConstantBitrate(u64),
27
28 /// The encoder shall maintain codec specific quality parameter constant (eg. QP for H.264)
29 /// disregarding bitrate.
30 ConstantQuality(u32),
31 }
32
33 impl RateControl {
34 #[allow(dead_code)]
is_same_variant(left: &Self, right: &Self) -> bool35 pub(crate) fn is_same_variant(left: &Self, right: &Self) -> bool {
36 std::mem::discriminant(left) == std::mem::discriminant(right)
37 }
38
bitrate_target(&self) -> Option<u64>39 pub(crate) fn bitrate_target(&self) -> Option<u64> {
40 match self {
41 RateControl::ConstantBitrate(target) => Some(*target),
42 RateControl::ConstantQuality(_) => None,
43 }
44 }
45 }
46
47 #[derive(Clone)]
48 pub enum PredictionStructure {
49 /// Simplest prediction structure, suitable eg. for RTC. Interframe is produced at the start of
50 /// the stream and every time when `limit` frames are reached. Following interframe frames
51 /// are frames relying solely on the last frame.
52 LowDelay { limit: u16 },
53 }
54
55 /// Dynamic parameters of the encoded stream that client may choose to change during the encoding
56 /// session without recreating the entire encoder instance.
57 #[derive(Debug, Clone, PartialEq, Eq)]
58 pub struct Tunings {
59 /// The stream's [`RateControl`]
60 pub rate_control: RateControl,
61 /// Stream framerate in frames per second
62 pub framerate: u32,
63 /// Minimum value of codec specific quality parameter constant (eg. QP for H.264)
64 pub min_quality: u32,
65 /// Maximum value of codec specific quality parameter constant (eg. QP for H.264)
66 pub max_quality: u32,
67 }
68
69 impl Default for Tunings {
default() -> Self70 fn default() -> Self {
71 Self {
72 rate_control: RateControl::ConstantBitrate(200_000),
73 framerate: 30,
74 min_quality: 0,
75 max_quality: u32::MAX,
76 }
77 }
78 }
79
80 /// Encoder's input metadata
81 #[derive(Debug, Clone)]
82 pub struct FrameMetadata {
83 pub timestamp: u64,
84 pub layout: FrameLayout,
85 pub force_keyframe: bool,
86 }
87
88 /// Encoder's coded output with contained frame.
89 pub struct CodedBitstreamBuffer {
90 /// [`FrameMetadata`] of the frame that is compressed in [`Self::bitstream`]
91 pub metadata: FrameMetadata,
92
93 /// Bitstream with compressed frame together with optionally other compressed control messages
94 pub bitstream: Vec<u8>,
95 }
96
97 impl CodedBitstreamBuffer {
new(metadata: FrameMetadata, bitstream: Vec<u8>) -> Self98 pub fn new(metadata: FrameMetadata, bitstream: Vec<u8>) -> Self {
99 Self { metadata, bitstream }
100 }
101 }
102
103 impl From<CodedBitstreamBuffer> for Vec<u8> {
from(value: CodedBitstreamBuffer) -> Self104 fn from(value: CodedBitstreamBuffer) -> Self {
105 value.bitstream
106 }
107 }
108
109 #[derive(Debug)]
110 pub enum EncodeError {
111 Unsupported,
112 InvalidInternalState,
113 StatelessBackendError(StatelessBackendError),
114 StatefulBackendError(StatefulBackendError),
115 H264SynthesizerError(H264SynthesizerError),
116 AV1SynthesizerError(AV1SynthesizerError),
117 }
118
119 impl fmt::Display for EncodeError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result120 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 match self {
122 EncodeError::Unsupported => write!(f, "unsupported"),
123 EncodeError::InvalidInternalState => {
124 write!(f, "invalid internal state. This is likely a bug.")
125 }
126 EncodeError::StatelessBackendError(x) => write!(f, "{}", x.to_string()),
127 EncodeError::StatefulBackendError(x) => write!(f, "{}", x.to_string()),
128 EncodeError::H264SynthesizerError(x) => write!(f, "{}", x.to_string()),
129 EncodeError::AV1SynthesizerError(x) => write!(f, "{}", x.to_string()),
130 }
131 }
132 }
133
134 impl From<StatelessBackendError> for EncodeError {
from(err: StatelessBackendError) -> Self135 fn from(err: StatelessBackendError) -> Self {
136 EncodeError::StatelessBackendError(err)
137 }
138 }
139
140 impl From<StatefulBackendError> for EncodeError {
from(err: StatefulBackendError) -> Self141 fn from(err: StatefulBackendError) -> Self {
142 EncodeError::StatefulBackendError(err)
143 }
144 }
145
146 impl From<H264SynthesizerError> for EncodeError {
from(err: H264SynthesizerError) -> Self147 fn from(err: H264SynthesizerError) -> Self {
148 EncodeError::H264SynthesizerError(err)
149 }
150 }
151
152 impl From<AV1SynthesizerError> for EncodeError {
from(err: AV1SynthesizerError) -> Self153 fn from(err: AV1SynthesizerError) -> Self {
154 EncodeError::AV1SynthesizerError(err)
155 }
156 }
157
158 pub type EncodeResult<T> = Result<T, EncodeError>;
159
160 /// Generic video encoder interface.
161 pub trait VideoEncoder<Handle> {
162 /// Changes dynamic parameters (aka [`Tunings`]) of the encoded stream. The change may not be
163 /// effective right away. Depending on the used prediction structure, the `Predictor` may
164 /// choose to delay the change until entire or a some part of the structure had been encoded.
165 ///
166 /// Note: Currently changing the variant of [`RateControl`] is not supported.
tune(&mut self, tunings: Tunings) -> EncodeResult<()>167 fn tune(&mut self, tunings: Tunings) -> EncodeResult<()>;
168
169 /// Enqueues the frame for encoding. The implementation will drop the handle after it is no
170 /// longer be needed. The encoder is not required to immediately start processing the frame
171 /// and yield output bitstream. It is allowed to hold frames until certain conditions are met
172 /// eg. for specified prediction structures or referencing in order to further optimize
173 /// the compression rate of the bitstream.
encode(&mut self, meta: FrameMetadata, handle: Handle) -> Result<(), EncodeError>174 fn encode(&mut self, meta: FrameMetadata, handle: Handle) -> Result<(), EncodeError>;
175
176 /// Drains the encoder. This means that encoder is required to finish processing of all the
177 /// frames in the internal queue and yield output bitstream by the end of the call. The output
178 /// bitstream then can be polled using [`Self::poll`] function.
179 ///
180 /// Drain does not enforce the flush of the internal state, ie. the enqueued frame handles
181 /// do not have to be returned to user (dropped) and key frame is not enforced on the next
182 /// frame.
drain(&mut self) -> EncodeResult<()>183 fn drain(&mut self) -> EncodeResult<()>;
184
185 /// Polls on the encoder for the available output bitstream with compressed frames that where
186 /// submitted with [`Self::encode`].
187 ///
188 /// The call may also trigger a further processing aside of returning output. Therefore it
189 /// *recommended* that this function is called frequently.
poll(&mut self) -> EncodeResult<Option<CodedBitstreamBuffer>>190 fn poll(&mut self) -> EncodeResult<Option<CodedBitstreamBuffer>>;
191 }
192
simple_encode_loop<H>( encoder: &mut impl VideoEncoder<H>, frame_producer: &mut impl Iterator<Item = (FrameMetadata, H)>, mut coded_consumer: impl FnMut(CodedBitstreamBuffer), ) -> EncodeResult<()>193 pub fn simple_encode_loop<H>(
194 encoder: &mut impl VideoEncoder<H>,
195 frame_producer: &mut impl Iterator<Item = (FrameMetadata, H)>,
196 mut coded_consumer: impl FnMut(CodedBitstreamBuffer),
197 ) -> EncodeResult<()> {
198 for (meta, handle) in frame_producer.by_ref() {
199 encoder.encode(meta, handle)?;
200 while let Some(coded) = encoder.poll()? {
201 coded_consumer(coded);
202 }
203 }
204
205 encoder.drain()?;
206 while let Some(coded) = encoder.poll()? {
207 coded_consumer(coded);
208 }
209
210 Ok(())
211 }
212
213 #[cfg(test)]
214 pub(crate) mod tests {
215 #[cfg(feature = "v4l2")]
216 use crate::encoder::FrameMetadata;
217 #[cfg(feature = "v4l2")]
218 use crate::utils::UserPtrFrame;
219 #[cfg(feature = "v4l2")]
220 use crate::Fourcc;
221 #[cfg(feature = "v4l2")]
222 use crate::FrameLayout;
223
get_test_frame_t(ts: u64, max_ts: u64) -> f32224 pub fn get_test_frame_t(ts: u64, max_ts: u64) -> f32 {
225 2.0 * std::f32::consts::PI * (ts as f32) / (max_ts as f32)
226 }
227
gen_test_frame<F>(frame_width: usize, frame_height: usize, t: f32, mut set_pix: F) where F: FnMut(usize, usize, [f32; 3]),228 pub fn gen_test_frame<F>(frame_width: usize, frame_height: usize, t: f32, mut set_pix: F)
229 where
230 F: FnMut(usize, usize, [f32; 3]),
231 {
232 let width = frame_width as f32;
233 let height = frame_height as f32;
234 let (sin, cos) = f32::sin_cos(t);
235 let (sin2, cos2) = (sin.powi(2), cos.powi(2));
236
237 // Pick the dot position
238 let dot_col = height * (1.1 + 2.0 * sin * cos) / 2.2;
239 let dot_row = width * (1.1 + sin) / 2.2;
240 let dot_size2 = (width.min(height) * 0.05).powi(2);
241
242 // Luma
243 for frame_row in 0..frame_height {
244 #[allow(clippy::needless_range_loop)]
245 for frame_col in 0..frame_width {
246 let row = frame_row as f32;
247 let col = frame_col as f32;
248
249 let dist = (dot_col - col).powi(2) + (dot_row - row).powi(2);
250
251 let y = if dist < dot_size2 { 0.0 } else { (row + col) / (width + height) };
252
253 let (u, v) = if dist < dot_size2 {
254 (0.5, 0.5)
255 } else {
256 ((row / width) * sin2, (col / height) * cos2)
257 };
258
259 set_pix(frame_col, frame_row, [y, u, v]);
260 }
261 }
262 }
263
fill_test_frame_nm12( width: usize, height: usize, strides: [usize; 2], t: f32, y_plane: &mut [u8], uv_plane: &mut [u8], )264 pub fn fill_test_frame_nm12(
265 width: usize,
266 height: usize,
267 strides: [usize; 2],
268 t: f32,
269 y_plane: &mut [u8],
270 uv_plane: &mut [u8],
271 ) {
272 gen_test_frame(width, height, t, |col, row, yuv| {
273 /// Maximum value of color component for NV12
274 const MAX_COMP_VAL: f32 = 0xff as f32;
275
276 let (y, u, v) = (
277 (yuv[0] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
278 (yuv[1] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
279 (yuv[2] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u8,
280 );
281 let y_pos = row * strides[0] + col;
282
283 y_plane[y_pos] = y;
284
285 // Subsample with upper left pixel
286 if col % 2 == 0 && row % 2 == 0 {
287 let u_pos = (row / 2) * strides[1] + col;
288 let v_pos = u_pos + 1;
289
290 uv_plane[u_pos] = u;
291 uv_plane[v_pos] = v;
292 }
293 });
294 }
295
fill_test_frame_nv12( width: usize, height: usize, strides: [usize; 2], offsets: [usize; 2], t: f32, raw: &mut [u8], )296 pub fn fill_test_frame_nv12(
297 width: usize,
298 height: usize,
299 strides: [usize; 2],
300 offsets: [usize; 2],
301 t: f32,
302 raw: &mut [u8],
303 ) {
304 let (y_plane, uv_plane) = raw.split_at_mut(offsets[1]);
305 let y_plane = &mut y_plane[offsets[0]..];
306
307 fill_test_frame_nm12(width, height, strides, t, y_plane, uv_plane)
308 }
309
fill_test_frame_p010( width: usize, height: usize, strides: [usize; 2], offsets: [usize; 2], t: f32, raw: &mut [u8], )310 pub fn fill_test_frame_p010(
311 width: usize,
312 height: usize,
313 strides: [usize; 2],
314 offsets: [usize; 2],
315 t: f32,
316 raw: &mut [u8],
317 ) {
318 gen_test_frame(width, height, t, |col, row, yuv| {
319 /// Maximum value of color component for P010
320 const MAX_COMP_VAL: f32 = 0x3ff as f32;
321
322 let (y, u, v) = (
323 (yuv[0] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
324 (yuv[1] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
325 (yuv[2] * MAX_COMP_VAL).clamp(0.0, MAX_COMP_VAL) as u16,
326 );
327 let y_pos = offsets[0] + row * strides[0] + 2 * col;
328
329 raw[y_pos] = ((y << 6) & 0xa0) as u8;
330 raw[y_pos + 1] = (y >> 2) as u8;
331
332 // Subsample with upper left pixel
333 if col % 2 == 0 && row % 2 == 0 {
334 let u_pos = offsets[1] + (row / 2) * strides[1] + 2 * col;
335 let v_pos = u_pos + 2;
336
337 raw[u_pos] = ((u << 6) & 0xa0) as u8;
338 raw[u_pos + 1] = (u >> 2) as u8;
339 raw[v_pos] = ((v << 6) & 0xa0) as u8;
340 raw[v_pos + 1] = (v >> 2) as u8;
341 }
342 });
343 }
344
345 #[cfg(feature = "v4l2")]
userptr_test_frame_generator( frame_count: u64, layout: FrameLayout, buffer_size: usize, ) -> impl Iterator<Item = (FrameMetadata, UserPtrFrame)>346 pub fn userptr_test_frame_generator(
347 frame_count: u64,
348 layout: FrameLayout,
349 buffer_size: usize,
350 ) -> impl Iterator<Item = (FrameMetadata, UserPtrFrame)> {
351 (0..frame_count).map(move |timestamp| {
352 let frame = UserPtrFrame::alloc(layout.clone(), buffer_size);
353
354 let t = get_test_frame_t(timestamp, frame_count);
355
356 if (frame.layout.format.0 == Fourcc::from(b"NM12")
357 || frame.layout.format.0 == Fourcc::from(b"NV12"))
358 && frame.layout.planes.len() == 2
359 && frame.buffers.len() == 2
360 {
361 // SAFETY: Slice matches the allocation
362 let y_plane = unsafe {
363 std::slice::from_raw_parts_mut(
364 frame.buffers[frame.layout.planes[0].buffer_index],
365 frame.mem_layout.size(),
366 )
367 };
368 // SAFETY: Slice matches the allocation
369 let uv_plane = unsafe {
370 std::slice::from_raw_parts_mut(
371 frame.buffers[frame.layout.planes[1].buffer_index],
372 frame.mem_layout.size(),
373 )
374 };
375
376 fill_test_frame_nm12(
377 frame.layout.size.width as usize,
378 frame.layout.size.height as usize,
379 [frame.layout.planes[0].stride, frame.layout.planes[1].stride],
380 t,
381 y_plane,
382 uv_plane,
383 );
384 } else if frame.layout.format.0 == Fourcc::from(b"NV12")
385 && frame.layout.planes.len() == 1
386 && frame.buffers.len() == 1
387 {
388 // SAFETY: Slice matches the allocation
389 let raw = unsafe {
390 std::slice::from_raw_parts_mut(
391 frame.buffers[frame.layout.planes[0].buffer_index]
392 .add(frame.layout.planes[0].offset),
393 frame.mem_layout.size(),
394 )
395 };
396
397 let uv_stride = frame.layout.planes[0].stride;
398 let uv_offset = frame.layout.planes[0].stride * (frame.layout.size.height as usize);
399
400 fill_test_frame_nv12(
401 frame.layout.size.width as usize,
402 frame.layout.size.height as usize,
403 [frame.layout.planes[0].stride, uv_stride],
404 [frame.layout.planes[0].offset, uv_offset],
405 t,
406 raw,
407 );
408 } else {
409 panic!("Unrecognized frame layout used during test");
410 }
411
412 let meta =
413 FrameMetadata { timestamp, layout: frame.layout.clone(), force_keyframe: false };
414
415 (meta, frame)
416 })
417 }
418 }
419