• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::any::Any;
6 use std::borrow::Borrow;
7 use std::rc::Rc;
8 
9 use anyhow::Context;
10 use libva::BufferType;
11 use libva::Display;
12 use libva::EncCodedBuffer;
13 use libva::EncPictureParameter;
14 use libva::EncPictureParameterBufferH264;
15 use libva::EncSequenceParameter;
16 use libva::EncSequenceParameterBufferH264;
17 use libva::EncSliceParameter;
18 use libva::EncSliceParameterBufferH264;
19 use libva::H264EncFrameCropOffsets;
20 use libva::H264EncPicFields;
21 use libva::H264EncSeqFields;
22 use libva::H264VuiFields;
23 use libva::Picture;
24 use libva::PictureH264;
25 use libva::Surface;
26 use libva::SurfaceMemoryDescriptor;
27 use libva::VAProfile;
28 use libva::VA_INVALID_ID;
29 use libva::VA_PICTURE_H264_LONG_TERM_REFERENCE;
30 use libva::VA_PICTURE_H264_SHORT_TERM_REFERENCE;
31 
32 use crate::backend::vaapi::encoder::tunings_to_libva_rc;
33 use crate::backend::vaapi::encoder::CodedOutputPromise;
34 use crate::backend::vaapi::encoder::Reconstructed;
35 use crate::backend::vaapi::encoder::VaapiBackend;
36 use crate::codec::h264::parser::Pps;
37 use crate::codec::h264::parser::Profile;
38 use crate::codec::h264::parser::SliceHeader;
39 use crate::codec::h264::parser::Sps;
40 use crate::encoder::h264::EncoderConfig;
41 use crate::encoder::h264::H264;
42 use crate::encoder::stateless::h264::predictor::MAX_QP;
43 use crate::encoder::stateless::h264::predictor::MIN_QP;
44 use crate::encoder::stateless::h264::BackendRequest;
45 use crate::encoder::stateless::h264::DpbEntry;
46 use crate::encoder::stateless::h264::DpbEntryMeta;
47 use crate::encoder::stateless::h264::IsReference;
48 use crate::encoder::stateless::h264::StatelessEncoder;
49 use crate::encoder::stateless::h264::StatelessH264EncoderBackend;
50 use crate::encoder::stateless::ReadyPromise;
51 use crate::encoder::stateless::StatelessBackendError;
52 use crate::encoder::stateless::StatelessBackendResult;
53 use crate::encoder::stateless::StatelessVideoEncoderBackend;
54 use crate::encoder::EncodeResult;
55 use crate::encoder::RateControl;
56 use crate::video_frame::VideoFrame;
57 use crate::BlockingMode;
58 use crate::Fourcc;
59 use crate::Resolution;
60 
61 type Request<'l, H> = BackendRequest<H, Reconstructed>;
62 
63 impl<M, H> StatelessVideoEncoderBackend<H264> for VaapiBackend<M, H>
64 where
65     M: SurfaceMemoryDescriptor,
66     H: std::borrow::Borrow<Surface<M>> + 'static,
67 {
68     type Picture = H;
69     type Reconstructed = Reconstructed;
70     type CodedPromise = CodedOutputPromise<M, H>;
71     type ReconPromise = ReadyPromise<Self::Reconstructed>;
72 }
73 
74 impl<M, H> VaapiBackend<M, H>
75 where
76     M: SurfaceMemoryDescriptor,
77     H: std::borrow::Borrow<Surface<M>> + 'static,
78 {
79     /// Builds an invalid [`libva::PictureH264`]. This is usually a place
80     /// holder to fill staticly sized array.
build_invalid_va_h264_pic_enc() -> libva::PictureH26481     fn build_invalid_va_h264_pic_enc() -> libva::PictureH264 {
82         libva::PictureH264::new(libva::VA_INVALID_ID, 0, libva::VA_PICTURE_H264_INVALID, 0, 0)
83     }
84 
85     /// Builds [`libva::PictureH264`] from `frame`
build_h264_pic(surface: &Reconstructed, meta: &DpbEntryMeta) -> PictureH26486     fn build_h264_pic(surface: &Reconstructed, meta: &DpbEntryMeta) -> PictureH264 {
87         let flags = match meta.is_reference {
88             IsReference::No => 0,
89             IsReference::LongTerm => VA_PICTURE_H264_LONG_TERM_REFERENCE,
90             IsReference::ShortTerm => VA_PICTURE_H264_SHORT_TERM_REFERENCE,
91         };
92 
93         PictureH264::new(
94             surface.surface_id(),
95             meta.frame_num,
96             flags,
97             meta.poc as i32,
98             meta.poc as i32,
99         )
100     }
101 
102     /// Builds [`BufferType::EncSequenceParameter`] from `sps`
build_enc_seq_param( sps: &Sps, bits_per_second: u32, intra_period: u32, ip_period: u32, ) -> BufferType103     fn build_enc_seq_param(
104         sps: &Sps,
105         bits_per_second: u32,
106         intra_period: u32,
107         ip_period: u32,
108     ) -> BufferType {
109         let intra_idr_period = intra_period;
110 
111         let seq_fields = H264EncSeqFields::new(
112             sps.chroma_format_idc as u32,
113             sps.frame_mbs_only_flag as u32,
114             sps.mb_adaptive_frame_field_flag as u32,
115             sps.seq_scaling_matrix_present_flag as u32,
116             sps.direct_8x8_inference_flag as u32,
117             sps.log2_max_frame_num_minus4 as u32,
118             sps.pic_order_cnt_type as u32,
119             sps.log2_max_pic_order_cnt_lsb_minus4 as u32,
120             sps.delta_pic_order_always_zero_flag as u32,
121         );
122 
123         let frame_crop = if sps.frame_cropping_flag {
124             Some(H264EncFrameCropOffsets::new(
125                 sps.frame_crop_left_offset,
126                 sps.frame_crop_right_offset,
127                 sps.frame_crop_top_offset,
128                 sps.frame_crop_bottom_offset,
129             ))
130         } else {
131             None
132         };
133 
134         let vui_fields = if sps.vui_parameters_present_flag {
135             Some(H264VuiFields::new(
136                 sps.vui_parameters.aspect_ratio_idc as u32,
137                 sps.vui_parameters.timing_info_present_flag as u32,
138                 sps.vui_parameters.bitstream_restriction_flag as u32,
139                 sps.vui_parameters.log2_max_mv_length_horizontal,
140                 sps.vui_parameters.log2_max_mv_length_vertical,
141                 sps.vui_parameters.fixed_frame_rate_flag as u32,
142                 sps.vui_parameters.low_delay_hrd_flag as u32,
143                 sps.vui_parameters.motion_vectors_over_pic_boundaries_flag as u32,
144             ))
145         } else {
146             None
147         };
148 
149         let mut offset_for_ref_frame = [0i32; 256];
150         offset_for_ref_frame[..255].copy_from_slice(&sps.offset_for_ref_frame[..]);
151 
152         BufferType::EncSequenceParameter(EncSequenceParameter::H264(
153             EncSequenceParameterBufferH264::new(
154                 sps.seq_parameter_set_id,
155                 sps.level_idc as u8,
156                 intra_period,
157                 intra_idr_period,
158                 ip_period,
159                 bits_per_second,
160                 sps.max_num_ref_frames as u32,
161                 sps.pic_width_in_mbs_minus1 + 1,
162                 sps.pic_height_in_map_units_minus1 + 1,
163                 &seq_fields,
164                 sps.bit_depth_luma_minus8,
165                 sps.bit_depth_chroma_minus8,
166                 sps.num_ref_frames_in_pic_order_cnt_cycle,
167                 sps.offset_for_non_ref_pic,
168                 sps.offset_for_top_to_bottom_field,
169                 offset_for_ref_frame,
170                 frame_crop,
171                 vui_fields,
172                 sps.vui_parameters.aspect_ratio_idc,
173                 sps.vui_parameters.sar_width as u32,
174                 sps.vui_parameters.sar_height as u32,
175                 sps.vui_parameters.num_units_in_tick,
176                 sps.vui_parameters.time_scale,
177             ),
178         ))
179     }
180 
181     /// Builds [`BufferType::EncPictureParameter`] from [`Request`] and sets bitstream
182     /// output to `coded_buf`.
build_enc_pic_param( request: &Request<'_, H>, coded_buf: &EncCodedBuffer, recon: &Reconstructed, ) -> BufferType183     fn build_enc_pic_param(
184         request: &Request<'_, H>,
185         coded_buf: &EncCodedBuffer,
186         recon: &Reconstructed,
187     ) -> BufferType {
188         let pic_fields = H264EncPicFields::new(
189             request.is_idr as u32,
190             (request.dpb_meta.is_reference != IsReference::No) as u32,
191             request.pps.entropy_coding_mode_flag as u32,
192             request.pps.weighted_pred_flag as u32,
193             request.pps.weighted_bipred_idc as u32,
194             request.pps.constrained_intra_pred_flag as u32,
195             request.pps.transform_8x8_mode_flag as u32,
196             request.pps.deblocking_filter_control_present_flag as u32,
197             request.pps.redundant_pic_cnt_present_flag as u32,
198             0,
199             request.pps.pic_scaling_matrix_present_flag as u32,
200         );
201 
202         let curr_pic = Self::build_h264_pic(recon, &request.dpb_meta);
203 
204         assert!(request.ref_list_0.len() + request.ref_list_1.len() <= 16);
205 
206         let mut reference_frames: [PictureH264; 16] = (0..16)
207             .map(|_| Self::build_invalid_va_h264_pic_enc())
208             .collect::<Vec<_>>()
209             .try_into()
210             .unwrap_or_else(|_| panic!());
211 
212         for (idx, ref_frame) in
213             request.ref_list_0.iter().chain(request.ref_list_1.iter()).enumerate().take(16)
214         {
215             reference_frames[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta);
216         }
217 
218         BufferType::EncPictureParameter(EncPictureParameter::H264(
219             EncPictureParameterBufferH264::new(
220                 curr_pic,
221                 reference_frames,
222                 coded_buf.id(),
223                 request.pps.pic_parameter_set_id,
224                 request.pps.seq_parameter_set_id,
225                 0, // last_pic, don't appned EOS
226                 request.dpb_meta.frame_num as u16,
227                 (request.pps.pic_init_qp_minus26 + 26) as u8,
228                 request.pps.num_ref_idx_l0_default_active_minus1,
229                 request.pps.num_ref_idx_l1_default_active_minus1,
230                 request.pps.chroma_qp_index_offset,
231                 request.pps.second_chroma_qp_index_offset,
232                 &pic_fields,
233             ),
234         ))
235     }
236 
237     /// Builds [`BufferType::EncSliceParameter`]
build_enc_slice_param( pps: &Pps, header: &SliceHeader, ref_list_0: &[Rc<DpbEntry<Reconstructed>>], ref_list_1: &[Rc<DpbEntry<Reconstructed>>], num_macroblocks: u32, ) -> BufferType238     fn build_enc_slice_param(
239         pps: &Pps,
240         header: &SliceHeader,
241         ref_list_0: &[Rc<DpbEntry<Reconstructed>>],
242         ref_list_1: &[Rc<DpbEntry<Reconstructed>>],
243         num_macroblocks: u32,
244     ) -> BufferType {
245         let mut ref_pic_list_0: [PictureH264; 32] = (0..32)
246             .map(|_| Self::build_invalid_va_h264_pic_enc())
247             .collect::<Vec<_>>()
248             .try_into()
249             .unwrap_or_else(|_| panic!());
250 
251         for (idx, ref_frame) in ref_list_0.iter().enumerate().take(16) {
252             ref_pic_list_0[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta);
253         }
254 
255         let mut ref_pic_list_1: [PictureH264; 32] = (0..32)
256             .map(|_| Self::build_invalid_va_h264_pic_enc())
257             .collect::<Vec<_>>()
258             .try_into()
259             .unwrap_or_else(|_| panic!());
260 
261         for (idx, ref_frame) in ref_list_1.iter().enumerate().take(16) {
262             ref_pic_list_1[idx] = Self::build_h264_pic(&ref_frame.recon_pic, &ref_frame.meta);
263         }
264 
265         let mut luma_weight_l0_flag = false;
266         let mut luma_offset_l0 = [0i16; 32];
267 
268         if header.pred_weight_table.luma_weight_l0 != [0i16; 32] {
269             luma_weight_l0_flag = true;
270             for (i, val) in header.pred_weight_table.luma_offset_l0.iter().enumerate() {
271                 luma_offset_l0[i] = (*val).into();
272             }
273         }
274 
275         let mut chroma_weight_l0_flag = false;
276         let mut chroma_offset_l0 = [[0i16; 2]; 32];
277 
278         if header.pred_weight_table.chroma_weight_l0 != [[0i16; 2]; 32] {
279             chroma_weight_l0_flag = true;
280             for (i, val) in header.pred_weight_table.chroma_offset_l0.iter().enumerate() {
281                 chroma_offset_l0[i] = [val[0].into(), val[1].into()];
282             }
283         }
284 
285         let mut luma_weight_l1_flag = false;
286         let mut luma_offset_l1 = [0i16; 32];
287 
288         if header.pred_weight_table.luma_weight_l1 != [0i16; 32] {
289             luma_weight_l1_flag = true;
290             for (i, val) in header.pred_weight_table.luma_offset_l1.iter().enumerate() {
291                 luma_offset_l1[i] = *val;
292             }
293         }
294 
295         let mut chroma_weight_l1_flag = false;
296         let mut chroma_offset_l1 = [[0i16; 2]; 32];
297 
298         if header.pred_weight_table.chroma_weight_l1 != [[0i16; 2]; 32] {
299             chroma_weight_l1_flag = true;
300             for (i, val) in header.pred_weight_table.chroma_offset_l1.iter().enumerate() {
301                 chroma_offset_l1[i] = [val[0].into(), val[1].into()];
302             }
303         }
304 
305         let (num_ref_idx_l0_active_minus1, num_ref_idx_l1_active_minus1) =
306             if header.num_ref_idx_active_override_flag {
307                 (header.num_ref_idx_l0_active_minus1, header.num_ref_idx_l1_active_minus1)
308             } else {
309                 (pps.num_ref_idx_l0_default_active_minus1, pps.num_ref_idx_l1_default_active_minus1)
310             };
311         BufferType::EncSliceParameter(EncSliceParameter::H264(EncSliceParameterBufferH264::new(
312             header.first_mb_in_slice,
313             num_macroblocks,
314             VA_INVALID_ID,
315             header.slice_type as u8,
316             pps.pic_parameter_set_id,
317             header.idr_pic_id,
318             header.pic_order_cnt_lsb,
319             header.delta_pic_order_cnt_bottom,
320             header.delta_pic_order_cnt,
321             header.direct_spatial_mv_pred_flag as u8,
322             header.num_ref_idx_active_override_flag as u8,
323             num_ref_idx_l0_active_minus1,
324             num_ref_idx_l1_active_minus1,
325             ref_pic_list_0,
326             ref_pic_list_1,
327             header.pred_weight_table.luma_log2_weight_denom,
328             header.pred_weight_table.chroma_log2_weight_denom,
329             luma_weight_l0_flag as u8,
330             header.pred_weight_table.luma_weight_l0,
331             luma_offset_l0,
332             chroma_weight_l0_flag as u8,
333             header.pred_weight_table.chroma_weight_l0,
334             chroma_offset_l0,
335             luma_weight_l1_flag as u8,
336             header.pred_weight_table.luma_weight_l1,
337             luma_offset_l1,
338             chroma_weight_l1_flag as u8,
339             header.pred_weight_table.chroma_weight_l1,
340             chroma_offset_l1,
341             header.cabac_init_idc,
342             header.slice_qp_delta,
343             header.disable_deblocking_filter_idc,
344             header.slice_alpha_c0_offset_div2,
345             header.slice_beta_offset_div2,
346         )))
347     }
348 }
349 
350 impl<M, H> StatelessH264EncoderBackend for VaapiBackend<M, H>
351 where
352     M: SurfaceMemoryDescriptor,
353     H: Borrow<Surface<M>> + 'static,
354 {
encode_slice( &mut self, request: Request<'_, H>, ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)>355     fn encode_slice(
356         &mut self,
357         request: Request<'_, H>,
358     ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)> {
359         let coded_buf = self.new_coded_buffer(&request.tunings.rate_control)?;
360         let recon = self.new_scratch_picture()?;
361 
362         // Use bitrate from RateControl or ask driver to ignore
363         let bits_per_second = request.tunings.rate_control.bitrate_target().unwrap_or(0) as u32;
364         let seq_param = Self::build_enc_seq_param(
365             &request.sps,
366             bits_per_second,
367             request.intra_period,
368             request.ip_period,
369         );
370 
371         let pic_param = Self::build_enc_pic_param(&request, &coded_buf, &recon);
372         let slice_param = Self::build_enc_slice_param(
373             &request.pps,
374             &request.header,
375             &request.ref_list_0,
376             &request.ref_list_1,
377             request.num_macroblocks as u32,
378         );
379 
380         // Clone reference frames
381         let references: Vec<Rc<dyn Any>> = request
382             .ref_list_0
383             .iter()
384             .cloned()
385             .chain(request.ref_list_1.iter().cloned())
386             .map(|entry| entry as Rc<dyn Any>)
387             .collect();
388 
389         // Clone picture using [`Picture::new_from_same_surface`] to avoid
390         // creatig a shared cell picture between its references and processed
391         // picture.
392         let mut picture = Picture::new(
393             request.dpb_meta.frame_num as u64,
394             Rc::clone(self.context()),
395             request.input,
396         );
397 
398         let rc_param =
399             tunings_to_libva_rc::<{ MIN_QP as u32 }, { MAX_QP as u32 }>(&request.tunings)?;
400         let rc_param = BufferType::EncMiscParameter(libva::EncMiscParameter::RateControl(rc_param));
401 
402         let framerate_param = BufferType::EncMiscParameter(libva::EncMiscParameter::FrameRate(
403             libva::EncMiscParameterFrameRate::new(request.tunings.framerate, 0),
404         ));
405 
406         picture.add_buffer(self.context().create_buffer(seq_param)?);
407         picture.add_buffer(self.context().create_buffer(pic_param)?);
408         picture.add_buffer(self.context().create_buffer(slice_param)?);
409         picture.add_buffer(self.context().create_buffer(rc_param)?);
410         picture.add_buffer(self.context().create_buffer(framerate_param)?);
411 
412         // Start processing the picture encoding
413         let picture = picture.begin().context("picture begin")?;
414         let picture = picture.render().context("picture render")?;
415         let picture = picture.end().context("picture end")?;
416 
417         // HACK: Make sure that slice nalu start code is at least 4 bytes.
418         // TODO: Use packed headers to supply slice header with nalu start code of size 4 and get
419         // rid of this hack.
420         let mut coded_output = request.coded_output;
421         coded_output.push(0);
422 
423         // libva will handle the synchronization of reconstructed surface with implicit fences.
424         // Therefore return the reconstructed frame immediately.
425         let reference_promise = ReadyPromise::from(recon);
426 
427         let bitstream_promise =
428             CodedOutputPromise::new(picture, references, coded_buf, coded_output);
429 
430         Ok((reference_promise, bitstream_promise))
431     }
432 }
433 
434 impl<V: VideoFrame> StatelessEncoder<V, VaapiBackend<V::MemDescriptor, Surface<V::MemDescriptor>>> {
new_vaapi( display: Rc<Display>, config: EncoderConfig, fourcc: Fourcc, coded_size: Resolution, low_power: bool, blocking_mode: BlockingMode, ) -> EncodeResult<Self>435     pub fn new_vaapi(
436         display: Rc<Display>,
437         config: EncoderConfig,
438         fourcc: Fourcc,
439         coded_size: Resolution,
440         low_power: bool,
441         blocking_mode: BlockingMode,
442     ) -> EncodeResult<Self> {
443         let va_profile = match config.profile {
444             Profile::Baseline => VAProfile::VAProfileH264ConstrainedBaseline,
445             Profile::Main => VAProfile::VAProfileH264Main,
446             Profile::High => VAProfile::VAProfileH264High,
447             _ => return Err(StatelessBackendError::UnsupportedProfile.into()),
448         };
449 
450         let bitrate_control = match config.initial_tunings.rate_control {
451             RateControl::ConstantBitrate(_) => libva::VA_RC_CBR,
452             RateControl::ConstantQuality(_) => libva::VA_RC_CQP,
453         };
454 
455         let backend =
456             VaapiBackend::new(display, va_profile, fourcc, coded_size, bitrate_control, low_power)?;
457 
458         Self::new_h264(backend, config, blocking_mode)
459     }
460 }
461 
462 #[cfg(test)]
463 pub(super) mod tests {
464     use libva::Display;
465     use libva::UsageHint;
466     use libva::VAEntrypoint::VAEntrypointEncSliceLP;
467     use libva::VAProfile::VAProfileH264Main;
468     use libva::VA_RT_FORMAT_YUV420;
469 
470     use super::*;
471     use crate::backend::vaapi::encoder::tests::upload_test_frame_nv12;
472     use crate::backend::vaapi::encoder::tests::TestFrameGenerator;
473     use crate::backend::vaapi::surface_pool::PooledVaSurface;
474     use crate::backend::vaapi::surface_pool::VaSurfacePool;
475     use crate::codec::h264::parser::Level;
476     use crate::codec::h264::parser::PpsBuilder;
477     use crate::codec::h264::parser::Profile;
478     use crate::codec::h264::parser::SliceHeaderBuilder;
479     use crate::codec::h264::parser::SliceType;
480     use crate::codec::h264::parser::SpsBuilder;
481     use crate::decoder::FramePool;
482     use crate::encoder::simple_encode_loop;
483     use crate::encoder::stateless::h264::BackendRequest;
484     use crate::encoder::stateless::h264::EncoderConfig;
485     use crate::encoder::stateless::h264::StatelessEncoder;
486     use crate::encoder::stateless::BackendPromise;
487     use crate::encoder::stateless::StatelessEncoderBackendImport;
488     use crate::encoder::FrameMetadata;
489     use crate::encoder::Tunings;
490     use crate::FrameLayout;
491     use crate::PlaneLayout;
492     use crate::Resolution;
493 
494     #[test]
495     // Ignore this test by default as it requires libva-compatible hardware.
496     #[ignore]
test_simple_encode_slice()497     fn test_simple_encode_slice() {
498         type Descriptor = ();
499         type Surface = libva::Surface<Descriptor>;
500         const WIDTH: u32 = 256;
501         const HEIGHT: u32 = 256;
502         let fourcc = b"NV12".into();
503 
504         let frame_layout = FrameLayout {
505             format: (fourcc, 0),
506             size: Resolution { width: WIDTH, height: HEIGHT },
507             planes: vec![
508                 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH as usize },
509                 PlaneLayout {
510                     buffer_index: 0,
511                     offset: (WIDTH * HEIGHT) as usize,
512                     stride: WIDTH as usize,
513                 },
514             ],
515         };
516 
517         let display = Display::open().unwrap();
518         let entrypoints = display.query_config_entrypoints(VAProfileH264Main).unwrap();
519         let low_power = entrypoints.contains(&VAEntrypointEncSliceLP);
520 
521         let mut backend = VaapiBackend::<Descriptor, Surface>::new(
522             Rc::clone(&display),
523             VAProfileH264Main,
524             fourcc,
525             Resolution { width: WIDTH, height: HEIGHT },
526             libva::VA_RC_CBR,
527             low_power,
528         )
529         .unwrap();
530 
531         let mut surfaces = display
532             .create_surfaces(
533                 VA_RT_FORMAT_YUV420,
534                 Some(frame_layout.format.0 .0),
535                 WIDTH,
536                 HEIGHT,
537                 Some(UsageHint::USAGE_HINT_ENCODER),
538                 vec![()],
539             )
540             .unwrap();
541 
542         let surface = surfaces.pop().unwrap();
543 
544         upload_test_frame_nv12(&display, &surface, 0.0);
545 
546         let input_meta =
547             FrameMetadata { layout: frame_layout, force_keyframe: false, timestamp: 0 };
548 
549         let pic = backend.import_picture(&input_meta, surface).unwrap();
550 
551         let sps = SpsBuilder::new()
552             .seq_parameter_set_id(0)
553             .profile_idc(Profile::Main)
554             .level_idc(Level::L4)
555             .resolution(WIDTH, HEIGHT)
556             .chroma_format_idc(3)
557             .frame_mbs_only_flag(true)
558             .direct_8x8_inference_flag(true)
559             .max_num_ref_frames(1)
560             .max_frame_num(32)
561             .pic_order_cnt_type(0)
562             .max_pic_order_cnt_lsb(128)
563             .delta_pic_order_always_zero_flag(false)
564             .bit_depth_chroma(8)
565             .bit_depth_luma(8)
566             .sar_resolution(1, 1)
567             .build();
568 
569         let pps = PpsBuilder::new(Rc::clone(&sps))
570             .pic_parameter_set_id(0)
571             .pic_init_qp_minus26(0)
572             .deblocking_filter_control_present_flag(true)
573             .build();
574 
575         let header = SliceHeaderBuilder::new(&pps)
576             .slice_type(SliceType::I)
577             .first_mb_in_slice(0)
578             .idr_pic_id(0)
579             .build();
580 
581         let dpb_entry_meta =
582             DpbEntryMeta { poc: 0, frame_num: 0, is_reference: IsReference::ShortTerm };
583 
584         let request = BackendRequest {
585             sps: Rc::clone(&sps),
586             pps: Rc::clone(&pps),
587             header,
588             dpb_meta: dpb_entry_meta,
589             input: pic,
590             input_meta,
591             ref_list_0: vec![],
592             ref_list_1: vec![],
593             intra_period: 1,
594             ip_period: 0,
595             num_macroblocks: (WIDTH * HEIGHT) as usize / (16 * 16),
596             is_idr: true,
597             tunings: Tunings {
598                 rate_control: RateControl::ConstantBitrate(30_000),
599                 ..Default::default()
600             },
601             coded_output: vec![],
602         };
603 
604         let (_, output) = backend.encode_slice(request).unwrap();
605         let output = output.sync().unwrap();
606 
607         let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
608         if write_to_file {
609             use std::io::Write;
610 
611             use crate::codec::h264::synthesizer::Synthesizer;
612             let mut out = std::fs::File::create("test_simple_encode_slice.h264").unwrap();
613 
614             Synthesizer::<'_, Sps, &mut std::fs::File>::synthesize(3, &sps, &mut out, true)
615                 .unwrap();
616             Synthesizer::<'_, Pps, &mut std::fs::File>::synthesize(3, &pps, &mut out, true)
617                 .unwrap();
618             out.write_all(&output).unwrap();
619             out.flush().unwrap();
620         }
621     }
622 
623     #[test]
624     // Ignore this test by default as it requires libva-compatible hardware.
625     #[ignore]
test_vaapi_encoder()626     fn test_vaapi_encoder() {
627         type VaapiH264Encoder<'l> =
628             StatelessEncoder<PooledVaSurface<()>, VaapiBackend<(), PooledVaSurface<()>>>;
629 
630         const WIDTH: usize = 512;
631         const HEIGHT: usize = 512;
632 
633         let _ = env_logger::try_init();
634 
635         let display = libva::Display::open().unwrap();
636         let entrypoints = display.query_config_entrypoints(VAProfileH264Main).unwrap();
637         let low_power = entrypoints.contains(&VAEntrypointEncSliceLP);
638 
639         let config = EncoderConfig {
640             profile: Profile::Main,
641             resolution: Resolution { width: WIDTH as u32, height: HEIGHT as u32 },
642             initial_tunings: Tunings {
643                 rate_control: RateControl::ConstantBitrate(1_200_000),
644                 framerate: 30,
645                 ..Default::default()
646             },
647             ..Default::default()
648         };
649 
650         let frame_layout = FrameLayout {
651             format: (b"NV12".into(), 0),
652             size: Resolution { width: WIDTH as u32, height: HEIGHT as u32 },
653             planes: vec![
654                 PlaneLayout { buffer_index: 0, offset: 0, stride: WIDTH },
655                 PlaneLayout { buffer_index: 0, offset: WIDTH * HEIGHT, stride: WIDTH },
656             ],
657         };
658 
659         let mut encoder = VaapiH264Encoder::new_vaapi(
660             Rc::clone(&display),
661             config,
662             frame_layout.format.0,
663             frame_layout.size,
664             low_power,
665             BlockingMode::Blocking,
666         )
667         .unwrap();
668 
669         let mut pool = VaSurfacePool::new(
670             Rc::clone(&display),
671             VA_RT_FORMAT_YUV420,
672             Some(UsageHint::USAGE_HINT_ENCODER),
673             Resolution { width: WIDTH as u32, height: HEIGHT as u32 },
674         );
675 
676         pool.add_frames(vec![(); 16]).unwrap();
677 
678         let mut frame_producer = TestFrameGenerator::new(100, display, pool, frame_layout);
679 
680         let mut bitstream = Vec::new();
681 
682         simple_encode_loop(&mut encoder, &mut frame_producer, |coded| {
683             bitstream.extend(coded.bitstream)
684         })
685         .unwrap();
686 
687         let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
688         if write_to_file {
689             use std::io::Write;
690             let mut out = std::fs::File::create("test_vaapi_encoder.h264").unwrap();
691             out.write_all(&bitstream).unwrap();
692             out.flush().unwrap();
693         }
694     }
695 }
696