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::*;
51 /// Returns a 32-bit CRC for the visible part of `image`, which must be in NV12 format.
52 fn crc_nv12_image(image: &Image) -> u32 {
53 let data = image.as_ref();
54 let va_image = image.image();
55 let offsets = &va_image.offsets;
56 let pitches = &va_image.pitches;
57 let width = va_image.width as usize;
58 let height = va_image.height as usize;
60 // We only support NV12 images
61 assert_eq!(va_image.format.fourcc, u32::from_ne_bytes(*b"NV12"));
63 assert_eq!(va_image.num_planes, 2);
65 let mut hasher = crc32fast::Hasher::new();
67 let offset = offsets[0] as usize;
68 let pitch = pitches[0] as usize;
69 let y_plane = data[offset..(offset + pitch * height)]
71 .map(|line| &line[0..width]);
73 let offset = offsets[1] as usize;
74 let pitch = pitches[1] as usize;
75 let uv_plane = data[offset..(offset + pitch * ((height + 1) / 2))]
77 .map(|line| &line[0..width]);
79 for line in y_plane.chain(uv_plane) {
87 // Ignore this test by default as it requires libva-compatible hardware.
89 fn libva_utils_mpeg2vldemo() {
90 // Adapted from <https://github.com/intel/libva-utils/blob/master/decode/mpeg2vldemo.cpp>
91 let display = Display::open().unwrap();
93 assert!(!display.query_vendor_string().unwrap().is_empty());
94 let profiles = display.query_config_profiles().unwrap();
95 assert!(!profiles.is_empty());
97 let profile = bindings::VAProfile::VAProfileMPEG2Main;
98 let entrypoints = display.query_config_entrypoints(profile).unwrap();
99 assert!(!entrypoints.is_empty());
102 .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
104 let format = bindings::constants::VA_RT_FORMAT_YUV420;
108 let mut attrs = vec![bindings::VAConfigAttrib {
109 type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
113 let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
115 .get_config_attributes(profile, entrypoint, &mut attrs)
117 assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
118 assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
120 let config = display.create_config(attrs, profile, entrypoint).unwrap();
122 let mut surfaces = display
128 Some(UsageHint::USAGE_HINT_DECODER),
132 let context = display
136 (((height + 15) / 16) * 16) as i32,
142 // The picture data is adapted from libva-utils at decode/mpeg2vldemo.cpp
143 // Data dump of a 16x16 MPEG2 video clip,it has one I frame
144 let mut mpeg2_clip: Vec<u8> = vec![
145 0x00, 0x00, 0x01, 0xb3, 0x01, 0x00, 0x10, 0x13, 0xff, 0xff, 0xe0, 0x18, 0x00, 0x00,
146 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08,
147 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5,
148 0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x13, 0xe1, 0x00, 0x15, 0x81,
149 0x54, 0xe0, 0x2a, 0x05, 0x43, 0x00, 0x2d, 0x60, 0x18, 0x01, 0x4e, 0x82, 0xb9, 0x58,
150 0xb1, 0x83, 0x49, 0xa4, 0xa0, 0x2e, 0x05, 0x80, 0x4b, 0x7a, 0x00, 0x01, 0x38, 0x20,
151 0x80, 0xe8, 0x05, 0xff, 0x60, 0x18, 0xe0, 0x1d, 0x80, 0x98, 0x01, 0xf8, 0x06, 0x00,
152 0x54, 0x02, 0xc0, 0x18, 0x14, 0x03, 0xb2, 0x92, 0x80, 0xc0, 0x18, 0x94, 0x42, 0x2c,
153 0xb2, 0x11, 0x64, 0xa0, 0x12, 0x5e, 0x78, 0x03, 0x3c, 0x01, 0x80, 0x0e, 0x80, 0x18,
154 0x80, 0x6b, 0xca, 0x4e, 0x01, 0x0f, 0xe4, 0x32, 0xc9, 0xbf, 0x01, 0x42, 0x69, 0x43,
155 0x50, 0x4b, 0x01, 0xc9, 0x45, 0x80, 0x50, 0x01, 0x38, 0x65, 0xe8, 0x01, 0x03, 0xf3,
156 0xc0, 0x76, 0x00, 0xe0, 0x03, 0x20, 0x28, 0x18, 0x01, 0xa9, 0x34, 0x04, 0xc5, 0xe0,
157 0x0b, 0x0b, 0x04, 0x20, 0x06, 0xc0, 0x89, 0xff, 0x60, 0x12, 0x12, 0x8a, 0x2c, 0x34,
158 0x11, 0xff, 0xf6, 0xe2, 0x40, 0xc0, 0x30, 0x1b, 0x7a, 0x01, 0xa9, 0x0d, 0x00, 0xac,
162 let picture_coding_extension =
163 MPEG2PictureCodingExtension::new(0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1);
164 let pic_param = PictureParameterBufferMPEG2::new(
171 &picture_coding_extension,
174 let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
176 let iq_matrix = IQMatrixBufferMPEG2::new(
182 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26,
183 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34,
184 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69,
188 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,
189 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,
190 0, 0, 0, 0, 0, 0, 0, 0, 0,
196 let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
198 let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
200 let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
202 let test_data_offset = 47;
203 let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
206 context.create_buffer(pic_param).unwrap(),
207 context.create_buffer(slice_param).unwrap(),
208 context.create_buffer(iq_matrix).unwrap(),
209 context.create_buffer(slice_data).unwrap(),
212 let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
213 for buffer in buffers {
214 picture.add_buffer(buffer);
217 // Actual client code can just chain the calls.
218 let picture = picture.begin().unwrap();
219 let picture = picture.render().unwrap();
220 let picture = picture.end().unwrap();
221 let picture = picture.sync().map_err(|(e, _)| e).unwrap();
223 // Test whether we can map the resulting surface to obtain the raw yuv
225 let image_fmts = display.query_image_formats().unwrap();
226 let image_fmt = image_fmts
228 .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
229 .expect("No valid VAImageFormat found for NV12");
231 let image = Image::new(&picture, image_fmt, width, height, false).unwrap();
233 assert_eq!(crc_nv12_image(&image), 0xa5713e52);