Skip to content

Commit 9169bea

Browse files
Implement a decoder
1 parent c78bd18 commit 9169bea

File tree

4 files changed

+357
-0
lines changed

4 files changed

+357
-0
lines changed

build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ fn main() {
1111

1212
headers.push("libavcodec/avcodec.h");
1313
headers.push("libavutil/opt.h");
14+
headers.push("libavutil/mem.h");
15+
headers.push("libavutil/imgutils.h");
16+
headers.push("libavutil/pixdesc.h");
1417

1518
let lib1 = pkg_config::probe_library("libavcodec").expect("find libavcodec");
1619
let lib2 = pkg_config::probe_library("libavutil").expect("find libavutil");
@@ -45,8 +48,12 @@ fn main() {
4548
.allowlist_item("av_frame_.*")
4649
.allowlist_item("av_init_packet")
4750
.allowlist_item("av_packet_.*")
51+
.allowlist_item("av_buffer_.*")
4852
.allowlist_item("av_strerror")
4953
.allowlist_item("av_log_set_level")
54+
.allowlist_item("av_malloc")
55+
.allowlist_item("av_image_.*")
56+
.allowlist_item("av_pix_.*")
5057
.allowlist_item("log_to_string.*")
5158
.default_enum_style(EnumVariation::Rust {
5259
non_exhaustive: false,

src/decoder.rs

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
use std::ffi::c_void;
2+
use std::ptr;
3+
4+
use super::{
5+
av_log_set_callback, err_code_to_string, log_callback, set_log_level, sys, Codec, CodecKind,
6+
Error, FrameRef, PixelFormat,
7+
};
8+
9+
use tracing::Level;
10+
11+
pub struct Decoder {
12+
ctx: *mut sys::AVCodecContext,
13+
/// Maps rotation values to the PTS of the incoming packet.
14+
pts_map: PtsMap,
15+
}
16+
17+
unsafe impl Send for Decoder {}
18+
19+
struct PtsMap {
20+
map: [(i64, usize); 16],
21+
cur: usize,
22+
}
23+
24+
pub trait DecoderPacket {
25+
/// Returns
26+
fn data(&mut self) -> PacketData;
27+
fn pts(&self) -> i64;
28+
fn rotation(&self) -> usize;
29+
}
30+
31+
pub struct PacketData {
32+
inner: Box<[u8]>,
33+
}
34+
35+
/// A single frame of video or audio.
36+
pub struct DecodedFrame(*mut sys::AVFrame);
37+
38+
impl Decoder {
39+
/// Create a new decoder
40+
pub fn new(codec: &Codec) -> Result<Self, Error> {
41+
set_log_level(Level::DEBUG);
42+
unsafe {
43+
av_log_set_callback(Some(log_callback));
44+
}
45+
46+
if codec.kind() != CodecKind::Decoder {
47+
return Err(Error::CodecIsNotDecoder(codec.name));
48+
}
49+
50+
let codec = codec.ptr;
51+
let ctx: *mut sys::AVCodecContext = unsafe { sys::avcodec_alloc_context3(codec) };
52+
if ctx.is_null() {
53+
return Err(Error::CreateContextFailed);
54+
}
55+
56+
let dec = Decoder {
57+
ctx,
58+
pts_map: PtsMap::default(),
59+
};
60+
61+
// TODO: options
62+
63+
let err = unsafe { sys::avcodec_open2(ctx, codec, ptr::null_mut()) };
64+
if err < 0 {
65+
return Err(Error::CodecOpenError(err, err_code_to_string(err)));
66+
}
67+
68+
Ok(dec)
69+
}
70+
71+
/// Decode some compressed data.
72+
///
73+
/// Returns an iterator over the resulting frames.
74+
pub fn decode(
75+
&mut self,
76+
packet: &mut dyn DecoderPacket,
77+
) -> Result<impl Iterator<Item = Result<DecodedFrame, Error>> + '_, Error> {
78+
let mut pkt = unsafe {
79+
let pkt = sys::av_packet_alloc();
80+
if pkt.is_null() {
81+
return Err(Error::AlllocateFailed("av_malloc for Decoder::decode"));
82+
}
83+
84+
let data = packet.data();
85+
// The buffer used for the packet is required to have
86+
// `sys::AV_INPUT_BUFFER_PADDING_SIZE` padding bytes, this is guaranteed for us by
87+
// `PacketData`.
88+
let len = data.inner.len();
89+
// This is a fat pointer i.e. 2 words
90+
let data_ptr = Box::into_raw(data.inner);
91+
let buf = sys::av_buffer_create(
92+
data_ptr.cast(),
93+
// This might look useless, but depending on the version of libavcodec used it's
94+
// required.
95+
#[allow(clippy::useless_conversion)]
96+
len.try_into().unwrap(),
97+
Some(free_boxed_slice),
98+
// We store the length of the slice as the opaque data so we can re-create the fat
99+
// pointer for freeing in `free_boxed_slice`.
100+
len as *mut c_void,
101+
0,
102+
);
103+
assert!(!buf.is_null());
104+
(*pkt).buf = buf;
105+
(*pkt).data = data_ptr.cast();
106+
(*pkt).pts = packet.pts();
107+
// This should be the size of the data without the padding
108+
(*pkt).size = (len as i32) - sys::AV_INPUT_BUFFER_PADDING_SIZE as i32;
109+
110+
pkt
111+
};
112+
self.pts_map.set(packet.pts(), packet.rotation());
113+
let ret = unsafe { sys::avcodec_send_packet(self.ctx, pkt) };
114+
// Regardless of errors we are done with this packet, parts of the packet might have been
115+
// retained in the decoder.
116+
unsafe {
117+
sys::av_packet_free(&mut pkt);
118+
}
119+
if ret < 0 {
120+
return Err(Error::DecodePacketFailed(ret, err_code_to_string(ret)));
121+
}
122+
123+
Ok(DecoderIterator {
124+
dec: self,
125+
ended: false,
126+
})
127+
}
128+
129+
pub fn timebase(&self) -> u32 {
130+
unsafe {
131+
let num = (*self.ctx).time_base.num;
132+
let den = (*self.ctx).time_base.den;
133+
// Assumption here that numerator is 1
134+
assert_eq!(num, 1);
135+
assert!(den.is_positive());
136+
den as u32
137+
}
138+
}
139+
}
140+
141+
struct DecoderIterator<'a> {
142+
dec: &'a mut Decoder,
143+
ended: bool,
144+
}
145+
146+
impl<'a> Iterator for DecoderIterator<'a> {
147+
type Item = Result<DecodedFrame, Error>;
148+
149+
fn next(&mut self) -> Option<Self::Item> {
150+
if self.ended {
151+
return None;
152+
}
153+
154+
let frame = DecodedFrame::default();
155+
156+
let ret = unsafe { sys::avcodec_receive_frame(self.dec.ctx, frame.0) };
157+
if ret == sys::AVErrorEAgain || ret == sys::AVErrorEof {
158+
self.ended = true;
159+
return None;
160+
} else if ret < 0 {
161+
self.ended = true;
162+
return Some(Err(Error::ReceiveFrameFailed(ret, err_code_to_string(ret))));
163+
}
164+
unsafe {
165+
// This is a pointer but it's entirely opaque to libavcodec so we can use it to store
166+
// some arbitrary pointer sized data.
167+
(*frame.0).opaque = self.dec.pts_map.get(frame.pts()).unwrap_or(0) as *mut c_void;
168+
};
169+
170+
Some(Ok(frame))
171+
}
172+
}
173+
174+
impl PacketData {
175+
pub fn new(mut data: Vec<u8>) -> Self {
176+
data.extend_from_slice(&[0; sys::AV_INPUT_BUFFER_PADDING_SIZE as usize]);
177+
178+
Self {
179+
inner: data.into_boxed_slice(),
180+
}
181+
}
182+
}
183+
184+
impl From<&[u8]> for PacketData {
185+
fn from(value: &[u8]) -> Self {
186+
let new_size = value.len() + sys::AV_INPUT_BUFFER_PADDING_SIZE as usize;
187+
let mut vec = Vec::with_capacity(new_size);
188+
vec.extend_from_slice(value);
189+
190+
Self::new(vec)
191+
}
192+
}
193+
194+
impl DecodedFrame {
195+
/// Return the inner ptr for the frame.
196+
///
197+
/// ## Safety
198+
/// This pointer **MUST** eventually be passed back to [`Frame::from_raw`] to avoid leaking
199+
/// memory.
200+
pub unsafe fn into_raw(mut self) -> *mut c_void {
201+
let ptr = self.0;
202+
self.0 = ptr::null_mut();
203+
204+
ptr as *mut c_void
205+
}
206+
207+
/// Create a [`Frame`] from a raw pointer obtained from [`Frame::into_raw`].
208+
///
209+
/// ## Safety
210+
/// `ptr` **MUST** have been originally obtained from [`Frame::into_raw`]
211+
pub unsafe fn from_raw(ptr: *mut c_void) -> Self {
212+
assert!(!ptr.is_null());
213+
214+
Self(ptr.cast())
215+
}
216+
217+
/// The presentation timestamp for this frame.
218+
pub fn pts(&self) -> i64 {
219+
// SAFETY: The pointer is valid while self is alive.
220+
unsafe { (*self.0).pts }
221+
}
222+
223+
/// The rotation of the frame.
224+
pub fn rotation(&self) -> usize {
225+
// SAFETY: The pointer is valid while self is alive.
226+
unsafe { (*self.0).opaque as usize }
227+
}
228+
}
229+
230+
impl FrameRef for DecodedFrame {
231+
fn width(&self) -> usize {
232+
// SAFETY: The pointer is valid while self is alive.
233+
unsafe { (*self.0).width as usize }
234+
}
235+
236+
fn height(&self) -> usize {
237+
// SAFETY: The pointer is valid while self is alive.
238+
unsafe { (*self.0).height as usize }
239+
}
240+
241+
fn plane_count(&self) -> usize {
242+
// SAFETY: The pointer is valid while self is alive.
243+
unsafe {
244+
assert_eq!(
245+
(*self.0).format,
246+
PixelFormat::AV_PIX_FMT_YUV420P as i32,
247+
"Only YUV420P is supported"
248+
);
249+
let fmt = sys::av_pix_fmt_desc_get(PixelFormat::AV_PIX_FMT_YUV420P);
250+
(*fmt).nb_components.into()
251+
}
252+
}
253+
254+
fn get_plane(&self, i: usize) -> &[u8] {
255+
// SAFETY:
256+
// * The pointer is valid while self is alive.
257+
// * The value calculated for `len` is correct
258+
unsafe {
259+
assert_eq!(
260+
(*self.0).format,
261+
PixelFormat::AV_PIX_FMT_YUV420P as i32,
262+
"Only YUV420P is supported"
263+
);
264+
let ptr: *mut u8 = (*self.0).data[i];
265+
266+
let len = sys::av_image_get_linesize(
267+
PixelFormat::AV_PIX_FMT_YUV420P,
268+
self.width() as i32,
269+
i as i32,
270+
) * self.height() as i32;
271+
272+
std::slice::from_raw_parts(ptr, len as usize)
273+
}
274+
}
275+
276+
fn get_stride(&self, i: usize) -> usize {
277+
assert!(i < sys::AV_NUM_DATA_POINTERS as usize);
278+
279+
// SAFETY: The pointer is valid while self is alive.
280+
unsafe { (*self.0).linesize[i] as usize }
281+
}
282+
}
283+
284+
impl PtsMap {
285+
fn set(&mut self, pts: i64, value: usize) {
286+
self.map[self.cur] = (pts, value);
287+
self.cur = (self.cur + 1) % self.map.len();
288+
}
289+
290+
fn get(&self, pts: i64) -> Option<usize> {
291+
self.map
292+
.iter()
293+
.find(|(p, _)| *p == pts)
294+
.copied()
295+
.map(|(_, v)| v)
296+
}
297+
}
298+
299+
impl Default for DecodedFrame {
300+
fn default() -> Self {
301+
let ptr = unsafe { sys::av_frame_alloc() };
302+
assert!(!ptr.is_null());
303+
304+
Self(ptr)
305+
}
306+
}
307+
308+
impl Drop for DecodedFrame {
309+
fn drop(&mut self) {
310+
if self.0.is_null() {
311+
return;
312+
}
313+
314+
unsafe {
315+
sys::av_frame_free(&mut self.0);
316+
}
317+
}
318+
}
319+
320+
impl Default for PtsMap {
321+
fn default() -> Self {
322+
Self {
323+
map: [(-1, 0); 16],
324+
cur: 0,
325+
}
326+
}
327+
}
328+
329+
extern "C" fn free_boxed_slice(opaque: *mut c_void, data: *mut u8) {
330+
let len = opaque as usize;
331+
let ptr = std::ptr::slice_from_raw_parts_mut(data, len);
332+
333+
// SAFETY: The pointer was originally created from a Box<[u8]> and the length was that from
334+
// said boxed slice.
335+
let _ = unsafe { Box::from_raw(ptr) };
336+
}

src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub enum Error {
88
#[error("Codec is not an encoder: {0}")]
99
CodecIsNotEncoder(&'static str),
1010

11+
#[error("Codec is not a decoder: {0}")]
12+
CodecIsNotDecoder(&'static str),
13+
1114
#[error("Failed to avcodec_alloc_context3")]
1215
CreateContextFailed,
1316

@@ -20,6 +23,15 @@ pub enum Error {
2023
#[error("Failed to encode frame: {0} {1}")]
2124
EncodeFrameFailed(i32, String),
2225

26+
#[error("Failed to decode packet: {0} {1}")]
27+
DecodePacketFailed(i32, String),
28+
2329
#[error("Failed to receive encoded packet: {0} {1}")]
2430
ReceivePacketFailed(i32, String),
31+
32+
#[error("Failed to receive decoded frame: {0} {1}")]
33+
ReceiveFrameFailed(i32, String),
34+
35+
#[error("Failed to allocate memory: {0}")]
36+
AlllocateFailed(&'static str),
2537
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use sys::AVPixelFormat as PixelFormat;
99

1010
mod encoder;
1111
pub use encoder::{Encoder, EncoderConfig, EncoderProfile, PacketProducer};
12+
mod decoder;
13+
pub use decoder::{DecodedFrame, Decoder, DecoderPacket, PacketData};
1214
mod error;
1315
pub use error::Error;
1416

0 commit comments

Comments
 (0)