nihed-cros-libva: introduce more enums instead of constants
[nihav-player.git] / nihed-cros-libva / src / lib.rs
CommitLineData
68362724
KS
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
12mod bindings;
13pub mod buffer;
14mod config;
15mod context;
16mod display;
0f2fb233 17mod formats;
68362724
KS
18mod generic_value;
19mod image;
20mod picture;
21mod status;
22mod surface;
23mod usage_hint;
24
25pub use bindings::constants;
26pub use bindings::VAConfigAttrib;
27pub use bindings::VAConfigAttribType;
28pub use bindings::VAEntrypoint;
29pub use bindings::VAImageFormat;
30pub use bindings::VAProfile;
31pub use bindings::VASurfaceAttribType;
32pub use bindings::VASurfaceID;
68362724
KS
33pub use buffer::*;
34pub use config::*;
35pub use context::*;
36pub use display::*;
0f2fb233 37pub use formats::*;
68362724
KS
38pub use generic_value::*;
39pub use image::*;
40pub use picture::*;
69e6ce02 41pub use status::*;
68362724
KS
42pub use surface::*;
43pub use usage_hint::*;
44
45#[cfg(test)]
46mod 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}