ffmpeg_the_third/codec/
codec.rs1use 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 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 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 #[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 #[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 #[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 #[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 #[cfg(feature = "ffmpeg_7_1")]
233 pub fn supports_rate(self, rate: crate::Rational) -> bool {
234 self.supported_rates().supports(rate)
235 }
236
237 #[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 #[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 #[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 #[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 #[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 #[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 #[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 self.next = (curr as *const AVChannelLayout).add(1).as_ref().unwrap();
379 Some(ChannelLayout::from(curr))
380 }
381 }
382 }
383
384 unsafe fn zeroed_layout() -> AVChannelLayout {
386 std::mem::zeroed()
387 }
388}