• 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::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