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