• 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::sync::Arc;
6 
7 use v4l2r::controls::codec::VideoGopSize;
8 use v4l2r::controls::codec::VideoH264IPeriod;
9 use v4l2r::controls::codec::VideoH264Level;
10 use v4l2r::controls::codec::VideoH264MaxQp;
11 use v4l2r::controls::codec::VideoH264MinQp;
12 use v4l2r::controls::codec::VideoH264Profile;
13 use v4l2r::controls::codec::VideoPrependSpsPpsToIdr;
14 use v4l2r::device::Device;
15 
16 use crate::backend::v4l2::encoder::CaptureBuffers;
17 use crate::backend::v4l2::encoder::ControlError;
18 use crate::backend::v4l2::encoder::EncoderCodec;
19 use crate::backend::v4l2::encoder::InitializationError;
20 use crate::backend::v4l2::encoder::OutputBufferHandle;
21 use crate::backend::v4l2::encoder::V4L2Backend;
22 use crate::codec::h264::parser::Level;
23 use crate::codec::h264::parser::Profile;
24 use crate::encoder::h264::EncoderConfig;
25 use crate::encoder::h264::H264;
26 use crate::encoder::stateful::StatefulEncoder;
27 use crate::encoder::PredictionStructure;
28 use crate::encoder::Tunings;
29 use crate::Fourcc;
30 use crate::Resolution;
31 
32 const PIXEL_FORMAT_H264: v4l2r::PixelFormat = v4l2r::PixelFormat::from_fourcc(b"H264");
33 
34 pub type V4L2H264Backend<Handle, CaptureBufferz> = V4L2Backend<Handle, CaptureBufferz, H264>;
35 
36 pub type V4L2StatefulH264Encoder<Handle, CaptureBufferz> =
37     StatefulEncoder<Handle, V4L2H264Backend<Handle, CaptureBufferz>>;
38 
39 impl From<Level> for VideoH264Level {
from(value: Level) -> Self40     fn from(value: Level) -> Self {
41         match value {
42             Level::L1 => VideoH264Level::L1_0,
43             Level::L1B => VideoH264Level::L1B,
44             Level::L1_1 => VideoH264Level::L1_1,
45             Level::L1_2 => VideoH264Level::L1_2,
46             Level::L1_3 => VideoH264Level::L1_3,
47             Level::L2_0 => VideoH264Level::L2_0,
48             Level::L2_1 => VideoH264Level::L2_1,
49             Level::L2_2 => VideoH264Level::L2_2,
50             Level::L3 => VideoH264Level::L3_0,
51             Level::L3_1 => VideoH264Level::L3_1,
52             Level::L3_2 => VideoH264Level::L3_2,
53             Level::L4 => VideoH264Level::L4_0,
54             Level::L4_1 => VideoH264Level::L4_1,
55             Level::L4_2 => VideoH264Level::L4_2,
56             Level::L5 => VideoH264Level::L5_0,
57             Level::L5_1 => VideoH264Level::L5_1,
58             Level::L5_2 => VideoH264Level::L5_2,
59             Level::L6 => VideoH264Level::L6_0,
60             Level::L6_1 => VideoH264Level::L6_1,
61             Level::L6_2 => VideoH264Level::L6_2,
62         }
63     }
64 }
65 
66 impl From<Profile> for VideoH264Profile {
from(value: Profile) -> Self67     fn from(value: Profile) -> Self {
68         match value {
69             Profile::Baseline => Self::Baseline,
70             Profile::Main => Self::Main,
71             Profile::Extended => Self::Extended,
72             Profile::High => Self::High,
73             Profile::High10 => Self::High10,
74             Profile::High422P => Self::High422,
75         }
76     }
77 }
78 
79 impl<Handle, CaptureBufferz> EncoderCodec for V4L2H264Backend<Handle, CaptureBufferz>
80 where
81     Handle: OutputBufferHandle,
82     CaptureBufferz: CaptureBuffers,
83 {
apply_tunings(device: &Device, tunings: &Tunings) -> Result<(), ControlError>84     fn apply_tunings(device: &Device, tunings: &Tunings) -> Result<(), ControlError> {
85         let min_qp = VideoH264MinQp(tunings.min_quality.clamp(1, 51) as i32);
86         Self::apply_ctrl(device, "h264 min qp", min_qp)?;
87 
88         let max_qp = VideoH264MaxQp(tunings.max_quality.clamp(1, 51) as i32);
89         Self::apply_ctrl(device, "h264 max qp", max_qp)?;
90 
91         Ok(())
92     }
93 }
94 
95 impl<Handle, CaptureBufferz> V4L2H264Backend<Handle, CaptureBufferz>
96 where
97     Handle: OutputBufferHandle,
98     CaptureBufferz: CaptureBuffers,
99 {
new( device: Arc<Device>, capture_buffers: CaptureBufferz, config: EncoderConfig, fourcc: Fourcc, coded_size: Resolution, tunings: Tunings, ) -> Result<Self, InitializationError>100     pub fn new(
101         device: Arc<Device>,
102         capture_buffers: CaptureBufferz,
103         config: EncoderConfig,
104         fourcc: Fourcc,
105         coded_size: Resolution,
106         tunings: Tunings,
107     ) -> Result<Self, InitializationError> {
108         match config.pred_structure {
109             PredictionStructure::LowDelay { limit } => {
110                 let limit = limit as i32;
111 
112                 Self::apply_ctrl(&device, "gop size", VideoGopSize(limit))?;
113                 Self::apply_ctrl(&device, "h264 i period", VideoH264IPeriod(limit))?;
114             }
115         }
116 
117         Self::apply_ctrl(&device, "prepend SPS PPS to IDR", VideoPrependSpsPpsToIdr(true))?;
118         Self::apply_ctrl(&device, "h264 profile", VideoH264Profile::from(config.profile))?;
119         Self::apply_ctrl(&device, "h264 level", VideoH264Level::from(config.level))?;
120 
121         Self::create(
122             device,
123             capture_buffers,
124             fourcc,
125             coded_size,
126             config.resolution,
127             PIXEL_FORMAT_H264,
128             tunings,
129         )
130     }
131 }
132 
133 impl<Handle, CaptureBufferz> V4L2StatefulH264Encoder<Handle, CaptureBufferz>
134 where
135     Handle: OutputBufferHandle,
136     CaptureBufferz: CaptureBuffers,
137 {
new( device: Arc<Device>, capture_buffers: CaptureBufferz, config: EncoderConfig, fourcc: Fourcc, coded_size: Resolution, tunings: Tunings, ) -> Result<Self, InitializationError>138     pub fn new(
139         device: Arc<Device>,
140         capture_buffers: CaptureBufferz,
141         config: EncoderConfig,
142         fourcc: Fourcc,
143         coded_size: Resolution,
144         tunings: Tunings,
145     ) -> Result<Self, InitializationError> {
146         Ok(Self::create(
147             tunings.clone(),
148             V4L2H264Backend::new(device, capture_buffers, config, fourcc, coded_size, tunings)?,
149         ))
150     }
151 }
152 
153 #[cfg(test)]
154 mod tests {
155     use super::*;
156 
157     use std::path::PathBuf;
158     use std::sync::Arc;
159 
160     use v4l2r::device::Device;
161     use v4l2r::device::DeviceConfig;
162 
163     use crate::backend::v4l2::encoder::find_device_with_capture;
164     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
165     use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
166     use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
167     use crate::backend::v4l2::encoder::tests::GbmDevice;
168     use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
169     use crate::backend::v4l2::encoder::MmapingCapture;
170     use crate::encoder::simple_encode_loop;
171     use crate::encoder::tests::userptr_test_frame_generator;
172     use crate::encoder::RateControl;
173     use crate::utils::DmabufFrame;
174     use crate::Resolution;
175 
176     #[ignore]
177     // Ignore this test by default as it requires v4l2m2m-compatible hardware.
178     #[test]
test_v4l2_encoder_userptr()179     fn test_v4l2_encoder_userptr() {
180         const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
181         const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
182         const FRAME_COUNT: u64 = 100;
183 
184         let _ = env_logger::try_init();
185 
186         let device = find_device_with_capture(PIXEL_FORMAT_H264).expect("no H264 encoder found");
187         let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
188         let device = Arc::new(device);
189 
190         let mut encoder = V4L2StatefulH264Encoder::new(
191             device,
192             MmapingCapture,
193             EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
194             Fourcc::from(b"NM12"),
195             CODED_SIZE,
196             Tunings { rate_control: RateControl::ConstantBitrate(400_000), ..Default::default() },
197         )
198         .unwrap();
199 
200         let format: v4l2r::Format = encoder.backend().output_format().unwrap();
201         let layout = v4l2_format_to_frame_layout(&format);
202 
203         let mut bitstream = Vec::new();
204         let buffer_size =
205             format.plane_fmt.iter().map(|plane| plane.sizeimage).max().unwrap() as usize;
206         let mut frame_producer = userptr_test_frame_generator(FRAME_COUNT, layout, buffer_size);
207 
208         simple_encode_loop(&mut encoder, &mut frame_producer, |coded| {
209             bitstream.extend(coded.bitstream)
210         })
211         .expect("encode loop");
212 
213         let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
214         if write_to_file {
215             use std::io::Write;
216             let mut out = std::fs::File::create("test_v4l2_encoder_userptr.h264").unwrap();
217             out.write_all(&bitstream).unwrap();
218             out.flush().unwrap();
219         }
220     }
221 
222     #[ignore]
223     // Ignore this test by default as it requires v4l2m2m-compatible hardware.
224     #[test]
test_v4l2_encoder_mmap()225     fn test_v4l2_encoder_mmap() {
226         const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
227         const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
228         const FRAME_COUNT: u64 = 100;
229 
230         let _ = env_logger::try_init();
231 
232         let device = find_device_with_capture(PIXEL_FORMAT_H264).expect("no H264 encoder found");
233         let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
234         let device = Arc::new(device);
235 
236         let encoder = V4L2StatefulH264Encoder::new(
237             device,
238             MmapingCapture,
239             EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
240             Fourcc::from(b"NM12"),
241             CODED_SIZE,
242             Tunings { rate_control: RateControl::ConstantBitrate(400_000), ..Default::default() },
243         )
244         .unwrap();
245 
246         let mut bitstream = Vec::new();
247         perform_v4l2_encoder_mmap_test(FRAME_COUNT, encoder, |coded| {
248             bitstream.extend(coded.bitstream)
249         });
250 
251         let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
252         if write_to_file {
253             use std::io::Write;
254             let mut out = std::fs::File::create("test_v4l2_encoder_mmap.h264").unwrap();
255             out.write_all(&bitstream).unwrap();
256             out.flush().unwrap();
257         }
258     }
259 
260     #[ignore]
261     // Ignore this test by default as it requires v4l2m2m-compatible hardware.
262     #[test]
test_v4l2_encoder_dmabuf()263     fn test_v4l2_encoder_dmabuf() {
264         const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
265         const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
266         const FRAME_COUNT: u64 = 100;
267 
268         let _ = env_logger::try_init();
269 
270         let device = find_device_with_capture(PIXEL_FORMAT_H264).expect("no H264 encoder found");
271         let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
272         let device = Arc::new(device);
273 
274         let gbm = GbmDevice::open(PathBuf::from("/dev/dri/renderD128"))
275             .and_then(gbm::Device::new)
276             .expect("failed to create GBM device");
277 
278         let gbm = Arc::new(gbm);
279 
280         let encoder = V4L2StatefulH264Encoder::<DmabufFrame, _>::new(
281             device.clone(),
282             BoPoolAllocator::new(gbm.clone()),
283             EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
284             Fourcc::from(b"NV12"),
285             CODED_SIZE,
286             Tunings {
287                 framerate: 30,
288                 rate_control: RateControl::ConstantBitrate(400_000),
289                 ..Default::default()
290             },
291         )
292         .unwrap();
293 
294         let mut bitstream = Vec::new();
295 
296         perform_v4l2_encoder_dmabuf_test(
297             CODED_SIZE,
298             VISIBLE_SIZE,
299             FRAME_COUNT,
300             gbm,
301             encoder,
302             |coded| bitstream.extend(coded.bitstream),
303         );
304 
305         let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
306         if write_to_file {
307             use std::io::Write;
308             let mut out = std::fs::File::create("test_v4l2_encoder_dmabuf.h264").unwrap();
309             out.write_all(&bitstream).unwrap();
310             out.flush().unwrap();
311         }
312     }
313 }
314