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