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::any::Any; 6 use std::borrow::Borrow; 7 use std::rc::Rc; 8 9 use anyhow::Context; 10 use libva::BufferType; 11 use libva::Display; 12 use libva::EncCodedBuffer; 13 use libva::EncPictureParameter; 14 use libva::EncPictureParameterBufferH264; 15 use libva::EncSequenceParameter; 16 use libva::EncSequenceParameterBufferH264; 17 use libva::EncSliceParameter; 18 use libva::EncSliceParameterBufferH264; 19 use libva::H264EncFrameCropOffsets; 20 use libva::H264EncPicFields; 21 use libva::H264EncSeqFields; 22 use libva::H264VuiFields; 23 use libva::Picture; 24 use libva::PictureH264; 25 use libva::Surface; 26 use libva::SurfaceMemoryDescriptor; 27 use libva::VAProfile; 28 use libva::VA_INVALID_ID; 29 use libva::VA_PICTURE_H264_LONG_TERM_REFERENCE; 30 use libva::VA_PICTURE_H264_SHORT_TERM_REFERENCE; 31 32 use crate::backend::vaapi::encoder::tunings_to_libva_rc; 33 use crate::backend::vaapi::encoder::CodedOutputPromise; 34 use crate::backend::vaapi::encoder::Reconstructed; 35 use crate::backend::vaapi::encoder::VaapiBackend; 36 use crate::codec::h264::parser::Pps; 37 use crate::codec::h264::parser::Profile; 38 use crate::codec::h264::parser::SliceHeader; 39 use crate::codec::h264::parser::Sps; 40 use crate::encoder::h264::EncoderConfig; 41 use crate::encoder::h264::H264; 42 use crate::encoder::stateless::h264::predictor::MAX_QP; 43 use crate::encoder::stateless::h264::predictor::MIN_QP; 44 use crate::encoder::stateless::h264::BackendRequest; 45 use crate::encoder::stateless::h264::DpbEntry; 46 use crate::encoder::stateless::h264::DpbEntryMeta; 47 use crate::encoder::stateless::h264::IsReference; 48 use crate::encoder::stateless::h264::StatelessEncoder; 49 use crate::encoder::stateless::h264::StatelessH264EncoderBackend; 50 use crate::encoder::stateless::ReadyPromise; 51 use crate::encoder::stateless::StatelessBackendError; 52 use crate::encoder::stateless::StatelessBackendResult; 53 use crate::encoder::stateless::StatelessVideoEncoderBackend; 54 use crate::encoder::EncodeResult; 55 use crate::encoder::RateControl; 56 use crate::video_frame::VideoFrame; 57 use crate::BlockingMode; 58 use crate::Fourcc; 59 use crate::Resolution; 60 61 type Request<'l, H> = BackendRequest<H, Reconstructed>; 62 63 impl<M, H> StatelessVideoEncoderBackend<H264> for VaapiBackend<M, H> 64 where 65 M: SurfaceMemoryDescriptor, 66 H: std::borrow::Borrow<Surface<M>> + 'static, 67 { 68 type Picture = H; 69 type Reconstructed = Reconstructed; 70 type CodedPromise = CodedOutputPromise<M, H>; 71 type ReconPromise = ReadyPromise<Self::Reconstructed>; 72 } 73 74 impl<M, H> VaapiBackend<M, H> 75 where 76 M: SurfaceMemoryDescriptor, 77 H: std::borrow::Borrow<Surface<M>> + 'static, 78 { 79 /// Builds an invalid [`libva::PictureH264`]. This is usually a place 80 /// holder to fill staticly sized array. build_invalid_va_h264_pic_enc() -> libva::PictureH26481 fn build_invalid_va_h264_pic_enc() -> libva::PictureH264 { 82 libva::PictureH264::new(libva::VA_INVALID_ID, 0, libva::VA_PICTURE_H264_INVALID, 0, 0) 83 } 84 85 /// Builds [`libva::PictureH264`] from `frame` build_h264_pic(surface: &Reconstructed, meta: &DpbEntryMeta) -> PictureH26486 fn build_h264_pic(surface: &Reconstructed, meta: &DpbEntryMeta) -> PictureH264 { 87 let flags = match meta.is_reference { 88 IsReference::No => 0, 89 IsReference::LongTerm => VA_PICTURE_H264_LONG_TERM_REFERENCE, 90 IsReference::ShortTerm => VA_PICTURE_H264_SHORT_TERM_REFERENCE, 91 }; 92 93 PictureH264::new( 94 surface.surface_id(), 95 meta.frame_num, 96 flags, 97 meta.poc as i32, 98 meta.poc as i32, 99 ) 100 } 101 102 /// Builds [`BufferType::EncSequenceParameter`] from `sps` build_enc_seq_param( sps: &Sps, bits_per_second: u32, intra_period: u32, ip_period: u32, ) -> BufferType103 fn build_enc_seq_param( 104 sps: &Sps, 105 bits_per_second: u32, 106 intra_period: u32, 107 ip_period: u32, 108 ) -> BufferType { 109 let intra_idr_period = intra_period; 110 111 let seq_fields = H264EncSeqFields::new( 112 sps.chroma_format_idc as u32, 113 sps.frame_mbs_only_flag as u32, 114 sps.mb_adaptive_frame_field_flag as u32, 115 sps.seq_scaling_matrix_present_flag as u32, 116 sps.direct_8x8_inference_flag as u32, 117 sps.log2_max_frame_num_minus4 as u32, 118 sps.pic_order_cnt_type as u32, 119 sps.log2_max_pic_order_cnt_lsb_minus4 as u32, 120 sps.delta_pic_order_always_zero_flag as u32, 121 ); 122 123 let frame_crop = if sps.frame_cropping_flag { 124 Some(H264EncFrameCropOffsets::new( 125 sps.frame_crop_left_offset, 126 sps.frame_crop_right_offset, 127 sps.frame_crop_top_offset, 128 sps.frame_crop_bottom_offset, 129 )) 130 } else { 131 None 132 }; 133 134 let vui_fields = if sps.vui_parameters_present_flag { 135 Some(H264VuiFields::new( 136 sps.vui_parameters.aspect_ratio_idc as u32, 137 sps.vui_parameters.timing_info_present_flag as u32, 138 sps.vui_parameters.bitstream_restriction_flag as u32, 139 sps.vui_parameters.log2_max_mv_length_horizontal, 140 sps.vui_parameters.log2_max_mv_length_vertical, 141 sps.vui_parameters.fixed_frame_rate_flag as u32, 142 sps.vui_parameters.low_delay_hrd_flag as u32, 143 sps.vui_parameters.motion_vectors_over_pic_boundaries_flag as u32, 144 )) 145 } else { 146 None 147 }; 148 149 let mut offset_for_ref_frame = [0i32; 256]; 150 offset_for_ref_frame[..255].copy_from_slice(&sps.offset_for_ref_frame[..]); 151 152 BufferType::EncSequenceParameter(EncSequenceParameter::H264( 153 EncSequenceParameterBufferH264::new( 154 sps.seq_parameter_set_id, 155 sps.level_idc as u8, 156 intra_period, 157 intra_idr_period, 158 ip_period, 159 bits_per_second, 160 sps.max_num_ref_frames as u32, 161 sps.pic_width_in_mbs_minus1 + 1, 162 sps.pic_height_in_map_units_minus1 + 1, 163 &seq_fields, 164 sps.bit_depth_luma_minus8, 165 sps.bit_depth_chroma_minus8, 166 sps.num_ref_frames_in_pic_order_cnt_cycle, 167 sps.offset_for_non_ref_pic, 168 sps.offset_for_top_to_bottom_field, 169 offset_for_ref_frame, 170 frame_crop, 171 vui_fields, 172 sps.vui_parameters.aspect_ratio_idc, 173 sps.vui_parameters.sar_width as u32, 174 sps.vui_parameters.sar_height as u32, 175 sps.vui_parameters.num_units_in_tick, 176 sps.vui_parameters.time_scale, 177 ), 178 )) 179 } 180 181 /// Builds [`BufferType::EncPictureParameter`] from [`Request`] and sets bitstream 182 /// output to `coded_buf`. build_enc_pic_param( request: &Request<'_, H>, coded_buf: &EncCodedBuffer, recon: &Reconstructed, ) -> BufferType183 fn build_enc_pic_param( 184 request: &Request<'_, H>, 185 coded_buf: &EncCodedBuffer, 186 recon: &Reconstructed, 187 ) -> BufferType { 188 let pic_fields = H264EncPicFields::new( 189 request.is_idr as u32, 190 (request.dpb_meta.is_reference != IsReference::No) as u32, 191 request.pps.entropy_coding_mode_flag as u32, 192 request.pps.weighted_pred_flag as u32, 193 request.pps.weighted_bipred_idc as u32, 194 request.pps.constrained_intra_pred_flag as u32, 195 request.pps.transform_8x8_mode_flag as u32, 196 request.pps.deblocking_filter_control_present_flag as u32, 197 request.pps.redundant_pic_cnt_present_flag as u32, 198 0, 199 request.pps.pic_scaling_matrix_present_flag as u32, 200 ); 201 202 let curr_pic = Self::build_h264_pic(recon, &request.dpb_meta); 203 204 assert!(request.ref_list_0.len() + request.ref_list_1.len() <= 16); 205 206 let mut reference_frames: [PictureH264; 16] = (0..16) 207 .map(|_| Self::build_invalid_va_h264_pic_enc()) 208 .collect::<Vec<_>>() 209 .try_into() 210 .unwrap_or_else(|_| panic!()); 211 212 for (idx, ref_frame) in 213 request.ref_list_0.iter().chain(request.ref_list_1.iter()).enumerate().take(16) 214 { 215 reference_frames[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta); 216 } 217 218 BufferType::EncPictureParameter(EncPictureParameter::H264( 219 EncPictureParameterBufferH264::new( 220 curr_pic, 221 reference_frames, 222 coded_buf.id(), 223 request.pps.pic_parameter_set_id, 224 request.pps.seq_parameter_set_id, 225 0, // last_pic, don't appned EOS 226 request.dpb_meta.frame_num as u16, 227 (request.pps.pic_init_qp_minus26 + 26) as u8, 228 request.pps.num_ref_idx_l0_default_active_minus1, 229 request.pps.num_ref_idx_l1_default_active_minus1, 230 request.pps.chroma_qp_index_offset, 231 request.pps.second_chroma_qp_index_offset, 232 &pic_fields, 233 ), 234 )) 235 } 236 237 /// Builds [`BufferType::EncSliceParameter`] build_enc_slice_param( pps: &Pps, header: &SliceHeader, ref_list_0: &[Rc<DpbEntry<Reconstructed>>], ref_list_1: &[Rc<DpbEntry<Reconstructed>>], num_macroblocks: u32, ) -> BufferType238 fn build_enc_slice_param( 239 pps: &Pps, 240 header: &SliceHeader, 241 ref_list_0: &[Rc<DpbEntry<Reconstructed>>], 242 ref_list_1: &[Rc<DpbEntry<Reconstructed>>], 243 num_macroblocks: u32, 244 ) -> BufferType { 245 let mut ref_pic_list_0: [PictureH264; 32] = (0..32) 246 .map(|_| Self::build_invalid_va_h264_pic_enc()) 247 .collect::<Vec<_>>() 248 .try_into() 249 .unwrap_or_else(|_| panic!()); 250 251 for (idx, ref_frame) in ref_list_0.iter().enumerate().take(16) { 252 ref_pic_list_0[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta); 253 } 254 255 let mut ref_pic_list_1: [PictureH264; 32] = (0..32) 256 .map(|_| Self::build_invalid_va_h264_pic_enc()) 257 .collect::<Vec<_>>() 258 .try_into() 259 .unwrap_or_else(|_| panic!()); 260 261 for (idx, ref_frame) in ref_list_1.iter().enumerate().take(16) { 262 ref_pic_list_1[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta); 263 } 264 265 let mut luma_weight_l0_flag = false; 266 let mut luma_offset_l0 = [0i16; 32]; 267 268 if header.pred_weight_table.luma_weight_l0 != [0i16; 32] { 269 luma_weight_l0_flag = true; 270 for (i, val) in header.pred_weight_table.luma_offset_l0.iter().enumerate() { 271 luma_offset_l0[i] = (*val).into(); 272 } 273 } 274 275 let mut chroma_weight_l0_flag = false; 276 let mut chroma_offset_l0 = [[0i16; 2]; 32]; 277 278 if header.pred_weight_table.chroma_weight_l0 != [[0i16; 2]; 32] { 279 chroma_weight_l0_flag = true; 280 for (i, val) in header.pred_weight_table.chroma_offset_l0.iter().enumerate() { 281 chroma_offset_l0[i] = [val[0].into(), val[1].into()]; 282 } 283 } 284 285 let mut luma_weight_l1_flag = false; 286 let mut luma_offset_l1 = [0i16; 32]; 287 288 if header.pred_weight_table.luma_weight_l1 != [0i16; 32] { 289 luma_weight_l1_flag = true; 290 for (i, val) in header.pred_weight_table.luma_offset_l1.iter().enumerate() { 291 luma_offset_l1[i] = *val; 292 } 293 } 294 295 let mut chroma_weight_l1_flag = false; 296 let mut chroma_offset_l1 = [[0i16; 2]; 32]; 297 298 if header.pred_weight_table.chroma_weight_l1 != [[0i16; 2]; 32] { 299 chroma_weight_l1_flag = true; 300 for (i, val) in header.pred_weight_table.chroma_offset_l1.iter().enumerate() { 301 chroma_offset_l1[i] = [val[0].into(), val[1].into()]; 302 } 303 } 304 305 let (num_ref_idx_l0_active_minus1, num_ref_idx_l1_active_minus1) = 306 if header.num_ref_idx_active_override_flag { 307 (header.num_ref_idx_l0_active_minus1, header.num_ref_idx_l1_active_minus1) 308 } else { 309 (pps.num_ref_idx_l0_default_active_minus1, pps.num_ref_idx_l1_default_active_minus1) 310 }; 311 BufferType::EncSliceParameter(EncSliceParameter::H264(EncSliceParameterBufferH264::new( 312 header.first_mb_in_slice, 313 num_macroblocks, 314 VA_INVALID_ID, 315 header.slice_type as u8, 316 pps.pic_parameter_set_id, 317 header.idr_pic_id, 318 header.pic_order_cnt_lsb, 319 header.delta_pic_order_cnt_bottom, 320 header.delta_pic_order_cnt, 321 header.direct_spatial_mv_pred_flag as u8, 322 header.num_ref_idx_active_override_flag as u8, 323 num_ref_idx_l0_active_minus1, 324 num_ref_idx_l1_active_minus1, 325 ref_pic_list_0, 326 ref_pic_list_1, 327 header.pred_weight_table.luma_log2_weight_denom, 328 header.pred_weight_table.chroma_log2_weight_denom, 329 luma_weight_l0_flag as u8, 330 header.pred_weight_table.luma_weight_l0, 331 luma_offset_l0, 332 chroma_weight_l0_flag as u8, 333 header.pred_weight_table.chroma_weight_l0, 334 chroma_offset_l0, 335 luma_weight_l1_flag as u8, 336 header.pred_weight_table.luma_weight_l1, 337 luma_offset_l1, 338 chroma_weight_l1_flag as u8, 339 header.pred_weight_table.chroma_weight_l1, 340 chroma_offset_l1, 341 header.cabac_init_idc, 342 header.slice_qp_delta, 343 header.disable_deblocking_filter_idc, 344 header.slice_alpha_c0_offset_div2, 345 header.slice_beta_offset_div2, 346 ))) 347 } 348 } 349 350 impl<M, H> StatelessH264EncoderBackend for VaapiBackend<M, H> 351 where 352 M: SurfaceMemoryDescriptor, 353 H: Borrow<Surface<M>> + 'static, 354 { encode_slice( &mut self, request: Request<'_, H>, ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)>355 fn encode_slice( 356 &mut self, 357 request: Request<'_, H>, 358 ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)> { 359 let coded_buf = self.new_coded_buffer(&request.tunings.rate_control)?; 360 let recon = self.new_scratch_picture()?; 361 362 // Use bitrate from RateControl or ask driver to ignore 363 let bits_per_second = request.tunings.rate_control.bitrate_target().unwrap_or(0) as u32; 364 let seq_param = Self::build_enc_seq_param( 365 &request.sps, 366 bits_per_second, 367 request.intra_period, 368 request.ip_period, 369 ); 370 371 let pic_param = Self::build_enc_pic_param(&request, &coded_buf, &recon); 372 let slice_param = Self::build_enc_slice_param( 373 &request.pps, 374 &request.header, 375 &request.ref_list_0, 376 &request.ref_list_1, 377 request.num_macroblocks as u32, 378 ); 379 380 // Clone reference frames 381 let references: Vec<Rc<dyn Any>> = request 382 .ref_list_0 383 .iter() 384 .cloned() 385 .chain(request.ref_list_1.iter().cloned()) 386 .map(|entry| entry as Rc<dyn Any>) 387 .collect(); 388 389 // Clone picture using [`Picture::new_from_same_surface`] to avoid 390 // creatig a shared cell picture between its references and processed 391 // picture. 392 let mut picture = Picture::new( 393 request.dpb_meta.frame_num as u64, 394 Rc::clone(self.context()), 395 request.input, 396 ); 397 398 let rc_param = 399 tunings_to_libva_rc::<{ MIN_QP as u32 }, { MAX_QP as u32 }>(&request.tunings)?; 400 let rc_param = BufferType::EncMiscParameter(libva::EncMiscParameter::RateControl(rc_param)); 401 402 let framerate_param = BufferType::EncMiscParameter(libva::EncMiscParameter::FrameRate( 403 libva::EncMiscParameterFrameRate::new(request.tunings.framerate, 0), 404 )); 405 406 picture.add_buffer(self.context().create_buffer(seq_param)?); 407 picture.add_buffer(self.context().create_buffer(pic_param)?); 408 picture.add_buffer(self.context().create_buffer(slice_param)?); 409 picture.add_buffer(self.context().create_buffer(rc_param)?); 410 picture.add_buffer(self.context().create_buffer(framerate_param)?); 411 412 // Start processing the picture encoding 413 let picture = picture.begin().context("picture begin")?; 414 let picture = picture.render().context("picture render")?; 415 let picture = picture.end().context("picture end")?; 416 417 // HACK: Make sure that slice nalu start code is at least 4 bytes. 418 // TODO: Use packed headers to supply slice header with nalu start code of size 4 and get 419 // rid of this hack. 420 let mut coded_output = request.coded_output; 421 coded_output.push(0); 422 423 // libva will handle the synchronization of reconstructed surface with implicit fences. 424 // Therefore return the reconstructed frame immediately. 425 let reference_promise = ReadyPromise::from(recon); 426 427 let bitstream_promise = 428 CodedOutputPromise::new(picture, references, coded_buf, coded_output); 429 430 Ok((reference_promise, bitstream_promise)) 431 } 432 } 433 434 impl<V: VideoFrame> StatelessEncoder<V, VaapiBackend<V::MemDescriptor, Surface<V::MemDescriptor>>> { new_vaapi( display: Rc<Display>, config: EncoderConfig, fourcc: Fourcc, coded_size: Resolution, low_power: bool, blocking_mode: BlockingMode, ) -> EncodeResult<Self>435 pub fn new_vaapi( 436 display: Rc<Display>, 437 config: EncoderConfig, 438 fourcc: Fourcc, 439 coded_size: Resolution, 440 low_power: bool, 441 blocking_mode: BlockingMode, 442 ) -> EncodeResult<Self> { 443 let va_profile = match config.profile { 444 Profile::Baseline => VAProfile::VAProfileH264ConstrainedBaseline, 445 Profile::Main => VAProfile::VAProfileH264Main, 446 Profile::High => VAProfile::VAProfileH264High, 447 _ => return Err(StatelessBackendError::UnsupportedProfile.into()), 448 }; 449 450 let bitrate_control = match config.initial_tunings.rate_control { 451 RateControl::ConstantBitrate(_) => libva::VA_RC_CBR, 452 RateControl::ConstantQuality(_) => libva::VA_RC_CQP, 453 }; 454 455 let backend = 456 VaapiBackend::new(display, va_profile, fourcc, coded_size, bitrate_control, low_power)?; 457 458 Self::new_h264(backend, config, blocking_mode) 459 } 460 } 461 462 #[cfg(test)] 463 pub(super) mod tests { 464 use libva::Display; 465 use libva::UsageHint; 466 use libva::VAEntrypoint::VAEntrypointEncSliceLP; 467 use libva::VAProfile::VAProfileH264Main; 468 use libva::VA_RT_FORMAT_YUV420; 469 470 use super::*; 471 use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12; 472 use crate::backend::vaapi::encoder::tests::TestFrameGenerator; 473 use crate::backend::vaapi::surface_pool::PooledVaSurface; 474 use crate::backend::vaapi::surface_pool::VaSurfacePool; 475 use crate::codec::h264::parser::Level; 476 use crate::codec::h264::parser::PpsBuilder; 477 use crate::codec::h264::parser::Profile; 478 use crate::codec::h264::parser::SliceHeaderBuilder; 479 use crate::codec::h264::parser::SliceType; 480 use crate::codec::h264::parser::SpsBuilder; 481 use crate::decoder::FramePool; 482 use crate::encoder::simple_encode_loop; 483 use crate::encoder::stateless::h264::BackendRequest; 484 use crate::encoder::stateless::h264::EncoderConfig; 485 use crate::encoder::stateless::h264::StatelessEncoder; 486 use crate::encoder::stateless::BackendPromise; 487 use crate::encoder::stateless::StatelessEncoderBackendImport; 488 use crate::encoder::FrameMetadata; 489 use crate::encoder::Tunings; 490 use crate::FrameLayout; 491 use crate::PlaneLayout; 492 use crate::Resolution; 493 494 #[test] 495 // Ignore this test by default as it requires libva-compatible hardware. 496 #[ignore] test_simple_encode_slice()497 fn test_simple_encode_slice() { 498 type Descriptor = (); 499 type Surface = libva::Surface<Descriptor>; 500 const WIDTH: u32 = 256; 501 const HEIGHT: u32 = 256; 502 let fourcc = b"NV12".into(); 503 504 let frame_layout = FrameLayout { 505 format: (fourcc, 0), 506 size: Resolution { width: WIDTH, height: HEIGHT }, 507 planes: vec![ 508 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH as usize }, 509 PlaneLayout { 510 buffer_index: 0, 511 offset: (WIDTH * HEIGHT) as usize, 512 stride: WIDTH as usize, 513 }, 514 ], 515 }; 516 517 let display = Display::open().unwrap(); 518 let entrypoints = display.query_config_entrypoints(VAProfileH264Main).unwrap(); 519 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 520 521 let mut backend = VaapiBackend::<Descriptor, Surface>::new( 522 Rc::clone(&display), 523 VAProfileH264Main, 524 fourcc, 525 Resolution { width: WIDTH, height: HEIGHT }, 526 libva::VA_RC_CBR, 527 low_power, 528 ) 529 .unwrap(); 530 531 let mut surfaces = display 532 .create_surfaces( 533 VA_RT_FORMAT_YUV420, 534 Some(frame_layout.format.0 .0), 535 WIDTH, 536 HEIGHT, 537 Some(UsageHint::USAGE_HINT_ENCODER), 538 vec![()], 539 ) 540 .unwrap(); 541 542 let surface = surfaces.pop().unwrap(); 543 544 upload_test_frame_nv12(&display, &surface, 0.0); 545 546 let input_meta = 547 FrameMetadata { layout: frame_layout, force_keyframe: false, timestamp: 0 }; 548 549 let pic = backend.import_picture(&input_meta, surface).unwrap(); 550 551 let sps = SpsBuilder::new() 552 .seq_parameter_set_id(0) 553 .profile_idc(Profile::Main) 554 .level_idc(Level::L4) 555 .resolution(WIDTH, HEIGHT) 556 .chroma_format_idc(3) 557 .frame_mbs_only_flag(true) 558 .direct_8x8_inference_flag(true) 559 .max_num_ref_frames(1) 560 .max_frame_num(32) 561 .pic_order_cnt_type(0) 562 .max_pic_order_cnt_lsb(128) 563 .delta_pic_order_always_zero_flag(false) 564 .bit_depth_chroma(8) 565 .bit_depth_luma(8) 566 .sar_resolution(1, 1) 567 .build(); 568 569 let pps = PpsBuilder::new(Rc::clone(&sps)) 570 .pic_parameter_set_id(0) 571 .pic_init_qp_minus26(0) 572 .deblocking_filter_control_present_flag(true) 573 .build(); 574 575 let header = SliceHeaderBuilder::new(&pps) 576 .slice_type(SliceType::I) 577 .first_mb_in_slice(0) 578 .idr_pic_id(0) 579 .build(); 580 581 let dpb_entry_meta = 582 DpbEntryMeta { poc: 0, frame_num: 0, is_reference: IsReference::ShortTerm }; 583 584 let request = BackendRequest { 585 sps: Rc::clone(&sps), 586 pps: Rc::clone(&pps), 587 header, 588 dpb_meta: dpb_entry_meta, 589 input: pic, 590 input_meta, 591 ref_list_0: vec![], 592 ref_list_1: vec![], 593 intra_period: 1, 594 ip_period: 0, 595 num_macroblocks: (WIDTH * HEIGHT) as usize / (16 * 16), 596 is_idr: true, 597 tunings: Tunings { 598 rate_control: RateControl::ConstantBitrate(30_000), 599 ..Default::default() 600 }, 601 coded_output: vec![], 602 }; 603 604 let (_, output) = backend.encode_slice(request).unwrap(); 605 let output = output.sync().unwrap(); 606 607 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 608 if write_to_file { 609 use std::io::Write; 610 611 use crate::codec::h264::synthesizer::Synthesizer; 612 let mut out = std::fs::File::create("test_simple_encode_slice.h264").unwrap(); 613 614 Synthesizer::<'_, Sps, &mut std::fs::File>::synthesize(3, &sps, &mut out, true) 615 .unwrap(); 616 Synthesizer::<'_, Pps, &mut std::fs::File>::synthesize(3, &pps, &mut out, true) 617 .unwrap(); 618 out.write_all(&output).unwrap(); 619 out.flush().unwrap(); 620 } 621 } 622 623 #[test] 624 // Ignore this test by default as it requires libva-compatible hardware. 625 #[ignore] test_vaapi_encoder()626 fn test_vaapi_encoder() { 627 type VaapiH264Encoder<'l> = 628 StatelessEncoder<PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>; 629 630 const WIDTH: usize = 512; 631 const HEIGHT: usize = 512; 632 633 let _ = env_logger::try_init(); 634 635 let display = libva::Display::open().unwrap(); 636 let entrypoints = display.query_config_entrypoints(VAProfileH264Main).unwrap(); 637 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 638 639 let config = EncoderConfig { 640 profile: Profile::Main, 641 resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 642 initial_tunings: Tunings { 643 rate_control: RateControl::ConstantBitrate(1_200_000), 644 framerate: 30, 645 ..Default::default() 646 }, 647 ..Default::default() 648 }; 649 650 let frame_layout = FrameLayout { 651 format: (b"NV12".into(), 0), 652 size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 653 planes: vec![ 654 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH }, 655 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH }, 656 ], 657 }; 658 659 let mut encoder = VaapiH264Encoder::new_vaapi( 660 Rc::clone(&display), 661 config, 662 frame_layout.format.0, 663 frame_layout.size, 664 low_power, 665 BlockingMode::Blocking, 666 ) 667 .unwrap(); 668 669 let mut pool = VaSurfacePool::new( 670 Rc::clone(&display), 671 VA_RT_FORMAT_YUV420, 672 Some(UsageHint::USAGE_HINT_ENCODER), 673 Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 674 ); 675 676 pool.add_frames(vec![(); 16]).unwrap(); 677 678 let mut frame_producer = TestFrameGenerator::new(100, display, pool, frame_layout); 679 680 let mut bitstream = Vec::new(); 681 682 simple_encode_loop(&mut encoder, &mut frame_producer, |coded| { 683 bitstream.extend(coded.bitstream) 684 }) 685 .unwrap(); 686 687 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 688 if write_to_file { 689 use std::io::Write; 690 let mut out = std::fs::File::create("test_vaapi_encoder.h264").unwrap(); 691 out.write_all(&bitstream).unwrap(); 692 out.flush().unwrap(); 693 } 694 } 695 } 696