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