Skip to main content

pixelflux/encoders/vaapi/
h265.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! The HEVC arm: the sequence, picture, and slice parameters, the packed VPS, SPS, PPS, and slice
8//! headers, and the reference picture set behind them. Main profile, Main 10 at 10 bits, or the
9//! range extensions' Main 4:4:4 and Main 4:4:4 10 for a 4:4:4 session, with the coding tools
10//! and block sizes the driver reports, one reference per slice, and a decoded picture buffer of
11//! the frames the level admits: every slice header carries an explicit short-term reference
12//! picture set naming the frames the decoder keeps, with the newest one the client still has
13//! as the one used. Where the driver says a predicted slice has to carry both reference lists
14//! (`VA_PREDICTION_DIRECTION_BI_NOT_EMPTY`, Intel's low-power encoder), a predicted frame is a B
15//! slice whose two lists name that same frame, which codes what a P slice would: such an
16//! encoder fails a P slice on some generations and takes it on others.
17
18use 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;
25/// The quantizer the PPS names for a constant-rate session, and the base of every slice's
26/// delta.
27const PIC_INIT_QP_CBR: u32 = 30;
28const PIC_INIT_QP_CQP: u32 = 26;
29
30/// The quantizer a constant-rate slice names where the rate control codes its own in every
31/// coding unit as a delta from the slice's (Intel's full encoder): the middle of the range
32/// at `bit_depth`, from which a delta within the syntax's -(26 + QpBdOffsetY / 2) to
33/// 25 + QpBdOffsetY / 2 reaches every quantizer. From 30, a unit the rate control takes under
34/// 4 codes a delta past that range, which a strict decoder refuses and a lenient one decodes
35/// as garbage.
36fn centered_qp(bit_depth: u32) -> u32 {
37    26 - 3 * (bit_depth - 8)
38}
39
40/// The finest quantizer Intel's low-power HEVC encoder codes: the driver clips the slice's to
41/// 10..51 when it programs VDENC and looks its rate tables up from 10, and its rate control floors
42/// its own minimum at 10. A slice naming a finer one is coded at this one regardless, so the
43/// decoder dequantizes it too fine and the picture comes out washed out, the more so the finer
44/// the slice names (31 dB at 9 and 16 dB at 2 against 59 dB at 10, Arc A750).
45pub(super) const INTEL_LOW_POWER_MIN_QP: u32 = 10;
46
47/// The coding tools and block sizes a session runs with: what the driver reports, or the
48/// defaults of `Arm::new` where it reports none.
49#[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    /// The depth of the coding units that each carry a quantizer delta, 0 for one per tree block.
77    cu_qp_delta_depth: u32,
78    /// The surface alignment, the smallest coding block but where `new` says otherwise.
79    align: u32,
80    /// Whether a predicted frame is coded as a B slice with the one reference in both lists.
81    low_delay_b: bool,
82    sequence: VAEncSequenceParameterBufferHEVC,
83    headers: Vec<u8>,
84}
85
86impl Arm {
87    /// The arm on `profile`, with the driver's feature and block-size attributes where it
88    /// reports them. Intel's full encoder (`unit_qp_deltas`), which reports no block sizes, codes
89    /// blocks from 8x8 to 32x32 (Skylake's, as FFmpeg's defaults say) on surfaces aligned to 16.
90    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    /// The surface alignment: the smallest coding block, or 16 on Intel's full encoder.
159    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    /// The `general_level_idc` the sequence names.
172    pub(super) fn level(&self) -> u32 {
173        self.level_idc
174    }
175
176    /// The profile a session declares: the range extensions for 4:4:4, else Main 10 or Main.
177    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    /// The stream's sequence: level and tier from the ladder, the level never below the one
226    /// the decoded picture buffer was sized for, the picture in coding tree blocks, and the
227    /// VPS, SPS, and PPS the key frames carry.
228    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        // A frame's units share the quantizer of Intel's full encoder's rate control, so one delta
260        // per tree block carries it, as FFmpeg codes there.
261        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    /// The picture: its order count since the key frame, the frames the client still has,
446    /// newest first, as the driver's reference list and the slice header's reference picture
447    /// set, and every slice.
448    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        // A key frame names no co-located picture: iHD reads the reference the index names
499        // whatever the frame's type, and reference 0 of a key frame is invalid.
500        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}