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