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