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