nihed-cros-libva: introduce more enums instead of constants
[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 #[cfg(test)]
46 mod tests {
47 use std::rc::Rc;
48
49 use super::*;
50
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;
59
60 // We only support NV12 images
61 assert_eq!(va_image.format.fourcc, u32::from_ne_bytes(*b"NV12"));
62 // Consistency check
63 assert_eq!(va_image.num_planes, 2);
64
65 let mut hasher = crc32fast::Hasher::new();
66
67 let offset = offsets[0] as usize;
68 let pitch = pitches[0] as usize;
69 let y_plane = data[offset..(offset + pitch * height)]
70 .chunks(pitch)
71 .map(|line| &line[0..width]);
72
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))]
76 .chunks(pitch)
77 .map(|line| &line[0..width]);
78
79 for line in y_plane.chain(uv_plane) {
80 hasher.update(line);
81 }
82
83 hasher.finalize()
84 }
85
86 #[test]
87 // Ignore this test by default as it requires libva-compatible hardware.
88 #[ignore]
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();
92
93 assert!(!display.query_vendor_string().unwrap().is_empty());
94 let profiles = display.query_config_profiles().unwrap();
95 assert!(!profiles.is_empty());
96
97 let profile = bindings::VAProfile::VAProfileMPEG2Main;
98 let entrypoints = display.query_config_entrypoints(profile).unwrap();
99 assert!(!entrypoints.is_empty());
100 assert!(entrypoints
101 .iter()
102 .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
103
104 let format = bindings::constants::VA_RT_FORMAT_YUV420;
105 let width = 16;
106 let height = 16;
107
108 let mut attrs = vec![bindings::VAConfigAttrib {
109 type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
110 value: 0,
111 }];
112
113 let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
114 display
115 .get_config_attributes(profile, entrypoint, &mut attrs)
116 .unwrap();
117 assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
118 assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
119
120 let config = display.create_config(attrs, profile, entrypoint).unwrap();
121
122 let mut surfaces = display
123 .create_surfaces(
124 format,
125 None,
126 width,
127 height,
128 Some(UsageHint::USAGE_HINT_DECODER),
129 1,
130 )
131 .unwrap();
132 let context = display
133 .create_context(
134 &config,
135 width as i32,
136 (((height + 15) / 16) * 16) as i32,
137 Some(&surfaces),
138 true,
139 )
140 .unwrap();
141
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,
159 0x64,
160 ];
161
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(
165 16,
166 16,
167 0xffffffff,
168 0xffffffff,
169 1,
170 0xffff,
171 &picture_coding_extension,
172 );
173
174 let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
175
176 let iq_matrix = IQMatrixBufferMPEG2::new(
177 1,
178 1,
179 0,
180 0,
181 [
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,
185 83,
186 ],
187 [
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,
191 ],
192 [0; 64],
193 [0; 64],
194 );
195
196 let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
197
198 let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
199
200 let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
201
202 let test_data_offset = 47;
203 let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
204
205 let buffers = vec![
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(),
210 ];
211
212 let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
213 for buffer in buffers {
214 picture.add_buffer(buffer);
215 }
216
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();
222
223 // Test whether we can map the resulting surface to obtain the raw yuv
224 // data
225 let image_fmts = display.query_image_formats().unwrap();
226 let image_fmt = image_fmts
227 .into_iter()
228 .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
229 .expect("No valid VAImageFormat found for NV12");
230
231 let image = Image::new(&picture, image_fmt, width, height, false).unwrap();
232
233 assert_eq!(crc_nv12_image(&image), 0xa5713e52);
234 }
235 }