av_decoder/
lib.rs

1//! FFMPEG's AV Decoder
2
3use ::core::ffi::c_void;
4use ::core::ops::{Deref, DerefMut};
5use ::core::{ptr, slice};
6use c_str_macro::c_str;
7use ffmpeg_sys_next::*;
8use libc::c_int;
9use log::*;
10use nalgebra as na;
11use ofps::prelude::v1::{ptrplus::*, Result, *};
12use ofps::utils::*;
13use std::io::*;
14use std::mem::MaybeUninit;
15
16ofps::define_descriptor!(av, Decoder, |input| {
17    let f = open_file(&input)?;
18    AvDecoder::try_new(f).map(|d| Box::new(d) as _)
19});
20
21pub struct AvBuf(&'static mut [u8]);
22
23impl AvBuf {
24    pub fn try_new(size: usize) -> Result<Self> {
25        let buf = unsafe { av_malloc(size) as *mut u8 };
26
27        if buf.is_null() {
28            Err(anyhow!("Failed to allocate buffer"))
29        } else {
30            Ok(Self(unsafe { slice::from_raw_parts_mut(buf, size) }))
31        }
32    }
33}
34
35impl Drop for AvBuf {
36    fn drop(&mut self) {
37        unsafe { av_free(self.0.as_mut_ptr() as *mut _) }
38    }
39}
40
41impl Deref for AvBuf {
42    type Target = [u8];
43
44    fn deref(&self) -> &[u8] {
45        self.0
46    }
47}
48
49impl DerefMut for AvBuf {
50    fn deref_mut(&mut self) -> &mut [u8] {
51        self.0
52    }
53}
54
55pub struct AvContext<T: ?Sized> {
56    #[allow(clippy::redundant_allocation)]
57    _stream: Box<Box<T>>,
58    pub fmt_ctx: &'static mut AVFormatContext,
59    pub avio_ctx: &'static mut AVIOContext,
60}
61
62impl<T: ?Sized> Drop for AvContext<T> {
63    fn drop(&mut self) {
64        // SAFETY: the references will be dangling,
65        // but after the drop nobody will read them.
66        unsafe {
67            avformat_close_input(&mut (self.fmt_ctx as *mut _));
68            av_free((*self.avio_ctx).buffer as *mut _);
69            av_free(self.avio_ctx as *mut _ as *mut c_void);
70        }
71    }
72}
73
74impl<T: Read + ?Sized> AvContext<T> {
75    pub fn try_new(stream: Box<T>) -> Result<Self> {
76        let mut buf = AvBuf::try_new(8196)?;
77
78        let mut stream: Box<Box<T>> = stream.into();
79
80        // SAFETY: Box<T> stream is being passed, which is the expected stream type in the
81        // read_callback function.
82        let avio_ctx = unsafe {
83            avio_alloc_context(
84                buf.as_mut_ptr(),
85                buf.len() as _,
86                0,
87                (&mut *stream) as *mut Box<T> as *mut _,
88                Some(Self::read_callback),
89                None,
90                None,
91            )
92            .as_mut()
93        }
94        .ok_or_else(|| anyhow!("Failed to allocate AVIOContext"))?;
95
96        let mut fmt_ctx = unsafe { avformat_alloc_context().as_mut() }.ok_or_else(|| {
97            unsafe { av_free((*avio_ctx).buffer as *mut _) };
98            unsafe { av_free(avio_ctx as *mut AVIOContext as *mut _) };
99            anyhow!("Failed to allocate AVFormatContext")
100        })?;
101
102        fmt_ctx.pb = avio_ctx;
103
104        match unsafe {
105            avformat_open_input(
106                fmt_ctx.as_mut_ptr(),
107                ptr::null(),
108                ptr::null_mut(),
109                ptr::null_mut(),
110            )
111        } {
112            0 => {
113                std::mem::forget(buf);
114                Ok(Self {
115                    _stream: stream,
116                    fmt_ctx,
117                    avio_ctx,
118                })
119            }
120            e => {
121                unsafe { av_free((*avio_ctx).buffer as *mut _) };
122                unsafe { av_free(avio_ctx as *mut AVIOContext as *mut _) };
123                Err(anyhow!("Unable to open input ({:x})", e))
124            }
125        }
126    }
127
128    unsafe extern "C" fn read_callback(
129        opaque: *mut c_void,
130        buf: *mut u8,
131        buf_size: c_int,
132    ) -> c_int {
133        let ret = (*(opaque as *mut Box<T>))
134            .read(slice::from_raw_parts_mut(buf, buf_size as _))
135            .map(|r| r as c_int)
136            .map(|r| {
137                trace!("{}", r);
138                r
139            })
140            .map_err(|e| {
141                error!("{}", e);
142                e
143            })
144            .unwrap_or(-1);
145
146        // We do not allow files that get appended at the end
147        if ret == 0 && buf_size != 0 {
148            -1
149        } else {
150            ret
151        }
152    }
153
154    pub fn dump_format(&mut self) {
155        unsafe { av_dump_format(self.fmt_ctx, 0, std::ptr::null(), 0) };
156    }
157}
158
159pub struct AvDecoder<T: ?Sized> {
160    pub av_ctx: AvContext<T>,
161    codec_ctx: &'static mut AVCodecContext,
162    av_frame: &'static mut AVFrame,
163    stream_idx: i32,
164    framerate: f64,
165    aspect_ratio: (usize, usize),
166    sws_av_frame: &'static mut AVFrame,
167    sws_ctx: Option<&'static mut SwsContext>,
168}
169
170impl<T: ?Sized> Properties for AvDecoder<T> {}
171
172unsafe impl<T: Send + ?Sized> Send for AvDecoder<T> {}
173unsafe impl<T: Sync + ?Sized> Sync for AvDecoder<T> {}
174
175impl<T: ?Sized> Drop for AvDecoder<T> {
176    fn drop(&mut self) {
177        // SAFETY: the references will be dangling,
178        // but after the drop nobody will read them.
179        unsafe {
180            av_frame_free(&mut (self.av_frame as *mut _));
181            avcodec_free_context(&mut (self.codec_ctx as *mut _));
182            av_frame_free(&mut (self.sws_av_frame as *mut _));
183            if let Some(sws_ctx) = self.sws_ctx.take() {
184                sws_freeContext(sws_ctx);
185            }
186        };
187    }
188}
189
190struct RefFrame<'a> {
191    frame: &'a mut AVFrame,
192}
193
194impl<'a> Drop for RefFrame<'a> {
195    fn drop(&mut self) {
196        unsafe { av_frame_unref(self.frame) };
197    }
198}
199
200impl<'a> RefFrame<'a> {
201    fn new(codec_ctx: &mut AVCodecContext, frame: &'a mut AVFrame) -> Result<Option<Self>> {
202        match unsafe { avcodec_receive_frame(codec_ctx, frame) } {
203            // TODO: match non-fatal errors
204            -11 => Ok(None),
205            e if e < 0 => return Err(anyhow!("Failed to recv frame ({})", e)),
206            _ => Ok(Some(Self { frame })),
207        }
208    }
209}
210
211impl<'a> Deref for RefFrame<'a> {
212    type Target = AVFrame;
213
214    fn deref(&self) -> &Self::Target {
215        self.frame
216    }
217}
218
219impl<'a> DerefMut for RefFrame<'a> {
220    fn deref_mut(&mut self) -> &mut Self::Target {
221        self.frame
222    }
223}
224
225impl<T: Read + ?Sized> AvDecoder<T> {
226    pub fn try_new(stream: Box<T>) -> Result<Self> {
227        let av_ctx = AvContext::try_new(stream)?;
228
229        let mut decoder: Option<&mut AVCodec> = None;
230
231        let stream_idx = match unsafe {
232            av_find_best_stream(
233                av_ctx.fmt_ctx,
234                AVMediaType::AVMEDIA_TYPE_VIDEO,
235                -1,
236                -1,
237                decoder.as_mut_ptr() as *mut _ as *mut *const AVCodec,
238                0,
239            )
240        } {
241            e if e < 0 => Err(anyhow!("Failed to find a stream ({})", e)),
242            i => Ok(i),
243        }?;
244
245        let mut codec_ctx = unsafe { avcodec_alloc_context3(decoder.as_deref().as_ptr()).as_mut() }
246            .ok_or_else(|| anyhow!("Failed to allocate codec context"))?;
247
248        let stream = unsafe { (*av_ctx.fmt_ctx.streams.offset(stream_idx as _)).as_mut() }
249            .ok_or_else(|| anyhow!("Stream info null"))?;
250
251        debug!("{:?}", stream);
252
253        let framerate = if stream.avg_frame_rate.den != 0 && stream.avg_frame_rate.num != 0 {
254            stream.avg_frame_rate.num as f64 / stream.avg_frame_rate.den as f64
255        } else {
256            stream.time_base.den as f64 / stream.time_base.num as f64
257        };
258
259        match unsafe { avcodec_parameters_to_context(codec_ctx, stream.codecpar) } {
260            e if e < 0 => {
261                unsafe { avcodec_free_context(codec_ctx.as_mut_ptr()) };
262                return Err(anyhow!("Failed to get codec parameters ({})", e));
263            }
264            _ => {}
265        }
266
267        let mut av_opts: Option<&mut AVDictionary> = None;
268
269        unsafe {
270            av_dict_set(
271                av_opts.as_mut_ptr(),
272                c_str!("flags2").as_ptr(),
273                c_str!("+export_mvs").as_ptr(),
274                0,
275            );
276        }
277
278        match unsafe { avcodec_open2(codec_ctx, decoder.as_deref().as_ptr(), av_opts.as_mut_ptr()) }
279        {
280            e if e < 0 => {
281                unsafe { avcodec_free_context(codec_ctx.as_mut_ptr()) };
282                return Err(anyhow!("Failed to open codec ({})", e));
283            }
284            _ => {}
285        }
286
287        let av_frame = unsafe { av_frame_alloc().as_mut() }
288            .ok_or_else(|| anyhow!("Unable to allocate frame"))?;
289
290        let sws_av_frame = unsafe { av_frame_alloc().as_mut() }
291            .ok_or_else(|| anyhow!("Unable to allocate sws frame"))?;
292
293        debug!(
294            "{:x} {:x}",
295            codec_ctx.pix_fmt as usize,
296            AVPixelFormat::AV_PIX_FMT_RGBA as usize
297        );
298        debug!(
299            "{:?} {:?}",
300            unsafe { av_pix_fmt_desc_get(codec_ctx.pix_fmt) },
301            unsafe { av_pix_fmt_desc_get(AVPixelFormat::AV_PIX_FMT_RGBA) }
302        );
303
304        Ok(Self {
305            av_ctx,
306            codec_ctx,
307            av_frame,
308            stream_idx,
309            framerate,
310            aspect_ratio: (0, 0),
311            sws_av_frame,
312            sws_ctx: None,
313        })
314    }
315
316    // https://stackoverflow.com/questions/67828088/pyav-ffmpeg-libav-access-side-data-without-decoding-the-video
317    // For now we will decode the packets, but later we should be able to do it without a decoder.
318
319    // HEVC uses CTUs: https://en.wikipedia.org/wiki/Coding_tree_unit
320
321    pub fn extract_mvs(
322        &mut self,
323        packet: &mut AVPacket,
324        mf: &mut MotionVectors,
325        out_frame: Option<(&mut Vec<RGBA>, &mut usize)>,
326    ) -> Result<bool> {
327        match unsafe { avcodec_send_packet(self.codec_ctx, packet) } {
328            e if e < 0 => return Err(anyhow!("Failed to send packet ({})", e)),
329            _ => {}
330        }
331
332        if let Some(frame) = RefFrame::new(self.codec_ctx, self.av_frame)? {
333            self.aspect_ratio = (frame.width as usize, frame.height as usize);
334
335            if let Some((out_frame, out_height)) = out_frame {
336                out_frame.clear();
337
338                // https://stackoverflow.com/questions/41196429/ffmpeg-avframe-to-per-channel-array-conversion
339                let sws_ctx = match self.sws_ctx.take() {
340                    Some(ctx) => ctx,
341                    None => unsafe {
342                        let sws_frame = &mut self.sws_av_frame;
343
344                        sws_frame.format = AVPixelFormat::AV_PIX_FMT_RGBA as _;
345                        sws_frame.width = frame.width;
346                        sws_frame.height = frame.height;
347
348                        av_frame_get_buffer(*sws_frame, 32);
349
350                        sws_getContext(
351                            frame.width,
352                            frame.height,
353                            std::mem::transmute(frame.format),
354                            sws_frame.width,
355                            sws_frame.height,
356                            std::mem::transmute(sws_frame.format),
357                            0,
358                            ptr::null_mut(),
359                            ptr::null_mut(),
360                            ptr::null(),
361                        )
362                        .as_mut()
363                    }
364                    .ok_or_else(|| anyhow!("Unable to allocate sws context"))?,
365                };
366
367                let sws_frame = &mut self.sws_av_frame;
368
369                unsafe {
370                    sws_scale(
371                        sws_ctx,
372                        frame.data.as_ptr() as *const *const _,
373                        frame.linesize.as_ptr(),
374                        0,
375                        frame.height,
376                        sws_frame.data.as_mut_ptr(),
377                        sws_frame.linesize.as_mut_ptr(),
378                    )
379                };
380
381                let frame_data = sws_frame.data[0];
382                let linesize = sws_frame.linesize[0] as usize;
383                let width = sws_frame.width as usize;
384                *out_height = sws_frame.height as usize;
385                for y in 0..*out_height {
386                    let frame_slice =
387                        unsafe { slice::from_raw_parts(frame_data.add(linesize * y), width * 4) };
388                    for chunk in frame_slice.chunks_exact(4) {
389                        out_frame.push(RGBA::from_rgb_slice(chunk));
390                    }
391                }
392
393                self.sws_ctx = Some(sws_ctx);
394            }
395
396            if let Some(side_data) = unsafe {
397                av_frame_get_side_data(&*frame, AVFrameSideDataType::AV_FRAME_DATA_MOTION_VECTORS)
398                    .as_ref()
399            } {
400                let size = side_data.size as usize / std::mem::size_of::<AVMotionVector>();
401                let motion_vectors =
402                    unsafe { slice::from_raw_parts(side_data.data as *const AVMotionVector, size) };
403
404                let frame_norm =
405                    na::Vector2::new(1f32 / frame.width as f32, 1f32 / frame.height as f32);
406
407                // TODO: use dst or src?
408                for mv in motion_vectors {
409                    let pos = na::Vector2::new(mv.src_x as f32, mv.src_y as f32)
410                        .component_mul(&frame_norm)
411                        .into();
412                    let motion = na::Vector2::new(mv.motion_x as f32, mv.motion_y as f32)
413                        .component_div(&na::Vector2::new(
414                            mv.motion_scale as f32,
415                            mv.motion_scale as f32,
416                        ))
417                        .component_mul(&-frame_norm);
418
419                    mf.push((pos, motion));
420                }
421
422                Ok(true)
423            } else {
424                Ok(false)
425            }
426        } else {
427            Ok(false)
428        }
429    }
430}
431
432impl<T: Read + ?Sized> Decoder for AvDecoder<T> {
433    fn process_frame(
434        &mut self,
435        mf: &mut MotionVectors,
436        mut out_frame: Option<(&mut Vec<RGBA>, &mut usize)>,
437        mut skip: usize,
438    ) -> Result<bool> {
439        let mut packet = MaybeUninit::uninit();
440        let mut reached_stream = false;
441        let mut ret = false;
442
443        while !reached_stream || skip > 0 {
444            match unsafe { av_read_frame(self.av_ctx.fmt_ctx, packet.as_mut_ptr()) } {
445                e if e < 0 => return Err(anyhow!("Failed to read frame ({})", e)),
446                _ => {
447                    if skip > 0 {
448                        skip -= 1;
449                        if skip > 20 {
450                            continue;
451                        }
452                    }
453
454                    let packet = unsafe { packet.assume_init_mut() };
455
456                    trace!("Read packet: {} {}", packet.stream_index, packet.size);
457
458                    if packet.stream_index == self.stream_idx {
459                        debug!("Reached wanted stream!");
460                        reached_stream = true;
461                        ret = self.extract_mvs(packet, mf, out_frame.take())?;
462                    }
463
464                    unsafe { av_packet_unref(packet) };
465                }
466            }
467        }
468
469        Ok(ret)
470    }
471
472    fn get_framerate(&self) -> Option<f64> {
473        Some(self.framerate)
474    }
475
476    fn get_aspect(&self) -> Option<(usize, usize)> {
477        Some(self.aspect_ratio)
478    }
479}