83b459a5fbb0a83d262ad0bd78ca7c7190f44407
[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 generic_value;
18 mod image;
19 mod picture;
20 mod status;
21 mod surface;
22 mod usage_hint;
23
24 pub use bindings::constants;
25 pub use bindings::VAConfigAttrib;
26 pub use bindings::VAConfigAttribType;
27 pub use bindings::VAEntrypoint;
28 pub use bindings::VAImageFormat;
29 pub use bindings::VAProfile;
30 pub use bindings::VASurfaceAttribType;
31 pub use bindings::VASurfaceID;
32 pub use bindings::VASurfaceStatus;
33 pub use buffer::*;
34 pub use config::*;
35 pub use context::*;
36 pub use display::*;
37 pub use generic_value::*;
38 pub use image::*;
39 pub use picture::*;
40 pub use status::*;
41 pub use surface::*;
42 pub use usage_hint::*;
43
44 #[cfg(test)]
45 mod tests {
46 use std::rc::Rc;
47
48 use super::*;
49
50 /// Returns a 32-bit CRC for the visible part of `image`, which must be in NV12 format.
51 fn crc_nv12_image(image: &Image) -> u32 {
52 let data = image.as_ref();
53 let va_image = image.image();
54 let offsets = &va_image.offsets;
55 let pitches = &va_image.pitches;
56 let width = va_image.width as usize;
57 let height = va_image.height as usize;
58
59 // We only support NV12 images
60 assert_eq!(va_image.format.fourcc, u32::from_ne_bytes(*b"NV12"));
61 // Consistency check
62 assert_eq!(va_image.num_planes, 2);
63
64 let mut hasher = crc32fast::Hasher::new();
65
66 let offset = offsets[0] as usize;
67 let pitch = pitches[0] as usize;
68 let y_plane = data[offset..(offset + pitch * height)]
69 .chunks(pitch)
70 .map(|line| &line[0..width]);
71
72 let offset = offsets[1] as usize;
73 let pitch = pitches[1] as usize;
74 let uv_plane = data[offset..(offset + pitch * ((height + 1) / 2))]
75 .chunks(pitch)
76 .map(|line| &line[0..width]);
77
78 for line in y_plane.chain(uv_plane) {
79 hasher.update(line);
80 }
81
82 hasher.finalize()
83 }
84
85 #[test]
86 // Ignore this test by default as it requires libva-compatible hardware.
87 #[ignore]
88 fn libva_utils_mpeg2vldemo() {
89 // Adapted from <https://github.com/intel/libva-utils/blob/master/decode/mpeg2vldemo.cpp>
90 let display = Display::open().unwrap();
91
92 assert!(!display.query_vendor_string().unwrap().is_empty());
93 let profiles = display.query_config_profiles().unwrap();
94 assert!(!profiles.is_empty());
95
96 let profile = bindings::VAProfile::VAProfileMPEG2Main;
97 let entrypoints = display.query_config_entrypoints(profile).unwrap();
98 assert!(!entrypoints.is_empty());
99 assert!(entrypoints
100 .iter()
101 .any(|e| *e == bindings::VAEntrypoint::VAEntrypointVLD));
102
103 let format = bindings::constants::VA_RT_FORMAT_YUV420;
104 let width = 16;
105 let height = 16;
106
107 let mut attrs = vec![bindings::VAConfigAttrib {
108 type_: bindings::VAConfigAttribType::VAConfigAttribRTFormat,
109 value: 0,
110 }];
111
112 let entrypoint = bindings::VAEntrypoint::VAEntrypointVLD;
113 display
114 .get_config_attributes(profile, entrypoint, &mut attrs)
115 .unwrap();
116 assert!(attrs[0].value != bindings::constants::VA_ATTRIB_NOT_SUPPORTED);
117 assert!(attrs[0].value & bindings::constants::VA_RT_FORMAT_YUV420 != 0);
118
119 let config = display.create_config(attrs, profile, entrypoint).unwrap();
120
121 let mut surfaces = display
122 .create_surfaces(
123 format,
124 None,
125 width,
126 height,
127 Some(UsageHint::USAGE_HINT_DECODER),
128 1,
129 )
130 .unwrap();
131 let context = display
132 .create_context(
133 &config,
134 width as i32,
135 (((height + 15) / 16) * 16) as i32,
136 Some(&surfaces),
137 true,
138 )
139 .unwrap();
140
141 // The picture data is adapted from libva-utils at decode/mpeg2vldemo.cpp
142 // Data dump of a 16x16 MPEG2 video clip,it has one I frame
143 let mut mpeg2_clip: Vec<u8> = vec![
144 0x00, 0x00, 0x01, 0xb3, 0x01, 0x00, 0x10, 0x13, 0xff, 0xff, 0xe0, 0x18, 0x00, 0x00,
145 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08,
146 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xb5,
147 0x8f, 0xff, 0xf3, 0x41, 0x80, 0x00, 0x00, 0x01, 0x01, 0x13, 0xe1, 0x00, 0x15, 0x81,
148 0x54, 0xe0, 0x2a, 0x05, 0x43, 0x00, 0x2d, 0x60, 0x18, 0x01, 0x4e, 0x82, 0xb9, 0x58,
149 0xb1, 0x83, 0x49, 0xa4, 0xa0, 0x2e, 0x05, 0x80, 0x4b, 0x7a, 0x00, 0x01, 0x38, 0x20,
150 0x80, 0xe8, 0x05, 0xff, 0x60, 0x18, 0xe0, 0x1d, 0x80, 0x98, 0x01, 0xf8, 0x06, 0x00,
151 0x54, 0x02, 0xc0, 0x18, 0x14, 0x03, 0xb2, 0x92, 0x80, 0xc0, 0x18, 0x94, 0x42, 0x2c,
152 0xb2, 0x11, 0x64, 0xa0, 0x12, 0x5e, 0x78, 0x03, 0x3c, 0x01, 0x80, 0x0e, 0x80, 0x18,
153 0x80, 0x6b, 0xca, 0x4e, 0x01, 0x0f, 0xe4, 0x32, 0xc9, 0xbf, 0x01, 0x42, 0x69, 0x43,
154 0x50, 0x4b, 0x01, 0xc9, 0x45, 0x80, 0x50, 0x01, 0x38, 0x65, 0xe8, 0x01, 0x03, 0xf3,
155 0xc0, 0x76, 0x00, 0xe0, 0x03, 0x20, 0x28, 0x18, 0x01, 0xa9, 0x34, 0x04, 0xc5, 0xe0,
156 0x0b, 0x0b, 0x04, 0x20, 0x06, 0xc0, 0x89, 0xff, 0x60, 0x12, 0x12, 0x8a, 0x2c, 0x34,
157 0x11, 0xff, 0xf6, 0xe2, 0x40, 0xc0, 0x30, 0x1b, 0x7a, 0x01, 0xa9, 0x0d, 0x00, 0xac,
158 0x64,
159 ];
160
161 let picture_coding_extension =
162 MPEG2PictureCodingExtension::new(0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1);
163 let pic_param = PictureParameterBufferMPEG2::new(
164 16,
165 16,
166 0xffffffff,
167 0xffffffff,
168 1,
169 0xffff,
170 &picture_coding_extension,
171 );
172
173 let pic_param = BufferType::PictureParameter(PictureParameter::MPEG2(pic_param));
174
175 let iq_matrix = IQMatrixBufferMPEG2::new(
176 1,
177 1,
178 0,
179 0,
180 [
181 8, 16, 16, 19, 16, 19, 22, 22, 22, 22, 22, 22, 26, 24, 26, 27, 27, 27, 26, 26, 26,
182 26, 27, 27, 27, 29, 29, 29, 34, 34, 34, 29, 29, 29, 27, 27, 29, 29, 32, 32, 34, 34,
183 37, 38, 37, 35, 35, 34, 35, 38, 38, 40, 40, 40, 48, 48, 46, 46, 56, 56, 58, 69, 69,
184 83,
185 ],
186 [
187 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,
188 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,
189 0, 0, 0, 0, 0, 0, 0, 0, 0,
190 ],
191 [0; 64],
192 [0; 64],
193 );
194
195 let iq_matrix = BufferType::IQMatrix(IQMatrix::MPEG2(iq_matrix));
196
197 let slice_param = SliceParameterBufferMPEG2::new(150, 0, 0, 38, 0, 0, 2, 0);
198
199 let slice_param = BufferType::SliceParameter(SliceParameter::MPEG2(slice_param));
200
201 let test_data_offset = 47;
202 let slice_data = BufferType::SliceData(mpeg2_clip.drain(test_data_offset..).collect());
203
204 let buffers = vec![
205 context.create_buffer(pic_param).unwrap(),
206 context.create_buffer(slice_param).unwrap(),
207 context.create_buffer(iq_matrix).unwrap(),
208 context.create_buffer(slice_data).unwrap(),
209 ];
210
211 let mut picture = Picture::new(0, Rc::clone(&context), surfaces.remove(0));
212 for buffer in buffers {
213 picture.add_buffer(buffer);
214 }
215
216 // Actual client code can just chain the calls.
217 let picture = picture.begin().unwrap();
218 let picture = picture.render().unwrap();
219 let picture = picture.end().unwrap();
220 let picture = picture.sync().map_err(|(e, _)| e).unwrap();
221
222 // Test whether we can map the resulting surface to obtain the raw yuv
223 // data
224 let image_fmts = display.query_image_formats().unwrap();
225 let image_fmt = image_fmts
226 .into_iter()
227 .find(|f| f.fourcc == bindings::constants::VA_FOURCC_NV12)
228 .expect("No valid VAImageFormat found for NV12");
229
230 let image = Image::new(&picture, image_fmt, width, height, false).unwrap();
231
232 assert_eq!(crc_nv12_image(&image), 0xa5713e52);
233 }
234 }