a095c1c35e69b268195251c003417a989106672f
[nihav-player.git] / nihed-cros-libva / src / lib.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
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 #![deny(missing_docs)]
11
12 mod bindings;
13 pub mod buffer;
14 mod config;
15 mod context;
16 mod display;
17 mod formats;
18 mod generic_value;
19 mod image;
20 mod picture;
21 mod status;
22 mod surface;
23 mod usage_hint;
24
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;
33 pub use buffer::*;
34 pub use config::*;
35 pub use context::*;
36 pub use display::*;
37 pub use formats::*;
38 pub use generic_value::*;
39 pub use image::*;
40 pub use picture::*;
41 pub use status::*;
42 pub use surface::*;
43 pub use usage_hint::*;
44
45 pub use bindings::constants::VA_INVALID_ID;
46
47 #[cfg(test)]
48 mod tests {
49 use std::rc::Rc;
50
51 use super::*;
52
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;
61
62 // We only support NV12 images
63 assert_eq!(va_image.format.fourcc(), Ok(VAFourcc::NV12));
64 // Consistency check
65 assert_eq!(va_image.num_planes, 2);
66
67 let mut hasher = crc32fast::Hasher::new();
68
69 let offset = offsets[0] as usize;
70 let pitch = pitches[0] as usize;
71 let y_plane = data[offset..(offset + pitch * height)]
72 .chunks(pitch)
73 .map(|line| &line[0..width]);
74
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))]
78 .chunks(pitch)
79 .map(|line| &line[0..width]);
80
81 for line in y_plane.chain(uv_plane) {
82 hasher.update(line);
83 }
84
85 hasher.finalize()
86 }
87
88 #[test]
89 // Ignore this test by default as it requires libva-compatible hardware.
90 #[ignore]
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();
94
95 assert!(!display.query_vendor_string().unwrap().is_empty());
96 let profiles = display.query_config_profiles().unwrap();
97 assert!(!profiles.is_empty());
98
99 let profile = bindings::VAProfile::VAProfileMPEG2Main;
100 let entrypoints = display.query_config_entrypoints(profile).unwrap();
101 assert!(!entrypoints.is_empty());
102 assert!(entrypoints
103 .iter()
104 .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
105
106 let format = RTFormat::YUV420;
107 let width = 16;
108 let height = 16;
109
110 let mut attrs = vec![bindings::VAConfigAttrib {
111 type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
112 value: 0,
113 }];
114
115 let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
116 display
117 .get_config_attributes(profile, entrypoint, &mut attrs)
118 .unwrap();
119 assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
120 assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
121
122 let config = display.create_config(attrs, profile, entrypoint).unwrap();
123
124 let mut surfaces = display
125 .create_surfaces(
126 format,
127 None,
128 width,
129 height,
130 Some(UsageHint::Decoder.into()),
131 1,
132 )
133 .unwrap();
134 let context = display
135 .create_context(
136 &config,
137 width as i32,
138 (((height + 15) / 16) * 16) as i32,
139 Some(&surfaces),
140 true,
141 )
142 .unwrap();
143
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,
161 0x64,
162 ];
163
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(
167 16,
168 16,
169 0xffffffff,
170 0xffffffff,
171 1,
172 0xffff,
173 &picture_coding_extension,
174 );
175
176 let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
177
178 let iq_matrix = IQMatrixBufferMPEG2::new(
179 1,
180 1,
181 0,
182 0,
183 [
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,
187 83,
188 ],
189 [
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,
193 ],
194 [0; 64],
195 [0; 64],
196 );
197
198 let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
199
200 let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
201
202 let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
203
204 let test_data_offset = 47;
205 let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
206
207 let buffers = vec![
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(),
212 ];
213
214 let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
215 for buffer in buffers {
216 picture.add_buffer(buffer);
217 }
218
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();
224
225 // Test whether we can map the resulting surface to obtain the raw yuv
226 // data
227 let image_fmts = display.query_image_formats().unwrap();
228 let image_fmt = image_fmts
229 .into_iter()
230 .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
231 .expect("No valid VAImageFormat found for NV12");
232
233 let image = Image::new(&picture, image_fmt, width, height, false).unwrap();
234
235 assert_eq!(crc_nv12_image(&image), 0xa5713e52);
236 }
237 }