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