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