cros_codecs/decoder/stateless/av1/
vaapi.rs

1// Copyright 2023 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
5use 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
41/// The number of surfaces to allocate for this codec.
42const 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        // See table 6.4.1.
58        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            /* TODO: these were not exported in cros-libva */
219            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, /* large_scale_tile */
312    );
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, /* film grain is unsupported for now */
427        vec![],                               /* anchor_frames_list */
428        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, /* output_frame_width_in_tiles_minus_1 */
431        0, /* output_frame_height_in_tiles_minus_1 */
432        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, /* large-scale tile not supported */
443        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        /* all tiles must be submitted in the same slice parameter array */
480        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                /* The spec mandates a 2:1 or 1.5:1 ratio, let's go with 2:1 to
516                 * accomodate the other case. See 6.7.5 in the spec */
517                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    // Creates a new instance of the decoder using the VAAPI backend.
613    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    /// Run `test` using the vaapi decoder, in both blocking and non-blocking modes.
640    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 this test by default as it requires libva-compatible hardware.
668    #[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 this test by default as it requires libva-compatible hardware.
680    #[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}