cros_codecs/decoder/stateless/vp8/
vaapi.rs

1// Copyright 2022 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::convert::TryFrom;
6use std::rc::Rc;
7
8use anyhow::Context;
9use libva::BufferType;
10use libva::Display;
11use libva::IQMatrix;
12use libva::IQMatrixBufferVP8;
13use libva::Picture as VaPicture;
14use libva::ProbabilityDataBufferVP8;
15use libva::SurfaceMemoryDescriptor;
16
17use crate::backend::vaapi::decoder::va_surface_id;
18use crate::backend::vaapi::decoder::PoolCreationMode;
19use crate::backend::vaapi::decoder::VaStreamInfo;
20use crate::backend::vaapi::decoder::VaapiBackend;
21use crate::backend::vaapi::decoder::VaapiPicture;
22use crate::codec::vp8::parser::Header;
23use crate::codec::vp8::parser::MbLfAdjustments;
24use crate::codec::vp8::parser::Segmentation;
25use crate::decoder::stateless::vp8::StatelessVp8DecoderBackend;
26use crate::decoder::stateless::vp8::Vp8;
27use crate::decoder::stateless::NewPictureError;
28use crate::decoder::stateless::NewPictureResult;
29use crate::decoder::stateless::NewStatelessDecoderError;
30use crate::decoder::stateless::StatelessBackendResult;
31use crate::decoder::stateless::StatelessDecoder;
32use crate::decoder::stateless::StatelessDecoderBackendPicture;
33use crate::decoder::BlockingMode;
34use crate::decoder::FramePool;
35use crate::Resolution;
36
37/// The number of surfaces to allocate for this codec. Same as GStreamer's vavp8dec.
38const NUM_SURFACES: usize = 7;
39
40impl VaStreamInfo for &Header {
41    fn va_profile(&self) -> anyhow::Result<i32> {
42        Ok(libva::VAProfile::VAProfileVP8Version0_3)
43    }
44
45    fn rt_format(&self) -> anyhow::Result<u32> {
46        Ok(libva::constants::VA_RT_FORMAT_YUV420)
47    }
48
49    fn min_num_surfaces(&self) -> usize {
50        NUM_SURFACES
51    }
52
53    fn coded_size(&self) -> (u32, u32) {
54        (self.width as u32, self.height as u32)
55    }
56
57    fn visible_rect(&self) -> ((u32, u32), (u32, u32)) {
58        ((0, 0), self.coded_size())
59    }
60}
61
62/// A clamp such that min <= x <= max
63fn clamp<T: PartialOrd>(x: T, low: T, high: T) -> T {
64    if x > high {
65        high
66    } else if x < low {
67        low
68    } else {
69        x
70    }
71}
72
73fn build_iq_matrix(
74    frame_hdr: &Header,
75    segmentation: &Segmentation,
76) -> anyhow::Result<libva::BufferType> {
77    let mut quantization_index: [[u16; 6]; 4] = Default::default();
78
79    for (i, quantization_index) in quantization_index.iter_mut().enumerate() {
80        let mut qi_base: i16;
81
82        if segmentation.segmentation_enabled {
83            qi_base = i16::from(segmentation.quantizer_update_value[i]);
84            if !segmentation.segment_feature_mode {
85                qi_base += i16::from(frame_hdr.quant_indices.y_ac_qi);
86            }
87        } else {
88            qi_base = i16::from(frame_hdr.quant_indices.y_ac_qi);
89        }
90
91        let mut qi = qi_base;
92        quantization_index[0] = u16::try_from(clamp(qi, 0, 127))?;
93        qi = qi_base + i16::from(frame_hdr.quant_indices.y_dc_delta);
94        quantization_index[1] = u16::try_from(clamp(qi, 0, 127))?;
95        qi = qi_base + i16::from(frame_hdr.quant_indices.y2_dc_delta);
96        quantization_index[2] = u16::try_from(clamp(qi, 0, 127))?;
97        qi = qi_base + i16::from(frame_hdr.quant_indices.y2_ac_delta);
98        quantization_index[3] = u16::try_from(clamp(qi, 0, 127))?;
99        qi = qi_base + i16::from(frame_hdr.quant_indices.uv_dc_delta);
100        quantization_index[4] = u16::try_from(clamp(qi, 0, 127))?;
101        qi = qi_base + i16::from(frame_hdr.quant_indices.uv_ac_delta);
102        quantization_index[5] = u16::try_from(clamp(qi, 0, 127))?;
103    }
104
105    Ok(BufferType::IQMatrix(IQMatrix::VP8(IQMatrixBufferVP8::new(
106        quantization_index,
107    ))))
108}
109
110fn build_probability_table(frame_hdr: &Header) -> libva::BufferType {
111    BufferType::Probability(ProbabilityDataBufferVP8::new(frame_hdr.coeff_prob))
112}
113
114fn build_pic_param(
115    frame_hdr: &Header,
116    resolution: &Resolution,
117    seg: &Segmentation,
118    adj: &MbLfAdjustments,
119    last: u32,
120    golden: u32,
121    alt: u32,
122) -> anyhow::Result<libva::BufferType> {
123    let mut loop_filter_level: [u8; 4] = Default::default();
124    let mut loop_filter_deltas_ref_frame: [i8; 4] = Default::default();
125    let mut loop_filter_deltas_mode: [i8; 4] = Default::default();
126
127    for i in 0..4 {
128        let mut level;
129        if seg.segmentation_enabled {
130            level = seg.lf_update_value[i];
131            if !seg.segment_feature_mode {
132                level += i8::try_from(frame_hdr.loop_filter_level)?;
133            }
134        } else {
135            level = i8::try_from(frame_hdr.loop_filter_level)?;
136        }
137
138        loop_filter_level[i] = clamp(u8::try_from(level)?, 0, 63);
139        loop_filter_deltas_ref_frame[i] = adj.ref_frame_delta[i];
140        loop_filter_deltas_mode[i] = adj.mb_mode_delta[i];
141    }
142
143    let pic_fields = libva::VP8PicFields::new(
144        u32::from(!frame_hdr.key_frame),
145        u32::from(frame_hdr.version),
146        u32::from(seg.segmentation_enabled),
147        u32::from(seg.update_mb_segmentation_map),
148        u32::from(seg.update_segment_feature_data),
149        u32::from(frame_hdr.filter_type),
150        u32::from(frame_hdr.sharpness_level),
151        u32::from(adj.loop_filter_adj_enable),
152        u32::from(adj.mode_ref_lf_delta_update),
153        u32::from(frame_hdr.sign_bias_golden),
154        u32::from(frame_hdr.sign_bias_alternate),
155        u32::from(frame_hdr.mb_no_coeff_skip),
156        u32::from(frame_hdr.loop_filter_level == 0),
157    );
158
159    let bool_coder_ctx = libva::BoolCoderContextVPX::new(
160        u8::try_from(frame_hdr.bd_range)?,
161        u8::try_from(frame_hdr.bd_value)?,
162        u8::try_from(frame_hdr.bd_count)?,
163    );
164
165    let pic_param = libva::PictureParameterBufferVP8::new(
166        resolution.width,
167        resolution.height,
168        last,
169        golden,
170        alt,
171        &pic_fields,
172        seg.segment_prob,
173        loop_filter_level,
174        loop_filter_deltas_ref_frame,
175        loop_filter_deltas_mode,
176        frame_hdr.prob_skip_false,
177        frame_hdr.prob_intra,
178        frame_hdr.prob_last,
179        frame_hdr.prob_golden,
180        frame_hdr.mode_probs.intra_16x16_prob,
181        frame_hdr.mode_probs.intra_chroma_prob,
182        frame_hdr.mv_prob,
183        &bool_coder_ctx,
184    );
185
186    Ok(libva::BufferType::PictureParameter(
187        libva::PictureParameter::VP8(pic_param),
188    ))
189}
190
191fn build_slice_param(frame_hdr: &Header, slice_size: usize) -> anyhow::Result<libva::BufferType> {
192    let mut partition_size: [u32; 9] = Default::default();
193    let num_of_partitions = frame_hdr.num_dct_partitions() + 1;
194
195    partition_size[0] = frame_hdr.first_part_size - ((frame_hdr.header_size + 7) >> 3);
196
197    partition_size[1..num_of_partitions]
198        .clone_from_slice(&frame_hdr.partition_size[..(num_of_partitions - 1)]);
199
200    Ok(libva::BufferType::SliceParameter(
201        libva::SliceParameter::VP8(libva::SliceParameterBufferVP8::new(
202            u32::try_from(slice_size)?,
203            u32::from(frame_hdr.data_chunk_size),
204            0,
205            frame_hdr.header_size,
206            u8::try_from(num_of_partitions)?,
207            partition_size,
208        )),
209    ))
210}
211
212impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoderBackendPicture<Vp8> for VaapiBackend<M> {
213    type Picture = VaapiPicture<M>;
214}
215
216impl<M: SurfaceMemoryDescriptor + 'static> StatelessVp8DecoderBackend for VaapiBackend<M> {
217    fn new_sequence(&mut self, header: &Header) -> StatelessBackendResult<()> {
218        self.new_sequence(header, PoolCreationMode::Highest)
219    }
220
221    fn new_picture(&mut self, timestamp: u64) -> NewPictureResult<Self::Picture> {
222        let highest_pool = self.highest_pool();
223        let surface = highest_pool
224            .get_surface()
225            .ok_or(NewPictureError::OutOfOutputBuffers)?;
226
227        let metadata = self.metadata_state.get_parsed()?;
228
229        Ok(VaPicture::new(
230            timestamp,
231            Rc::clone(&metadata.context),
232            surface,
233        ))
234    }
235
236    fn submit_picture(
237        &mut self,
238        mut picture: Self::Picture,
239        hdr: &Header,
240        last_ref: &Option<Self::Handle>,
241        golden_ref: &Option<Self::Handle>,
242        alt_ref: &Option<Self::Handle>,
243        bitstream: &[u8],
244        segmentation: &Segmentation,
245        mb_lf_adjust: &MbLfAdjustments,
246    ) -> StatelessBackendResult<Self::Handle> {
247        let highest_pool = self.highest_pool();
248        let last_ref = va_surface_id(last_ref);
249        let golden_ref = va_surface_id(golden_ref);
250        let alt_ref = va_surface_id(alt_ref);
251
252        let coded_resolution = highest_pool.coded_resolution();
253        let metadata = self.metadata_state.get_parsed()?;
254        let context = &metadata.context;
255
256        let iq_buffer = context
257            .create_buffer(build_iq_matrix(hdr, segmentation)?)
258            .context("while creating IQ matrix buffer")?;
259
260        let probs = context
261            .create_buffer(build_probability_table(hdr))
262            .context("while creating probability table buffer")?;
263
264        let pic_param = context
265            .create_buffer(build_pic_param(
266                hdr,
267                &coded_resolution,
268                segmentation,
269                mb_lf_adjust,
270                last_ref,
271                golden_ref,
272                alt_ref,
273            )?)
274            .context("while creating pic params buffer")?;
275
276        let slice_param = context
277            .create_buffer(build_slice_param(hdr, bitstream.len())?)
278            .context("while creating slice params buffer")?;
279
280        let slice_data = context
281            .create_buffer(libva::BufferType::SliceData(Vec::from(bitstream)))
282            .context("while creating slice data buffer")?;
283
284        // Add buffers with the parsed data.
285        picture.add_buffer(iq_buffer);
286        picture.add_buffer(probs);
287        picture.add_buffer(pic_param);
288        picture.add_buffer(slice_param);
289        picture.add_buffer(slice_data);
290
291        self.process_picture::<Vp8>(picture)
292    }
293}
294
295impl<M: SurfaceMemoryDescriptor + 'static> StatelessDecoder<Vp8, VaapiBackend<M>> {
296    // Creates a new instance of the decoder using the VAAPI backend.
297    pub fn new_vaapi<S>(
298        display: Rc<Display>,
299        blocking_mode: BlockingMode,
300    ) -> Result<Self, NewStatelessDecoderError>
301    where
302        M: From<S>,
303        S: From<M>,
304    {
305        Self::new(VaapiBackend::new(display, false), blocking_mode)
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use libva::BufferType;
312    use libva::Display;
313    use libva::IQMatrix;
314    use libva::PictureParameter;
315    use libva::SliceParameter;
316
317    use crate::codec::vp8::parser::Parser;
318    use crate::decoder::stateless::tests::test_decode_stream;
319    use crate::decoder::stateless::tests::TestStream;
320    use crate::decoder::stateless::StatelessDecoder;
321    use crate::decoder::BlockingMode;
322    use crate::utils::simple_playback_loop;
323    use crate::utils::simple_playback_loop_owned_frames;
324    use crate::utils::IvfIterator;
325    use crate::DecodedFormat;
326    use crate::Resolution;
327
328    use super::*;
329
330    /// Run `test` using the vaapi decoder, in both blocking and non-blocking modes.
331    fn test_decoder_vaapi(
332        test: &TestStream,
333        output_format: DecodedFormat,
334        blocking_mode: BlockingMode,
335    ) {
336        let display = Display::open().unwrap();
337        let decoder = StatelessDecoder::<Vp8, _>::new_vaapi::<()>(display, blocking_mode).unwrap();
338
339        test_decode_stream(
340            |d, s, c| {
341                simple_playback_loop(
342                    d,
343                    IvfIterator::new(s),
344                    c,
345                    &mut simple_playback_loop_owned_frames,
346                    output_format,
347                    blocking_mode,
348                )
349            },
350            decoder,
351            test,
352            true,
353            false,
354        );
355    }
356
357    #[test]
358    // Ignore this test by default as it requires libva-compatible hardware.
359    #[ignore]
360    fn test_25fps_block() {
361        use crate::decoder::stateless::vp8::tests::DECODE_TEST_25FPS;
362        test_decoder_vaapi(
363            &DECODE_TEST_25FPS,
364            DecodedFormat::NV12,
365            BlockingMode::Blocking,
366        );
367    }
368
369    #[test]
370    // Ignore this test by default as it requires libva-compatible hardware.
371    #[ignore]
372    fn test_25fps_nonblock() {
373        use crate::decoder::stateless::vp8::tests::DECODE_TEST_25FPS;
374        test_decoder_vaapi(
375            &DECODE_TEST_25FPS,
376            DecodedFormat::NV12,
377            BlockingMode::NonBlocking,
378        );
379    }
380
381    #[test]
382    /// Check that we are able to build the VA picture parameters from the stream properly.
383    fn build_pic_params() {
384        const TEST_STREAM: &[u8] = include_bytes!("../../../codec/vp8/test_data/test-25fps.vp8");
385        const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_0: &[u8] =
386            include_bytes!("../../../codec/vp8/test_data/test-25fps-vp8-probability-table-0.bin");
387        const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_1: &[u8] =
388            include_bytes!("../../../codec/vp8/test_data/test-25fps-vp8-probability-table-1.bin");
389        const TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_2: &[u8] =
390            include_bytes!("../../../codec/vp8/test_data/test-25fps-vp8-probability-table-2.bin");
391
392        let mut parser: Parser = Default::default();
393        let mut ivf_iter = IvfIterator::new(TEST_STREAM);
394
395        // FRAME 0
396
397        let packet = ivf_iter.next().unwrap();
398        assert_eq!(packet.len(), 14788);
399
400        let frame = parser.parse_frame(packet).unwrap();
401
402        let resolution = Resolution {
403            width: frame.header.width as u32,
404            height: frame.header.height as u32,
405        };
406
407        let pic_param = build_pic_param(
408            &frame.header,
409            &resolution,
410            parser.segmentation(),
411            parser.mb_lf_adjust(),
412            libva::constants::VA_INVALID_SURFACE,
413            libva::constants::VA_INVALID_SURFACE,
414            libva::constants::VA_INVALID_SURFACE,
415        )
416        .unwrap();
417        let pic_param = match pic_param {
418            BufferType::PictureParameter(PictureParameter::VP8(pic_param)) => pic_param,
419            _ => panic!(),
420        };
421
422        let iq_matrix = build_iq_matrix(&frame.header, parser.segmentation()).unwrap();
423        let iq_matrix = match iq_matrix {
424            BufferType::IQMatrix(IQMatrix::VP8(iq_matrix)) => iq_matrix,
425            _ => panic!(),
426        };
427
428        let prob_table = build_probability_table(&frame.header);
429        let prob_table = match prob_table {
430            BufferType::Probability(prob_table) => prob_table,
431            _ => panic!(),
432        };
433
434        let slice_param = build_slice_param(&frame.header, packet.len()).unwrap();
435        let slice_param = match slice_param {
436            BufferType::SliceParameter(SliceParameter::VP8(slice_param)) => slice_param,
437            _ => panic!(),
438        };
439
440        assert_eq!(iq_matrix.inner().quantization_index, [[4; 6]; 4]);
441        for i in 0..4 {
442            for j in 0..8 {
443                for k in 0..3 {
444                    for l in 0..11 {
445                        const OFF_I: usize = 8 * 3 * 11;
446                        const OFF_J: usize = 3 * 11;
447                        const OFF_K: usize = 11;
448                        // maybe std::transmute?
449                        assert_eq!(
450                            prob_table.inner().dct_coeff_probs[i][j][k][l],
451                            TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_0
452                                [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
453                        );
454                    }
455                }
456            }
457        }
458
459        assert_eq!(pic_param.inner().frame_width, 320);
460        assert_eq!(pic_param.inner().frame_height, 240);
461        assert_eq!(
462            pic_param.inner().last_ref_frame,
463            libva::constants::VA_INVALID_SURFACE
464        );
465        assert_eq!(
466            pic_param.inner().golden_ref_frame,
467            libva::constants::VA_INVALID_SURFACE
468        );
469        assert_eq!(
470            pic_param.inner().alt_ref_frame,
471            libva::constants::VA_INVALID_SURFACE
472        );
473        assert_eq!(
474            pic_param.inner().out_of_loop_frame,
475            libva::constants::VA_INVALID_SURFACE
476        );
477
478        // Safe because this bitfield is initialized by the decoder.
479        assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
480            libva::VP8PicFields::new(0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1)
481                .inner()
482                .value
483        });
484
485        assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
486        assert_eq!(pic_param.inner().loop_filter_level, [0; 4]);
487        assert_eq!(
488            pic_param.inner().loop_filter_deltas_ref_frame,
489            [2, 0, -2, -2]
490        );
491        assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
492        assert_eq!(pic_param.inner().prob_skip_false, 0xbe);
493        assert_eq!(pic_param.inner().prob_intra, 0);
494        assert_eq!(pic_param.inner().prob_last, 0);
495        assert_eq!(pic_param.inner().prob_gf, 0);
496
497        assert_eq!(pic_param.inner().y_mode_probs, [0x91, 0x9c, 0xa3, 0x80]);
498        assert_eq!(pic_param.inner().uv_mode_probs, [0x8e, 0x72, 0xb7]);
499        assert_eq!(
500            pic_param.inner().mv_probs[0],
501            [
502                0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84, 0x4b, 0x91,
503                0xb2, 0xce, 0xef, 0xfe, 0xfe
504            ]
505        );
506        assert_eq!(
507            pic_param.inner().mv_probs[1],
508            [
509                0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82, 0x4a, 0x94,
510                0xb4, 0xcb, 0xec, 0xfe, 0xfe,
511            ]
512        );
513
514        assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xfc);
515        assert_eq!(pic_param.inner().bool_coder_ctx.value, 0x39);
516        assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x0);
517
518        assert_eq!(
519            slice_param.inner(),
520            libva::SliceParameterBufferVP8::new(
521                14788,
522                10,
523                0,
524                3040,
525                2,
526                [926, 13472, 0, 0, 0, 0, 0, 0, 0],
527            )
528            .inner(),
529        );
530
531        // FRAME 1
532
533        let packet = ivf_iter.next().unwrap();
534        assert_eq!(packet.len(), 257);
535
536        let frame = parser.parse_frame(packet).unwrap();
537
538        let pic_param = build_pic_param(
539            &frame.header,
540            &resolution,
541            parser.segmentation(),
542            parser.mb_lf_adjust(),
543            0,
544            0,
545            0,
546        )
547        .unwrap();
548        let pic_param = match pic_param {
549            BufferType::PictureParameter(PictureParameter::VP8(pic_param)) => pic_param,
550            _ => panic!(),
551        };
552
553        let iq_matrix = build_iq_matrix(&frame.header, parser.segmentation()).unwrap();
554        let iq_matrix = match iq_matrix {
555            BufferType::IQMatrix(IQMatrix::VP8(iq_matrix)) => iq_matrix,
556            _ => panic!(),
557        };
558
559        let prob_table = build_probability_table(&frame.header);
560        let prob_table = match prob_table {
561            BufferType::Probability(prob_table) => prob_table,
562            _ => panic!(),
563        };
564
565        let slice_param = build_slice_param(&frame.header, packet.len()).unwrap();
566        let slice_param = match slice_param {
567            BufferType::SliceParameter(SliceParameter::VP8(slice_param)) => slice_param,
568            _ => panic!(),
569        };
570
571        assert_eq!(iq_matrix.inner().quantization_index, [[0x7f; 6]; 4]);
572        for i in 0..4 {
573            for j in 0..8 {
574                for k in 0..3 {
575                    for l in 0..11 {
576                        const OFF_I: usize = 8 * 3 * 11;
577                        const OFF_J: usize = 3 * 11;
578                        const OFF_K: usize = 11;
579                        // maybe std::transmute?
580                        assert_eq!(
581                            prob_table.inner().dct_coeff_probs[i][j][k][l],
582                            TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_1
583                                [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
584                        );
585                    }
586                }
587            }
588        }
589        assert_eq!(pic_param.inner().frame_width, 320);
590        assert_eq!(pic_param.inner().frame_height, 240);
591        assert_eq!(pic_param.inner().last_ref_frame, 0);
592        assert_eq!(pic_param.inner().golden_ref_frame, 0);
593        assert_eq!(pic_param.inner().alt_ref_frame, 0);
594        assert_eq!(
595            pic_param.inner().out_of_loop_frame,
596            libva::constants::VA_INVALID_SURFACE
597        );
598
599        // Safe because this bitfield is initialized by the decoder.
600        assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
601            libva::VP8PicFields::new(1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0)
602                .inner()
603                .value
604        });
605
606        assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
607        assert_eq!(pic_param.inner().loop_filter_level, [44; 4]);
608        assert_eq!(
609            pic_param.inner().loop_filter_deltas_ref_frame,
610            [2, 0, -2, -2]
611        );
612        assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
613        assert_eq!(pic_param.inner().prob_skip_false, 0x11);
614        assert_eq!(pic_param.inner().prob_intra, 0x2a);
615        assert_eq!(pic_param.inner().prob_last, 0xff);
616        assert_eq!(pic_param.inner().prob_gf, 0x80);
617
618        assert_eq!(pic_param.inner().y_mode_probs, [0x70, 0x56, 0x8c, 0x25]);
619        assert_eq!(pic_param.inner().uv_mode_probs, [0xa2, 0x65, 0xcc]);
620        assert_eq!(
621            pic_param.inner().mv_probs[0],
622            [
623                0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84, 0x4b, 0x91,
624                0xb2, 0xce, 0xef, 0xfe, 0xfe,
625            ]
626        );
627        assert_eq!(
628            pic_param.inner().mv_probs[1],
629            [
630                0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82, 0x4a, 0x94,
631                0xb4, 0xcb, 0xec, 0xfe, 0xfe,
632            ]
633        );
634
635        assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xde);
636        assert_eq!(pic_param.inner().bool_coder_ctx.value, 0x39);
637        assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x7);
638
639        assert_eq!(
640            slice_param.inner(),
641            libva::SliceParameterBufferVP8::new(257, 3, 0, 129, 2, [143, 94, 0, 0, 0, 0, 0, 0, 0],)
642                .inner()
643        );
644
645        // FRAME 2
646
647        let packet = ivf_iter.next().unwrap();
648        assert_eq!(packet.len(), 131);
649
650        let frame = parser.parse_frame(packet).unwrap();
651
652        let pic_param = build_pic_param(
653            &frame.header,
654            &resolution,
655            parser.segmentation(),
656            parser.mb_lf_adjust(),
657            1,
658            0,
659            0,
660        )
661        .unwrap();
662        let pic_param = match pic_param {
663            BufferType::PictureParameter(PictureParameter::VP8(pic_param)) => pic_param,
664            _ => panic!(),
665        };
666
667        let iq_matrix = build_iq_matrix(&frame.header, parser.segmentation()).unwrap();
668        let iq_matrix = match iq_matrix {
669            BufferType::IQMatrix(IQMatrix::VP8(iq_matrix)) => iq_matrix,
670            _ => panic!(),
671        };
672
673        let prob_table = build_probability_table(&frame.header);
674        let prob_table = match prob_table {
675            BufferType::Probability(prob_table) => prob_table,
676            _ => panic!(),
677        };
678
679        let slice_param = build_slice_param(&frame.header, packet.len()).unwrap();
680        let slice_param = match slice_param {
681            BufferType::SliceParameter(SliceParameter::VP8(slice_param)) => slice_param,
682            _ => panic!(),
683        };
684
685        assert_eq!(iq_matrix.inner().quantization_index, [[0x7f; 6]; 4]);
686        for i in 0..4 {
687            for j in 0..8 {
688                for k in 0..3 {
689                    for l in 0..11 {
690                        const OFF_I: usize = 8 * 3 * 11;
691                        const OFF_J: usize = 3 * 11;
692                        const OFF_K: usize = 11;
693                        // maybe std::transmute?
694                        assert_eq!(
695                            prob_table.inner().dct_coeff_probs[i][j][k][l],
696                            TEST_25_FPS_VP8_STREAM_PROBABILITY_TABLE_2
697                                [(i * OFF_I) + (j * OFF_J) + (k * OFF_K) + l]
698                        );
699                    }
700                }
701            }
702        }
703        assert_eq!(pic_param.inner().frame_width, 320);
704        assert_eq!(pic_param.inner().frame_height, 240);
705        assert_eq!(pic_param.inner().last_ref_frame, 1);
706        assert_eq!(pic_param.inner().golden_ref_frame, 0);
707        assert_eq!(pic_param.inner().alt_ref_frame, 0);
708        assert_eq!(
709            pic_param.inner().out_of_loop_frame,
710            libva::constants::VA_INVALID_SURFACE
711        );
712
713        // Safe because this bitfield is initialized by the decoder.
714        assert_eq!(unsafe { pic_param.inner().pic_fields.value }, unsafe {
715            libva::VP8PicFields::new(1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0)
716                .inner()
717                .value
718        });
719
720        assert_eq!(pic_param.inner().mb_segment_tree_probs, [0; 3]);
721        assert_eq!(pic_param.inner().loop_filter_level, [28; 4]);
722        assert_eq!(
723            pic_param.inner().loop_filter_deltas_ref_frame,
724            [2, 0, -2, -2]
725        );
726        assert_eq!(pic_param.inner().loop_filter_deltas_mode, [4, -2, 2, 4]);
727        assert_eq!(pic_param.inner().prob_skip_false, 0x6);
728        assert_eq!(pic_param.inner().prob_intra, 0x1);
729        assert_eq!(pic_param.inner().prob_last, 0xf8);
730        assert_eq!(pic_param.inner().prob_gf, 0xff);
731
732        assert_eq!(pic_param.inner().y_mode_probs, [0x70, 0x56, 0x8c, 0x25]);
733        assert_eq!(pic_param.inner().uv_mode_probs, [0xa2, 0x65, 0xcc]);
734        assert_eq!(
735            pic_param.inner().mv_probs[0],
736            [
737                0xa2, 0x80, 0xe1, 0x92, 0xac, 0x93, 0xd6, 0x27, 0x9c, 0x80, 0x81, 0x84, 0x4b, 0x91,
738                0xb2, 0xce, 0xef, 0xfe, 0xfe,
739            ]
740        );
741        assert_eq!(
742            pic_param.inner().mv_probs[1],
743            [
744                0xa4, 0x80, 0xcc, 0xaa, 0x77, 0xeb, 0x8c, 0xe6, 0xe4, 0x80, 0x82, 0x82, 0x4a, 0x94,
745                0xb4, 0xcb, 0xec, 0xfe, 0xfe,
746            ]
747        );
748
749        assert_eq!(pic_param.inner().bool_coder_ctx.range, 0xb1);
750        assert_eq!(pic_param.inner().bool_coder_ctx.value, 0xd);
751        assert_eq!(pic_param.inner().bool_coder_ctx.count, 0x2);
752
753        assert_eq!(
754            slice_param.inner(),
755            libva::SliceParameterBufferVP8::new(131, 3, 0, 86, 2, [66, 51, 0, 0, 0, 0, 0, 0, 0],)
756                .inner()
757        );
758    }
759}