moonfire_ffmpeg/
avcodec.rs

1// Copyright (C) 2017-2020 Scott Lamb <slamb@slamb.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::avutil::{
5    moonfire_ffmpeg_frame_stuff, AVFrame, Dictionary, Error, ImageDimensions, MediaType,
6    PixelFormat, Rational, VideoFrame,
7};
8use std::cell::Ref;
9use std::ptr;
10
11//#[link(name = "avcodec")]
12extern "C" {
13    pub(crate) fn avcodec_version() -> libc::c_int;
14    pub(crate) fn avcodec_configuration() -> *mut libc::c_char;
15    fn avcodec_alloc_context3(codec: *const AVCodec) -> *mut AVCodecContext;
16    fn avcodec_decode_video2(
17        ctx: *const AVCodecContext,
18        picture: *mut AVFrame,
19        got_picture_ptr: *mut libc::c_int,
20        pkt: *const AVPacket,
21    ) -> libc::c_int;
22    fn avcodec_get_name(codec_id: libc::c_int) -> *const libc::c_char;
23    fn avcodec_find_decoder(codec_id: libc::c_int) -> *const AVCodec;
24    fn avcodec_find_encoder(codec_id: libc::c_int) -> *const AVCodec;
25    fn avcodec_free_context(ctx: *mut *mut AVCodecContext);
26    fn avcodec_open2(
27        ctx: *mut AVCodecContext,
28        codec: *const AVCodec,
29        options: *mut crate::avutil::Dictionary,
30    ) -> libc::c_int;
31    fn avcodec_parameters_to_context(
32        ctx: *mut AVCodecContext,
33        par: *const AVCodecParameters,
34    ) -> libc::c_int;
35    pub(crate) fn av_init_packet(p: *mut AVPacket);
36    fn av_packet_unref(p: *mut AVPacket);
37}
38
39//#[link(name = "wrapper")]
40extern "C" {
41    pub(crate) static moonfire_ffmpeg_compiled_libavcodec_version: libc::c_int;
42
43    static moonfire_ffmpeg_av_codec_id_aac: libc::c_int;
44    static moonfire_ffmpeg_av_codec_id_h264: libc::c_int;
45
46    fn moonfire_ffmpeg_codecpar_codec_id(ctx: *const AVCodecParameters) -> CodecId;
47    fn moonfire_ffmpeg_codecpar_codec_type(ctx: *const AVCodecParameters) -> MediaType;
48    fn moonfire_ffmpeg_codecpar_dims(ctx: *const AVCodecParameters) -> ImageDimensions;
49    fn moonfire_ffmpeg_codecpar_extradata(ctx: *const AVCodecParameters) -> DataLen;
50
51    fn moonfire_ffmpeg_cctx_codec_id(ctx: *const AVCodecContext) -> CodecId;
52    fn moonfire_ffmpeg_cctx_codec_type(ctx: *const AVCodecContext) -> MediaType;
53    fn moonfire_ffmpeg_cctx_pix_fmt(ctx: *const AVCodecContext) -> PixelFormat;
54    fn moonfire_ffmpeg_cctx_height(ctx: *const AVCodecContext) -> libc::c_int;
55    fn moonfire_ffmpeg_cctx_width(ctx: *const AVCodecContext) -> libc::c_int;
56    fn moonfire_ffmpeg_cctx_params(ctx: *const AVCodecContext, p: *mut VideoParameters);
57    fn moonfire_ffmpeg_cctx_set_params(ctx: *mut AVCodecContext, p: *const VideoParameters);
58
59    pub(crate) fn moonfire_ffmpeg_packet_alloc() -> *mut AVPacket;
60    pub(crate) fn moonfire_ffmpeg_packet_free(p: *mut AVPacket);
61    fn moonfire_ffmpeg_packet_is_key(p: *const AVPacket) -> bool;
62    fn moonfire_ffmpeg_packet_pts(p: *const AVPacket) -> i64;
63    fn moonfire_ffmpeg_packet_dts(p: *const AVPacket) -> i64;
64    fn moonfire_ffmpeg_packet_duration(p: *const AVPacket) -> libc::c_int;
65    fn moonfire_ffmpeg_packet_set_pts(p: *mut AVPacket, pts: i64);
66    fn moonfire_ffmpeg_packet_set_dts(p: *mut AVPacket, dts: i64);
67    fn moonfire_ffmpeg_packet_set_duration(p: *mut AVPacket, dur: libc::c_int);
68    fn moonfire_ffmpeg_packet_data(p: *const AVPacket) -> DataLen;
69    fn moonfire_ffmpeg_packet_stream_index(p: *const AVPacket) -> libc::c_uint;
70}
71
72// No ABI stability assumption here; use heap allocation/deallocation and accessors only.
73#[repr(C)]
74struct AVCodec {
75    _private: [u8; 0],
76}
77#[repr(C)]
78pub struct AVCodecContext {
79    _private: [u8; 0],
80}
81#[repr(C)]
82pub struct AVCodecParameters {
83    _private: [u8; 0],
84}
85#[repr(C)]
86pub(crate) struct AVPacket {
87    _private: [u8; 0],
88}
89
90impl AVCodecContext {
91    pub fn width(&self) -> libc::c_int {
92        unsafe { moonfire_ffmpeg_cctx_width(self) }
93    }
94    pub fn height(&self) -> libc::c_int {
95        unsafe { moonfire_ffmpeg_cctx_height(self) }
96    }
97    pub fn pix_fmt(&self) -> PixelFormat {
98        unsafe { moonfire_ffmpeg_cctx_pix_fmt(self) }
99    }
100    pub fn codec_id(&self) -> CodecId {
101        unsafe { moonfire_ffmpeg_cctx_codec_id(self) }
102    }
103    pub fn codec_type(&self) -> MediaType {
104        unsafe { moonfire_ffmpeg_cctx_codec_type(self) }
105    }
106    pub fn params(&self) -> VideoParameters {
107        let mut p = std::mem::MaybeUninit::uninit();
108        unsafe {
109            moonfire_ffmpeg_cctx_params(self, p.as_mut_ptr());
110            p.assume_init()
111        }
112    }
113}
114
115// matches moonfire_ffmpeg_data_len
116#[repr(C)]
117struct DataLen {
118    data: *const u8,
119    len: libc::size_t,
120}
121
122pub struct Packet<'i>(pub(crate) Ref<'i, *mut AVPacket>);
123
124impl<'i> Packet<'i> {
125    pub fn is_key(&self) -> bool {
126        unsafe { moonfire_ffmpeg_packet_is_key(*self.0) }
127    }
128    pub fn pts(&self) -> Option<i64> {
129        match unsafe { moonfire_ffmpeg_packet_pts(*self.0) } {
130            v if v == unsafe { crate::avutil::moonfire_ffmpeg_av_nopts_value } => None,
131            v => Some(v),
132        }
133    }
134    pub fn set_pts(&mut self, pts: Option<i64>) {
135        let real_pts = match pts {
136            None => unsafe { crate::avutil::moonfire_ffmpeg_av_nopts_value },
137            Some(v) => v,
138        };
139        unsafe {
140            moonfire_ffmpeg_packet_set_pts(*self.0, real_pts);
141        }
142    }
143    pub fn dts(&self) -> i64 {
144        unsafe { moonfire_ffmpeg_packet_dts(*self.0) }
145    }
146    pub fn set_dts(&mut self, dts: i64) {
147        unsafe {
148            moonfire_ffmpeg_packet_set_dts(*self.0, dts);
149        }
150    }
151    pub fn duration(&self) -> i32 {
152        unsafe { moonfire_ffmpeg_packet_duration(*self.0) }
153    }
154    pub fn set_duration(&mut self, dur: i32) {
155        unsafe { moonfire_ffmpeg_packet_set_duration(*self.0, dur) }
156    }
157    pub fn stream_index(&self) -> usize {
158        unsafe { moonfire_ffmpeg_packet_stream_index(*self.0) as usize }
159    }
160    pub fn data(&self) -> Option<&[u8]> {
161        unsafe {
162            let d = moonfire_ffmpeg_packet_data(*self.0);
163            if d.data.is_null() {
164                None
165            } else {
166                Some(::std::slice::from_raw_parts(d.data, d.len))
167            }
168        }
169    }
170}
171
172impl<'i> Drop for Packet<'i> {
173    fn drop(&mut self) {
174        unsafe {
175            av_packet_unref(*self.0);
176        }
177    }
178}
179
180impl AVCodecParameters {
181    pub fn extradata(&self) -> &[u8] {
182        unsafe {
183            let d = moonfire_ffmpeg_codecpar_extradata(self);
184            ::std::slice::from_raw_parts(d.data, d.len)
185        }
186    }
187    pub fn dims(&self) -> ImageDimensions {
188        assert!(self.codec_type().is_video());
189        unsafe { moonfire_ffmpeg_codecpar_dims(self) }
190    }
191    pub fn codec_id(&self) -> CodecId {
192        unsafe { moonfire_ffmpeg_codecpar_codec_id(self) }
193    }
194    pub fn codec_type(&self) -> MediaType {
195        unsafe { moonfire_ffmpeg_codecpar_codec_type(self) }
196    }
197}
198
199pub struct InputCodecParameters<'s>(pub(crate) &'s AVCodecParameters);
200
201impl<'s> InputCodecParameters<'s> {
202    pub fn new_decoder(&self, options: &mut Dictionary) -> Result<DecodeContext, Error> {
203        let decoder = match self.codec_id().find_decoder() {
204            Some(d) => d,
205            None => {
206                return Err(Error::decoder_not_found());
207            }
208        };
209        let mut c = decoder.alloc_context()?;
210        Error::wrap(unsafe { avcodec_parameters_to_context(c.ctx.as_ptr(), self.0) })?;
211        c.open(options)?;
212        Ok(c)
213    }
214}
215
216impl<'s> std::ops::Deref for InputCodecParameters<'s> {
217    type Target = AVCodecParameters;
218    fn deref(&self) -> &AVCodecParameters {
219        self.0
220    }
221}
222
223#[derive(Copy, Clone)]
224#[repr(transparent)]
225pub struct CodecId(libc::c_int);
226
227impl CodecId {
228    pub fn is_aac(self) -> bool {
229        self.0 == unsafe { moonfire_ffmpeg_av_codec_id_aac }
230    }
231
232    pub fn is_h264(self) -> bool {
233        self.0 == unsafe { moonfire_ffmpeg_av_codec_id_h264 }
234    }
235
236    pub fn find_decoder(self) -> Option<Decoder> {
237        // avcodec_find_decoder returns an AVCodec which lives forever.
238        unsafe { avcodec_find_decoder(self.0).as_ref() }.map(|d| Decoder(d))
239    }
240
241    pub fn find_encoder(self) -> Option<Encoder> {
242        // avcodec_find_encoder returns an AVCodec which lives forever.
243        unsafe { avcodec_find_encoder(self.0).as_ref() }.map(|e| Encoder(e))
244    }
245}
246
247impl std::fmt::Debug for CodecId {
248    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
249        write!(f, "CodecId({} /* {} */)", self.0, self)
250    }
251}
252
253impl std::fmt::Display for CodecId {
254    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
255        let s = unsafe { std::ffi::CStr::from_ptr(avcodec_get_name(self.0)) };
256        let s = s.to_str().map_err(|_| std::fmt::Error)?;
257        std::fmt::Display::fmt(s, f)
258    }
259}
260
261#[derive(Copy, Clone)]
262pub struct Decoder(&'static AVCodec);
263
264impl Decoder {
265    fn alloc_context(self) -> Result<DecodeContext, Error> {
266        let ctx = ptr::NonNull::new(unsafe { avcodec_alloc_context3(self.0) })
267            .ok_or_else(Error::enomem)?;
268        Ok(DecodeContext { decoder: self, ctx })
269    }
270}
271
272pub struct DecodeContext {
273    decoder: Decoder,
274    ctx: ptr::NonNull<AVCodecContext>,
275}
276
277impl Drop for DecodeContext {
278    fn drop(&mut self) {
279        let mut ctx = self.ctx.as_ptr();
280        unsafe { avcodec_free_context(&mut ctx) }
281    }
282}
283
284impl DecodeContext {
285    fn open(&mut self, options: &mut Dictionary) -> Result<(), Error> {
286        Error::wrap(unsafe { avcodec_open2(self.ctx.as_mut(), self.decoder.0, options) })?;
287        Ok(())
288    }
289
290    pub fn ctx(&self) -> &AVCodecContext {
291        unsafe { self.ctx.as_ref() }
292    }
293
294    pub fn decode_video(&self, pkt: &Packet, frame: &mut VideoFrame) -> Result<bool, Error> {
295        let mut got_picture: libc::c_int = 0;
296        Error::wrap(unsafe {
297            avcodec_decode_video2(
298                self.ctx.as_ptr(),
299                frame.frame.as_mut(),
300                &mut got_picture,
301                *pkt.0,
302            )
303        })?;
304        if got_picture != 0 {
305            unsafe { moonfire_ffmpeg_frame_stuff(frame.frame.as_ptr(), &mut frame.stuff) };
306            return Ok(true);
307        };
308        Ok(false)
309    }
310}
311
312#[derive(Copy, Clone)]
313pub struct Encoder(&'static AVCodec);
314
315impl Encoder {
316    /*pub fn alloc_context(self) -> Result<EncodeContext, Error> {
317        let ctx = unsafe { avcodec_alloc_context3(self.0) };
318        if ctx.is_null() {
319            return Err(Error::enomem());
320        }
321        Ok(EncodeContext {
322            encoder: self,
323            ctx,
324        })
325    }*/
326}
327
328pub struct EncodeContext<'a>(&'a mut AVCodecContext);
329
330/*impl Drop for EncodeContext {
331    fn drop(&mut self) {
332        unsafe { avcodec_free_context(&mut self.ctx) }
333    }
334}*/
335
336#[derive(Copy, Clone, Debug)]
337#[repr(C)]
338pub struct VideoParameters {
339    width: libc::c_int,
340    height: libc::c_int,
341    sample_aspect_ratio: Rational,
342    pix_fmt: PixelFormat,
343    time_base: Rational,
344}
345
346impl<'a> EncodeContext<'a> {
347    pub fn set_params(&mut self, p: &VideoParameters) {
348        unsafe { moonfire_ffmpeg_cctx_set_params(self.0, p) };
349    }
350
351    pub fn open(&mut self, encoder: Encoder, options: &mut Dictionary) -> Result<(), Error> {
352        Error::wrap(unsafe { avcodec_open2(self.0, encoder.0, options) })?;
353        Ok(())
354    }
355}