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