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::num::TryFromIntError; 6 use std::rc::Rc; 7 8 use libva::AV1EncLoopFilterFlags; 9 use libva::AV1EncLoopRestorationFlags; 10 use libva::AV1EncModeControlFlags; 11 use libva::AV1EncPictureFlags; 12 use libva::AV1EncQMatrixFlags; 13 use libva::AV1EncSeqFields; 14 use libva::AV1EncTileGroupObuHdrInfo; 15 use libva::EncCodedBuffer; 16 use libva::EncPictureParameterBufferAV1; 17 use libva::EncSegParamAV1; 18 use libva::EncSegParamFlagsAV1; 19 use libva::EncSequenceParameterBufferAV1; 20 use libva::EncTileGroupBufferAV1; 21 use libva::Picture; 22 use libva::RefFrameCtrlAV1; 23 use libva::Surface; 24 use libva::SurfaceMemoryDescriptor; 25 use libva::VAProfile::VAProfileAV1Profile0; 26 use libva::VAProfile::VAProfileAV1Profile1; 27 use libva::VaError; 28 use libva::VA_INVALID_ID; 29 30 use crate::backend::vaapi::encoder::CodedOutputPromise; 31 use crate::backend::vaapi::encoder::Reconstructed; 32 use crate::backend::vaapi::encoder::VaapiBackend; 33 use crate::codec::av1::parser::Profile; 34 use crate::codec::av1::parser::ReferenceFrameType; 35 use crate::codec::av1::parser::CDEF_MAX; 36 use crate::codec::av1::parser::MAX_SEGMENTS; 37 use crate::codec::av1::parser::MAX_TILE_COLS; 38 use crate::codec::av1::parser::MAX_TILE_ROWS; 39 use crate::codec::av1::parser::REFS_PER_FRAME; 40 use crate::codec::av1::parser::SEG_LVL_MAX; 41 use crate::encoder::av1::EncoderConfig; 42 use crate::encoder::av1::AV1; 43 use crate::encoder::stateless::av1::predictor::MAX_BASE_QINDEX; 44 use crate::encoder::stateless::av1::predictor::MIN_BASE_QINDEX; 45 use crate::encoder::stateless::av1::BackendRequest; 46 use crate::encoder::stateless::av1::StatelessAV1EncoderBackend; 47 use crate::encoder::stateless::ReadyPromise; 48 use crate::encoder::stateless::StatelessBackendError; 49 use crate::encoder::stateless::StatelessBackendResult; 50 use crate::encoder::stateless::StatelessEncoder; 51 use crate::encoder::stateless::StatelessVideoEncoderBackend; 52 use crate::encoder::EncodeError; 53 use crate::encoder::EncodeResult; 54 use crate::encoder::RateControl; 55 use crate::video_frame::VideoFrame; 56 use crate::BlockingMode; 57 use crate::Fourcc; 58 use crate::Resolution; 59 60 type Request<H> = BackendRequest<H, Reconstructed>; 61 62 #[derive(thiserror::Error, Debug)] 63 pub enum BackendError { 64 #[error(transparent)] 65 ConversionError(#[from] TryFromIntError), 66 67 #[error("vaBeginPicture failed: {0}")] 68 BeginPictureError(VaError), 69 #[error("vaRenderPicture failed: {0}")] 70 RenderPictureError(VaError), 71 #[error("vaRenderPicture failed: {0}")] 72 EndPictureError(VaError), 73 } 74 75 impl From<BackendError> for StatelessBackendError { from(value: BackendError) -> Self76 fn from(value: BackendError) -> Self { 77 StatelessBackendError::Other(anyhow::anyhow!(value)) 78 } 79 } 80 81 type Result<T> = std::result::Result<T, BackendError>; 82 83 impl<M, Handle> StatelessVideoEncoderBackend<AV1> for VaapiBackend<M, Handle> 84 where 85 M: SurfaceMemoryDescriptor, 86 Handle: std::borrow::Borrow<Surface<M>>, 87 { 88 type Picture = Handle; 89 type Reconstructed = Reconstructed; 90 type CodedPromise = CodedOutputPromise<M, Handle>; 91 type ReconPromise = ReadyPromise<Self::Reconstructed>; 92 } 93 94 impl<M, H> VaapiBackend<M, H> 95 where 96 M: SurfaceMemoryDescriptor, 97 H: std::borrow::Borrow<Surface<M>> + 'static, 98 { build_seq_param(request: &Request<H>) -> Result<EncSequenceParameterBufferAV1>99 fn build_seq_param(request: &Request<H>) -> Result<EncSequenceParameterBufferAV1> { 100 assert!( 101 request.sequence.operating_points_cnt_minus_1 == 0, 102 "Only a single operating point is supported for now" 103 ); 104 const OPERATING_POINT: usize = 0; 105 106 let seq_profile = request.sequence.seq_profile as u8; 107 let seq_level_idx = request.sequence.operating_points[OPERATING_POINT].seq_level_idx; 108 let seq_tier = request.sequence.operating_points[OPERATING_POINT].seq_tier; 109 let hierarchical_flag = 0; 110 111 // TODO: Enable bitrate control 112 let bits_per_second = 0; 113 114 // AV1 5.5.2 115 let bit_depth_minus8 = if request.sequence.seq_profile == Profile::Profile2 116 && request.sequence.color_config.high_bitdepth 117 { 118 if request.sequence.color_config.twelve_bit { 119 12 120 } else { 121 10 122 } 123 } else if request.sequence.color_config.high_bitdepth { 124 10 125 } else { 126 8 127 }; 128 129 let order_hint_bits_minus_1 = u8::try_from(request.sequence.order_hint_bits_minus_1)?; 130 131 Ok(EncSequenceParameterBufferAV1::new( 132 seq_profile, 133 seq_level_idx, 134 seq_tier, 135 hierarchical_flag, 136 request.intra_period, 137 request.ip_period, 138 bits_per_second, 139 &AV1EncSeqFields::new( 140 request.sequence.still_picture, 141 request.sequence.use_128x128_superblock, 142 request.sequence.enable_filter_intra, 143 request.sequence.enable_intra_edge_filter, 144 request.sequence.enable_interintra_compound, 145 request.sequence.enable_masked_compound, 146 request.sequence.enable_warped_motion, 147 request.sequence.enable_dual_filter, 148 request.sequence.enable_order_hint, 149 request.sequence.enable_jnt_comp, 150 request.sequence.enable_ref_frame_mvs, 151 request.sequence.enable_superres, 152 request.sequence.enable_cdef, 153 request.sequence.enable_restoration, 154 bit_depth_minus8, 155 request.sequence.color_config.subsampling_x, 156 request.sequence.color_config.subsampling_y, 157 request.sequence.color_config.mono_chrome, 158 ), 159 order_hint_bits_minus_1, 160 )) 161 } 162 build_ref_ctrl( refs: &[Option<Rc<Reconstructed>>; REFS_PER_FRAME], ctrl: &[ReferenceFrameType; REFS_PER_FRAME], ) -> RefFrameCtrlAV1163 fn build_ref_ctrl( 164 refs: &[Option<Rc<Reconstructed>>; REFS_PER_FRAME], 165 ctrl: &[ReferenceFrameType; REFS_PER_FRAME], 166 ) -> RefFrameCtrlAV1 { 167 let ctrl = ctrl.map(|type_| { 168 if type_ == ReferenceFrameType::Intra { 169 return 0; 170 } 171 172 let idx = type_ as u32 - ReferenceFrameType::Last as u32; 173 if refs[idx as usize].is_none() { 174 return 0; 175 } 176 177 type_ as u32 178 }); 179 180 RefFrameCtrlAV1::new(ctrl[0], ctrl[1], ctrl[2], ctrl[3], ctrl[4], ctrl[5], ctrl[6]) 181 } 182 build_pic_param( request: &Request<H>, recon: &Reconstructed, coded: &EncCodedBuffer, ) -> Result<EncPictureParameterBufferAV1>183 fn build_pic_param( 184 request: &Request<H>, 185 recon: &Reconstructed, 186 coded: &EncCodedBuffer, 187 ) -> Result<EncPictureParameterBufferAV1> { 188 let coded_buf = coded.id(); 189 let reconstructed_frame = recon.surface_id(); 190 191 let mut reference_frames = [VA_INVALID_ID; 8]; 192 193 for (i, frame) in reference_frames.iter_mut().enumerate().take(REFS_PER_FRAME) { 194 let Some(ref_frame) = &request.references[i] else { 195 continue; 196 }; 197 198 *frame = ref_frame.surface_id(); 199 } 200 201 let mut ref_frame_idx = [0; 7]; 202 for (i, idx) in ref_frame_idx.iter_mut().enumerate() { 203 *idx = request.frame.ref_frame_idx[i]; 204 } 205 206 let frame_width_minus_1 = u16::try_from(request.frame.frame_width - 1)?; 207 let frame_height_minus_1 = u16::try_from(request.frame.frame_height - 1)?; 208 209 // Single temporal layer is used. 210 const HIERARCHICAL_LEVEL_PLUS1: u8 = 0; 211 212 let primary_ref_frame = u8::try_from(request.frame.primary_ref_frame)?; 213 let order_hint = u8::try_from(request.frame.order_hint)?; 214 let refresh_frame_flags = u8::try_from(request.frame.refresh_frame_flags)?; 215 216 let ref_frame_ctrl_l0 = 217 Self::build_ref_ctrl(&request.references, &request.ref_frame_ctrl_l0); 218 let ref_frame_ctrl_l1 = 219 Self::build_ref_ctrl(&request.references, &request.ref_frame_ctrl_l1); 220 221 let frame_type = request.frame.frame_type as u32; 222 223 // Export Frame Obu rather then TileGroup Obu 224 const ENABLE_FRAME_OBU: bool = false; 225 226 // We don't use long term reference frames for now. 227 const LONG_TERM_REFERENCE: bool = false; 228 229 // Current we always expect the reconstructed frame. 230 const DISABLE_FRAME_RECON: bool = false; 231 232 // Palette mode is not used. This also implies force_integer_mv and 233 // allow_screen_content_tools should be false. 234 const PALETTE_MODE_ENABLE: bool = false; 235 const FORCE_INTEGER_MV: bool = false; 236 const ALLOW_SCREEN_CONTENT_TOOLS: bool = false; 237 238 // Use 16x16 block size for now. 239 // TODO: Use maximum available 240 const SEG_ID_BLOCK_SIZE: u8 = 0; 241 242 // Use single tile group; 243 const NUM_TILE_GROUPS_MINUS1: u8 = 0; 244 245 let filter_level = [ 246 request.frame.loop_filter_params.loop_filter_level[0], 247 request.frame.loop_filter_params.loop_filter_level[1], 248 ]; 249 250 let filter_level_u = request.frame.loop_filter_params.loop_filter_level[2]; 251 let filter_level_v = request.frame.loop_filter_params.loop_filter_level[3]; 252 253 let superres_scale_denominator = u8::try_from(request.frame.superres_denom)?; 254 255 let interpolation_filter = request.frame.interpolation_filter as u8; 256 257 let mut loop_filter_ref_deltas = [0; 8]; 258 for (i, delta) in loop_filter_ref_deltas.iter_mut().enumerate() { 259 *delta = request.frame.loop_filter_params.loop_filter_ref_deltas[i]; 260 } 261 262 let base_qindex = u8::try_from(request.frame.quantization_params.base_q_idx)?; 263 let y_dc_delta_q = i8::try_from(request.frame.quantization_params.delta_q_y_dc)?; 264 let u_dc_delta_q = i8::try_from(request.frame.quantization_params.delta_q_u_dc)?; 265 let u_ac_delta_q = i8::try_from(request.frame.quantization_params.delta_q_u_ac)?; 266 let v_dc_delta_q = i8::try_from(request.frame.quantization_params.delta_q_v_dc)?; 267 let v_ac_delta_q = i8::try_from(request.frame.quantization_params.delta_q_v_ac)?; 268 269 // Clamp tunings's quaility range to correct range 270 let min_base_qindex = request.tunings.min_quality.max(MIN_BASE_QINDEX); 271 let min_base_qindex = u8::try_from(min_base_qindex)?; 272 let max_base_qindex = request.tunings.max_quality.min(MAX_BASE_QINDEX); 273 let max_base_qindex = u8::try_from(max_base_qindex)?; 274 275 let qm_y = u16::try_from(request.frame.quantization_params.qm_y)?; 276 let qm_u = u16::try_from(request.frame.quantization_params.qm_u)?; 277 let qm_v = u16::try_from(request.frame.quantization_params.qm_v)?; 278 279 let tx_mode = request.frame.tx_mode as u32; 280 281 // Make driver make decision use single reference or compound reference. 282 const REFERENCE_MODE: u32 = 0 /* REFERENCE_MODE_SELECT */; 283 284 let segmentation_temporal_update = 285 request.frame.segmentation_params.segmentation_temporal_update; 286 287 const SEGMENT_NUMBER: u8 = 0; 288 assert!( 289 !request.frame.segmentation_params.segmentation_enabled, 290 "Unsupported segmentation_enabled=1" 291 ); 292 293 // Segementation feature mask 294 let mut feature_mask = [0u8; MAX_SEGMENTS]; 295 for (seg, mask) in feature_mask.iter_mut().enumerate() { 296 for lvl in 0..u8::try_from(SEG_LVL_MAX)? { 297 if request.frame.segmentation_params.feature_enabled[seg][lvl as usize] { 298 *mask |= 1u8 << lvl; 299 } 300 } 301 } 302 303 assert!( 304 request.frame.tile_info.tile_cols == 1 305 && request.frame.tile_info.tile_cols_log2 == 0 306 && request.frame.tile_info.tile_rows == 1 307 && request.frame.tile_info.tile_rows_log2 == 0, 308 "Single tile is only supported for now" 309 ); 310 let tile_cols = u8::try_from(request.frame.tile_info.tile_cols)?; 311 let tile_rows = u8::try_from(request.frame.tile_info.tile_rows)?; 312 313 let mut width_in_sbs_minus_1 = [0u16; MAX_TILE_COLS - 1]; 314 for (i, width) in width_in_sbs_minus_1.iter_mut().enumerate() { 315 *width = u16::try_from(request.frame.tile_info.width_in_sbs_minus_1[i])?; 316 } 317 318 let mut height_in_sbs_minus_1 = [0u16; MAX_TILE_ROWS - 1]; 319 for (i, height) in height_in_sbs_minus_1.iter_mut().enumerate() { 320 *height = u16::try_from(request.frame.tile_info.height_in_sbs_minus_1[i])?; 321 } 322 323 let context_update_tile_id = u16::try_from(request.frame.tile_info.context_update_tile_id)?; 324 325 let cdef_damping_minus_3 = u8::try_from(request.frame.cdef_params.cdef_damping - 3)?; 326 327 let cdef_bits = u8::try_from(request.frame.cdef_params.cdef_bits)?; 328 let mut cdef_y_strengths = [0u8; CDEF_MAX]; 329 for (i, strength) in cdef_y_strengths.iter_mut().enumerate() { 330 *strength = u8::try_from(request.frame.cdef_params.cdef_y_pri_strength[i])?; 331 } 332 333 let mut cdef_uv_strengths = [0u8; CDEF_MAX]; 334 for (i, strength) in cdef_uv_strengths.iter_mut().enumerate() { 335 *strength = u8::try_from(request.frame.cdef_params.cdef_uv_pri_strength[i])?; 336 } 337 338 let yframe_restoration_type = 339 request.frame.loop_restoration_params.frame_restoration_type[0] as u16; 340 let cbframe_restoration_type = 341 request.frame.loop_restoration_params.frame_restoration_type[1] as u16; 342 let crframe_restoration_type = 343 request.frame.loop_restoration_params.frame_restoration_type[2] as u16; 344 345 let lr_unit_shift = u16::from(request.frame.loop_restoration_params.lr_unit_shift); 346 let lr_uv_shift = request.frame.loop_restoration_params.lr_uv_shift != 0; 347 348 // Warped motion params 349 let wm = [Default::default(); REFS_PER_FRAME]; 350 351 // Ignore by driver 352 const BIT_OFFSET_QINDEX: u32 = 0; 353 const BIT_OFFSET_SEGMENTATION: u32 = 0; 354 const BIT_OFFSET_LOOPFILTER_PARAMS: u32 = 0; 355 const BIT_OFFSET_CDEF_PARAMS: u32 = 0; 356 const SIZE_IN_BITS_CDEF_PARAMS: u32 = 0; 357 358 // TODO: use packed header and fill for bitrate control 359 const BYTE_OFFSET_FRAME_HDR_OBU_SIZE: u32 = 0; 360 const SIZE_IN_BITS_FRAME_HDR_OBU: u32 = 0; 361 362 let temporal_id = u8::try_from(request.frame.obu_header.temporal_id)?; 363 let spatial_id = u8::try_from(request.frame.obu_header.spatial_id)?; 364 365 const NUMBER_SKIP_FRAMES: u8 = 0; 366 const SKIP_FRAMES_REDUCED_SIZE: i32 = 0; 367 368 Ok(EncPictureParameterBufferAV1::new( 369 frame_width_minus_1, 370 frame_height_minus_1, 371 reconstructed_frame, 372 coded_buf, 373 reference_frames, 374 ref_frame_idx, 375 HIERARCHICAL_LEVEL_PLUS1, 376 primary_ref_frame, 377 order_hint, 378 refresh_frame_flags, 379 &ref_frame_ctrl_l0, 380 &ref_frame_ctrl_l1, 381 &AV1EncPictureFlags::new( 382 frame_type, 383 request.frame.error_resilient_mode, 384 request.frame.disable_cdf_update, 385 request.frame.use_superres, 386 request.frame.allow_high_precision_mv, 387 request.frame.use_ref_frame_mvs, 388 request.frame.disable_frame_end_update_cdf, 389 request.frame.reduced_tx_set, 390 ENABLE_FRAME_OBU, 391 LONG_TERM_REFERENCE, 392 DISABLE_FRAME_RECON, 393 request.frame.allow_intrabc, 394 PALETTE_MODE_ENABLE, 395 ALLOW_SCREEN_CONTENT_TOOLS, 396 FORCE_INTEGER_MV, 397 ), 398 SEG_ID_BLOCK_SIZE, 399 NUM_TILE_GROUPS_MINUS1, 400 temporal_id, 401 filter_level, 402 filter_level_u, 403 filter_level_v, 404 &AV1EncLoopFilterFlags::new( 405 request.frame.loop_filter_params.loop_filter_sharpness, 406 request.frame.loop_filter_params.loop_filter_delta_enabled, 407 request.frame.loop_filter_params.loop_filter_delta_update, 408 ), 409 superres_scale_denominator, 410 interpolation_filter, 411 loop_filter_ref_deltas, 412 request.frame.loop_filter_params.loop_filter_mode_deltas, 413 base_qindex, 414 y_dc_delta_q, 415 u_dc_delta_q, 416 u_ac_delta_q, 417 v_dc_delta_q, 418 v_ac_delta_q, 419 min_base_qindex, 420 max_base_qindex, 421 &AV1EncQMatrixFlags::new( 422 request.frame.quantization_params.using_qmatrix, 423 qm_y, 424 qm_u, 425 qm_v, 426 ), 427 &AV1EncModeControlFlags::new( 428 request.frame.quantization_params.delta_q_present, 429 request.frame.quantization_params.delta_q_res, 430 request.frame.loop_filter_params.delta_lf_present, 431 request.frame.loop_filter_params.delta_lf_res as u32, 432 request.frame.loop_filter_params.delta_lf_multi, 433 tx_mode, 434 REFERENCE_MODE, 435 request.frame.skip_mode_present, 436 ), 437 &EncSegParamAV1::new( 438 &EncSegParamFlagsAV1::new( 439 request.frame.segmentation_params.segmentation_enabled, 440 request.frame.segmentation_params.segmentation_update_map, 441 segmentation_temporal_update, 442 ), 443 SEGMENT_NUMBER, 444 request.frame.segmentation_params.feature_data, 445 feature_mask, 446 ), 447 tile_cols, 448 tile_rows, 449 width_in_sbs_minus_1, 450 height_in_sbs_minus_1, 451 context_update_tile_id, 452 cdef_damping_minus_3, 453 cdef_bits, 454 cdef_y_strengths, 455 cdef_uv_strengths, 456 &AV1EncLoopRestorationFlags::new( 457 yframe_restoration_type, 458 cbframe_restoration_type, 459 crframe_restoration_type, 460 lr_unit_shift, 461 lr_uv_shift, 462 ), 463 wm, 464 BIT_OFFSET_QINDEX, 465 BIT_OFFSET_SEGMENTATION, 466 BIT_OFFSET_LOOPFILTER_PARAMS, 467 BIT_OFFSET_CDEF_PARAMS, 468 SIZE_IN_BITS_CDEF_PARAMS, 469 BYTE_OFFSET_FRAME_HDR_OBU_SIZE, 470 SIZE_IN_BITS_FRAME_HDR_OBU, 471 &AV1EncTileGroupObuHdrInfo::new( 472 request.frame.obu_header.extension_flag, 473 request.frame.obu_header.has_size_field, 474 temporal_id, 475 spatial_id, 476 ), 477 NUMBER_SKIP_FRAMES, 478 SKIP_FRAMES_REDUCED_SIZE, 479 )) 480 } 481 build_tile_group_param() -> EncTileGroupBufferAV1482 fn build_tile_group_param() -> EncTileGroupBufferAV1 { 483 // Single tile is only supported for now. 484 EncTileGroupBufferAV1::new(0, 0) 485 } 486 } 487 488 impl<M, H> StatelessAV1EncoderBackend for VaapiBackend<M, H> 489 where 490 M: SurfaceMemoryDescriptor, 491 H: std::borrow::Borrow<Surface<M>> + 'static, 492 { encode_tile_group( &mut self, request: BackendRequest<Self::Picture, Self::Reconstructed>, ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)>493 fn encode_tile_group( 494 &mut self, 495 request: BackendRequest<Self::Picture, Self::Reconstructed>, 496 ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)> { 497 let coded_buf = self.new_coded_buffer(&request.tunings.rate_control)?; 498 let recon = self.new_scratch_picture()?; 499 500 let seq_param = Self::build_seq_param(&request)?; 501 let seq_param = 502 libva::BufferType::EncSequenceParameter(libva::EncSequenceParameter::AV1(seq_param)); 503 504 let pic_param = Self::build_pic_param(&request, &recon, &coded_buf)?; 505 let pic_param = 506 libva::BufferType::EncPictureParameter(libva::EncPictureParameter::AV1(pic_param)); 507 508 let tg_param = Self::build_tile_group_param(); 509 let tg_param = 510 libva::BufferType::EncSliceParameter(libva::EncSliceParameter::AV1(tg_param)); 511 512 let mut references = Vec::new(); 513 514 for ref_frame in &request.references { 515 let Some(ref_frame) = ref_frame else { 516 continue; 517 }; 518 519 references.push(ref_frame.clone() as Rc<dyn std::any::Any>); 520 } 521 522 let mut picture = 523 Picture::new(request.input_meta.timestamp, Rc::clone(self.context()), request.input); 524 525 picture.add_buffer(self.context().create_buffer(seq_param)?); 526 picture.add_buffer(self.context().create_buffer(pic_param)?); 527 picture.add_buffer(self.context().create_buffer(tg_param)?); 528 529 // Start processing the picture encoding 530 let picture = picture.begin().map_err(BackendError::BeginPictureError)?; 531 let picture = picture.render().map_err(BackendError::RenderPictureError)?; 532 let picture = picture.end().map_err(BackendError::EndPictureError)?; 533 534 // libva will handle the synchronization of reconstructed surface with implicit fences. 535 // Therefore return the reconstructed frame immediately. 536 let reference_promise = ReadyPromise::from(recon); 537 538 let bitstream_promise = 539 CodedOutputPromise::new(picture, references, coded_buf, request.coded_output); 540 541 Ok((reference_promise, bitstream_promise)) 542 } 543 } 544 545 impl<V: VideoFrame> 546 StatelessEncoder<AV1, V, VaapiBackend<V::MemDescriptor, Surface<V::MemDescriptor>>> 547 { new_vaapi( display: Rc<libva::Display>, config: EncoderConfig, fourcc: Fourcc, coded_size: Resolution, low_power: bool, blocking_mode: BlockingMode, ) -> EncodeResult<Self>548 pub fn new_vaapi( 549 display: Rc<libva::Display>, 550 config: EncoderConfig, 551 fourcc: Fourcc, 552 coded_size: Resolution, 553 low_power: bool, 554 blocking_mode: BlockingMode, 555 ) -> EncodeResult<Self> { 556 let va_profile = match config.profile { 557 Profile::Profile0 => VAProfileAV1Profile0, 558 Profile::Profile1 => VAProfileAV1Profile1, 559 _ => return Err(StatelessBackendError::UnsupportedProfile.into()), 560 }; 561 562 if !matches!(config.initial_tunings.rate_control, RateControl::ConstantQuality(_)) { 563 return Err(EncodeError::Unsupported); 564 } 565 566 let backend = VaapiBackend::new( 567 display, 568 va_profile, 569 fourcc, 570 coded_size, 571 libva::VA_RC_CQP, 572 low_power, 573 )?; 574 575 Self::new_av1(backend, config, blocking_mode) 576 } 577 } 578 579 #[cfg(test)] 580 mod tests { 581 use libva::Display; 582 use libva::UsageHint; 583 use libva::VAEntrypoint::VAEntrypointEncSliceLP; 584 use libva::VAProfile::VAProfileAV1Profile0; 585 use libva::VA_RT_FORMAT_YUV420; 586 use libva::VA_RT_FORMAT_YUV420_10; 587 588 use super::*; 589 use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12; 590 use crate::backend::vaapi::encoder::tests::TestFrameGenerator; 591 use crate::backend::vaapi::surface_pool::PooledVaSurface; 592 use crate::backend::vaapi::surface_pool::VaSurfacePool; 593 use crate::bitstream_utils::IvfFileHeader; 594 use crate::bitstream_utils::IvfFrameHeader; 595 use crate::codec::av1::parser::BitDepth; 596 use crate::codec::av1::parser::CdefParams; 597 use crate::codec::av1::parser::ColorConfig; 598 use crate::codec::av1::parser::FrameHeaderObu; 599 use crate::codec::av1::parser::FrameType; 600 use crate::codec::av1::parser::ObuHeader; 601 use crate::codec::av1::parser::ObuType; 602 use crate::codec::av1::parser::OperatingPoint; 603 use crate::codec::av1::parser::QuantizationParams; 604 use crate::codec::av1::parser::SequenceHeaderObu; 605 use crate::codec::av1::parser::TemporalDelimiterObu; 606 use crate::codec::av1::parser::TileInfo; 607 use crate::codec::av1::parser::TxMode; 608 use crate::codec::av1::parser::MAX_NUM_OPERATING_POINTS; 609 use crate::codec::av1::parser::PRIMARY_REF_NONE; 610 use crate::codec::av1::parser::SELECT_INTEGER_MV; 611 use crate::codec::av1::parser::SUPERRES_NUM; 612 use crate::codec::av1::synthesizer::Synthesizer; 613 use crate::decoder::FramePool; 614 use crate::encoder::simple_encode_loop; 615 use crate::encoder::stateless::BackendPromise; 616 use crate::encoder::stateless::StatelessEncoderBackendImport; 617 use crate::encoder::FrameMetadata; 618 use crate::encoder::RateControl; 619 use crate::encoder::Tunings; 620 use crate::FrameLayout; 621 use crate::PlaneLayout; 622 use crate::Resolution; 623 624 #[test] 625 // Ignore this test by default as it requires libva-compatible hardware. 626 #[ignore] test_single_frame()627 fn test_single_frame() { 628 let _ = env_logger::try_init(); 629 630 type Descriptor = (); 631 type Surface = libva::Surface<Descriptor>; 632 const WIDTH: u32 = 512; 633 const HEIGHT: u32 = 512; 634 let fourcc = b"NV12".into(); 635 636 let frame_layout = FrameLayout { 637 format: (fourcc, 0), 638 size: Resolution { width: WIDTH, height: HEIGHT }, 639 planes: vec![ 640 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH as usize }, 641 PlaneLayout { 642 buffer_index: 0, 643 offset: (WIDTH * HEIGHT) as usize, 644 stride: WIDTH as usize, 645 }, 646 ], 647 }; 648 649 let display = Display::open().unwrap(); 650 let entrypoints = display.query_config_entrypoints(VAProfileAV1Profile0).unwrap(); 651 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 652 653 let mut backend = VaapiBackend::<Descriptor, Surface>::new( 654 Rc::clone(&display), 655 VAProfileAV1Profile0, 656 fourcc, 657 Resolution { width: WIDTH, height: HEIGHT }, 658 libva::VA_RC_CQP, 659 low_power, 660 ) 661 .unwrap(); 662 663 let mut surfaces = display 664 .create_surfaces( 665 VA_RT_FORMAT_YUV420, 666 Some(frame_layout.format.0 .0), 667 WIDTH, 668 HEIGHT, 669 Some(UsageHint::USAGE_HINT_ENCODER), 670 vec![()], 671 ) 672 .unwrap(); 673 674 let surface = surfaces.pop().unwrap(); 675 676 upload_test_frame_nv12(&display, &surface, 0.0); 677 678 let input_meta = 679 FrameMetadata { layout: frame_layout, force_keyframe: false, timestamp: 0 }; 680 681 let input = backend.import_picture(&input_meta, surface).unwrap(); 682 683 let seq = SequenceHeaderObu { 684 obu_header: ObuHeader { 685 obu_type: ObuType::SequenceHeader, 686 extension_flag: false, 687 has_size_field: true, 688 temporal_id: 0, 689 spatial_id: 0, 690 }, 691 692 seq_profile: Profile::Profile0, 693 num_planes: 3, 694 695 frame_width_bits_minus_1: 16 - 1, 696 frame_height_bits_minus_1: 16 - 1, 697 max_frame_width_minus_1: (WIDTH - 1) as u16, 698 max_frame_height_minus_1: (HEIGHT - 1) as u16, 699 700 enable_order_hint: true, 701 order_hint_bits: 8, 702 order_hint_bits_minus_1: 7, 703 seq_force_integer_mv: SELECT_INTEGER_MV as u32, 704 705 operating_points: { 706 let mut ops: [OperatingPoint; MAX_NUM_OPERATING_POINTS] = Default::default(); 707 ops[0].seq_level_idx = 7; 708 ops 709 }, 710 711 color_config: ColorConfig { 712 subsampling_x: true, 713 subsampling_y: true, 714 ..Default::default() 715 }, 716 717 ..Default::default() 718 }; 719 720 let frame = FrameHeaderObu { 721 obu_header: ObuHeader { 722 obu_type: ObuType::FrameHeader, 723 extension_flag: false, 724 has_size_field: true, 725 temporal_id: 0, 726 spatial_id: 0, 727 }, 728 729 frame_type: FrameType::KeyFrame, 730 frame_is_intra: true, 731 primary_ref_frame: PRIMARY_REF_NONE, 732 refresh_frame_flags: 0xff, 733 error_resilient_mode: true, 734 735 reduced_tx_set: true, 736 tx_mode_select: 1, 737 tx_mode: TxMode::Select, 738 739 quantization_params: QuantizationParams { base_q_idx: 128, ..Default::default() }, 740 tile_info: TileInfo { 741 uniform_tile_spacing_flag: true, 742 tile_cols: 1, 743 tile_rows: 1, 744 tile_cols_log2: 0, 745 tile_rows_log2: 0, 746 width_in_sbs_minus_1: { 747 let mut value = [0u32; MAX_TILE_COLS]; 748 value[0] = WIDTH / 64 - 1; 749 value 750 }, 751 height_in_sbs_minus_1: { 752 let mut value = [0u32; MAX_TILE_ROWS]; 753 value[0] = HEIGHT / 64 - 1; 754 value 755 }, 756 ..Default::default() 757 }, 758 cdef_params: CdefParams { cdef_damping: 3, ..Default::default() }, 759 superres_denom: SUPERRES_NUM as u32, 760 upscaled_width: WIDTH, 761 frame_width: WIDTH, 762 frame_height: HEIGHT, 763 render_width: WIDTH, 764 render_height: HEIGHT, 765 766 ..Default::default() 767 }; 768 769 let request = Request { 770 sequence: seq.clone(), 771 frame: frame.clone(), 772 input, 773 input_meta, 774 references: [None, None, None, None, None, None, None], 775 ref_frame_ctrl_l0: [ReferenceFrameType::Intra; REFS_PER_FRAME], 776 ref_frame_ctrl_l1: [ReferenceFrameType::Intra; REFS_PER_FRAME], 777 intra_period: 4, 778 ip_period: 1, 779 tunings: Default::default(), 780 coded_output: Vec::new(), 781 }; 782 783 let (_, output) = backend.encode_tile_group(request).unwrap(); 784 let output = output.sync().unwrap(); 785 786 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 787 if write_to_file { 788 use std::io::Write; 789 790 let mut out = std::fs::File::create("test_single_frame.av1.ivf").unwrap(); 791 792 let td = TemporalDelimiterObu { 793 obu_header: ObuHeader { 794 obu_type: ObuType::TemporalDelimiter, 795 extension_flag: false, 796 has_size_field: true, 797 temporal_id: 0, 798 spatial_id: 0, 799 }, 800 }; 801 802 let file_header = 803 IvfFileHeader::new(IvfFileHeader::CODEC_AV1, WIDTH as u16, HEIGHT as u16, 30, 10); 804 805 file_header.writo_into(&mut out).unwrap(); 806 807 { 808 let mut hdr_buf = Vec::new(); 809 810 Synthesizer::<'_, TemporalDelimiterObu, _>::synthesize(&td, &mut hdr_buf).unwrap(); 811 Synthesizer::<'_, SequenceHeaderObu, _>::synthesize(&seq, &mut hdr_buf).unwrap(); 812 Synthesizer::<'_, FrameHeaderObu, _>::synthesize(&frame, &seq, &mut hdr_buf) 813 .unwrap(); 814 815 let frame_header = IvfFrameHeader { 816 frame_size: hdr_buf.len() as u32 + output.len() as u32, 817 timestamp: 0, 818 }; 819 820 frame_header.writo_into(&mut out).unwrap(); 821 out.write_all(&hdr_buf).unwrap(); 822 out.write_all(&output).unwrap(); 823 } 824 825 out.flush().unwrap(); 826 } 827 } 828 829 #[test] 830 // Ignore this test by default as it requires libva-compatible hardware. 831 #[ignore] test_vaapi_encoder()832 fn test_vaapi_encoder() { 833 type VaapiAv1Encoder<'l> = 834 StatelessEncoder<AV1, PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>; 835 836 const WIDTH: usize = 512; 837 const HEIGHT: usize = 512; 838 const FRAME_COUNT: u64 = 100; 839 840 let _ = env_logger::try_init(); 841 842 let display = libva::Display::open().unwrap(); 843 let entrypoints = display.query_config_entrypoints(VAProfileAV1Profile0).unwrap(); 844 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 845 846 let config = EncoderConfig { 847 profile: Profile::Profile0, 848 resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 849 initial_tunings: Tunings { 850 rate_control: RateControl::ConstantQuality(128), 851 framerate: 30, 852 ..Default::default() 853 }, 854 ..Default::default() 855 }; 856 857 let frame_layout = FrameLayout { 858 format: (b"NV12".into(), 0), 859 size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 860 planes: vec![ 861 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH }, 862 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH }, 863 ], 864 }; 865 866 let mut encoder = VaapiAv1Encoder::new_vaapi( 867 Rc::clone(&display), 868 config, 869 frame_layout.format.0, 870 frame_layout.size, 871 low_power, 872 BlockingMode::Blocking, 873 ) 874 .unwrap(); 875 876 let mut pool = VaSurfacePool::new( 877 Rc::clone(&display), 878 VA_RT_FORMAT_YUV420, 879 Some(UsageHint::USAGE_HINT_ENCODER), 880 Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 881 ); 882 883 pool.add_frames(vec![(); 16]).unwrap(); 884 885 let mut frame_producer = TestFrameGenerator::new(FRAME_COUNT, display, pool, frame_layout); 886 887 let mut bitstream = Vec::new(); 888 889 let file_header = IvfFileHeader::new( 890 IvfFileHeader::CODEC_AV1, 891 WIDTH as u16, 892 HEIGHT as u16, 893 30, 894 FRAME_COUNT as u32, 895 ); 896 897 file_header.writo_into(&mut bitstream).unwrap(); 898 899 simple_encode_loop(&mut encoder, &mut frame_producer, |coded| { 900 let header = IvfFrameHeader { 901 timestamp: coded.metadata.timestamp, 902 frame_size: coded.bitstream.len() as u32, 903 }; 904 905 header.writo_into(&mut bitstream).unwrap(); 906 bitstream.extend(coded.bitstream); 907 }) 908 .unwrap(); 909 910 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 911 if write_to_file { 912 use std::io::Write; 913 let mut out = std::fs::File::create("test_vaapi_encoder.av1.ivf").unwrap(); 914 out.write_all(&bitstream).unwrap(); 915 out.flush().unwrap(); 916 } 917 } 918 919 #[ignore] 920 // Ignore this test by default as it requires libva-compatible hardware. 921 #[test] test_vaapi_encoder_p010()922 fn test_vaapi_encoder_p010() { 923 type VaapiAv1Encoder<'l> = 924 StatelessEncoder<AV1, PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>; 925 926 const WIDTH: usize = 512; 927 const HEIGHT: usize = 512; 928 const FRAME_COUNT: u64 = 100; 929 930 let _ = env_logger::try_init(); 931 932 let display = libva::Display::open().unwrap(); 933 let entrypoints = display.query_config_entrypoints(VAProfileAV1Profile0).unwrap(); 934 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 935 936 let config = EncoderConfig { 937 profile: Profile::Profile0, 938 resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 939 bit_depth: BitDepth::Depth10, 940 initial_tunings: Tunings { 941 rate_control: RateControl::ConstantQuality(128), 942 framerate: 30, 943 ..Default::default() 944 }, 945 ..Default::default() 946 }; 947 948 let frame_layout = FrameLayout { 949 format: (b"P010".into(), 0), 950 size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 951 planes: vec![ 952 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH }, 953 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH }, 954 ], 955 }; 956 957 let mut encoder = VaapiAv1Encoder::new_vaapi( 958 Rc::clone(&display), 959 config, 960 frame_layout.format.0, 961 frame_layout.size, 962 low_power, 963 BlockingMode::Blocking, 964 ) 965 .unwrap(); 966 967 let mut pool = VaSurfacePool::new( 968 Rc::clone(&display), 969 VA_RT_FORMAT_YUV420_10, 970 Some(UsageHint::USAGE_HINT_ENCODER), 971 Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 972 ); 973 974 pool.add_frames(vec![(); 16]).unwrap(); 975 976 let mut frame_producer = TestFrameGenerator::new(FRAME_COUNT, display, pool, frame_layout); 977 978 let mut bitstream = Vec::new(); 979 980 let file_header = IvfFileHeader::new( 981 IvfFileHeader::CODEC_AV1, 982 WIDTH as u16, 983 HEIGHT as u16, 984 30, 985 FRAME_COUNT as u32, 986 ); 987 988 file_header.writo_into(&mut bitstream).unwrap(); 989 990 simple_encode_loop(&mut encoder, &mut frame_producer, |coded| { 991 let header = IvfFrameHeader { 992 timestamp: coded.metadata.timestamp, 993 frame_size: coded.bitstream.len() as u32, 994 }; 995 996 header.writo_into(&mut bitstream).unwrap(); 997 bitstream.extend(coded.bitstream); 998 }) 999 .unwrap(); 1000 1001 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 1002 if write_to_file { 1003 use std::io::Write; 1004 let mut out = std::fs::File::create("test_vaapi_encoder_p010.av1.ivf").unwrap(); 1005 out.write_all(&bitstream).unwrap(); 1006 out.flush().unwrap(); 1007 } 1008 } 1009 } 1010