1use std::rc::Rc;
6
7use anyhow::anyhow;
8use anyhow::Context;
9use libva::Display;
10use libva::Picture as VaPicture;
11use libva::SurfaceMemoryDescriptor;
12
13use crate::backend::vaapi::decoder::va_surface_id;
14use crate::backend::vaapi::decoder::DecodedHandle as VADecodedHandle;
15use crate::backend::vaapi::decoder::PoolCreationMode;
16use crate::backend::vaapi::decoder::VaStreamInfo;
17use crate::backend::vaapi::decoder::VaapiBackend;
18use crate::backend::vaapi::decoder::VaapiPicture;
19use crate::codec::av1::parser::BitDepth;
20use crate::codec::av1::parser::FrameHeaderObu;
21use crate::codec::av1::parser::Profile;
22use crate::codec::av1::parser::SequenceHeaderObu;
23use crate::codec::av1::parser::TileGroupObu;
24use crate::codec::av1::parser::WarpModelType;
25use crate::codec::av1::parser::MAX_SEGMENTS;
26use crate::codec::av1::parser::MAX_TILE_COLS;
27use crate::codec::av1::parser::MAX_TILE_ROWS;
28use crate::codec::av1::parser::NUM_REF_FRAMES;
29use crate::codec::av1::parser::SEG_LVL_MAX;
30use crate::decoder::stateless::av1::Av1;
31use crate::decoder::stateless::av1::StatelessAV1DecoderBackend;
32use crate::decoder::stateless::NewPictureError;
33use crate::decoder::stateless::NewPictureResult;
34use crate::decoder::stateless::NewStatelessDecoderError;
35use crate::decoder::stateless::StatelessBackendResult;
36use crate::decoder::stateless::StatelessDecoder;
37use crate::decoder::stateless::StatelessDecoderBackendPicture;
38use crate::decoder::BlockingMode;
39use crate::Resolution;
40
41const NUM_SURFACES: usize = 16;
43
44impl VaStreamInfo for &Rc<SequenceHeaderObu> {
45 fn va_profile(&self) -> anyhow::Result<i32> {
46 match self.seq_profile {
47 Profile::Profile0 => Ok(libva::VAProfile::VAProfileAV1Profile0),
48 Profile::Profile1 => Ok(libva::VAProfile::VAProfileAV1Profile1),
49 Profile::Profile2 => Err(anyhow!(
50 "Profile {:?} is not supported by VAAPI",
51 self.seq_profile
52 )),
53 }
54 }
55
56 fn rt_format(&self) -> anyhow::Result<u32> {
57 match self.seq_profile {
59 Profile::Profile0 => {
60 if self.bit_depth == BitDepth::Depth8 {
61 Ok(libva::constants::VA_RT_FORMAT_YUV420)
62 } else if self.bit_depth == BitDepth::Depth10 {
63 Ok(libva::constants::VA_RT_FORMAT_YUV420_10)
64 } else {
65 Err(anyhow!(
66 "Unsupported bit depth {:?} for profile {:?}",
67 self.bit_depth,
68 self.seq_profile
69 ))
70 }
71 }
72 Profile::Profile1 => {
73 if self.bit_depth == BitDepth::Depth8 {
74 Ok(libva::constants::VA_RT_FORMAT_YUV444)
75 } else if self.bit_depth == BitDepth::Depth10 {
76 Ok(libva::constants::VA_RT_FORMAT_YUV444_10)
77 } else {
78 Err(anyhow!(
79 "Unsupported bit depth {:?} for profile {:?}",
80 self.bit_depth,
81 self.seq_profile
82 ))
83 }
84 }
85 Profile::Profile2 => Err(anyhow!(
86 "Profile {:?} is not supported by VAAPI",
87 self.seq_profile
88 )),
89 }
90 }
91
92 fn min_num_surfaces(&self) -> usize {
93 NUM_SURFACES
94 }
95
96 fn coded_size(&self) -> (u32, u32) {
97 (
98 self.max_frame_width_minus_1 as u32 + 1,
99 self.max_frame_height_minus_1 as u32 + 1,
100 )
101 }
102
103 fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
104 ((0, 0), self.coded_size())
105 }
106}
107
108impl From<&FrameHeaderObu> for libva::AV1FilmGrain {
109 fn from(hdr: &FrameHeaderObu) -> Self {
110 let fg = &hdr.film_grain_params;
111
112 if fg.apply_grain {
113 log::warn!("Film grain is not officially supported yet.")
114 }
115
116 let film_grain_fields = libva::AV1FilmGrainFields::new(
117 u32::from(fg.apply_grain),
118 u32::from(fg.chroma_scaling_from_luma),
119 u32::from(fg.grain_scaling_minus_8),
120 fg.ar_coeff_lag,
121 fg.ar_coeff_shift_minus_6 as u32,
122 fg.grain_scale_shift as u32,
123 u32::from(fg.overlap_flag),
124 u32::from(fg.clip_to_restricted_range),
125 );
126
127 const NUM_POINT_Y: usize = 14;
128 let fg_point_y_value = {
129 let mut fg_point_y_value = [0u8; NUM_POINT_Y];
130 fg_point_y_value.copy_from_slice(&fg.point_y_value[0..NUM_POINT_Y]);
131 fg_point_y_value
132 };
133 let fg_point_y_scaling = {
134 let mut fg_point_y_scaling = [0u8; NUM_POINT_Y];
135 fg_point_y_scaling.copy_from_slice(&fg.point_y_scaling[0..NUM_POINT_Y]);
136 fg_point_y_scaling
137 };
138
139 const NUM_POINT_CB: usize = 10;
140 let fg_point_cb_value = {
141 let mut fg_point_cb_value = [0u8; NUM_POINT_CB];
142 fg_point_cb_value.copy_from_slice(&fg.point_cb_value[0..NUM_POINT_CB]);
143 fg_point_cb_value
144 };
145 let fg_point_cb_scaling = {
146 let mut fg_point_cb_scaling = [0u8; NUM_POINT_CB];
147 fg_point_cb_scaling.copy_from_slice(&fg.point_cb_scaling[0..NUM_POINT_CB]);
148 fg_point_cb_scaling
149 };
150
151 const NUM_POINT_CR: usize = 10;
152 let fg_point_cr_value = {
153 let mut fg_point_cr_value = [0u8; NUM_POINT_CR];
154 fg_point_cr_value.copy_from_slice(&fg.point_cr_value[0..NUM_POINT_CR]);
155 fg_point_cr_value
156 };
157 let fg_point_cr_scaling = {
158 let mut fg_point_cr_scaling = [0u8; NUM_POINT_CR];
159 fg_point_cr_scaling.copy_from_slice(&fg.point_cr_scaling[0..NUM_POINT_CR]);
160 fg_point_cr_scaling
161 };
162
163 let fg_ar_coeffs_y = {
164 let mut fg_ar_coeffs_y = [0i8; 24];
165 fg_ar_coeffs_y
166 .iter_mut()
167 .zip(fg.ar_coeffs_y_plus_128.iter().copied())
168 .for_each(|(dest, src)| *dest = ((src as i16) - 128) as i8);
169 fg_ar_coeffs_y
170 };
171 let fg_ar_coeffs_cb = {
172 let mut fg_ar_coeffs_cb = [0i8; 25];
173 fg_ar_coeffs_cb
174 .iter_mut()
175 .zip(fg.ar_coeffs_cb_plus_128.iter().copied())
176 .for_each(|(dest, src)| *dest = ((src as i16) - 128) as i8);
177 fg_ar_coeffs_cb
178 };
179 let fg_ar_coeffs_cr = {
180 let mut fg_ar_coeffs_cr = [0i8; 25];
181 fg_ar_coeffs_cr
182 .iter_mut()
183 .zip(fg.ar_coeffs_cr_plus_128.iter().copied())
184 .for_each(|(dest, src)| *dest = ((src as i16) - 128) as i8);
185 fg_ar_coeffs_cr
186 };
187
188 libva::AV1FilmGrain::new(
189 &film_grain_fields,
190 fg.grain_seed,
191 fg.num_y_points,
192 fg_point_y_value,
193 fg_point_y_scaling,
194 fg.num_cb_points,
195 fg_point_cb_value,
196 fg_point_cb_scaling,
197 fg.num_cr_points,
198 fg_point_cr_value,
199 fg_point_cr_scaling,
200 fg_ar_coeffs_y,
201 fg_ar_coeffs_cb,
202 fg_ar_coeffs_cr,
203 fg.cb_mult,
204 fg.cb_luma_mult,
205 fg.cb_offset,
206 fg.cr_mult,
207 fg.cr_luma_mult,
208 fg.cr_offset,
209 )
210 }
211}
212
213fn build_wm_info(hdr: &FrameHeaderObu) -> [libva::AV1WarpedMotionParams; 7] {
214 let mut wm = vec![];
215 let gm = &hdr.global_motion_params;
216 for i in 1..=7 {
217 let wm_type = match gm.gm_type[i] {
218 WarpModelType::Identity => 0,
220 WarpModelType::Translation => 1,
221 WarpModelType::RotZoom => 2,
222 WarpModelType::Affine => 3,
223 };
224
225 let params = {
226 let mut params = [0; 8];
227 params[0..6].copy_from_slice(&gm.gm_params[i][0..6]);
228 params
229 };
230
231 wm.push(libva::AV1WarpedMotionParams::new(
232 wm_type,
233 params,
234 u8::from(!gm.warp_valid[i]),
235 ));
236 }
237
238 match wm.try_into() {
239 Ok(wm) => wm,
240 Err(_) => unreachable!("The Vec should have the right size"),
241 }
242}
243
244fn build_pic_param<M: SurfaceMemoryDescriptor>(
245 hdr: &FrameHeaderObu,
246 seq: &SequenceHeaderObu,
247 current_frame: libva::VASurfaceID,
248 reference_frames: &[Option<VADecodedHandle<M>>; NUM_REF_FRAMES],
249) -> anyhow::Result<libva::BufferType> {
250 let seq_info_fields = libva::AV1SeqFields::new(
251 u32::from(seq.still_picture),
252 u32::from(seq.use_128x128_superblock),
253 u32::from(seq.enable_filter_intra),
254 u32::from(seq.enable_intra_edge_filter),
255 u32::from(seq.enable_interintra_compound),
256 u32::from(seq.enable_masked_compound),
257 u32::from(seq.enable_dual_filter),
258 u32::from(seq.enable_order_hint),
259 u32::from(seq.enable_jnt_comp),
260 u32::from(seq.enable_cdef),
261 u32::from(seq.color_config.mono_chrome),
262 u32::from(seq.color_config.color_range),
263 u32::from(seq.color_config.subsampling_x),
264 u32::from(seq.color_config.subsampling_y),
265 seq.color_config.chroma_sample_position as u32,
266 u32::from(seq.film_grain_params_present),
267 );
268
269 let seg = &hdr.segmentation_params;
270 let seg_info_fields = libva::AV1SegmentInfoFields::new(
271 u32::from(seg.segmentation_enabled),
272 u32::from(seg.segmentation_update_map),
273 u32::from(seg.segmentation_temporal_update),
274 u32::from(seg.segmentation_update_data),
275 );
276
277 let seg_feature_mask = {
278 let mut seg_feature_mask = [0u8; MAX_SEGMENTS];
279 #[allow(clippy::needless_range_loop)]
280 for i in 0..MAX_SEGMENTS {
281 let mut mask = 0;
282 for j in 0..SEG_LVL_MAX {
283 if seg.feature_enabled[i][j] {
284 mask |= 1 << j;
285 }
286 }
287 seg_feature_mask[i] = mask;
288 }
289 seg_feature_mask
290 };
291
292 let seg_info =
293 libva::AV1Segmentation::new(&seg_info_fields, seg.feature_data, seg_feature_mask);
294
295 let pic_info_fields = libva::AV1PicInfoFields::new(
296 hdr.frame_type as u32,
297 u32::from(hdr.show_frame),
298 u32::from(hdr.showable_frame),
299 u32::from(hdr.error_resilient_mode),
300 u32::from(hdr.disable_cdf_update),
301 hdr.allow_screen_content_tools,
302 hdr.force_integer_mv,
303 u32::from(hdr.allow_intrabc),
304 u32::from(hdr.use_superres),
305 u32::from(hdr.allow_high_precision_mv),
306 u32::from(hdr.is_motion_mode_switchable),
307 u32::from(hdr.use_ref_frame_mvs),
308 u32::from(hdr.disable_frame_end_update_cdf),
309 u32::from(hdr.tile_info.uniform_tile_spacing_flag),
310 u32::from(hdr.allow_warped_motion),
311 0, );
313
314 let bit_depth_idx = match seq.bit_depth {
315 BitDepth::Depth8 => 0,
316 BitDepth::Depth10 => 1,
317 BitDepth::Depth12 => 2,
318 };
319
320 let ref_frame_map: [libva::VASurfaceID; NUM_REF_FRAMES] = reference_frames
321 .iter()
322 .map(va_surface_id)
323 .collect::<Vec<_>>()
324 .try_into()
325 .unwrap();
326
327 let width_in_sbs_minus_1 = {
328 let mut width_in_sbs_minus_1 = [0; MAX_TILE_COLS - 1];
329 #[allow(clippy::needless_range_loop)]
330 for i in 0..width_in_sbs_minus_1.len() {
331 width_in_sbs_minus_1[i] = u16::try_from(hdr.tile_info.width_in_sbs_minus_1[i])
332 .context("Invalid width_in_sbs_minus_1")?;
333 }
334 width_in_sbs_minus_1
335 };
336
337 let height_in_sbs_minus_1 = {
338 let mut height_in_sbs_minus_1 = [0; MAX_TILE_ROWS - 1];
339 #[allow(clippy::needless_range_loop)]
340 for i in 0..height_in_sbs_minus_1.len() {
341 height_in_sbs_minus_1[i] = u16::try_from(hdr.tile_info.height_in_sbs_minus_1[i])
342 .context("Invalid height_in_sbs_minus_1")?;
343 }
344 height_in_sbs_minus_1
345 };
346
347 let lf = &hdr.loop_filter_params;
348 let filter_level = [lf.loop_filter_level[0], lf.loop_filter_level[1]];
349
350 let lf_fields = libva::AV1LoopFilterFields::new(
351 lf.loop_filter_sharpness,
352 u8::from(lf.loop_filter_delta_enabled),
353 u8::from(lf.loop_filter_delta_update),
354 );
355
356 let quant = &hdr.quantization_params;
357 let qmatrix_fields = libva::AV1QMatrixFields::new(
358 u16::from(quant.using_qmatrix),
359 u16::try_from(quant.qm_y).context("Invalid qm_y")?,
360 u16::try_from(quant.qm_u).context("Invalid qm_u")?,
361 u16::try_from(quant.qm_v).context("Invalid qm_v")?,
362 );
363
364 let mode_control_fields = libva::AV1ModeControlFields::new(
365 u32::from(quant.delta_q_present),
366 quant.delta_q_res,
367 u32::from(lf.delta_lf_present),
368 lf.delta_lf_res as u32,
369 lf.delta_lf_multi as u32,
370 hdr.tx_mode as u32,
371 u32::from(hdr.reference_select),
372 u32::from(hdr.reduced_tx_set),
373 u32::from(hdr.skip_mode_present),
374 );
375
376 let cdef = &hdr.cdef_params;
377 let (cdef_y_strengths, cdef_uv_strengths) = {
378 let num_cdef_strenghts = 1 << cdef.cdef_bits;
379 let mut cdef_y_strengths = [0; 8];
380 let mut cdef_uv_strengths = [0; 8];
381
382 #[allow(clippy::needless_range_loop)]
383 for i in 0..num_cdef_strenghts {
384 let mut sec_strength = cdef.cdef_y_sec_strength[i];
385 if sec_strength == 4 {
386 sec_strength -= 1;
387 }
388 cdef_y_strengths[i] =
389 u8::try_from(((cdef.cdef_y_pri_strength[i] & 0xf) << 2) | (sec_strength & 0x3))
390 .context("Failed to merge primary and secondary strengths")?;
391 }
392
393 #[allow(clippy::needless_range_loop)]
394 for i in 0..num_cdef_strenghts {
395 let mut sec_strength = cdef.cdef_uv_sec_strength[i];
396 if sec_strength == 4 {
397 sec_strength -= 1;
398 }
399 cdef_uv_strengths[i] =
400 u8::try_from(((cdef.cdef_uv_pri_strength[i] & 0xf) << 2) | (sec_strength & 0x3))
401 .context("Failed to merge primary and secondary strengths")?;
402 }
403
404 (cdef_y_strengths, cdef_uv_strengths)
405 };
406
407 let lr = &hdr.loop_restoration_params;
408 let loop_restoration_fields = libva::AV1LoopRestorationFields::new(
409 lr.frame_restoration_type[0] as u16,
410 lr.frame_restoration_type[1] as u16,
411 lr.frame_restoration_type[2] as u16,
412 u16::from(lr.lr_unit_shift),
413 u16::from(lr.lr_uv_shift),
414 );
415
416 let wm = build_wm_info(hdr);
417
418 let pic_param = libva::PictureParameterBufferAV1::new(
419 u8::try_from(seq.seq_profile as u32).context("Invalid profile")?,
420 u8::try_from(seq.order_hint_bits_minus_1).context("Invalid order hint bits")?,
421 bit_depth_idx,
422 u8::try_from(seq.color_config.matrix_coefficients as u32)
423 .context("Invalid matrix_coefficients")?,
424 &seq_info_fields,
425 current_frame,
426 libva::constants::VA_INVALID_SURFACE, vec![], u16::try_from(hdr.upscaled_width - 1).context("Invalid frame width")?,
429 u16::try_from(hdr.frame_height - 1).context("Invalid frame height")?,
430 0, 0, ref_frame_map,
433 hdr.ref_frame_idx,
434 u8::try_from(hdr.primary_ref_frame).context("Invalid primary_ref_frame")?,
435 u8::try_from(hdr.order_hint).context("Invalid order_hint")?,
436 &seg_info,
437 &libva::AV1FilmGrain::from(hdr),
438 u8::try_from(hdr.tile_info.tile_cols).context("Invalid tile_cols")?,
439 u8::try_from(hdr.tile_info.tile_rows).context("Invalid tile_rows")?,
440 width_in_sbs_minus_1,
441 height_in_sbs_minus_1,
442 0, u16::try_from(hdr.tile_info.context_update_tile_id)
444 .context("Invalid context_update_tile_id")?,
445 &pic_info_fields,
446 u8::try_from(hdr.superres_denom).context("Invalid superres_denom")?,
447 u8::try_from(hdr.interpolation_filter as u32).context("Invalid interpolation_filter")?,
448 filter_level,
449 lf.loop_filter_level[2],
450 lf.loop_filter_level[3],
451 &lf_fields,
452 lf.loop_filter_ref_deltas,
453 lf.loop_filter_mode_deltas,
454 u8::try_from(quant.base_q_idx).context("Invalid base_q_idx")?,
455 i8::try_from(quant.delta_q_y_dc).context("Invalid delta_q_y_dc")?,
456 i8::try_from(quant.delta_q_u_dc).context("Invalid delta_q_u_dc")?,
457 i8::try_from(quant.delta_q_u_ac).context("Invalid delta_q_u_ac")?,
458 i8::try_from(quant.delta_q_v_dc).context("Invalid delta_q_v_dc")?,
459 i8::try_from(quant.delta_q_v_ac).context("Invalid delta_q_v_ac")?,
460 &qmatrix_fields,
461 &mode_control_fields,
462 u8::try_from(hdr.cdef_params.cdef_damping - 3).context("Invalid cdef_damping")?,
463 u8::try_from(hdr.cdef_params.cdef_bits).context("Invalid cdef_bits")?,
464 cdef_y_strengths,
465 cdef_uv_strengths,
466 &loop_restoration_fields,
467 &wm,
468 );
469
470 Ok(libva::BufferType::PictureParameter(
471 libva::PictureParameter::AV1(pic_param),
472 ))
473}
474
475fn build_slice_params_for_tg(tg: &TileGroupObu) -> anyhow::Result<libva::BufferType> {
476 let mut slice_params = libva::SliceParameterBufferAV1::new();
477
478 for tile in &tg.tiles {
479 slice_params.add_slice_parameter(
481 tile.tile_size,
482 tile.tile_offset,
483 0,
484 u16::try_from(tile.tile_row).context("Invalid tile_row")?,
485 u16::try_from(tile.tile_col).context("Invalid tile_col")?,
486 u16::try_from(tg.tg_start).context("Invalid tg_start")?,
487 u16::try_from(tg.tg_end).context("Invalid tg_end")?,
488 0,
489 0,
490 );
491 }
492
493 Ok(libva::BufferType::SliceParameter(
494 libva::SliceParameter::AV1(slice_params),
495 ))
496}
497
498fn build_slice_data_for_tg(tg: TileGroupObu) -> libva::BufferType {
499 let TileGroupObu { obu, .. } = tg;
500 libva::BufferType::SliceData(Vec::from(obu.as_ref()))
501}
502
503impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoderBackendPicture<Av1> for VaapiBackend<M> {
504 type Picture = VaapiPicture<M>;
505}
506
507impl<M: SurfaceMemoryDescriptor + 'static> StatelessAV1DecoderBackend for VaapiBackend<M> {
508 fn new_sequence(
509 &mut self,
510 sequence: &Rc<SequenceHeaderObu>,
511 highest_spatial_layer: Option<u32>,
512 ) -> StatelessBackendResult<()> {
513 let pool_creation_mode = match highest_spatial_layer {
514 Some(highest_layer) => {
515 let layers = (0..=highest_layer).map(|layer| Resolution {
518 width: (sequence.max_frame_width_minus_1 as u32 + 1) / (layer + 1),
519 height: (sequence.max_frame_height_minus_1 as u32 + 1) / (layer + 1),
520 });
521
522 PoolCreationMode::Layers(layers.collect())
523 }
524 None => PoolCreationMode::Highest,
525 };
526 self.new_sequence(sequence, pool_creation_mode)
527 }
528
529 fn new_picture(
530 &mut self,
531 hdr: &FrameHeaderObu,
532 timestamp: u64,
533 highest_spatial_layer: Option<u32>,
534 ) -> NewPictureResult<Self::Picture> {
535 let pool = match highest_spatial_layer {
536 Some(_) => {
537 let layer = Resolution {
538 width: hdr.upscaled_width,
539 height: hdr.frame_height,
540 };
541
542 self.pool(layer)
543 .ok_or(NewPictureError::NoFramePool(layer))?
544 }
545 None => self.highest_pool(),
546 };
547
548 let surface = pool
549 .get_surface()
550 .ok_or(NewPictureError::OutOfOutputBuffers)?;
551
552 let metadata = self.metadata_state.get_parsed()?;
553 Ok(VaPicture::new(
554 timestamp,
555 Rc::clone(&metadata.context),
556 surface,
557 ))
558 }
559
560 fn begin_picture(
561 &mut self,
562 picture: &mut Self::Picture,
563 sequence: &SequenceHeaderObu,
564 hdr: &FrameHeaderObu,
565 reference_frames: &[Option<Self::Handle>; NUM_REF_FRAMES],
566 ) -> StatelessBackendResult<()> {
567 let metadata = self.metadata_state.get_parsed()?;
568
569 let pic_param = build_pic_param(hdr, sequence, picture.surface().id(), reference_frames)
570 .context("Failed to build picture parameter")?;
571 let pic_param = metadata
572 .context
573 .create_buffer(pic_param)
574 .context("Failed to create picture parameter buffer")?;
575 picture.add_buffer(pic_param);
576
577 Ok(())
578 }
579
580 fn decode_tile_group(
581 &mut self,
582 picture: &mut Self::Picture,
583 tile_group: TileGroupObu,
584 ) -> crate::decoder::stateless::StatelessBackendResult<()> {
585 let slice_params = build_slice_params_for_tg(&tile_group)?;
586 let slice_data = build_slice_data_for_tg(tile_group);
587
588 let metadata = self.metadata_state.get_parsed()?;
589 let context = &metadata.context;
590
591 let buffer = context
592 .create_buffer(slice_params)
593 .context("Failed to create slice parameter buffer")?;
594
595 picture.add_buffer(buffer);
596
597 let buffer = context
598 .create_buffer(slice_data)
599 .context("Failed to create slice data buffer")?;
600
601 picture.add_buffer(buffer);
602
603 Ok(())
604 }
605
606 fn submit_picture(&mut self, picture: Self::Picture) -> StatelessBackendResult<Self::Handle> {
607 self.process_picture::<Av1>(picture)
608 }
609}
610
611impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoder<Av1, VaapiBackend<M>> {
612 pub fn new_vaapi<S>(
614 display: Rc<Display>,
615 blocking_mode: BlockingMode,
616 ) -> Result<Self, NewStatelessDecoderError>
617 where
618 M: From<S>,
619 S: From<M>,
620 {
621 Self::new(VaapiBackend::new(display, true), blocking_mode)
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use libva::Display;
628
629 use crate::decoder::stateless::av1::Av1;
630 use crate::decoder::stateless::tests::test_decode_stream;
631 use crate::decoder::stateless::tests::TestStream;
632 use crate::decoder::stateless::StatelessDecoder;
633 use crate::decoder::BlockingMode;
634 use crate::utils::simple_playback_loop;
635 use crate::utils::simple_playback_loop_owned_frames;
636 use crate::utils::IvfIterator;
637 use crate::DecodedFormat;
638
639 fn test_decoder_vaapi(
641 test: &TestStream,
642 output_format: DecodedFormat,
643 blocking_mode: BlockingMode,
644 ) {
645 let display = Display::open().unwrap();
646 let decoder = StatelessDecoder::<Av1, _>::new_vaapi::<()>(display, blocking_mode).unwrap();
647
648 test_decode_stream(
649 |d, s, f| {
650 simple_playback_loop(
651 d,
652 IvfIterator::new(s),
653 f,
654 &mut simple_playback_loop_owned_frames,
655 output_format,
656 blocking_mode,
657 )
658 },
659 decoder,
660 test,
661 true,
662 false,
663 );
664 }
665
666 #[test]
667 #[ignore]
669 fn test_25fps_av1_blocking() {
670 use crate::decoder::stateless::av1::tests::DECODE_TEST_25FPS;
671 test_decoder_vaapi(
672 &DECODE_TEST_25FPS,
673 DecodedFormat::NV12,
674 BlockingMode::Blocking,
675 );
676 }
677
678 #[test]
679 #[ignore]
681 fn test_25fps_av1_non_blocking() {
682 use crate::decoder::stateless::av1::tests::DECODE_TEST_25FPS;
683 test_decoder_vaapi(
684 &DECODE_TEST_25FPS,
685 DecodedFormat::NV12,
686 BlockingMode::NonBlocking,
687 );
688 }
689}