1use va_sys::*;
19
20use super::super::bits::{BitWriter, nal_unit};
21use super::super::codec::{h265_level, h265_tier};
22use super::{Buffers, Frame, Negotiated};
23
24const LOG2_MAX_POC_LSB_MINUS4: u32 = 8;
25const PIC_INIT_QP_CBR: u32 = 30;
28const PIC_INIT_QP_CQP: u32 = 26;
29
30fn centered_qp(bit_depth: u32) -> u32 {
37 26 - 3 * (bit_depth - 8)
38}
39
40pub(super) const INTEL_LOW_POWER_MIN_QP: u32 = 10;
46
47#[derive(Clone, Copy)]
50struct Tools {
51 ctu_size: u32,
52 min_cb_size: u32,
53 log2_min_tb_minus2: u32,
54 log2_diff_max_min_tb: u32,
55 max_th_depth_inter: u32,
56 max_th_depth_intra: u32,
57 amp: bool,
58 sao: bool,
59 temporal_mvp: bool,
60 pcm: bool,
61 transform_skip: bool,
62 cu_qp_delta: bool,
63}
64
65pub(super) struct Arm {
66 profile: VAProfile,
67 tools: Tools,
68 level_idc: u32,
69 tier: u32,
70 ctb_width: u32,
71 ctb_height: u32,
72 slices: (u32, u32),
73 dpb: u32,
74 pic_init_qp: u32,
75 cu_qp_delta: bool,
76 cu_qp_delta_depth: u32,
78 align: u32,
80 low_delay_b: bool,
82 sequence: VAEncSequenceParameterBufferHEVC,
83 headers: Vec<u8>,
84}
85
86impl Arm {
87 pub(super) fn new(
91 profile: VAProfile,
92 features: Option<u32>,
93 block_sizes: Option<u32>,
94 prediction: Option<u32>,
95 unit_qp_deltas: bool,
96 ) -> Self {
97 let mut tools = Tools {
98 ctu_size: 32,
99 min_cb_size: 16,
100 log2_min_tb_minus2: 0,
101 log2_diff_max_min_tb: 3,
102 max_th_depth_inter: 3,
103 max_th_depth_intra: 3,
104 amp: true,
105 sao: false,
106 temporal_mvp: false,
107 pcm: false,
108 transform_skip: false,
109 cu_qp_delta: true,
110 };
111 let mut align = tools.min_cb_size;
112 if unit_qp_deltas && block_sizes.is_none() {
113 tools.min_cb_size = 8;
114 }
115 if let Some(value) = features {
116 let f = VAConfigAttribValEncHEVCFeatures { value };
117 unsafe {
118 tools.amp = f.bits.amp() != 0;
119 tools.sao = f.bits.sao() != 0;
120 tools.temporal_mvp = f.bits.temporal_mvp() != 0;
121 tools.pcm = f.bits.pcm() != 0;
122 tools.transform_skip = f.bits.transform_skip() != 0;
123 tools.cu_qp_delta = f.bits.cu_qp_delta() != 0;
124 }
125 }
126 if let Some(value) = block_sizes {
127 let b = VAConfigAttribValEncHEVCBlockSizes { value };
128 unsafe {
129 tools.ctu_size = 1 << (b.bits.log2_max_coding_tree_block_size_minus3() + 3);
130 tools.min_cb_size = 1 << (b.bits.log2_min_luma_coding_block_size_minus3() + 3);
131 tools.log2_min_tb_minus2 = b.bits.log2_min_luma_transform_block_size_minus2();
132 tools.log2_diff_max_min_tb = b.bits.log2_max_luma_transform_block_size_minus2()
133 - b.bits.log2_min_luma_transform_block_size_minus2();
134 tools.max_th_depth_inter = b.bits.max_max_transform_hierarchy_depth_inter();
135 tools.max_th_depth_intra = b.bits.max_max_transform_hierarchy_depth_intra();
136 }
137 align = tools.min_cb_size;
138 }
139 Self {
140 profile,
141 tools,
142 level_idc: 0,
143 tier: 0,
144 ctb_width: 0,
145 ctb_height: 0,
146 slices: (1, 1),
147 dpb: 1,
148 pic_init_qp: PIC_INIT_QP_CBR,
149 cu_qp_delta: false,
150 cu_qp_delta_depth: 0,
151 align,
152 low_delay_b: prediction.is_some_and(|p| p & VA_PREDICTION_DIRECTION_BI_NOT_EMPTY != 0),
153 sequence: unsafe { std::mem::zeroed() },
154 headers: Vec::new(),
155 }
156 }
157
158 pub(super) fn alignment(&self) -> (u32, u32) {
160 (self.align, self.align)
161 }
162
163 pub(super) fn ctu_size(&self) -> u32 {
164 self.tools.ctu_size
165 }
166
167 pub(super) fn slices(&self) -> (u32, u32) {
168 self.slices
169 }
170
171 pub(super) fn level(&self) -> u32 {
173 self.level_idc
174 }
175
176 fn profile_idc(n: &Negotiated) -> u32 {
178 match (n.fullcolor, n.bit_depth) {
179 (true, _) => 4,
180 (false, 10) => 2,
181 (false, _) => 1,
182 }
183 }
184
185 fn profile_tier_level(&self, w: &mut BitWriter, n: &Negotiated) {
186 let (fullcolor, bit_depth) = (n.fullcolor, n.bit_depth);
187 let profile_idc = Self::profile_idc(n);
188 w.u(2, 0);
189 w.u(1, u64::from(self.tier));
190 w.u(5, u64::from(profile_idc));
191 let mut compat = [false; 32];
192 compat[profile_idc as usize] = true;
193 if compat[1] {
194 compat[2] = true;
195 }
196 if compat[3] {
197 compat[1] = true;
198 compat[2] = true;
199 }
200 for flag in compat {
201 w.flag(flag);
202 }
203 w.flag(true);
204 w.flag(false);
205 w.flag(true);
206 w.flag(true);
207 if profile_idc == 4 {
208 w.flag(true);
209 w.flag(true);
210 w.flag(bit_depth == 8);
211 w.flag(!fullcolor);
212 w.flag(!fullcolor);
213 w.flag(false);
214 w.flag(false);
215 w.flag(false);
216 w.flag(true);
217 w.u(34, 0);
218 } else {
219 w.u(43, 0);
220 }
221 w.flag(false);
222 w.u(8, u64::from(self.level_idc));
223 }
224
225 pub(super) fn configure(
229 &mut self,
230 n: &Negotiated,
231 surface_width: u32,
232 surface_height: u32,
233 slices: (u32, u32),
234 ) {
235 let t = self.tools;
236 self.ctb_width = surface_width.div_ceil(t.ctu_size);
237 self.ctb_height = surface_height.div_ceil(t.ctu_size);
238 self.slices = slices;
239 self.dpb = n.dpb;
240 self.level_idc = h265_level(
241 n.width,
242 n.height,
243 n.fps.ceil(),
244 u64::from(n.bits_per_second),
245 true,
246 )
247 .max(n.dpb_level);
248 self.tier = h265_tier(self.level_idc);
249 self.pic_init_qp = if n.rc_mode == VA_RC_CQP {
250 PIC_INIT_QP_CQP
251 } else if n.unit_qp_deltas {
252 centered_qp(n.bit_depth)
253 } else {
254 PIC_INIT_QP_CBR
255 };
256 self.cu_qp_delta = n.rc_mode != VA_RC_CQP && t.cu_qp_delta;
257 let log2_min_cb = t.min_cb_size.trailing_zeros();
258 let log2_diff_cb = t.ctu_size.trailing_zeros() - log2_min_cb;
259 self.cu_qp_delta_depth = if n.unit_qp_deltas { 0 } else { log2_diff_cb };
262 let chroma_shift = if n.fullcolor { 0 } else { 1 };
263 let conformance = surface_width != n.width || surface_height != n.height;
264
265 let mut w = BitWriter::new();
266 w.u(4, 0);
267 w.flag(true);
268 w.flag(true);
269 w.u(6, 0);
270 w.u(3, 0);
271 w.flag(true);
272 w.u(16, 0xffff);
273 self.profile_tier_level(&mut w, n);
274 w.flag(false);
275 w.ue(self.dpb);
276 w.ue(0);
277 w.ue(0);
278 w.u(6, 0);
279 w.ue(0);
280 w.flag(true);
281 w.u(32, u64::from(n.fps.den));
282 w.u(32, u64::from(n.fps.num));
283 w.flag(true);
284 w.ue(0);
285 w.ue(0);
286 w.flag(false);
287 w.trailing_bits();
288 let vps = nal_unit(&[0x40, 0x01], w.as_bytes());
289
290 let mut w = BitWriter::new();
291 w.u(4, 0);
292 w.u(3, 0);
293 w.flag(true);
294 self.profile_tier_level(&mut w, n);
295 w.ue(0);
296 w.ue(if n.fullcolor { 3 } else { 1 });
297 if n.fullcolor {
298 w.flag(false);
299 }
300 w.ue(surface_width);
301 w.ue(surface_height);
302 w.flag(conformance);
303 if conformance {
304 w.ue(0);
305 w.ue((surface_width - n.width) >> chroma_shift);
306 w.ue(0);
307 w.ue((surface_height - n.height) >> chroma_shift);
308 }
309 w.ue(n.bit_depth - 8);
310 w.ue(n.bit_depth - 8);
311 w.ue(LOG2_MAX_POC_LSB_MINUS4);
312 w.flag(false);
313 w.ue(self.dpb);
314 w.ue(0);
315 w.ue(0);
316 w.ue(log2_min_cb - 3);
317 w.ue(log2_diff_cb);
318 w.ue(t.log2_min_tb_minus2);
319 w.ue(t.log2_diff_max_min_tb);
320 w.ue(t.max_th_depth_inter);
321 w.ue(t.max_th_depth_intra);
322 w.flag(false);
323 w.flag(t.amp);
324 w.flag(t.sao);
325 w.flag(t.pcm);
326 if t.pcm {
327 w.u(4, 0);
328 w.u(4, 0);
329 w.ue(0);
330 w.ue(0);
331 w.flag(false);
332 }
333 w.ue(0);
334 w.flag(false);
335 w.flag(t.temporal_mvp);
336 w.flag(false);
337 w.flag(true);
338 w.flag(false);
339 w.flag(false);
340 w.flag(true);
341 w.u(3, 5);
342 w.flag(false);
343 w.flag(true);
344 w.u(8, 1);
345 w.u(8, 1);
346 w.u(8, 1);
347 w.flag(false);
348 w.flag(false);
349 w.flag(false);
350 w.flag(false);
351 w.flag(false);
352 w.flag(true);
353 w.u(32, u64::from(n.fps.den));
354 w.u(32, u64::from(n.fps.num));
355 w.flag(true);
356 w.ue(0);
357 w.flag(false);
358 w.flag(true);
359 w.flag(false);
360 w.flag(true);
361 w.flag(true);
362 w.ue(0);
363 w.ue(0);
364 w.ue(0);
365 w.ue(15);
366 w.ue(15);
367 w.flag(false);
368 w.trailing_bits();
369 let sps = nal_unit(&[0x42, 0x01], w.as_bytes());
370
371 let mut w = BitWriter::new();
372 w.ue(0);
373 w.ue(0);
374 w.flag(false);
375 w.flag(false);
376 w.u(3, 0);
377 w.flag(false);
378 w.flag(false);
379 w.ue(0);
380 w.ue(0);
381 w.se(self.pic_init_qp as i32 - 26);
382 w.flag(false);
383 w.flag(t.transform_skip);
384 w.flag(self.cu_qp_delta);
385 if self.cu_qp_delta {
386 w.ue(self.cu_qp_delta_depth);
387 }
388 w.se(0);
389 w.se(0);
390 w.flag(false);
391 w.flag(false);
392 w.flag(false);
393 w.flag(false);
394 w.flag(false);
395 w.flag(false);
396 w.flag(true);
397 w.flag(false);
398 w.flag(false);
399 w.flag(false);
400 w.ue(0);
401 w.flag(false);
402 w.flag(false);
403 w.trailing_bits();
404 let pps = nal_unit(&[0x44, 0x01], w.as_bytes());
405 self.headers = [vps, sps, pps].concat();
406
407 let s = &mut self.sequence;
408 *s = unsafe { std::mem::zeroed() };
409 s.general_profile_idc = Self::profile_idc(n) as u8;
410 s.general_level_idc = self.level_idc as u8;
411 s.general_tier_flag = self.tier as u8;
412 s.intra_period = 1 << (4 + LOG2_MAX_POC_LSB_MINUS4);
413 s.intra_idr_period = 1 << (4 + LOG2_MAX_POC_LSB_MINUS4);
414 s.ip_period = 1;
415 s.bits_per_second = n.bits_per_second;
416 s.pic_width_in_luma_samples = surface_width as u16;
417 s.pic_height_in_luma_samples = surface_height as u16;
418 unsafe {
419 let f = &mut s.seq_fields.bits;
420 f.set_chroma_format_idc(if n.fullcolor { 3 } else { 1 });
421 f.set_bit_depth_luma_minus8(n.bit_depth - 8);
422 f.set_bit_depth_chroma_minus8(n.bit_depth - 8);
423 f.set_amp_enabled_flag(u32::from(t.amp));
424 f.set_sample_adaptive_offset_enabled_flag(u32::from(t.sao));
425 f.set_pcm_enabled_flag(u32::from(t.pcm));
426 f.set_sps_temporal_mvp_enabled_flag(u32::from(t.temporal_mvp));
427 }
428 s.log2_min_luma_coding_block_size_minus3 = (log2_min_cb - 3) as u8;
429 s.log2_diff_max_min_luma_coding_block_size = log2_diff_cb as u8;
430 s.log2_min_transform_block_size_minus2 = t.log2_min_tb_minus2 as u8;
431 s.log2_diff_max_min_transform_block_size = t.log2_diff_max_min_tb as u8;
432 s.max_transform_hierarchy_depth_inter = t.max_th_depth_inter as u8;
433 s.max_transform_hierarchy_depth_intra = t.max_th_depth_intra as u8;
434 s.vui_parameters_present_flag = 0;
435 }
436
437 pub(super) fn sequence(&mut self, n: &Negotiated, out: &mut Buffers) {
438 out.push(VAEncSequenceParameterBufferType, &self.sequence);
439 if n.packed & VA_ENC_PACKED_HEADER_SEQUENCE != 0 {
440 let bits = 8 * self.headers.len() as u32;
441 out.push_packed(VAEncPackedHeaderSequence, &self.headers, bits);
442 }
443 }
444
445 pub(super) fn picture(
449 &mut self,
450 n: &Negotiated,
451 frame: &Frame,
452 out: &mut Buffers,
453 ) -> Result<(), String> {
454 let t = self.tools;
455 let poc_of = |pts: u64| (pts - frame.key_pts) as i32;
456 let poc = poc_of(frame.pts);
457 let reference_pts = frame.reference.map(|(pts, _)| pts);
458 if !frame.key && reference_pts.is_none() {
459 return Err("a predicted HEVC picture without a reference".into());
460 }
461 let mut kept: Vec<(u64, VASurfaceID)> = frame
462 .held
463 .iter()
464 .filter(|h| !frame.key && !h.2 && h.0 >= frame.key_pts)
465 .map(|h| (h.0, h.1))
466 .collect();
467 kept.sort_by_key(|k| std::cmp::Reverse(k.0));
468 kept.truncate(15);
469 let invalid = VAPictureHEVC {
470 picture_id: VA_INVALID_ID,
471 pic_order_cnt: 0,
472 flags: VA_PICTURE_HEVC_INVALID,
473 va_reserved: [0; 4],
474 };
475 let picture = |pts: u64, surface: VASurfaceID| VAPictureHEVC {
476 picture_id: surface,
477 pic_order_cnt: poc_of(pts),
478 flags: if Some(pts) == reference_pts {
479 VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE
480 } else {
481 0
482 },
483 va_reserved: [0; 4],
484 };
485
486 let mut pic: VAEncPictureParameterBufferHEVC = unsafe { std::mem::zeroed() };
487 pic.decoded_curr_pic = VAPictureHEVC {
488 picture_id: frame.recon,
489 pic_order_cnt: poc,
490 flags: 0,
491 va_reserved: [0; 4],
492 };
493 pic.reference_frames = [invalid; 15];
494 for (i, &(pts, surface)) in kept.iter().enumerate() {
495 pic.reference_frames[i] = picture(pts, surface);
496 }
497 pic.coded_buf = frame.coded;
498 pic.collocated_ref_pic_index = if t.temporal_mvp && !frame.key {
501 0
502 } else {
503 0xff
504 };
505 pic.pic_init_qp = self.pic_init_qp as u8;
506 pic.diff_cu_qp_delta_depth = if self.cu_qp_delta {
507 self.cu_qp_delta_depth as u8
508 } else {
509 0
510 };
511 pic.nal_unit_type = if frame.key { 19 } else { 1 };
512 unsafe {
513 let f = &mut pic.pic_fields.bits;
514 f.set_idr_pic_flag(u32::from(frame.key));
515 f.set_coding_type(if frame.key { 1 } else { 2 });
516 f.set_reference_pic_flag(1);
517 f.set_transform_skip_enabled_flag(u32::from(t.transform_skip));
518 f.set_cu_qp_delta_enabled_flag(u32::from(self.cu_qp_delta));
519 f.set_pps_loop_filter_across_slices_enabled_flag(1);
520 }
521 out.push(VAEncPictureParameterBufferType, &pic);
522
523 let slice_qp = if n.rc_mode == VA_RC_CQP {
524 frame.qp.clamp(n.qp_floor.max(1), 51)
525 } else {
526 self.pic_init_qp
527 };
528 let slice_qp_delta = slice_qp as i32 - self.pic_init_qp as i32;
529 let ctbs = self.ctb_width * self.ctb_height;
530 let address_bits = 32 - (ctbs - 1).leading_zeros();
531 let (slice_count, slice_rows) = self.slices;
532 for i in 0..slice_count {
533 let first_row = i * slice_rows;
534 let rows = if i + 1 == slice_count {
535 self.ctb_height - first_row
536 } else {
537 slice_rows
538 };
539 let address = first_row * self.ctb_width;
540 let count = rows * self.ctb_width;
541 let last = i + 1 == slice_count;
542 let slice_type: u32 = match (frame.key, self.low_delay_b) {
543 (true, _) => 2,
544 (false, true) => 0,
545 (false, false) => 1,
546 };
547
548 if n.packed & VA_ENC_PACKED_HEADER_SLICE != 0 {
549 let mut w = BitWriter::new();
550 w.flag(i == 0);
551 if frame.key {
552 w.flag(false);
553 }
554 w.ue(0);
555 if i != 0 {
556 w.u(address_bits, u64::from(address));
557 }
558 w.ue(slice_type);
559 if !frame.key {
560 w.u(
561 4 + LOG2_MAX_POC_LSB_MINUS4,
562 (poc & ((1 << (4 + LOG2_MAX_POC_LSB_MINUS4)) - 1)) as u64,
563 );
564 w.flag(false);
565 w.ue(kept.len() as u32);
566 w.ue(0);
567 let mut previous = poc;
568 for &(pts, _) in &kept {
569 let p = poc_of(pts);
570 w.ue((previous - p - 1) as u32);
571 w.flag(Some(pts) == reference_pts);
572 previous = p;
573 }
574 if t.temporal_mvp {
575 w.flag(true);
576 }
577 }
578 if t.sao {
579 w.flag(true);
580 w.flag(true);
581 }
582 if !frame.key {
583 w.flag(false);
584 if self.low_delay_b {
585 w.flag(false);
586 if t.temporal_mvp {
587 w.flag(true);
588 }
589 }
590 w.ue(0);
591 }
592 w.se(slice_qp_delta);
593 w.flag(false);
594 w.trailing_bits();
595 let header = nal_unit(&[(pic.nal_unit_type << 1), 0x01], w.as_bytes());
596 let bits = 8 * header.len() as u32;
597 out.push_packed(VAEncPackedHeaderSlice, &header, bits);
598 }
599
600 let mut slice: VAEncSliceParameterBufferHEVC = unsafe { std::mem::zeroed() };
601 slice.slice_segment_address = address;
602 slice.num_ctu_in_slice = count;
603 slice.slice_type = slice_type as u8;
604 slice.slice_pic_parameter_set_id = 0;
605 slice.ref_pic_list0 = [invalid; 15];
606 slice.ref_pic_list1 = [invalid; 15];
607 if let Some((pts, surface)) = frame.reference {
608 slice.ref_pic_list0[0] = picture(pts, surface);
609 if self.low_delay_b {
610 slice.ref_pic_list1[0] = picture(pts, surface);
611 }
612 }
613 slice.max_num_merge_cand = 5;
614 slice.slice_qp_delta = slice_qp_delta as i8;
615 unsafe {
616 let f = &mut slice.slice_fields.bits;
617 f.set_last_slice_of_pic_flag(u32::from(last));
618 f.set_slice_temporal_mvp_enabled_flag(u32::from(t.temporal_mvp && !frame.key));
619 f.set_slice_sao_luma_flag(u32::from(t.sao));
620 f.set_slice_sao_chroma_flag(u32::from(t.sao));
621 f.set_collocated_from_l0_flag(1);
622 }
623 out.push(VAEncSliceParameterBufferType, &slice);
624 }
625 Ok(())
626 }
627}