cros_codecs/decoder/stateless/h265/
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::BufferType;
10use libva::Display;
11use libva::HevcSliceExtFlags;
12use libva::IQMatrix;
13use libva::IQMatrixBufferHEVC;
14use libva::Picture as VaPicture;
15use libva::PictureHEVC;
16use libva::PictureParameterBufferHEVC;
17use libva::SliceParameter;
18use libva::SliceParameterBufferHEVC;
19use libva::SliceParameterBufferHEVCRext;
20use libva::SurfaceMemoryDescriptor;
21
22use crate::backend::vaapi::decoder::DecodedHandle as VADecodedHandle;
23use crate::backend::vaapi::decoder::PoolCreationMode;
24use crate::backend::vaapi::decoder::VaStreamInfo;
25use crate::backend::vaapi::decoder::VaapiBackend;
26use crate::backend::vaapi::decoder::VaapiPicture;
27use crate::codec::h265::dpb::Dpb;
28use crate::codec::h265::parser::NaluType;
29use crate::codec::h265::parser::Pps;
30use crate::codec::h265::parser::Profile;
31use crate::codec::h265::parser::Slice;
32use crate::codec::h265::parser::Sps;
33use crate::codec::h265::picture::PictureData;
34use crate::codec::h265::picture::Reference;
35use crate::decoder::stateless::h265::clip3;
36use crate::decoder::stateless::h265::RefPicListEntry;
37use crate::decoder::stateless::h265::RefPicSet;
38use crate::decoder::stateless::h265::StatelessH265DecoderBackend;
39use crate::decoder::stateless::h265::H265;
40use crate::decoder::stateless::NewPictureError;
41use crate::decoder::stateless::NewPictureResult;
42use crate::decoder::stateless::NewStatelessDecoderError;
43use crate::decoder::stateless::PoolLayer;
44use crate::decoder::stateless::StatelessBackendResult;
45use crate::decoder::stateless::StatelessDecoder;
46use crate::decoder::stateless::StatelessDecoderBackend;
47use crate::decoder::stateless::StatelessDecoderBackendPicture;
48use crate::decoder::BlockingMode;
49use crate::Resolution;
50
51enum ScalingListType {
52    Sps,
53    Pps,
54    None,
55}
56
57impl VaStreamInfo for &Sps {
58    fn va_profile(&self) -> anyhow::Result<i32> {
59        let profile_idc = self.profile_tier_level.general_profile_idc;
60        let profile = Profile::n(profile_idc)
61            .with_context(|| format!("Invalid profile_idc {:?}", profile_idc))?;
62
63        let bit_depth = std::cmp::max(
64            self.bit_depth_luma_minus8 + 8,
65            self.bit_depth_chroma_minus8 + 8,
66        );
67
68        let chroma_format_idc = self.chroma_format_idc;
69        let err = Err(anyhow!(
70            "Invalid combination of profile, bit depth an chroma_format_idc: ({:?}, {}, {}",
71            profile,
72            bit_depth,
73            chroma_format_idc
74        ));
75
76        // TODO: This can still be much improved in light of table A.2.
77        match profile {
78            Profile::Main | Profile::MainStill | Profile::Main10 => {
79                match (bit_depth, chroma_format_idc) {
80                    (8, 0) | (8, 1) => Ok(libva::VAProfile::VAProfileHEVCMain),
81                    (8, 3) => Ok(libva::VAProfile::VAProfileHEVCMain444),
82                    (10, 0) | (10, 1) => Ok(libva::VAProfile::VAProfileHEVCMain10),
83                    (10, 2) => Ok(libva::VAProfile::VAProfileHEVCMain422_10),
84                    (12, 1) => Ok(libva::VAProfile::VAProfileHEVCMain12),
85                    (12, 2) => Ok(libva::VAProfile::VAProfileHEVCMain422_12),
86                    (12, 3) => Ok(libva::VAProfile::VAProfileHEVCMain444_12),
87                    _ => err,
88                }
89            }
90
91            // See table A.4.
92            Profile::ScalableMain => match (bit_depth, chroma_format_idc) {
93                (8, 1) => Ok(libva::VAProfile::VAProfileHEVCSccMain),
94                (8, 3) => Ok(libva::VAProfile::VAProfileHEVCSccMain444),
95                (10, 1) => Ok(libva::VAProfile::VAProfileHEVCSccMain10),
96                (10, 3) => Ok(libva::VAProfile::VAProfileHEVCSccMain444_10),
97                _ => err,
98            },
99
100            _ => unimplemented!("Adding more profile support based on A.3. is still TODO"),
101        }
102    }
103
104    fn rt_format(&self) -> anyhow::Result<u32> {
105        let bit_depth = std::cmp::max(
106            self.bit_depth_luma_minus8 + 8,
107            self.bit_depth_chroma_minus8 + 8,
108        );
109
110        let chroma_format_idc = self.chroma_format_idc;
111
112        match (bit_depth, chroma_format_idc) {
113            (8, 0) | (8, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420),
114            (8, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422),
115            (8, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444),
116            (9, 0) | (9, 1) | (10, 0) | (10, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_10),
117            (9, 2) | (10, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_10),
118            (9, 3) | (10, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_10),
119            (11, 0) | (11, 1) | (12, 0) | (12, 1) => Ok(libva::constants::VA_RT_FORMAT_YUV420_12),
120            (11, 2) | (12, 2) => Ok(libva::constants::VA_RT_FORMAT_YUV422_12),
121            (11, 3) | (12, 3) => Ok(libva::constants::VA_RT_FORMAT_YUV444_12),
122            _ => Err(anyhow!(
123                "unsupported bit depth/chroma format pair {}, {}",
124                bit_depth,
125                chroma_format_idc
126            )),
127        }
128    }
129
130    fn min_num_surfaces(&self) -> usize {
131        self.max_dpb_size() + 4
132    }
133
134    fn coded_size(&self) -> (u32, u32) {
135        (self.width().into(), self.height().into())
136    }
137
138    fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
139        let rect = self.visible_rectangle();
140
141        ((rect.min.x, rect.min.y), (rect.max.x, rect.max.y))
142    }
143}
144
145fn build_slice_ref_pic_list<M: SurfaceMemoryDescriptor>(
146    ref_pic_list: &[Option<RefPicListEntry<VADecodedHandle<M>>>; 16],
147    va_references: &[PictureHEVC; 15],
148) -> [u8; 15] {
149    let mut va_refs = [0xff; 15];
150
151    for (ref_pic_list_idx, ref_pic_list_entry) in ref_pic_list.iter().enumerate() {
152        if ref_pic_list_idx == 15 {
153            break;
154        }
155
156        if let Some(ref_pic_list_entry) = ref_pic_list_entry {
157            for (va_ref_idx, va_ref) in va_references.iter().enumerate() {
158                if va_ref.picture_id() == libva::constants::VA_INVALID_ID {
159                    break;
160                }
161
162                let pic_order_cnt = match ref_pic_list_entry {
163                    RefPicListEntry::CurrentPicture(p) => p.pic_order_cnt_val,
164                    RefPicListEntry::DpbEntry(p) => p.0.borrow().pic_order_cnt_val,
165                };
166
167                if va_ref.pic_order_cnt() == pic_order_cnt {
168                    va_refs[ref_pic_list_idx] = va_ref_idx as u8;
169                }
170            }
171        }
172    }
173
174    va_refs
175}
176
177fn va_rps_flag<M: SurfaceMemoryDescriptor>(
178    hevc_pic: &PictureData,
179    rps: &RefPicSet<VADecodedHandle<M>>,
180) -> u32 {
181    if rps
182        .ref_pic_set_st_curr_before
183        .iter()
184        .flatten()
185        .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
186    {
187        libva::constants::VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE
188    } else if rps
189        .ref_pic_set_st_curr_after
190        .iter()
191        .flatten()
192        .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
193    {
194        libva::constants::VA_PICTURE_HEVC_RPS_ST_CURR_AFTER
195    } else if rps
196        .ref_pic_set_lt_curr
197        .iter()
198        .flatten()
199        .any(|dpb_entry| *dpb_entry.0.borrow() == *hevc_pic)
200    {
201        libva::constants::VA_PICTURE_HEVC_RPS_LT_CURR
202    } else {
203        0
204    }
205}
206
207/// Builds an invalid VaPictureHEVC. These pictures are used to fill empty
208/// array slots there is no data to fill them with.
209fn build_invalid_va_hevc_pic() -> libva::PictureHEVC {
210    libva::PictureHEVC::new(
211        libva::constants::VA_INVALID_ID,
212        0,
213        libva::constants::VA_PICTURE_HEVC_INVALID,
214    )
215}
216
217fn fill_va_hevc_pic<M: SurfaceMemoryDescriptor>(
218    hevc_pic: &PictureData,
219    surface_id: libva::VASurfaceID,
220    rps: &RefPicSet<VADecodedHandle<M>>,
221) -> libva::PictureHEVC {
222    let mut flags = 0;
223
224    if matches!(hevc_pic.reference(), Reference::LongTerm) {
225        flags |= libva::constants::VA_PICTURE_HEVC_LONG_TERM_REFERENCE;
226    }
227
228    flags |= va_rps_flag(hevc_pic, rps);
229
230    libva::PictureHEVC::new(surface_id, hevc_pic.pic_order_cnt_val, flags)
231}
232
233fn is_range_extension_profile(va_profile: libva::VAProfile::Type) -> bool {
234    matches!(
235        va_profile,
236        libva::VAProfile::VAProfileHEVCMain422_10
237            | libva::VAProfile::VAProfileHEVCMain444
238            | libva::VAProfile::VAProfileHEVCMain444_10
239            | libva::VAProfile::VAProfileHEVCMain12
240            | libva::VAProfile::VAProfileHEVCMain422_12
241            | libva::VAProfile::VAProfileHEVCMain444_12
242    )
243}
244
245fn is_scc_ext_profile(va_profile: libva::VAProfile::Type) -> bool {
246    matches!(
247        va_profile,
248        libva::VAProfile::VAProfileHEVCSccMain
249            | libva::VAProfile::VAProfileHEVCSccMain10
250            | libva::VAProfile::VAProfileHEVCSccMain444
251            | libva::VAProfile::VAProfileHEVCMain444_10,
252    )
253}
254
255fn build_picture_rext(sps: &Sps, pps: &Pps) -> anyhow::Result<BufferType> {
256    let sps_rext = &sps.range_extension;
257    let pps_rext = &pps.range_extension;
258
259    let range_extension_pic_fields = libva::HevcRangeExtensionPicFields::new(
260        sps_rext.transform_skip_rotation_enabled_flag as u32,
261        sps_rext.transform_skip_context_enabled_flag as u32,
262        sps_rext.implicit_rdpcm_enabled_flag as u32,
263        sps_rext.explicit_rdpcm_enabled_flag as u32,
264        sps_rext.extended_precision_processing_flag as u32,
265        sps_rext.intra_smoothing_disabled_flag as u32,
266        sps_rext.high_precision_offsets_enabled_flag as u32,
267        sps_rext.persistent_rice_adaptation_enabled_flag as u32,
268        sps_rext.cabac_bypass_alignment_enabled_flag as u32,
269        pps_rext.cross_component_prediction_enabled_flag as u32,
270        pps_rext.chroma_qp_offset_list_enabled_flag as u32,
271    );
272
273    let rext = libva::PictureParameterBufferHEVCRext::new(
274        &range_extension_pic_fields,
275        pps_rext.diff_cu_chroma_qp_offset_depth as u8,
276        pps_rext.chroma_qp_offset_list_len_minus1 as u8,
277        pps_rext.log2_sao_offset_scale_luma as u8,
278        pps_rext.log2_sao_offset_scale_chroma as u8,
279        pps_rext.log2_max_transform_skip_block_size_minus2 as u8,
280        pps_rext.cb_qp_offset_list.map(|x| x as i8),
281        pps_rext.cr_qp_offset_list.map(|x| x as i8),
282    );
283
284    Ok(BufferType::PictureParameter(
285        libva::PictureParameter::HEVCRext(rext),
286    ))
287}
288
289fn build_picture_scc(sps: &Sps, pps: &Pps) -> anyhow::Result<BufferType> {
290    let sps_scc = &sps.scc_extension;
291    let pps_scc = &pps.scc_extension;
292
293    let scc_pic_fields = libva::HevcScreenContentPicFields::new(
294        pps_scc.curr_pic_ref_enabled_flag as u32,
295        sps_scc.palette_mode_enabled_flag as u32,
296        sps_scc.motion_vector_resolution_control_idc as u32,
297        sps_scc.intra_boundary_filtering_disabled_flag as u32,
298        pps_scc.residual_adaptive_colour_transform_enabled_flag as u32,
299        pps_scc.slice_act_qp_offsets_present_flag as u32,
300    );
301
302    let (predictor_palette_entries, predictor_palette_size) =
303        if pps_scc.palette_predictor_initializers_present_flag {
304            (
305                pps_scc
306                    .palette_predictor_initializer
307                    .map(|outer| outer.map(u16::from)),
308                pps_scc.num_palette_predictor_initializers,
309            )
310        } else if sps_scc.palette_predictor_initializers_present_flag {
311            (
312                sps_scc
313                    .palette_predictor_initializer
314                    .map(|outer| outer.map(|inner| inner as u16)),
315                sps_scc.num_palette_predictor_initializer_minus1 + 1,
316            )
317        } else {
318            ([[0; 128]; 3], 0)
319        };
320
321    let scc = libva::PictureParameterBufferHEVCScc::new(
322        &scc_pic_fields,
323        sps_scc.palette_max_size,
324        sps_scc.delta_palette_max_predictor_size,
325        predictor_palette_size,
326        predictor_palette_entries,
327        pps_scc.act_y_qp_offset_plus5,
328        pps_scc.act_cb_qp_offset_plus5,
329        pps_scc.act_cr_qp_offset_plus3,
330    );
331
332    Ok(BufferType::PictureParameter(
333        libva::PictureParameter::HEVCScc(scc),
334    ))
335}
336
337fn build_pic_param<M: SurfaceMemoryDescriptor>(
338    _: &Slice,
339    current_picture: &PictureData,
340    current_surface_id: libva::VASurfaceID,
341    dpb: &Dpb<VADecodedHandle<M>>,
342    rps: &RefPicSet<VADecodedHandle<M>>,
343    sps: &Sps,
344    pps: &Pps,
345) -> anyhow::Result<(BufferType, [PictureHEVC; 15])> {
346    let curr_pic = fill_va_hevc_pic(current_picture, current_surface_id, rps);
347
348    let mut reference_frames = vec![];
349
350    for ref_pic in dpb.get_all_references() {
351        let surface_id = ref_pic.1.borrow().surface().id();
352        let ref_pic = fill_va_hevc_pic(&ref_pic.0.borrow(), surface_id, rps);
353        reference_frames.push(ref_pic);
354    }
355
356    // RefPicListL0 and RefPicListL1 may signal that they want to refer to
357    // the current picture. We must tell VA that it is a reference as it is
358    // not in the DPB at this point.
359    if pps.scc_extension.curr_pic_ref_enabled_flag {
360        if reference_frames.len() >= 15 {
361            log::warn!(
362                "Bug: Trying to set the current picture as a VA reference, but the VA DPB is full."
363            )
364        } else {
365            reference_frames.push(curr_pic);
366        }
367    }
368
369    for _ in reference_frames.len()..15 {
370        reference_frames.push(build_invalid_va_hevc_pic());
371    }
372
373    let reference_frames = reference_frames.try_into();
374    let reference_frames = match reference_frames {
375        Ok(va_refs) => va_refs,
376        Err(_) => {
377            // Can't panic, we guarantee len() == 15.
378            panic!("Bug: wrong number of references, expected 15");
379        }
380    };
381
382    let pic_fields = libva::HevcPicFields::new(
383        sps.chroma_format_idc as u32,
384        sps.separate_colour_plane_flag as u32,
385        sps.pcm_enabled_flag as u32,
386        sps.scaling_list_enabled_flag as u32,
387        pps.transform_skip_enabled_flag as u32,
388        sps.amp_enabled_flag as u32,
389        sps.strong_intra_smoothing_enabled_flag as u32,
390        pps.sign_data_hiding_enabled_flag as u32,
391        pps.constrained_intra_pred_flag as u32,
392        pps.cu_qp_delta_enabled_flag as u32,
393        pps.weighted_pred_flag as u32,
394        pps.weighted_bipred_flag as u32,
395        pps.transquant_bypass_enabled_flag as u32,
396        pps.tiles_enabled_flag as u32,
397        pps.entropy_coding_sync_enabled_flag as u32,
398        pps.loop_filter_across_slices_enabled_flag as u32,
399        pps.loop_filter_across_tiles_enabled_flag as u32,
400        sps.pcm_loop_filter_disabled_flag as u32,
401        /* lets follow the FFMPEG and GStreamer train and set these to false */
402        0,
403        0,
404    );
405
406    let rap_pic_flag = current_picture.nalu_type as u32 >= NaluType::BlaWLp as u32
407        && current_picture.nalu_type as u32 <= NaluType::CraNut as u32;
408
409    let slice_parsing_fields = libva::HevcSliceParsingFields::new(
410        pps.lists_modification_present_flag as u32,
411        sps.long_term_ref_pics_present_flag as u32,
412        sps.temporal_mvp_enabled_flag as u32,
413        pps.cabac_init_present_flag as u32,
414        pps.output_flag_present_flag as u32,
415        pps.dependent_slice_segments_enabled_flag as u32,
416        pps.slice_chroma_qp_offsets_present_flag as u32,
417        sps.sample_adaptive_offset_enabled_flag as u32,
418        pps.deblocking_filter_override_enabled_flag as u32,
419        pps.deblocking_filter_disabled_flag as u32,
420        pps.slice_segment_header_extension_present_flag as u32,
421        rap_pic_flag as u32,
422        current_picture.nalu_type.is_idr() as u32,
423        current_picture.nalu_type.is_irap() as u32,
424    );
425
426    let pic_param = PictureParameterBufferHEVC::new(
427        curr_pic,
428        reference_frames,
429        sps.pic_width_in_luma_samples,
430        sps.pic_height_in_luma_samples,
431        &pic_fields,
432        sps.max_dec_pic_buffering_minus1[usize::from(sps.max_sub_layers_minus1)],
433        sps.bit_depth_luma_minus8,
434        sps.bit_depth_chroma_minus8,
435        sps.pcm_sample_bit_depth_luma_minus1,
436        sps.pcm_sample_bit_depth_chroma_minus1,
437        sps.log2_min_luma_coding_block_size_minus3,
438        sps.log2_diff_max_min_luma_coding_block_size,
439        sps.log2_min_luma_transform_block_size_minus2,
440        sps.log2_diff_max_min_luma_transform_block_size,
441        sps.log2_min_pcm_luma_coding_block_size_minus3,
442        sps.log2_diff_max_min_pcm_luma_coding_block_size,
443        sps.max_transform_hierarchy_depth_intra,
444        sps.max_transform_hierarchy_depth_inter,
445        pps.init_qp_minus26,
446        pps.diff_cu_qp_delta_depth,
447        pps.cb_qp_offset,
448        pps.cr_qp_offset,
449        pps.log2_parallel_merge_level_minus2,
450        pps.num_tile_columns_minus1,
451        pps.num_tile_rows_minus1,
452        pps.column_width_minus1.map(|x| x as u16),
453        pps.row_height_minus1.map(|x| x as u16),
454        &slice_parsing_fields,
455        sps.log2_max_pic_order_cnt_lsb_minus4,
456        sps.num_short_term_ref_pic_sets,
457        sps.num_long_term_ref_pics_sps,
458        pps.num_ref_idx_l0_default_active_minus1,
459        pps.num_ref_idx_l1_default_active_minus1,
460        pps.beta_offset_div2,
461        pps.tc_offset_div2,
462        pps.num_extra_slice_header_bits,
463        current_picture.short_term_ref_pic_set_size_bits,
464    );
465
466    Ok((
467        BufferType::PictureParameter(libva::PictureParameter::HEVC(pic_param)),
468        reference_frames,
469    ))
470}
471
472fn find_scaling_list(sps: &Sps, pps: &Pps) -> ScalingListType {
473    if pps.scaling_list_data_present_flag
474        || (sps.scaling_list_enabled_flag && !sps.scaling_list_data_present_flag)
475    {
476        ScalingListType::Pps
477    } else if sps.scaling_list_enabled_flag && sps.scaling_list_data_present_flag {
478        ScalingListType::Sps
479    } else {
480        ScalingListType::None
481    }
482}
483
484fn build_iq_matrix(sps: &Sps, pps: &Pps) -> BufferType {
485    let scaling_lists = match find_scaling_list(sps, pps) {
486        ScalingListType::Sps => &sps.scaling_list,
487        ScalingListType::Pps => &pps.scaling_list,
488        ScalingListType::None => panic!("No scaling list data available"),
489    };
490
491    let mut scaling_list_32x32 = [[0; 64]; 2];
492
493    for i in (0..6).step_by(3) {
494        for j in 0..64 {
495            scaling_list_32x32[i / 3][j] = scaling_lists.scaling_list_32x32[i][j];
496        }
497    }
498
499    let mut scaling_list_dc_32x32 = [0; 2];
500    for i in (0..6).step_by(3) {
501        scaling_list_dc_32x32[i / 3] =
502            (scaling_lists.scaling_list_dc_coef_minus8_32x32[i] + 8) as u8;
503    }
504
505    let mut scaling_list_4x4 = [[0; 16]; 6];
506    let mut scaling_list_8x8 = [[0; 64]; 6];
507    let mut scaling_list_16x16 = [[0; 64]; 6];
508    let mut scaling_list_32x32_r = [[0; 64]; 2];
509
510    (0..6).for_each(|i| {
511        super::get_raster_from_up_right_diagonal_4x4(
512            scaling_lists.scaling_list_4x4[i],
513            &mut scaling_list_4x4[i],
514        );
515
516        super::get_raster_from_up_right_diagonal_8x8(
517            scaling_lists.scaling_list_8x8[i],
518            &mut scaling_list_8x8[i],
519        );
520
521        super::get_raster_from_up_right_diagonal_8x8(
522            scaling_lists.scaling_list_16x16[i],
523            &mut scaling_list_16x16[i],
524        );
525    });
526
527    (0..2).for_each(|i| {
528        super::get_raster_from_up_right_diagonal_8x8(
529            scaling_list_32x32[i],
530            &mut scaling_list_32x32_r[i],
531        );
532    });
533
534    BufferType::IQMatrix(IQMatrix::HEVC(IQMatrixBufferHEVC::new(
535        scaling_list_4x4,
536        scaling_list_8x8,
537        scaling_list_16x16,
538        scaling_list_32x32_r,
539        scaling_lists
540            .scaling_list_dc_coef_minus8_16x16
541            .map(|x| (x + 8) as u8),
542        scaling_list_dc_32x32,
543    )))
544}
545
546impl<M: SurfaceMemoryDescriptor + 'static> VaapiBackend<M> {
547    fn submit_last_slice(
548        &mut self,
549        picture: &mut <Self as StatelessDecoderBackendPicture<H265>>::Picture,
550    ) -> anyhow::Result<()> {
551        if let Some(last_slice) = picture.last_slice.take() {
552            let metadata = self.metadata_state.get_parsed()?;
553            let context = &metadata.context;
554            let picture = &mut picture.picture;
555
556            let slice_param = BufferType::SliceParameter(SliceParameter::HEVC(last_slice.0));
557            let slice_param = context.create_buffer(slice_param)?;
558            picture.add_buffer(slice_param);
559
560            if let Some(slice_param_rext) = last_slice.1 {
561                let slice_param_rext =
562                    BufferType::SliceParameter(SliceParameter::HEVCRext(slice_param_rext));
563                let slice_param_rext = context.create_buffer(slice_param_rext)?;
564                picture.add_buffer(slice_param_rext);
565            }
566
567            let slice_data = BufferType::SliceData(last_slice.2);
568            let slice_data = context.create_buffer(slice_data)?;
569            picture.add_buffer(slice_data);
570        }
571
572        Ok(())
573    }
574}
575
576pub struct VaapiH265Picture<Picture> {
577    picture: Picture,
578
579    // We are always one slice behind, so that we can mark the last one in
580    // submit_picture()
581    last_slice: Option<(
582        SliceParameterBufferHEVC,
583        Option<SliceParameterBufferHEVCRext>,
584        Vec<u8>,
585    )>,
586
587    va_references: [PictureHEVC; 15],
588}
589
590impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoderBackendPicture<H265>
591    for VaapiBackend<M>
592{
593    type Picture = VaapiH265Picture<VaapiPicture<M>>;
594}
595
596impl<M: SurfaceMemoryDescriptor + 'static> StatelessH265DecoderBackend for VaapiBackend<M> {
597    fn new_sequence(&mut self, sps: &Sps) -> StatelessBackendResult<()> {
598        self.new_sequence(sps, PoolCreationMode::Highest)
599    }
600
601    fn new_picture(
602        &mut self,
603        coded_resolution: Resolution,
604        timestamp: u64,
605    ) -> NewPictureResult<Self::Picture> {
606        let layer = PoolLayer::Layer(coded_resolution);
607        let pool = self
608            .frame_pool(layer)
609            .pop()
610            .ok_or(NewPictureError::NoFramePool(coded_resolution))?;
611        let surface = pool
612            .get_surface()
613            .ok_or(NewPictureError::OutOfOutputBuffers)?;
614        let metadata = self.metadata_state.get_parsed()?;
615
616        Ok(VaapiH265Picture {
617            picture: VaPicture::new(timestamp, Rc::clone(&metadata.context), surface),
618            last_slice: Default::default(),
619            va_references: Default::default(),
620        })
621    }
622
623    fn begin_picture(
624        &mut self,
625        picture: &mut Self::Picture,
626        picture_data: &PictureData,
627        sps: &Sps,
628        pps: &Pps,
629        dpb: &Dpb<Self::Handle>,
630        rps: &RefPicSet<Self::Handle>,
631        slice: &Slice,
632    ) -> crate::decoder::stateless::StatelessBackendResult<()> {
633        let metadata = self.metadata_state.get_parsed()?;
634        let context = &metadata.context;
635
636        let surface_id = picture.picture.surface().id();
637
638        let (pic_param, reference_frames) =
639            build_pic_param(slice, picture_data, surface_id, dpb, rps, sps, pps)?;
640
641        picture.va_references = reference_frames;
642
643        let picture = &mut picture.picture;
644
645        let pic_param = context
646            .create_buffer(pic_param)
647            .context("while creating picture parameter buffer")?;
648
649        picture.add_buffer(pic_param);
650
651        if !matches!(find_scaling_list(sps, pps), ScalingListType::None) {
652            let iq_matrix = build_iq_matrix(sps, pps);
653            let iq_matrix = context
654                .create_buffer(iq_matrix)
655                .context("while creating IQ matrix buffer")?;
656
657            picture.add_buffer(iq_matrix);
658        }
659
660        let va_profile = sps.va_profile()?;
661        if is_range_extension_profile(va_profile) || is_scc_ext_profile(va_profile) {
662            let rext = build_picture_rext(sps, pps)?;
663            let rext = context
664                .create_buffer(rext)
665                .context("while creating picture parameter range extension buffer")?;
666
667            picture.add_buffer(rext);
668
669            if is_scc_ext_profile(va_profile) {
670                let scc = build_picture_scc(sps, pps)?;
671                let scc = context
672                    .create_buffer(scc)
673                    .context("while creating picture screen content coding buffer")?;
674
675                picture.add_buffer(scc);
676            }
677        }
678
679        Ok(())
680    }
681
682    fn decode_slice(
683        &mut self,
684        picture: &mut Self::Picture,
685        slice: &Slice,
686        sps: &Sps,
687        _: &Pps,
688        ref_pic_list0: &[Option<RefPicListEntry<Self::Handle>>; 16],
689        ref_pic_list1: &[Option<RefPicListEntry<Self::Handle>>; 16],
690    ) -> crate::decoder::stateless::StatelessBackendResult<()> {
691        self.submit_last_slice(picture)?;
692        let hdr = &slice.header;
693
694        let va_references = &picture.va_references;
695        let ref_pic_list0 = build_slice_ref_pic_list(ref_pic_list0, va_references);
696        let ref_pic_list1 = build_slice_ref_pic_list(ref_pic_list1, va_references);
697
698        let long_slice_flags = libva::HevcLongSliceFlags::new(
699            0,
700            hdr.dependent_slice_segment_flag as u32,
701            hdr.type_ as u32,
702            hdr.colour_plane_id as u32,
703            hdr.sao_luma_flag as u32,
704            hdr.sao_chroma_flag as u32,
705            hdr.mvd_l1_zero_flag as u32,
706            hdr.cabac_init_flag as u32,
707            hdr.temporal_mvp_enabled_flag as u32,
708            hdr.deblocking_filter_disabled_flag as u32,
709            hdr.collocated_from_l0_flag as u32,
710            hdr.loop_filter_across_slices_enabled_flag as u32,
711        );
712
713        let collocated_ref_idx = if hdr.temporal_mvp_enabled_flag {
714            hdr.collocated_ref_idx
715        } else {
716            0xff
717        };
718
719        let pwt = &hdr.pred_weight_table;
720
721        let mut delta_luma_weight_l0: [i8; 15usize] = Default::default();
722        let mut luma_offset_l0: [i8; 15usize] = Default::default();
723        let mut delta_chroma_weight_l0: [[i8; 2usize]; 15usize] = Default::default();
724        let mut chroma_offset_l0: [[i8; 2usize]; 15usize] = Default::default();
725        let mut delta_luma_weight_l1: [i8; 15usize] = Default::default();
726        let mut luma_offset_l1: [i8; 15usize] = Default::default();
727        let mut delta_chroma_weight_l1: [[i8; 2usize]; 15usize] = Default::default();
728        let mut chroma_offset_l1: [[i8; 2usize]; 15usize] = Default::default();
729
730        for i in 0..15 {
731            delta_luma_weight_l0[i] = pwt.delta_luma_weight_l0[i];
732            luma_offset_l0[i] = pwt.luma_offset_l0[i];
733
734            if hdr.type_.is_b() {
735                delta_luma_weight_l1[i] = pwt.delta_luma_weight_l1[i];
736                luma_offset_l1[i] = pwt.luma_offset_l1[i];
737            }
738
739            for j in 0..2 {
740                delta_chroma_weight_l0[i][j] = pwt.delta_chroma_weight_l0[i][j];
741                let delta_chroma_offset = pwt.delta_chroma_offset_l0[i][j];
742
743                let chroma_weight_l0 = (1 << pwt.chroma_log2_weight_denom)
744                    + i32::from(pwt.delta_chroma_weight_l0[i][j]);
745
746                let offset = sps.wp_offset_half_range_c as i32 + delta_chroma_offset as i32
747                    - ((sps.wp_offset_half_range_c as i32 * chroma_weight_l0)
748                        >> pwt.chroma_log2_weight_denom);
749
750                chroma_offset_l0[i][j] = clip3(
751                    -(sps.wp_offset_half_range_c as i32),
752                    (sps.wp_offset_half_range_c - 1) as i32,
753                    offset,
754                ) as _;
755
756                if hdr.type_.is_b() {
757                    delta_chroma_weight_l1[i][j] = pwt.delta_chroma_weight_l1[i][j];
758                    let delta_chroma_offset = pwt.delta_chroma_offset_l1[i][j];
759
760                    let chroma_weight_l1 = (1 << pwt.chroma_log2_weight_denom)
761                        + i32::from(pwt.delta_chroma_weight_l1[i][j]);
762
763                    let offset = sps.wp_offset_half_range_c as i32 + delta_chroma_offset as i32
764                        - ((sps.wp_offset_half_range_c as i32 * chroma_weight_l1)
765                            >> pwt.chroma_log2_weight_denom);
766
767                    chroma_offset_l1[i][j] = clip3(
768                        -(sps.wp_offset_half_range_c as i32),
769                        (sps.wp_offset_half_range_c - 1) as i32,
770                        offset,
771                    ) as _;
772                }
773            }
774        }
775
776        let slice_param = SliceParameterBufferHEVC::new(
777            slice.nalu.size as u32,
778            0,
779            libva::constants::VA_SLICE_DATA_FLAG_ALL,
780            (hdr.header_bit_size / 8) as _,
781            hdr.segment_address,
782            [ref_pic_list0, ref_pic_list1],
783            &long_slice_flags,
784            collocated_ref_idx,
785            hdr.num_ref_idx_l0_active_minus1,
786            hdr.num_ref_idx_l1_active_minus1,
787            hdr.qp_delta,
788            hdr.cb_qp_offset,
789            hdr.cr_qp_offset,
790            hdr.beta_offset_div2,
791            hdr.tc_offset_div2,
792            pwt.luma_log2_weight_denom,
793            pwt.delta_chroma_log2_weight_denom,
794            delta_luma_weight_l0,
795            luma_offset_l0,
796            delta_chroma_weight_l0,
797            chroma_offset_l0,
798            delta_luma_weight_l1,
799            luma_offset_l1,
800            delta_chroma_weight_l1,
801            chroma_offset_l1,
802            hdr.five_minus_max_num_merge_cand,
803            hdr.num_entry_point_offsets as _,
804            0,
805            hdr.n_emulation_prevention_bytes as _,
806        );
807
808        let va_profile = sps.va_profile()?;
809
810        let slice_param_ext =
811            if is_range_extension_profile(va_profile) || is_scc_ext_profile(va_profile) {
812                let slice_ext_flags = HevcSliceExtFlags::new(
813                    hdr.cu_chroma_qp_offset_enabled_flag as u32,
814                    hdr.use_integer_mv_flag as u32,
815                );
816
817                let slice_param_ext = SliceParameterBufferHEVCRext::new(
818                    luma_offset_l0.map(i16::from),
819                    chroma_offset_l0.map(|outer| outer.map(i16::from)),
820                    luma_offset_l1.map(i16::from),
821                    chroma_offset_l1.map(|outer| outer.map(i16::from)),
822                    &slice_ext_flags,
823                    hdr.slice_act_y_qp_offset,
824                    hdr.slice_act_cb_qp_offset,
825                    hdr.slice_act_cr_qp_offset,
826                );
827
828                Some(slice_param_ext)
829            } else {
830                None
831            };
832
833        let slice_data = Vec::from(slice.nalu.as_ref());
834
835        picture.last_slice = Some((slice_param, slice_param_ext, slice_data));
836
837        Ok(())
838    }
839
840    fn submit_picture(
841        &mut self,
842        mut picture: Self::Picture,
843    ) -> StatelessBackendResult<Self::Handle> {
844        if let Some(last_slice) = &mut picture.last_slice {
845            last_slice.0.set_as_last();
846        }
847        self.submit_last_slice(&mut picture)?;
848        self.process_picture::<H265>(picture.picture)
849    }
850}
851
852impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoder<H265, VaapiBackend<M>> {
853    // Creates a new instance of the decoder using the VAAPI backend.
854    pub fn new_vaapi<S>(
855        display: Rc<Display>,
856        blocking_mode: BlockingMode,
857    ) -> Result<Self, NewStatelessDecoderError>
858    where
859        M: From<S>,
860        S: From<M>,
861    {
862        Self::new(VaapiBackend::new(display, false), blocking_mode)
863    }
864}
865
866#[cfg(test)]
867mod tests {
868    use libva::Display;
869
870    use crate::codec::h265::parser::Nalu;
871    use crate::decoder::stateless::h265::H265;
872    use crate::decoder::stateless::tests::test_decode_stream;
873    use crate::decoder::stateless::tests::TestStream;
874    use crate::decoder::stateless::StatelessDecoder;
875    use crate::decoder::BlockingMode;
876    use crate::utils::simple_playback_loop;
877    use crate::utils::simple_playback_loop_owned_frames;
878    use crate::utils::NalIterator;
879    use crate::DecodedFormat;
880
881    /// Run `test` using the vaapi decoder, in both blocking and non-blocking modes.
882    fn test_decoder_vaapi(
883        test: &TestStream,
884        output_format: DecodedFormat,
885        blocking_mode: BlockingMode,
886    ) {
887        let display = Display::open().unwrap();
888        let decoder = StatelessDecoder::<H265, _>::new_vaapi::<()>(display, blocking_mode).unwrap();
889
890        test_decode_stream(
891            |d, s, f| {
892                simple_playback_loop(
893                    d,
894                    NalIterator::<Nalu>::new(s),
895                    f,
896                    &mut simple_playback_loop_owned_frames,
897                    output_format,
898                    blocking_mode,
899                )
900            },
901            decoder,
902            test,
903            true,
904            false,
905        );
906    }
907
908    #[test]
909    // Ignore this test by default as it requires libva-compatible hardware.
910    #[ignore]
911    fn test_64x64_progressive_i_block() {
912        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I;
913        test_decoder_vaapi(
914            &DECODE_64X64_PROGRESSIVE_I,
915            DecodedFormat::NV12,
916            BlockingMode::Blocking,
917        );
918    }
919
920    #[test]
921    // Ignore this test by default as it requires libva-compatible hardware.
922    #[ignore]
923    fn test_64x64_progressive_i_nonblock() {
924        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I;
925        test_decoder_vaapi(
926            &DECODE_64X64_PROGRESSIVE_I,
927            DecodedFormat::NV12,
928            BlockingMode::NonBlocking,
929        );
930    }
931
932    #[test]
933    // Ignore this test by default as it requires libva-compatible hardware.
934    #[ignore]
935    fn test_64x64_progressive_i_p_block() {
936        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I_P;
937        test_decoder_vaapi(
938            &DECODE_64X64_PROGRESSIVE_I_P,
939            DecodedFormat::NV12,
940            BlockingMode::Blocking,
941        );
942    }
943
944    #[test]
945    // Ignore this test by default as it requires libva-compatible hardware.
946    #[ignore]
947    fn test_64x64_progressive_i_p_nonblock() {
948        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I_P;
949        test_decoder_vaapi(
950            &DECODE_64X64_PROGRESSIVE_I_P,
951            DecodedFormat::NV12,
952            BlockingMode::NonBlocking,
953        );
954    }
955
956    #[test]
957    // Ignore this test by default as it requires libva-compatible hardware.
958    #[ignore]
959    fn test_64x64_progressive_i_p_b_p_block() {
960        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I_P_B_P;
961        test_decoder_vaapi(
962            &DECODE_64X64_PROGRESSIVE_I_P_B_P,
963            DecodedFormat::NV12,
964            BlockingMode::Blocking,
965        );
966    }
967
968    #[test]
969    // Ignore this test by default as it requires libva-compatible hardware.
970    #[ignore]
971    fn test_64x64_progressive_i_p_b_p_nonblock() {
972        use crate::decoder::stateless::h265::tests::DECODE_64X64_PROGRESSIVE_I_P_B_P;
973        test_decoder_vaapi(
974            &DECODE_64X64_PROGRESSIVE_I_P_B_P,
975            DecodedFormat::NV12,
976            BlockingMode::NonBlocking,
977        );
978    }
979}