• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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