• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 //! Implements a lightweight and safe interface over `libva`.
6 //!
7 //! The starting point to using this crate is to open a [`Display`], from which a [`Context`] and
8 //! [`Surface`]s can be allocated and used for doing actual work.
9 
10 #![cfg(unix)]
11 #![deny(missing_docs)]
12 
13 mod bindings;
14 mod buffer;
15 mod buffer_type;
16 mod config;
17 mod context;
18 mod display;
19 mod generic_value;
20 mod image;
21 mod picture;
22 mod status;
23 mod surface;
24 mod usage_hint;
25 
26 pub use bindings::constants;
27 pub use bindings::VAConfigAttrib;
28 pub use bindings::VAConfigAttribType;
29 pub use bindings::VAEntrypoint;
30 pub use bindings::VAImageFormat;
31 pub use bindings::VAProfile;
32 pub use bindings::VASurfaceAttribType;
33 pub use bindings::VASurfaceID;
34 pub use bindings::VASurfaceStatus;
35 pub use buffer::*;
36 pub use buffer_type::*;
37 pub use config::*;
38 pub use context::*;
39 pub use display::*;
40 pub use generic_value::*;
41 pub use image::*;
42 pub use picture::*;
43 pub use surface::*;
44 pub use usage_hint::*;
45 
46 #[cfg(test)]
47 mod tests {
48     use std::rc::Rc;
49 
50     use super::*;
51 
52     /// Returns a 32-bit CRC for the visible part of `image`, which must be in NV12 format.
crc_nv12_image(image: &Image) -> u3253     fn crc_nv12_image(image: &Image) -> u32 {
54         let data = image.as_ref();
55         let va_image = image.image();
56         let offsets = &va_image.offsets;
57         let pitches = &va_image.pitches;
58         let width = va_image.width as usize;
59         let height = va_image.height as usize;
60 
61         // We only support NV12 images
62         assert_eq!(va_image.format.fourcc, u32::from_ne_bytes(*b"NV12"));
63         // Consistency check
64         assert_eq!(va_image.num_planes, 2);
65 
66         let mut hasher = crc32fast::Hasher::new();
67 
68         let offset = offsets[0] as usize;
69         let pitch = pitches[0] as usize;
70         let y_plane = data[offset..(offset + pitch * height)]
71             .chunks(pitch)
72             .map(|line| &line[0..width]);
73 
74         let offset = offsets[1] as usize;
75         let pitch = pitches[1] as usize;
76         let uv_plane = data[offset..(offset + pitch * ((height + 1) / 2))]
77             .chunks(pitch)
78             .map(|line| &line[0..width]);
79 
80         for line in y_plane.chain(uv_plane) {
81             hasher.update(line);
82         }
83 
84         hasher.finalize()
85     }
86 
87     #[test]
88     // Ignore this test by default as it requires libva-compatible hardware.
89     #[ignore]
libva_utils_mpeg2vldemo()90     fn libva_utils_mpeg2vldemo() {
91         // Adapted from <https://github.com/intel/libva-utils/blob/master/decode/mpeg2vldemo.cpp>
92         let display = Display::open().unwrap();
93 
94         assert!(!display.query_vendor_string().unwrap().is_empty());
95         let profiles = display.query_config_profiles().unwrap();
96         assert!(!profiles.is_empty());
97 
98         let profile = bindings::VAProfile::VAProfileMPEG2Main;
99         let entrypoints = display.query_config_entrypoints(profile).unwrap();
100         assert!(!entrypoints.is_empty());
101         assert!(entrypoints
102             .iter()
103             .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
104 
105         let format = bindings::constants::VA_RT_FORMAT_YUV420;
106         let width = 16;
107         let height = 16;
108 
109         let mut attrs = vec![bindings::VAConfigAttrib {
110             type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
111             value: 0,
112         }];
113 
114         let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
115         display
116             .get_config_attributes(profile, entrypoint, &mut attrs)
117             .unwrap();
118         assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
119         assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
120 
121         let config = display.create_config(attrs, profile, entrypoint).unwrap();
122 
123         let mut surfaces = display
124             .create_surfaces(
125                 format,
126                 None,
127                 width,
128                 height,
129                 Some(UsageHint::USAGE_HINT_DECODER),
130                 1,
131             )
132             .unwrap();
133         let context = display
134             .create_context(
135                 &config,
136                 width as i32,
137                 (((height + 15) / 16) * 16) as i32,
138                 Some(&surfaces),
139                 true,
140             )
141             .unwrap();
142 
143         // The picture data is adapted from libva-utils at decode/mpeg2vldemo.cpp
144         // Data dump of a 16x16 MPEG2 video clip,it has one I frame
145         let mut mpeg2_clip: Vec<u8> = vec![
146             0x00, 0x00, 0x01, 0xb3, 0x01, 0x00, 0x10, 0x13, 0xff, 0xff, 0xe0, 0x18, 0x00, 0x00,
147             0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08,
148             0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5,
149             0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x13, 0xe1, 0x00, 0x15, 0x81,
150             0x54, 0xe0, 0x2a, 0x05, 0x43, 0x00, 0x2d, 0x60, 0x18, 0x01, 0x4e, 0x82, 0xb9, 0x58,
151             0xb1, 0x83, 0x49, 0xa4, 0xa0, 0x2e, 0x05, 0x80, 0x4b, 0x7a, 0x00, 0x01, 0x38, 0x20,
152             0x80, 0xe8, 0x05, 0xff, 0x60, 0x18, 0xe0, 0x1d, 0x80, 0x98, 0x01, 0xf8, 0x06, 0x00,
153             0x54, 0x02, 0xc0, 0x18, 0x14, 0x03, 0xb2, 0x92, 0x80, 0xc0, 0x18, 0x94, 0x42, 0x2c,
154             0xb2, 0x11, 0x64, 0xa0, 0x12, 0x5e, 0x78, 0x03, 0x3c, 0x01, 0x80, 0x0e, 0x80, 0x18,
155             0x80, 0x6b, 0xca, 0x4e, 0x01, 0x0f, 0xe4, 0x32, 0xc9, 0xbf, 0x01, 0x42, 0x69, 0x43,
156             0x50, 0x4b, 0x01, 0xc9, 0x45, 0x80, 0x50, 0x01, 0x38, 0x65, 0xe8, 0x01, 0x03, 0xf3,
157             0xc0, 0x76, 0x00, 0xe0, 0x03, 0x20, 0x28, 0x18, 0x01, 0xa9, 0x34, 0x04, 0xc5, 0xe0,
158             0x0b, 0x0b, 0x04, 0x20, 0x06, 0xc0, 0x89, 0xff, 0x60, 0x12, 0x12, 0x8a, 0x2c, 0x34,
159             0x11, 0xff, 0xf6, 0xe2, 0x40, 0xc0, 0x30, 0x1b, 0x7a, 0x01, 0xa9, 0x0d, 0x00, 0xac,
160             0x64,
161         ];
162 
163         let picture_coding_extension =
164             MPEG2PictureCodingExtension::new(0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1);
165         let pic_param = PictureParameterBufferMPEG2::new(
166             16,
167             16,
168             0xffffffff,
169             0xffffffff,
170             1,
171             0xffff,
172             &picture_coding_extension,
173         );
174 
175         let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
176 
177         let iq_matrix = IQMatrixBufferMPEG2::new(
178             1,
179             1,
180             0,
181             0,
182             [
183                 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26,
184                 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34,
185                 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69,
186                 83,
187             ],
188             [
189                 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
190                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191                 0, 0, 0, 0, 0, 0, 0, 0, 0,
192             ],
193             [0; 64],
194             [0; 64],
195         );
196 
197         let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
198 
199         let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
200 
201         let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
202 
203         let test_data_offset = 47;
204         let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
205 
206         let buffers = vec![
207             context.create_buffer(pic_param).unwrap(),
208             context.create_buffer(slice_param).unwrap(),
209             context.create_buffer(iq_matrix).unwrap(),
210             context.create_buffer(slice_data).unwrap(),
211         ];
212 
213         let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
214         for buffer in buffers {
215             picture.add_buffer(buffer);
216         }
217 
218         // Actual client code can just chain the calls.
219         let picture = picture.begin().unwrap();
220         let picture = picture.render().unwrap();
221         let picture = picture.end().unwrap();
222         let mut picture = picture.sync().unwrap();
223 
224         // Test whether we can map the resulting surface to obtain the raw yuv
225         // data
226         let image_fmts = display.query_image_formats().unwrap();
227         let image_fmt = image_fmts
228             .into_iter()
229             .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
230             .expect("No valid VAImageFormat found for NV12");
231 
232         let image = Image::new(&mut picture, image_fmt, width, height, false).unwrap();
233 
234         assert_eq!(crc_nv12_image(&image), 0xa5713e52);
235     }
236 }
237