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.
5 //! Implements a lightweight and safe interface over `libva`.
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.
10 #![deny(missing_docs)]
25 pub use bindings::constants;
26 pub use bindings::VAConfigAttrib;
27 pub use bindings::VAConfigAttribType;
28 pub use bindings::VAEntrypoint;
29 pub use bindings::VAImageFormat;
30 pub use bindings::VAProfile;
31 pub use bindings::VASurfaceAttribType;
32 pub use bindings::VASurfaceID;
38 pub use generic_value::*;
43 pub use usage_hint::*;
45 pub use bindings::constants::VA_INVALID_ID;
53 /// Returns a 32-bit CRC for the visible part of `image`, which must be in NV12 format.
54 fn crc_nv12_image(image: &Image) -> u32 {
55 let data = image.as_ref();
56 let va_image = image.image();
57 let offsets = &va_image.offsets;
58 let pitches = &va_image.pitches;
59 let width = va_image.width as usize;
60 let height = va_image.height as usize;
62 // We only support NV12 images
63 assert_eq!(va_image.format.fourcc(), Ok(VAFourcc::NV12));
65 assert_eq!(va_image.num_planes, 2);
67 let mut hasher = crc32fast::Hasher::new();
69 let offset = offsets[0] as usize;
70 let pitch = pitches[0] as usize;
71 let y_plane = data[offset..(offset + pitch * height)]
73 .map(|line| &line[0..width]);
75 let offset = offsets[1] as usize;
76 let pitch = pitches[1] as usize;
77 let uv_plane = data[offset..(offset + pitch * ((height + 1) / 2))]
79 .map(|line| &line[0..width]);
81 for line in y_plane.chain(uv_plane) {
89 // Ignore this test by default as it requires libva-compatible hardware.
91 fn libva_utils_mpeg2vldemo() {
92 // Adapted from <https://github.com/intel/libva-utils/blob/master/decode/mpeg2vldemo.cpp>
93 let display = Display::open().unwrap();
95 assert!(!display.query_vendor_string().unwrap().is_empty());
96 let profiles = display.query_config_profiles().unwrap();
97 assert!(!profiles.is_empty());
99 let profile = bindings::VAProfile::VAProfileMPEG2Main;
100 let entrypoints = display.query_config_entrypoints(profile).unwrap();
101 assert!(!entrypoints.is_empty());
104 .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
106 let format = RTFormat::YUV420;
110 let mut attrs = vec![bindings::VAConfigAttrib {
111 type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
115 let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
117 .get_config_attributes(profile, entrypoint, &mut attrs)
119 assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
120 assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
122 let config = display.create_config(attrs, profile, entrypoint).unwrap();
124 let mut surfaces = display
130 Some(UsageHint::Decoder.into()),
134 let context = display
138 (((height + 15) / 16) * 16) as i32,
144 // The picture data is adapted from libva-utils at decode/mpeg2vldemo.cpp
145 // Data dump of a 16x16 MPEG2 video clip,it has one I frame
146 let mut mpeg2_clip: Vec<u8> = vec![
147 0x00, 0x00, 0x01, 0xb3, 0x01, 0x00, 0x10, 0x13, 0xff, 0xff, 0xe0, 0x18, 0x00, 0x00,
148 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08,
149 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5,
150 0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x13, 0xe1, 0x00, 0x15, 0x81,
151 0x54, 0xe0, 0x2a, 0x05, 0x43, 0x00, 0x2d, 0x60, 0x18, 0x01, 0x4e, 0x82, 0xb9, 0x58,
152 0xb1, 0x83, 0x49, 0xa4, 0xa0, 0x2e, 0x05, 0x80, 0x4b, 0x7a, 0x00, 0x01, 0x38, 0x20,
153 0x80, 0xe8, 0x05, 0xff, 0x60, 0x18, 0xe0, 0x1d, 0x80, 0x98, 0x01, 0xf8, 0x06, 0x00,
154 0x54, 0x02, 0xc0, 0x18, 0x14, 0x03, 0xb2, 0x92, 0x80, 0xc0, 0x18, 0x94, 0x42, 0x2c,
155 0xb2, 0x11, 0x64, 0xa0, 0x12, 0x5e, 0x78, 0x03, 0x3c, 0x01, 0x80, 0x0e, 0x80, 0x18,
156 0x80, 0x6b, 0xca, 0x4e, 0x01, 0x0f, 0xe4, 0x32, 0xc9, 0xbf, 0x01, 0x42, 0x69, 0x43,
157 0x50, 0x4b, 0x01, 0xc9, 0x45, 0x80, 0x50, 0x01, 0x38, 0x65, 0xe8, 0x01, 0x03, 0xf3,
158 0xc0, 0x76, 0x00, 0xe0, 0x03, 0x20, 0x28, 0x18, 0x01, 0xa9, 0x34, 0x04, 0xc5, 0xe0,
159 0x0b, 0x0b, 0x04, 0x20, 0x06, 0xc0, 0x89, 0xff, 0x60, 0x12, 0x12, 0x8a, 0x2c, 0x34,
160 0x11, 0xff, 0xf6, 0xe2, 0x40, 0xc0, 0x30, 0x1b, 0x7a, 0x01, 0xa9, 0x0d, 0x00, 0xac,
164 let picture_coding_extension =
165 MPEG2PictureCodingExtension::new(0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1);
166 let pic_param = PictureParameterBufferMPEG2::new(
173 &picture_coding_extension,
176 let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
178 let iq_matrix = IQMatrixBufferMPEG2::new(
184 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26,
185 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34,
186 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69,
190 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,
191 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,
192 0, 0, 0, 0, 0, 0, 0, 0, 0,
198 let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
200 let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
202 let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
204 let test_data_offset = 47;
205 let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
208 context.create_buffer(pic_param).unwrap(),
209 context.create_buffer(slice_param).unwrap(),
210 context.create_buffer(iq_matrix).unwrap(),
211 context.create_buffer(slice_data).unwrap(),
214 let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
215 for buffer in buffers {
216 picture.add_buffer(buffer);
219 // Actual client code can just chain the calls.
220 let picture = picture.begin().unwrap();
221 let picture = picture.render().unwrap();
222 let picture = picture.end().unwrap();
223 let picture = picture.sync().map_err(|(e, _)| e).unwrap();
225 // Test whether we can map the resulting surface to obtain the raw yuv
227 let image_fmts = display.query_image_formats().unwrap();
228 let image_fmt = image_fmts
230 .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
231 .expect("No valid VAImageFormat found for NV12");
233 let image = Image::new(&picture, image_fmt, width, height, false).unwrap();
235 assert_eq!(crc_nv12_image(&image), 0xa5713e52);