• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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