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