Skip to main content

ez_ffmpeg/core/
codec.rs

1use ffmpeg_sys_next::{
2    av_codec_is_decoder, av_codec_is_encoder, av_codec_iterate, avcodec_descriptor_next,
3    AVCodecDescriptor, AVCodecID, AVMediaType,
4};
5use std::ffi::{c_void, CStr};
6use std::ptr::{null, null_mut};
7
8#[derive(Clone)]
9pub(crate) struct Codec {
10    inner: *const ffmpeg_sys_next::AVCodec,
11}
12// SAFETY: inner points at FFmpeg's static, immutable codec registry
13// (avcodec_find_*): valid for the process lifetime, never written.
14unsafe impl Send for Codec {}
15
16impl Codec {
17    #[inline]
18    pub(crate) fn null() -> Self {
19        Self { inner: null() }
20    }
21    #[inline]
22    pub(crate) fn is_null(&self) -> bool {
23        self.inner.is_null()
24    }
25
26    #[inline]
27    pub(crate) fn new(avcodec: *const ffmpeg_sys_next::AVCodec) -> Self {
28        Self { inner: avcodec }
29    }
30
31    #[inline]
32    pub(crate) fn as_ptr(&self) -> *const ffmpeg_sys_next::AVCodec {
33        self.inner
34    }
35}
36
37/// Holds metadata about a specific codec recognized by FFmpeg.
38///
39/// This struct consolidates information from both [`AVCodec`](ffmpeg_sys_next::AVCodec) (via `codec_name`, `codec_long_name`,
40/// etc.) and the [`AVCodecDescriptor`] (via `desc_name`) into a single, user-friendly format. It
41/// can be used to inspect properties of encoders or decoders available in your FFmpeg build.
42///
43/// # Fields
44/// * `codec_name` - The short name of the codec (e.g., `"h264"`, `"aac"`).
45/// * `codec_long_name` - A more descriptive name of the codec (e.g., `"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"`).
46/// * `desc_name` - The descriptor’s name, typically very similar or identical to `codec_name`.
47/// * `media_type` - The media category (`AVMEDIA_TYPE_AUDIO`, `AVMEDIA_TYPE_VIDEO`, etc.).
48/// * `codec_id` - The internal FFmpeg `AVCodecID`, an enum identifying the codec.
49/// * `codec_capabilities` - A bitmask indicating codec capabilities (e.g., intra-only, lossless).
50#[derive(Clone, Debug)]
51pub struct CodecInfo {
52    /// The short name from `AVCodec.name`.
53    pub codec_name: String,
54    /// The long name from `AVCodec.long_name`.
55    pub codec_long_name: String,
56    /// The descriptor’s name from `AVCodecDescriptor.name`.
57    pub desc_name: String,
58
59    /// The media type (e.g., audio, video, subtitle).
60    pub media_type: AVMediaType,
61    /// The numeric codec ID (e.g., `AV_CODEC_ID_H264`).
62    pub codec_id: AVCodecID,
63    /// A bitmask of capabilities from `AVCodec.capabilities`.
64    pub codec_capabilities: i32,
65}
66
67/// Retrieves a list of **all encoders** (e.g., for H.264, AAC, etc.) recognized by FFmpeg.
68///
69/// Each returned [`CodecInfo`] contains metadata such as the short codec name, long codec name,
70/// descriptor name, media type, and capabilities. This function helps you discover which encoders
71/// are compiled into your FFmpeg build.
72///
73/// # Example
74///
75/// ```rust,ignore
76/// let encoders = get_encoders();
77/// for encoder in encoders {
78///     println!("Encoder: {} ({})", encoder.codec_name, encoder.codec_long_name);
79/// }
80/// ```
81pub fn get_encoders() -> Vec<CodecInfo> {
82    get_codec_infos(1)
83}
84
85/// Retrieves a list of **all decoders** (e.g., for H.264, AAC, etc.) recognized by FFmpeg.
86///
87/// Each returned [`CodecInfo`] contains metadata such as the short codec name, long codec name,
88/// descriptor name, media type, and capabilities. This function helps you discover which decoders
89/// are compiled into your FFmpeg build.
90///
91/// # Example
92///
93/// ```rust,ignore
94/// let decoders = get_decoders();
95/// for decoder in decoders {
96///     println!("Decoder: {} ({})", decoder.codec_name, decoder.codec_long_name);
97/// }
98/// ```
99pub fn get_decoders() -> Vec<CodecInfo> {
100    get_codec_infos(0)
101}
102
103fn get_codec_infos(encoder: i32) -> Vec<CodecInfo> {
104    let mut codec_infos = Vec::new();
105    let descs = get_codecs_sorted();
106    for desc in descs {
107        let mut iter = null_mut();
108        unsafe {
109            loop {
110                let codec = next_codec_for_id((*desc).id, &mut iter, encoder);
111                if codec.is_null() {
112                    break;
113                }
114
115                let codec_name = (*codec.inner).name;
116                let codec_name = CStr::from_ptr(codec_name).to_str();
117                let codec_long_name = (*codec.inner).long_name;
118                let codec_long_name = CStr::from_ptr(codec_long_name).to_str();
119                let desc_name = (*desc).name;
120                let desc_name = CStr::from_ptr(desc_name).to_str();
121
122                if let (Ok(codec_name), Ok(codec_long_name), Ok(desc_name)) =
123                    (codec_name, codec_long_name, desc_name)
124                {
125                    let codec_info = CodecInfo {
126                        codec_name: codec_name.to_string(),
127                        codec_long_name: codec_long_name.to_string(),
128                        desc_name: desc_name.to_string(),
129                        media_type: (*codec.inner).type_,
130                        codec_id: (*codec.inner).id,
131                        codec_capabilities: (*codec.inner).capabilities,
132                    };
133                    codec_infos.push(codec_info);
134                }
135            }
136        }
137    }
138    codec_infos
139}
140
141fn get_codecs_sorted() -> Vec<*const AVCodecDescriptor> {
142    let mut desc = null();
143    let mut codecs = Vec::new();
144
145    unsafe {
146        loop {
147            desc = avcodec_descriptor_next(desc);
148            if desc.is_null() {
149                break;
150            }
151            codecs.push(desc)
152        }
153    }
154    if !codecs.is_empty() {
155        codecs.sort_by(|&a, &b| compare_codec_desc(a, b));
156    }
157
158    codecs
159}
160
161fn compare_codec_desc(
162    a: *const AVCodecDescriptor,
163    b: *const AVCodecDescriptor,
164) -> std::cmp::Ordering {
165    unsafe {
166        match ((*a).type_ as i32).cmp(&((*b).type_ as i32)) {
167            std::cmp::Ordering::Equal => (*a).name.cmp(&(*b).name),
168            other => other,
169        }
170    }
171}
172
173fn next_codec_for_id(id: AVCodecID, iter: *mut *mut c_void, encoder: i32) -> Codec {
174    loop {
175        unsafe {
176            let c = av_codec_iterate(iter);
177            if c.is_null() {
178                break;
179            }
180
181            if (*c).id == id {
182                let result = if encoder != 0 {
183                    av_codec_is_encoder(c)
184                } else {
185                    av_codec_is_decoder(c)
186                };
187                if result != 0 {
188                    return Codec::new(c);
189                }
190            }
191        }
192    }
193
194    Codec::null()
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    #[test]
202    fn test_print_codes() {
203        let encoders = get_encoders();
204        for encoder in encoders {
205            println!("{:?}", encoder);
206        }
207
208        println!("----------------------");
209
210        let decoders = get_decoders();
211        for decoder in decoders {
212            println!("{:?}", decoder);
213        }
214    }
215}