vpx_rs/dec.rs
1/*
2 * Copyright (c) 2025, Saso Kiselkov. All rights reserved.
3 *
4 * Use of this source code is governed by the 2-clause BSD license,
5 * which can be found in a file named LICENSE, located at the root
6 * of this source tree.
7 */
8
9use bitflags::bitflags;
10use std::mem::MaybeUninit;
11use std::ptr::NonNull;
12
13use crate::common::StreamInfo;
14use crate::image::YUVImageData;
15use crate::NoDebugWrapper;
16use crate::{call_vpx, call_vpx_ptr};
17use crate::{Error, Result};
18
19#[allow(missing_docs)]
20/// Specifies the codec to be used for decoding.
21#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub enum CodecId {
23 VP8,
24 VP9,
25}
26
27impl CodecId {
28 pub(crate) fn iface(&self) -> Result<*const vpx_sys::vpx_codec_iface_t> {
29 match self {
30 Self::VP8 => call_vpx_ptr!(
31 vpx_sys::vpx_codec_vp8_dx(),
32 Error::CodecInterfaceNotAvailable,
33 ),
34 Self::VP9 => call_vpx_ptr!(
35 vpx_sys::vpx_codec_vp9_dx(),
36 Error::CodecInterfaceNotAvailable,
37 ),
38 }
39 }
40}
41
42/// The main VP8/VP9 video decoder.
43///
44/// # Example
45/// ```
46/// use std::num::NonZero;
47/// use vpx_rs::{Decoder, DecoderConfig, Error, DecodedImageData};
48///
49/// // Initialize a new decoder
50/// fn new_vp8_decoder(width: u32, height: u32,) -> Result<Decoder, Error> {
51/// // Prepare the configuration
52/// let config = DecoderConfig::new(
53/// vpx_rs::dec::CodecId::VP8,
54/// width,
55/// height,
56/// );
57/// // Instantiate the decoder
58/// Decoder::new(config)
59/// }
60///
61/// // Passes a buffer to the decoder and extracts any completed frames
62/// fn decode_vp8_buf(
63/// decoder: &mut Decoder,
64/// stream_data: &[u8],
65/// ) -> Result<(), Error> {
66/// for decoded_image in decoder.decode(stream_data)?.into_iter() {
67/// let (format, bit_depth) = match decoded_image.data() {
68/// DecodedImageData::Data8b(data) =>
69/// (format!("{:?}", data.format()), 8),
70/// DecodedImageData::Data16b(data) =>
71/// (format!("{:?}", data.format()), 16),
72/// };
73/// println!(
74/// "Decoded a {}x{} pixel image in {} format with bit depth {}",
75/// decoded_image.width(),
76/// decoded_image.height(),
77/// format,
78/// bit_depth,
79/// );
80/// }
81/// Ok(())
82/// }
83/// ```
84#[derive(Debug)]
85pub struct Decoder {
86 ctx: NoDebugWrapper<vpx_sys::vpx_codec_ctx_t>,
87}
88
89// We are Send-safe, because we don't hold onto any mutexes between calls
90// and no functions are async. Also, we have no interior mutability through
91// immutable references.
92unsafe impl Send for Decoder {}
93unsafe impl Sync for Decoder {}
94
95impl Decoder {
96 /// Constructs a new decoder for the given configuration. After
97 /// successful construction, you can start sending bitstream buffers
98 /// to the decoder by calling [`Decoder::decode()`].
99 pub fn new(config: DecoderConfig) -> Result<Self> {
100 let mut ctx = unsafe { MaybeUninit::zeroed().assume_init() };
101 let (iface, cfg) = config.cfg()?;
102 call_vpx!(
103 vpx_sys::vpx_codec_dec_init_ver(
104 &mut ctx,
105 iface,
106 &cfg,
107 config.flags.bits() as _,
108 vpx_sys::VPX_DECODER_ABI_VERSION as _,
109 ),
110 Error::VpxCodecInitFailed,
111 )?;
112 Ok(Self {
113 ctx: NoDebugWrapper(ctx),
114 })
115 }
116 /// Submits data to be decoded by the decoder, yielding a number of
117 /// decoded images as a result. Please note that even if you only send
118 /// data for a single frame at a time, due to internal bitstream frame
119 /// rendering, the decoder might produce any number of frames per
120 /// `decode` call. You should retrieve all decoded images from the
121 /// returned iterator before trying to push more data to the decoder.
122 /// The images will always be returned to you in presentation order,
123 /// so there's no need to perform image reordering on your end.
124 pub fn decode(&mut self, data: &[u8]) -> Result<DecodedImageIterator> {
125 call_vpx!(
126 vpx_sys::vpx_codec_decode(
127 &mut self.ctx.0,
128 data.as_ptr(),
129 data.len() as _,
130 std::ptr::null_mut(),
131 0,
132 ),
133 Error::ErrorDecodingBitstream,
134 )?;
135 Ok(DecodedImageIterator {
136 ctx: &mut self.ctx.0,
137 iter: std::ptr::null_mut(),
138 })
139 }
140 /// Returns stream information for the decoder. This might not return
141 /// stream information initially, especially before sending any data
142 /// to the decoder.
143 pub fn stream_info(&mut self) -> Option<StreamInfo> {
144 crate::common::get_stream_info(&mut self.ctx.0)
145 }
146}
147
148impl Drop for Decoder {
149 fn drop(&mut self) {
150 // We unwrap here, because the only documented cases for this to fail
151 // are when passing a null pointer (which we cannot do) and the context
152 // not being initialized (which by design cannot happen in Rust).
153 unsafe {
154 let error = vpx_sys::vpx_codec_destroy(&mut self.ctx.0);
155 assert_eq!(error, vpx_sys::VPX_CODEC_OK);
156 }
157 }
158}
159
160/// The configuration for a new decoder. You must pass this to
161/// [`Decoder::new()`].
162#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
163pub struct DecoderConfig {
164 /// The codec to initialize in the decoder.
165 pub codec: CodecId,
166 /// The initial width of the decoder in pixels. Please note that
167 /// VPx streams can change resolution dynamically, so this is only
168 /// an initial hint to the decoder and you should look at the
169 /// decoded images to follow a potential resolution change.
170 pub width: u32,
171 /// The initial height of the decoder in pixels. Please note that
172 /// VPx streams can change resolution dynamically, so this is only
173 /// an initial hint to the decoder and you should look at the
174 /// decoded images to follow a potential resolution change.
175 pub height: u32,
176 /// Maximum number of threads to use, default 1. Zero is ignored and
177 /// treated as 1.
178 pub threads: u32,
179 /// Flags to set on the decoder.
180 pub flags: DecoderFlags,
181}
182
183impl DecoderConfig {
184 /// Constructs a new decoder configuration.
185 /// Note: `width` and `height` are in pixels.
186 pub fn new(codec: CodecId, width: u32, height: u32) -> Self {
187 Self {
188 codec,
189 width,
190 height,
191 threads: 1,
192 flags: DecoderFlags::default(),
193 }
194 }
195 fn cfg(
196 &self,
197 ) -> Result<(*const vpx_sys::vpx_codec_iface, vpx_sys::vpx_codec_dec_cfg)>
198 {
199 Ok((
200 self.codec.iface()?,
201 vpx_sys::vpx_codec_dec_cfg {
202 w: self.width,
203 h: self.height,
204 threads: self.threads,
205 },
206 ))
207 }
208}
209
210bitflags! {
211 /// Configuration flags for the decoder.
212 #[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
213 pub struct DecoderFlags: u32 {
214 /// Postprocess the decoded frame
215 const POSTPROC = vpx_sys::VPX_CODEC_USE_POSTPROC;
216 /// Conceal errors in decoded frames
217 const ERROR_CONCEALMENT = vpx_sys::VPX_CODEC_USE_ERROR_CONCEALMENT;
218 /// The input frame should be passed to the decoder one fragment
219 /// at a time
220 const INPUT_FRAGMENTS = vpx_sys::VPX_CODEC_USE_INPUT_FRAGMENTS;
221 /// Enable frame-based multi-threading
222 const FRAME_THREADING = vpx_sys::VPX_CODEC_USE_FRAME_THREADING;
223 }
224}
225
226/// An iterator returned from a call to [`Decoder::decode`]. Due to frame
227/// reordering, the decoder can return any number of frames for a single
228/// `decode` call. You should retrieve all decoded images from this iterator
229/// before trying to push more data to the decoder. The images will always
230/// be returned to you in presentation order, so there's no need to perform
231/// image reordering on your end.
232pub struct DecodedImageIterator<'a> {
233 ctx: &'a mut vpx_sys::vpx_codec_ctx,
234 iter: vpx_sys::vpx_codec_iter_t,
235}
236
237// This is send-safe, because we don't hold onto any mutexes between calls
238// and none of our functions are async. Also, we have no interior mutability
239// through immutable references, so we're Sync-safe.
240unsafe impl Send for DecodedImageIterator<'_> {}
241unsafe impl Sync for DecodedImageIterator<'_> {}
242
243impl Iterator for DecodedImageIterator<'_> {
244 type Item = DecodedImage;
245 fn next(&mut self) -> Option<Self::Item> {
246 unsafe {
247 let img = vpx_sys::vpx_codec_get_frame(self.ctx, &mut self.iter);
248 Some(DecodedImage {
249 img: NonNull::new(img)?,
250 })
251 }
252 }
253}
254
255/// Data structure owning a decoded image from a VPx video stream.
256/// Call [`DecodedImage::data()`] to access the image data.
257#[derive(Debug, PartialEq, Eq, Hash)]
258pub struct DecodedImage {
259 // We own the vpx_image, so during drop, we free the image.
260 img: NonNull<vpx_sys::vpx_image>,
261}
262
263// A decoded image is both send and sync safe, because it is completely
264// owned by us and holds no mutexes for access.
265unsafe impl Send for DecodedImage {}
266unsafe impl Sync for DecodedImage {}
267
268/// Encapsulates the specific pixel type returned in the decoded image.
269/// libvpx will return [`u16`] pixel data from high-bit-depth (10-bit
270/// and 12-bit) video streams.
271#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
272pub enum DecodedImageData<'a> {
273 /// Underlying data is 8-bits per sample. This variant is generated
274 /// for a normal bit depth (8-bit) video stream.
275 Data8b(YUVImageData<'a, u8>),
276 /// Underlying data is 16-bits per sample. This variant is generated
277 /// for a high bit depth (10-bit/12-bit) video stream.
278 Data16b(YUVImageData<'a, u16>),
279}
280
281impl DecodedImage {
282 /// Returns the color space of the decoded image.
283 pub fn color_space(&self) -> vpx_sys::vpx_color_space {
284 unsafe { self.img.as_ref().cs }
285 }
286 /// Returns the display width of the decoded image in pixels.
287 pub fn width(&self) -> u32 {
288 unsafe { self.img.as_ref().d_w }
289 }
290 /// Returns the display height of the decoded image in pixels.
291 pub fn height(&self) -> u32 {
292 unsafe { self.img.as_ref().d_h }
293 }
294 /// Allows access to the decoded image data.
295 pub fn data(&self) -> DecodedImageData {
296 unsafe {
297 use vpx_sys::vpx_img_fmt::*;
298 let img = self.img.as_ref();
299 match img.fmt {
300 VPX_IMG_FMT_YV12 | VPX_IMG_FMT_NV12 | VPX_IMG_FMT_I420
301 | VPX_IMG_FMT_I422 | VPX_IMG_FMT_I444 | VPX_IMG_FMT_I440 => {
302 DecodedImageData::Data8b(
303 YUVImageData::<u8>::from_vpx_image(img),
304 )
305 }
306 VPX_IMG_FMT_I42016 | VPX_IMG_FMT_I42216
307 | VPX_IMG_FMT_I44416 | VPX_IMG_FMT_I44016 => {
308 DecodedImageData::Data16b(
309 YUVImageData::<u16>::from_vpx_image(img),
310 )
311 }
312 _ => unreachable!(
313 "libvpx returned invalid image format: {:?}",
314 img.fmt,
315 ),
316 }
317 }
318 }
319}
320
321impl Drop for DecodedImage {
322 fn drop(&mut self) {
323 unsafe { vpx_sys::vpx_img_free(self.img.as_ptr()) }
324 }
325}
326
327#[cfg(test)]
328mod test {
329 use super::{
330 DecodedImage, DecodedImageIterator, Decoder, DecoderConfig,
331 DecoderFlags,
332 };
333 #[test]
334 fn test_send() {
335 fn assert_send<T: Send>() {}
336 assert_send::<Decoder>();
337 assert_send::<DecoderConfig>();
338 assert_send::<DecoderFlags>();
339 assert_send::<DecodedImage>();
340 assert_send::<DecodedImageIterator>();
341 }
342 #[test]
343 fn test_sync() {
344 fn assert_sync<T: Sync>() {}
345 assert_sync::<Decoder>();
346 assert_sync::<DecoderConfig>();
347 assert_sync::<DecoderFlags>();
348 assert_sync::<DecodedImage>();
349 assert_sync::<DecodedImageIterator>();
350 }
351}