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