ffmpeg_the_third/codec/
codec.rs

1use std::marker::PhantomData;
2use std::ptr::NonNull;
3
4use super::config::{FrameRateIter, PixelFormatIter, SampleFormatIter, SampleRateIter};
5use super::descriptor::{CodecDescriptor, CodecDescriptorIter};
6use super::profile::ProfileIter;
7use super::{Capabilities, Id};
8use crate::ffi::*;
9use crate::iters::TerminatedPtrIter;
10use crate::{media, utils};
11
12#[cfg(feature = "ffmpeg_7_1")]
13use crate::codec::config::{ColorRangeIter, ColorSpaceIter, Supported};
14
15pub fn list_descriptors() -> CodecDescriptorIter {
16    CodecDescriptorIter::new()
17}
18
19pub type Audio = Codec<AudioType>;
20pub type Video = Codec<VideoType>;
21pub type Data = Codec<DataType>;
22pub type Subtitle = Codec<SubtitleType>;
23pub type Attachment = Codec<AttachmentType>;
24
25#[derive(PartialEq, Eq, Copy, Clone)]
26pub struct Codec<Type = UnknownType> {
27    ptr: NonNull<AVCodec>,
28    _marker: PhantomData<Type>,
29}
30
31#[derive(PartialEq, Eq, Copy, Clone)]
32pub struct UnknownType;
33#[derive(PartialEq, Eq, Copy, Clone)]
34pub struct VideoType;
35#[derive(PartialEq, Eq, Copy, Clone)]
36pub struct AudioType;
37#[derive(PartialEq, Eq, Copy, Clone)]
38pub struct DataType;
39#[derive(PartialEq, Eq, Copy, Clone)]
40pub struct SubtitleType;
41#[derive(PartialEq, Eq, Copy, Clone)]
42pub struct AttachmentType;
43
44unsafe impl<T> Send for Codec<T> {}
45unsafe impl<T> Sync for Codec<T> {}
46
47impl Codec<UnknownType> {
48    /// Create a new reference to a codec from a raw pointer.
49    ///
50    /// Returns `None` if `ptr` is null.
51    pub unsafe fn from_raw(ptr: *const AVCodec) -> Option<Self> {
52        NonNull::new(ptr as *mut _).map(|ptr| Self {
53            ptr,
54            _marker: PhantomData,
55        })
56    }
57
58    // Helper function to easily convert to another codec type.
59    // TODO: Does this need to be unsafe?
60    /// Ensure that `self.medium()` is correct for `Codec<U>`.
61    fn as_other_codec<U>(self) -> Codec<U> {
62        Codec {
63            ptr: self.ptr,
64            _marker: PhantomData,
65        }
66    }
67
68    pub fn is_video(&self) -> bool {
69        self.medium() == media::Type::Video
70    }
71
72    pub fn video(self) -> Option<Video> {
73        if self.is_video() {
74            Some(self.as_other_codec())
75        } else {
76            None
77        }
78    }
79
80    pub fn is_audio(&self) -> bool {
81        self.medium() == media::Type::Audio
82    }
83
84    pub fn audio(self) -> Option<Audio> {
85        if self.is_audio() {
86            Some(self.as_other_codec())
87        } else {
88            None
89        }
90    }
91
92    pub fn is_data(&self) -> bool {
93        self.medium() == media::Type::Data
94    }
95
96    pub fn data(self) -> Option<Data> {
97        if self.is_data() {
98            Some(self.as_other_codec())
99        } else {
100            None
101        }
102    }
103
104    pub fn is_subtitle(&self) -> bool {
105        self.medium() == media::Type::Subtitle
106    }
107
108    pub fn subtitle(self) -> Option<Subtitle> {
109        if self.is_subtitle() {
110            Some(self.as_other_codec())
111        } else {
112            None
113        }
114    }
115
116    pub fn is_attachment(&self) -> bool {
117        self.medium() == media::Type::Attachment
118    }
119
120    pub fn attachment(self) -> Option<Attachment> {
121        if self.is_attachment() {
122            Some(self.as_other_codec())
123        } else {
124            None
125        }
126    }
127}
128
129impl<T> Codec<T> {
130    pub fn as_ptr(&self) -> *const AVCodec {
131        self.ptr.as_ptr()
132    }
133
134    pub fn is_encoder(&self) -> bool {
135        unsafe { av_codec_is_encoder(self.as_ptr()) != 0 }
136    }
137
138    pub fn is_decoder(&self) -> bool {
139        unsafe { av_codec_is_decoder(self.as_ptr()) != 0 }
140    }
141
142    pub fn name(&self) -> &str {
143        unsafe { utils::str_from_c_ptr((*self.as_ptr()).name) }
144    }
145
146    pub fn description(&self) -> &str {
147        unsafe { utils::optional_str_from_c_ptr((*self.as_ptr()).long_name).unwrap_or("") }
148    }
149
150    pub fn medium(&self) -> media::Type {
151        unsafe { media::Type::from((*self.as_ptr()).type_) }
152    }
153
154    pub fn id(&self) -> Id {
155        unsafe { Id::from((*self.as_ptr()).id) }
156    }
157
158    pub fn max_lowres(&self) -> i32 {
159        unsafe { (*self.as_ptr()).max_lowres.into() }
160    }
161
162    pub fn capabilities(&self) -> Capabilities {
163        unsafe { Capabilities::from_bits_truncate((*self.as_ptr()).capabilities as u32) }
164    }
165
166    pub fn profiles(&self) -> Option<ProfileIter> {
167        unsafe {
168            if (*self.as_ptr()).profiles.is_null() {
169                None
170            } else {
171                Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles))
172            }
173        }
174    }
175
176    pub fn descriptor(self) -> Option<CodecDescriptor> {
177        unsafe {
178            let ptr = avcodec_descriptor_get(self.id().into());
179            CodecDescriptor::from_raw(ptr)
180        }
181    }
182}
183
184impl Codec<AudioType> {
185    /// Checks if the given sample rate is supported by this audio codec.
186    #[cfg(feature = "ffmpeg_7_1")]
187    pub fn supports_rate(self, rate: libc::c_int) -> bool {
188        self.supported_rates().supports(rate)
189    }
190
191    /// Returns a [`Supported`] representing the supported sample rates.
192    #[cfg(feature = "ffmpeg_7_1")]
193    pub fn supported_rates(self) -> Supported<SampleRateIter<'static>> {
194        use super::config::supported_sample_rates;
195        supported_sample_rates(self, None).expect("audio codec returns supported sample rates")
196    }
197
198    pub fn rates(&self) -> Option<SampleRateIter<'_>> {
199        unsafe { SampleRateIter::from_raw((*self.as_ptr()).supported_samplerates) }
200    }
201
202    /// Checks if the given sample format is supported by this audio codec.
203    #[cfg(feature = "ffmpeg_7_1")]
204    pub fn supports_format(self, format: crate::format::Sample) -> bool {
205        self.supported_formats().supports(format)
206    }
207
208    /// Returns a [`Supported`] representing the supported sample formats.
209    #[cfg(feature = "ffmpeg_7_1")]
210    pub fn supported_formats(self) -> Supported<SampleFormatIter<'static>> {
211        use super::config::supported_sample_formats;
212        supported_sample_formats(self, None).expect("audio codec returns supported sample formats")
213    }
214
215    pub fn formats(&self) -> Option<SampleFormatIter<'_>> {
216        unsafe { SampleFormatIter::from_raw((*self.as_ptr()).sample_fmts) }
217    }
218
219    #[cfg(not(feature = "ffmpeg_7_0"))]
220    pub fn channel_layouts(&self) -> Option<ChannelLayoutMaskIter> {
221        unsafe { ChannelLayoutMaskIter::from_raw((*self.as_ptr()).channel_layouts) }
222    }
223
224    #[cfg(feature = "ffmpeg_5_1")]
225    pub fn ch_layouts(&self) -> Option<ChannelLayoutIter<'_>> {
226        unsafe { ChannelLayoutIter::from_raw((*self.as_ptr()).ch_layouts) }
227    }
228}
229
230impl Codec<VideoType> {
231    /// Checks if the given frame rate is supported by this video codec.
232    #[cfg(feature = "ffmpeg_7_1")]
233    pub fn supports_rate(self, rate: crate::Rational) -> bool {
234        self.supported_rates().supports(rate)
235    }
236
237    /// Returns a [`Supported`] representing the supported frame rates.
238    #[cfg(feature = "ffmpeg_7_1")]
239    pub fn supported_rates(self) -> Supported<FrameRateIter<'static>> {
240        use crate::codec::config::supported_frame_rates;
241        supported_frame_rates(self, None).expect("video codec returns supported frame rates")
242    }
243
244    pub fn rates(&self) -> Option<FrameRateIter<'_>> {
245        unsafe { FrameRateIter::from_raw((*self.as_ptr()).supported_framerates) }
246    }
247
248    /// Checks if the given pixel format is supported by this video codec.
249    #[cfg(feature = "ffmpeg_7_1")]
250    pub fn supports_format(self, format: crate::format::Pixel) -> bool {
251        self.supported_formats().supports(format)
252    }
253
254    /// Returns a [`Supported`] representing the supported pixel formats.
255    #[cfg(feature = "ffmpeg_7_1")]
256    pub fn supported_formats(self) -> Supported<PixelFormatIter<'static>> {
257        use crate::codec::config::supported_pixel_formats;
258        supported_pixel_formats(self, None).expect("video codec returns supported pixel formats")
259    }
260
261    pub fn formats(&self) -> Option<PixelFormatIter<'_>> {
262        unsafe { PixelFormatIter::from_raw((*self.as_ptr()).pix_fmts) }
263    }
264
265    /// Checks if the given color space is supported by this video codec.
266    #[cfg(feature = "ffmpeg_7_1")]
267    pub fn supports_color_space(self, space: crate::color::Space) -> bool {
268        self.supported_color_spaces().supports(space)
269    }
270
271    /// Returns a [`Supported`] representing the supported color spaces.
272    #[cfg(feature = "ffmpeg_7_1")]
273    pub fn supported_color_spaces(self) -> Supported<ColorSpaceIter<'static>> {
274        use crate::codec::config::supported_color_spaces;
275        supported_color_spaces(self, None).expect("video codec returns supported color spaces")
276    }
277
278    /// Checks if the given color range is supported by this video codec.
279    #[cfg(feature = "ffmpeg_7_1")]
280    pub fn supports_color_range(self, range: crate::color::Range) -> bool {
281        self.supported_color_ranges().supports(range)
282    }
283
284    /// Returns a [`Supported`] representing the supported color ranges.
285    #[cfg(feature = "ffmpeg_7_1")]
286    pub fn supported_color_ranges(self) -> Supported<ColorRangeIter<'static>> {
287        use crate::codec::config::supported_color_ranges;
288        supported_color_ranges(self, None).expect("video codec returns supported color ranges")
289    }
290}
291
292#[cfg(not(feature = "ffmpeg_7_0"))]
293use crate::ChannelLayoutMask;
294
295#[cfg(not(feature = "ffmpeg_7_0"))]
296pub struct ChannelLayoutMaskIter {
297    ptr: NonNull<u64>,
298}
299
300#[cfg(not(feature = "ffmpeg_7_0"))]
301impl ChannelLayoutMaskIter {
302    pub fn from_raw(ptr: *const u64) -> Option<Self> {
303        NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
304    }
305
306    pub fn best(self, max: i32) -> ChannelLayoutMask {
307        self.fold(ChannelLayoutMask::MONO, |acc, cur| {
308            if cur.channels() > acc.channels() && cur.channels() <= max {
309                cur
310            } else {
311                acc
312            }
313        })
314    }
315}
316
317#[cfg(not(feature = "ffmpeg_7_0"))]
318impl Iterator for ChannelLayoutMaskIter {
319    type Item = ChannelLayoutMask;
320
321    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
322        unsafe {
323            let ptr = self.ptr.as_ptr();
324            if *ptr == 0 {
325                return None;
326            }
327
328            let layout = ChannelLayoutMask::from_bits_truncate(*ptr);
329            self.ptr = NonNull::new_unchecked(ptr.add(1));
330
331            Some(layout)
332        }
333    }
334}
335
336#[cfg(feature = "ffmpeg_5_1")]
337pub use ch_layout::ChannelLayoutIter;
338
339#[cfg(feature = "ffmpeg_5_1")]
340mod ch_layout {
341    use super::*;
342    use crate::ChannelLayout;
343
344    pub struct ChannelLayoutIter<'a> {
345        next: &'a AVChannelLayout,
346    }
347
348    impl<'a> ChannelLayoutIter<'a> {
349        pub unsafe fn from_raw(ptr: *const AVChannelLayout) -> Option<Self> {
350            ptr.as_ref().map(|next| Self { next })
351        }
352    }
353
354    impl<'a> ChannelLayoutIter<'a> {
355        pub fn best(self, max: u32) -> ChannelLayout<'a> {
356            self.fold(ChannelLayout::MONO, |acc, cur| {
357                if cur.channels() > acc.channels() && cur.channels() <= max {
358                    cur
359                } else {
360                    acc
361                }
362            })
363        }
364    }
365
366    impl<'a> Iterator for ChannelLayoutIter<'a> {
367        type Item = ChannelLayout<'a>;
368
369        fn next(&mut self) -> Option<Self::Item> {
370            unsafe {
371                let curr = self.next;
372                if *curr == zeroed_layout() {
373                    return None;
374                }
375
376                // SAFETY: We trust that there is always an initialized layout up until
377                // the zeroed-out AVChannelLayout, which signals the end of iteration.
378                self.next = (curr as *const AVChannelLayout).add(1).as_ref().unwrap();
379                Some(ChannelLayout::from(curr))
380            }
381        }
382    }
383
384    // TODO: Remove this with a const variable when zeroed() is const (1.75.0)
385    unsafe fn zeroed_layout() -> AVChannelLayout {
386        std::mem::zeroed()
387    }
388}