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::EncPictureParameter; 13 use libva::EncPictureParameterBufferVP9; 14 use libva::EncSequenceParameter; 15 use libva::EncSequenceParameterBufferVP9; 16 use libva::Picture; 17 use libva::Surface; 18 use libva::SurfaceMemoryDescriptor; 19 use libva::VAProfile::VAProfileVP9Profile0; 20 use libva::VAProfile::VAProfileVP9Profile2; 21 use libva::VP9EncPicFlags; 22 use libva::VP9EncRefFlags; 23 use libva::VA_INVALID_SURFACE; 24 25 use crate::backend::vaapi::encoder::tunings_to_libva_rc; 26 use crate::backend::vaapi::encoder::CodedOutputPromise; 27 use crate::backend::vaapi::encoder::Reconstructed; 28 use crate::backend::vaapi::encoder::VaapiBackend; 29 use crate::codec::vp9::parser::BitDepth; 30 use crate::codec::vp9::parser::FrameType; 31 use crate::codec::vp9::parser::InterpolationFilter; 32 use crate::codec::vp9::parser::ALTREF_FRAME; 33 use crate::codec::vp9::parser::GOLDEN_FRAME; 34 use crate::codec::vp9::parser::LAST_FRAME; 35 use crate::codec::vp9::parser::NUM_REF_FRAMES; 36 use crate::encoder::stateless::vp9::predictor::MAX_Q_IDX; 37 use crate::encoder::stateless::vp9::predictor::MIN_Q_IDX; 38 use crate::encoder::stateless::vp9::BackendRequest; 39 use crate::encoder::stateless::vp9::ReferenceUse; 40 use crate::encoder::stateless::vp9::StatelessEncoder; 41 use crate::encoder::stateless::vp9::StatelessVP9EncoderBackend; 42 use crate::encoder::stateless::ReadyPromise; 43 use crate::encoder::stateless::StatelessBackendResult; 44 use crate::encoder::stateless::StatelessVideoEncoderBackend; 45 use crate::encoder::vp9::EncoderConfig; 46 use crate::encoder::vp9::VP9; 47 use crate::encoder::EncodeResult; 48 use crate::encoder::RateControl; 49 use crate::video_frame::VideoFrame; 50 use crate::BlockingMode; 51 use crate::Fourcc; 52 use crate::Resolution; 53 54 impl<M, Handle> StatelessVideoEncoderBackend<VP9> for VaapiBackend<M, Handle> 55 where 56 M: SurfaceMemoryDescriptor, 57 Handle: Borrow<Surface<M>>, 58 { 59 type Picture = Handle; 60 type Reconstructed = Reconstructed; 61 type CodedPromise = CodedOutputPromise<M, Handle>; 62 type ReconPromise = ReadyPromise<Self::Reconstructed>; 63 } 64 65 impl<M, Handle> StatelessVP9EncoderBackend for VaapiBackend<M, Handle> 66 where 67 M: SurfaceMemoryDescriptor, 68 Handle: Borrow<Surface<M>>, 69 { encode_frame( &mut self, request: BackendRequest<Self::Picture, Self::Reconstructed>, ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)>70 fn encode_frame( 71 &mut self, 72 request: BackendRequest<Self::Picture, Self::Reconstructed>, 73 ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)> { 74 let coded_buf = self.new_coded_buffer(&request.tunings.rate_control)?; 75 let recon = self.new_scratch_picture()?; 76 77 // Use bitrate from RateControl or ask driver to ignore 78 let bits_per_second = request.tunings.rate_control.bitrate_target().unwrap_or(0) as u32; 79 80 let seq_param = BufferType::EncSequenceParameter(EncSequenceParameter::VP9( 81 EncSequenceParameterBufferVP9::new( 82 request.input_meta.layout.size.width, 83 request.input_meta.layout.size.height, 84 0, 85 10, 86 2000, 87 bits_per_second, 88 1024, 89 ), 90 )); 91 92 // From va_enc_vp9.h `ref_frame_ctrl_l0` documentation 93 const LAST_FRAME_AS_REF: u32 = 0x01; 94 const GOLDEN_FRAME_AS_REF: u32 = 0x02; 95 const ALTREF_FRAME_AS_REF: u32 = 0x04; 96 97 let mut references = Vec::<Rc<dyn Any>>::new(); 98 let mut reference_frames = [VA_INVALID_SURFACE; NUM_REF_FRAMES]; 99 100 let mut ref_frame_ctrl_l0 = 0; 101 let mut ref_frame_ctrl_l1 = 0; 102 103 let refs = [ 104 (&request.last_frame_ref, LAST_FRAME - 1, LAST_FRAME_AS_REF), 105 (&request.golden_frame_ref, GOLDEN_FRAME - 1, GOLDEN_FRAME_AS_REF), 106 (&request.altref_frame_ref, ALTREF_FRAME - 1, ALTREF_FRAME_AS_REF), 107 ]; 108 109 for (r, ref_idx, ref_ctrl) in refs { 110 let Some((ref_frame, ref_use)) = r else { 111 continue; 112 }; 113 114 reference_frames[request.header.ref_frame_idx[ref_idx] as usize] = 115 ref_frame.surface_id(); 116 references.push(ref_frame.clone()); 117 118 match ref_use { 119 ReferenceUse::Single => ref_frame_ctrl_l0 |= ref_ctrl, 120 ReferenceUse::Compound => ref_frame_ctrl_l1 |= ref_ctrl, 121 ReferenceUse::Hybrid => { 122 ref_frame_ctrl_l0 |= ref_ctrl; 123 ref_frame_ctrl_l1 |= ref_ctrl; 124 } 125 } 126 } 127 128 let force_kf = 129 request.header.frame_type == FrameType::KeyFrame || request.input_meta.force_keyframe; 130 131 let ref_flags = VP9EncRefFlags::new( 132 // Force keyframe if requested 133 force_kf as u32, 134 ref_frame_ctrl_l0, 135 ref_frame_ctrl_l1, 136 request.header.ref_frame_idx[LAST_FRAME - 1] as u32, 137 request.header.ref_frame_sign_bias[LAST_FRAME] as u32, 138 request.header.ref_frame_idx[GOLDEN_FRAME - 1] as u32, 139 request.header.ref_frame_sign_bias[GOLDEN_FRAME] as u32, 140 request.header.ref_frame_idx[ALTREF_FRAME - 1] as u32, 141 request.header.ref_frame_sign_bias[ALTREF_FRAME] as u32, 142 0, 143 ); 144 145 // From va_enc_vp9.h `mcomp_filter_type` documentation 146 let mcomp_filter_type = match request.header.interpolation_filter { 147 InterpolationFilter::EightTap => 0, 148 InterpolationFilter::EightTapSmooth => 1, 149 InterpolationFilter::EightTapSharp => 2, 150 InterpolationFilter::Bilinear => 3, 151 InterpolationFilter::Switchable => 4, 152 }; 153 154 // TODO: show_existing_frame 155 assert!(!request.header.show_existing_frame); 156 157 // From va_enc_vp9.h `comp_prediction_mode` documentation 158 const PRED_MODE_SINGLE: u32 = 0x00; 159 // const PRED_MODE_COMPOUND: u32 = 0x01; 160 const PRED_MODE_HYBRID: u32 = 0x02; 161 162 let comp_prediction_mode = if ref_frame_ctrl_l1 != 0 { 163 // Use hybrid prediction mode if any future reference frame are enabled 164 PRED_MODE_HYBRID 165 } else { 166 PRED_MODE_SINGLE 167 }; 168 169 let pic_flags = VP9EncPicFlags::new( 170 request.header.frame_type as u32, 171 request.header.show_frame as u32, 172 request.header.error_resilient_mode as u32, 173 request.header.intra_only as u32, 174 request.header.allow_high_precision_mv as u32, 175 mcomp_filter_type, 176 request.header.frame_parallel_decoding_mode as u32, 177 request.header.reset_frame_context as u32, 178 request.header.refresh_frame_context as u32, 179 request.header.frame_context_idx as u32, 180 request.header.seg.enabled as u32, 181 request.header.seg.temporal_update as u32, 182 request.header.seg.update_map as u32, 183 request.header.lossless as u32, 184 comp_prediction_mode, 185 1, 186 0, 187 ); 188 189 let pic_param = BufferType::EncPictureParameter(EncPictureParameter::VP9( 190 EncPictureParameterBufferVP9::new( 191 request.header.width, 192 request.header.height, 193 request.header.render_width, 194 request.header.render_height, 195 recon.surface_id(), 196 reference_frames, 197 coded_buf.id(), 198 &ref_flags, 199 &pic_flags, 200 request.header.refresh_frame_flags, 201 request.header.quant.base_q_idx, 202 request.header.quant.delta_q_y_dc, 203 request.header.quant.delta_q_uv_ac, 204 request.header.quant.delta_q_uv_dc, 205 request.header.lf.level, 206 request.header.lf.sharpness, 207 request.header.lf.ref_deltas, 208 request.header.lf.mode_deltas, 209 0, 210 0, 211 0, 212 0, 213 0, 214 0, 215 0, 216 request.header.tile_rows_log2, 217 request.header.tile_cols_log2, 218 // Don't skip frames 219 0, 220 0, 221 0, 222 ), 223 )); 224 225 let rc_param = 226 tunings_to_libva_rc::<{ MIN_Q_IDX as u32 }, { MAX_Q_IDX as u32 }>(&request.tunings)?; 227 let rc_param = 228 libva::BufferType::EncMiscParameter(libva::EncMiscParameter::RateControl(rc_param)); 229 230 let mut picture = 231 Picture::new(request.input_meta.timestamp, Rc::clone(self.context()), request.input); 232 233 let framerate_param = BufferType::EncMiscParameter(libva::EncMiscParameter::FrameRate( 234 libva::EncMiscParameterFrameRate::new(request.tunings.framerate, 0), 235 )); 236 237 picture.add_buffer(self.context().create_buffer(seq_param)?); 238 picture.add_buffer(self.context().create_buffer(pic_param)?); 239 picture.add_buffer(self.context().create_buffer(rc_param)?); 240 picture.add_buffer(self.context().create_buffer(framerate_param)?); 241 242 // Start processing the picture encoding 243 let picture = picture.begin().context("picture begin")?; 244 let picture = picture.render().context("picture render")?; 245 let picture = picture.end().context("picture end")?; 246 247 // libva will handle the synchronization of reconstructed surface with implicit fences. 248 // Therefore return the reconstructed frame immediately. 249 let reference_promise = ReadyPromise::from(recon); 250 251 let bitstream_promise = 252 CodedOutputPromise::new(picture, references, coded_buf, request.coded_output); 253 254 Ok((reference_promise, bitstream_promise)) 255 } 256 } 257 258 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>259 pub fn new_vaapi( 260 display: Rc<Display>, 261 config: EncoderConfig, 262 fourcc: Fourcc, 263 coded_size: Resolution, 264 low_power: bool, 265 blocking_mode: BlockingMode, 266 ) -> EncodeResult<Self> { 267 let bitrate_control = match config.initial_tunings.rate_control { 268 RateControl::ConstantBitrate(_) => libva::VA_RC_CBR, 269 RateControl::ConstantQuality(_) => libva::VA_RC_CQP, 270 }; 271 272 let va_profile = match config.bit_depth { 273 BitDepth::Depth8 => VAProfileVP9Profile0, 274 BitDepth::Depth10 | BitDepth::Depth12 => VAProfileVP9Profile2, 275 }; 276 277 let backend = 278 VaapiBackend::new(display, va_profile, fourcc, coded_size, bitrate_control, low_power)?; 279 Self::new_vp9(backend, config, blocking_mode) 280 } 281 } 282 283 #[cfg(test)] 284 pub(super) mod tests { 285 use std::rc::Rc; 286 287 use libva::Display; 288 use libva::UsageHint; 289 use libva::VAEntrypoint::VAEntrypointEncSliceLP; 290 use libva::VA_RT_FORMAT_YUV420; 291 use libva::VA_RT_FORMAT_YUV420_10; 292 293 use super::*; 294 use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12; 295 use crate::backend::vaapi::encoder::tests::TestFrameGenerator; 296 use crate::backend::vaapi::encoder::VaapiBackend; 297 use crate::backend::vaapi::surface_pool::PooledVaSurface; 298 use crate::backend::vaapi::surface_pool::VaSurfacePool; 299 use crate::bitstream_utils::IvfFileHeader; 300 use crate::bitstream_utils::IvfFrameHeader; 301 use crate::codec::vp9::parser::Header; 302 use crate::decoder::FramePool; 303 use crate::encoder::simple_encode_loop; 304 use crate::encoder::stateless::vp9::BackendRequest; 305 use crate::encoder::stateless::vp9::EncoderConfig; 306 use crate::encoder::stateless::vp9::StatelessEncoder; 307 use crate::encoder::stateless::BackendPromise; 308 use crate::encoder::stateless::StatelessEncoderBackendImport; 309 use crate::encoder::FrameMetadata; 310 use crate::encoder::Tunings; 311 use crate::FrameLayout; 312 use crate::PlaneLayout; 313 use crate::Resolution; 314 315 #[test] 316 // Ignore this test by default as it requires libva-compatible hardware. 317 #[ignore] test_simple_encode_frame()318 fn test_simple_encode_frame() { 319 type Descriptor = (); 320 type Surface = libva::Surface<Descriptor>; 321 const WIDTH: u32 = 256; 322 const HEIGHT: u32 = 256; 323 let fourcc = b"NV12".into(); 324 325 let frame_layout = FrameLayout { 326 format: (fourcc, 0), 327 size: Resolution { width: WIDTH, height: HEIGHT }, 328 planes: vec![ 329 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH as usize }, 330 PlaneLayout { 331 buffer_index: 0, 332 offset: (WIDTH * HEIGHT) as usize, 333 stride: WIDTH as usize, 334 }, 335 ], 336 }; 337 338 let display = Display::open().unwrap(); 339 let entrypoints = display.query_config_entrypoints(VAProfileVP9Profile0).unwrap(); 340 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 341 342 let mut backend = VaapiBackend::<Descriptor, Surface>::new( 343 Rc::clone(&display), 344 VAProfileVP9Profile0, 345 fourcc, 346 Resolution { width: WIDTH, height: HEIGHT }, 347 libva::VA_RC_CBR, 348 low_power, 349 ) 350 .unwrap(); 351 352 let mut surfaces = display 353 .create_surfaces( 354 VA_RT_FORMAT_YUV420, 355 Some(frame_layout.format.0 .0), 356 WIDTH, 357 HEIGHT, 358 Some(UsageHint::USAGE_HINT_ENCODER), 359 vec![()], 360 ) 361 .unwrap(); 362 363 let surface = surfaces.pop().unwrap(); 364 365 upload_test_frame_nv12(&display, &surface, 0.0); 366 367 let input_meta = 368 FrameMetadata { layout: frame_layout, force_keyframe: false, timestamp: 0 }; 369 370 let pic = backend.import_picture(&input_meta, surface).unwrap(); 371 372 let header = Header { 373 frame_type: FrameType::KeyFrame, 374 show_frame: true, 375 error_resilient_mode: false, 376 width: WIDTH, 377 height: HEIGHT, 378 render_and_frame_size_different: false, 379 render_width: WIDTH, 380 render_height: HEIGHT, 381 intra_only: true, 382 refresh_frame_flags: 0x01, 383 ref_frame_idx: [0, 0, 0], 384 385 ..Default::default() 386 }; 387 388 let request = BackendRequest { 389 header, 390 input: pic, 391 input_meta, 392 last_frame_ref: None, 393 golden_frame_ref: None, 394 altref_frame_ref: None, 395 tunings: Tunings { 396 rate_control: RateControl::ConstantBitrate(30_000), 397 ..Default::default() 398 }, 399 coded_output: Vec::new(), 400 }; 401 402 let (_, output) = backend.encode_frame(request).unwrap(); 403 let output = output.sync().unwrap(); 404 405 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 406 if write_to_file { 407 use std::io::Write; 408 409 let mut out = std::fs::File::create("test_simple_encode_frame.vp9.ivf").unwrap(); 410 411 let file_header = 412 IvfFileHeader::new(IvfFileHeader::CODEC_VP9, WIDTH as u16, HEIGHT as u16, 30, 1); 413 414 let frame_header = IvfFrameHeader { frame_size: output.len() as u32, timestamp: 0 }; 415 416 file_header.writo_into(&mut out).unwrap(); 417 frame_header.writo_into(&mut out).unwrap(); 418 419 out.write_all(&output).unwrap(); 420 out.flush().unwrap(); 421 } 422 } 423 424 #[test] 425 // Ignore this test by default as it requires libva-compatible hardware. 426 #[ignore] test_vaapi_encoder()427 fn test_vaapi_encoder() { 428 type VaapiVp9Encoder<'l> = 429 StatelessEncoder<PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>; 430 431 const WIDTH: usize = 512; 432 const HEIGHT: usize = 512; 433 const FRAME_COUNT: u64 = 100; 434 435 let _ = env_logger::try_init(); 436 437 let display = libva::Display::open().unwrap(); 438 let entrypoints = display.query_config_entrypoints(VAProfileVP9Profile0).unwrap(); 439 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 440 441 let config = EncoderConfig { 442 resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 443 initial_tunings: Tunings { 444 rate_control: RateControl::ConstantBitrate(200_000), 445 framerate: 30, 446 ..Default::default() 447 }, 448 ..Default::default() 449 }; 450 451 let frame_layout = FrameLayout { 452 format: (b"NV12".into(), 0), 453 size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 454 planes: vec![ 455 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH }, 456 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH }, 457 ], 458 }; 459 460 let mut encoder = VaapiVp9Encoder::new_vaapi( 461 Rc::clone(&display), 462 config, 463 frame_layout.format.0, 464 frame_layout.size, 465 low_power, 466 BlockingMode::Blocking, 467 ) 468 .unwrap(); 469 470 let mut pool = VaSurfacePool::new( 471 Rc::clone(&display), 472 VA_RT_FORMAT_YUV420, 473 Some(UsageHint::USAGE_HINT_ENCODER), 474 Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 475 ); 476 477 pool.add_frames(vec![(); 16]).unwrap(); 478 479 let mut frame_producer = TestFrameGenerator::new(FRAME_COUNT, display, pool, frame_layout); 480 481 let mut bitstream = Vec::new(); 482 483 let file_header = IvfFileHeader::new( 484 IvfFileHeader::CODEC_VP9, 485 WIDTH as u16, 486 HEIGHT as u16, 487 30, 488 FRAME_COUNT as u32, 489 ); 490 491 file_header.writo_into(&mut bitstream).unwrap(); 492 493 simple_encode_loop(&mut encoder, &mut frame_producer, |coded| { 494 let header = IvfFrameHeader { 495 timestamp: coded.metadata.timestamp, 496 frame_size: coded.bitstream.len() as u32, 497 }; 498 499 header.writo_into(&mut bitstream).unwrap(); 500 bitstream.extend(coded.bitstream); 501 }) 502 .unwrap(); 503 504 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 505 if write_to_file { 506 use std::io::Write; 507 let mut out = std::fs::File::create("test_vaapi_encoder.vp9.ivf").unwrap(); 508 out.write_all(&bitstream).unwrap(); 509 out.flush().unwrap(); 510 } 511 } 512 513 #[test] 514 // Ignore this test by default as it requires libva-compatible hardware. 515 #[ignore] test_vaapi_encoder_p010()516 fn test_vaapi_encoder_p010() { 517 type VaapiVp9Encoder<'l> = 518 StatelessEncoder<PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>; 519 520 const WIDTH: usize = 512; 521 const HEIGHT: usize = 512; 522 const FRAME_COUNT: u64 = 100; 523 524 let _ = env_logger::try_init(); 525 526 let display = libva::Display::open().unwrap(); 527 let entrypoints = display.query_config_entrypoints(VAProfileVP9Profile2).unwrap(); 528 let low_power = entrypoints.contains(&VAEntrypointEncSliceLP); 529 530 let config = EncoderConfig { 531 bit_depth: BitDepth::Depth10, 532 resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 533 initial_tunings: Tunings { 534 rate_control: RateControl::ConstantBitrate(200_000), 535 framerate: 30, 536 ..Default::default() 537 }, 538 ..Default::default() 539 }; 540 541 let frame_layout = FrameLayout { 542 format: (b"P010".into(), 0), 543 size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 544 planes: vec![ 545 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH }, 546 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH }, 547 ], 548 }; 549 550 let mut encoder = VaapiVp9Encoder::new_vaapi( 551 Rc::clone(&display), 552 config, 553 frame_layout.format.0, 554 frame_layout.size, 555 low_power, 556 BlockingMode::Blocking, 557 ) 558 .unwrap(); 559 560 let mut pool = VaSurfacePool::new( 561 Rc::clone(&display), 562 VA_RT_FORMAT_YUV420_10, 563 Some(UsageHint::USAGE_HINT_ENCODER), 564 Resolution { width: WIDTH as u32, height: HEIGHT as u32 }, 565 ); 566 567 pool.add_frames(vec![(); 16]).unwrap(); 568 569 let mut frame_producer = TestFrameGenerator::new(FRAME_COUNT, display, pool, frame_layout); 570 571 let mut bitstream = Vec::new(); 572 573 let file_header = IvfFileHeader::new( 574 IvfFileHeader::CODEC_VP9, 575 WIDTH as u16, 576 HEIGHT as u16, 577 30, 578 FRAME_COUNT as u32, 579 ); 580 581 file_header.writo_into(&mut bitstream).unwrap(); 582 583 simple_encode_loop(&mut encoder, &mut frame_producer, |coded| { 584 let header = IvfFrameHeader { 585 timestamp: coded.metadata.timestamp, 586 frame_size: coded.bitstream.len() as u32, 587 }; 588 589 header.writo_into(&mut bitstream).unwrap(); 590 bitstream.extend(coded.bitstream); 591 }) 592 .unwrap(); 593 594 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true"); 595 if write_to_file { 596 use std::io::Write; 597 let mut out = std::fs::File::create("test_vaapi_encoder_p010.vp9.ivf").unwrap(); 598 out.write_all(&bitstream).unwrap(); 599 out.flush().unwrap(); 600 } 601 } 602 } 603