1use crate::iters::TerminatedPtrIter;
2
3#[cfg(feature = "ffmpeg_7_1")]
4use std::ptr::NonNull;
5
6#[cfg(feature = "ffmpeg_7_1")]
7use crate::codec::codec::ChannelLayoutIter;
8#[cfg(feature = "ffmpeg_7_1")]
9use crate::codec::Context;
10#[cfg(feature = "ffmpeg_7_1")]
11use crate::ffi::*;
12#[cfg(feature = "ffmpeg_7_1")]
13use crate::Codec;
14#[cfg(feature = "ffmpeg_7_1")]
15use crate::Error;
16
17#[cfg(feature = "ffmpeg_7_1")]
18#[derive(Debug, Clone)]
19pub enum Supported<I> {
20 All,
21 Specific(I),
22}
23
24#[cfg(feature = "ffmpeg_7_1")]
25impl<T, I> Supported<I>
26where
27 T: PartialEq,
28 I: Iterator<Item = T>,
29{
30 pub fn all(&self) -> bool {
46 matches!(self, Supported::All)
47 }
48
49 pub fn supports(self, t: T) -> bool {
66 match self {
67 Supported::All => true,
68 Supported::Specific(mut iter) => iter.any(|elem| elem == t),
69 }
70 }
71}
72
73#[cfg(feature = "ffmpeg_7_1")]
74fn supported<WrapperType, AVType, CodecType, I>(
75 codec: Codec<CodecType>,
76 ctx: Option<&Context>,
77 cfg: AVCodecConfig,
78) -> Result<Supported<I>, Error>
79where
80 I: TerminatedPtrIter<AVType, WrapperType>,
81 AVType: Into<WrapperType>,
82{
83 let mut out_ptr: *const libc::c_void = std::ptr::null();
84
85 unsafe {
86 let avctx = ctx.map_or(std::ptr::null(), |ctx| ctx.as_ptr());
87
88 let ret = avcodec_get_supported_config(
89 avctx,
90 codec.as_ptr(),
91 cfg,
92 0, &mut out_ptr,
94 std::ptr::null_mut(), );
96
97 if ret < 0 {
98 return Err(Error::from(ret));
99 }
100
101 match NonNull::new(out_ptr as *mut _) {
102 Some(ptr) => Ok(Supported::Specific(I::from_ptr(ptr))),
104 None => Ok(Supported::All),
106 }
107 }
108}
109
110macro_rules! impl_config_iter {
111 (
112 $fn_name:ident,
113 $codec_cfg:expr,
114 $iter:ident,
115 $ty:ty,
116 $av_ty:ty,
117 $terminator:expr
118 ) => {
119 impl_config_iter_fn!($fn_name, $iter, $codec_cfg);
120 impl_config_iter_struct!($iter, $av_ty);
121 impl_config_iter_traits!($iter, $ty, $av_ty, $terminator);
122 };
123}
124
125macro_rules! impl_config_iter_struct {
126 ($iter:ident, $av_ty:ty) => {
127 #[derive(Debug, Clone)]
128 pub struct $iter<'a> {
129 next: std::ptr::NonNull<$av_ty>,
130 _marker: std::marker::PhantomData<&'a $av_ty>,
131 }
132 };
133}
134
135macro_rules! impl_config_iter_fn {
136 ($fn_name:ident, $iter:ident, $codec_cfg:expr) => {
137 #[cfg(feature = "ffmpeg_7_1")]
141 pub fn $fn_name<T>(
142 codec: Codec<T>,
143 ctx: Option<&Context>,
144 ) -> Result<Supported<$iter<'_>>, Error> {
145 supported(codec, ctx, $codec_cfg)
146 }
147 };
148}
149
150macro_rules! impl_config_iter_traits {
151 ($iter:ident, $ty:ty, $av_ty:ty, $terminator:expr) => {
152 impl<'a> TerminatedPtrIter<$av_ty, $ty> for $iter<'a> {
153 unsafe fn from_ptr(ptr: std::ptr::NonNull<$av_ty>) -> Self {
154 Self {
155 next: ptr,
156 _marker: std::marker::PhantomData,
157 }
158 }
159 }
160
161 impl<'a> std::iter::FusedIterator for $iter<'a> {}
164
165 impl<'a> Iterator for $iter<'a> {
171 type Item = $ty;
172
173 fn next(&mut self) -> Option<Self::Item> {
174 unsafe {
177 let curr = self.next.as_ptr();
178 if *curr == $terminator {
179 return None;
180 }
181
182 self.next = self.next.add(1);
183 Some((*curr).into())
184 }
185 }
186 }
187 };
188}
189
190impl_config_iter!(
191 supported_pixel_formats,
192 crate::ffi::AVCodecConfig::PIX_FORMAT,
193 PixelFormatIter,
194 crate::format::Pixel,
195 crate::ffi::AVPixelFormat,
196 crate::ffi::AVPixelFormat::NONE
197);
198
199impl_config_iter!(
200 supported_frame_rates,
201 crate::ffi::AVCodecConfig::FRAME_RATE,
202 FrameRateIter,
203 crate::Rational,
204 crate::ffi::AVRational,
205 crate::ffi::AVRational { num: 0, den: 0 }
206);
207
208impl_config_iter!(
209 supported_sample_rates,
210 crate::ffi::AVCodecConfig::SAMPLE_RATE,
211 SampleRateIter,
212 libc::c_int,
213 libc::c_int,
214 0 as libc::c_int
215);
216
217impl_config_iter!(
218 supported_sample_formats,
219 crate::ffi::AVCodecConfig::SAMPLE_FORMAT,
220 SampleFormatIter,
221 crate::format::Sample,
222 crate::ffi::AVSampleFormat,
223 crate::ffi::AVSampleFormat::NONE
224);
225
226#[cfg(feature = "ffmpeg_7_1")]
230pub fn supported_channel_layouts<T>(
231 codec: Codec<T>,
232 ctx: Option<&Context>,
233) -> Result<Supported<ChannelLayoutIter<'_>>, Error> {
234 supported(codec, ctx, AVCodecConfig::CHANNEL_LAYOUT)
235}
236
237#[cfg(feature = "ffmpeg_7_1")]
238impl_config_iter!(
239 supported_color_ranges,
240 crate::ffi::AVCodecConfig::COLOR_RANGE,
241 ColorRangeIter,
242 crate::color::Range,
243 crate::ffi::AVColorRange,
244 crate::ffi::AVColorRange::UNSPECIFIED
245);
246
247#[cfg(feature = "ffmpeg_7_1")]
248impl_config_iter!(
249 supported_color_spaces,
250 crate::ffi::AVCodecConfig::COLOR_SPACE,
251 ColorSpaceIter,
252 crate::color::Space,
253 crate::ffi::AVColorSpace,
254 crate::ffi::AVColorSpace::UNSPECIFIED
255);
256
257#[cfg(feature = "ffmpeg_8_1")]
258impl_config_iter!(
259 supported_alpha_modes,
260 crate::ffi::AVCodecConfig::ALPHA_MODE,
261 AlphaModeIter,
262 crate::format::AlphaMode,
263 crate::ffi::AVAlphaMode,
264 crate::ffi::AVAlphaMode::UNSPECIFIED
265);
266
267#[cfg(test)]
268#[cfg(feature = "ffmpeg_7_1")]
269mod test {
270 use super::*;
271
272 use crate::codec::{decoder, encoder, Compliance, Id};
273 use crate::color::Range;
274 use crate::format::Pixel;
275 use crate::Rational;
276
277 #[test]
281 fn audio_decoder() {
282 let codec = decoder::find(Id::MP3).expect("can find mp3 decoder");
283
284 assert!(supported_color_ranges(codec, None).is_err());
286
287 let format_iter = match supported_sample_formats(codec, None) {
288 Ok(Supported::Specific(f)) => f,
289 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
290 };
291
292 for format in format_iter {
293 println!("format: {format:#?}");
294 }
295 }
296
297 #[test]
298 fn audio_encoder() {
299 let codec = encoder::find(Id::OPUS).expect("can find opus encoder");
300
301 assert!(matches!(
304 supported_color_spaces(codec, None),
305 Ok(Supported::All)
306 ));
307 let format_iter = match supported_sample_formats(codec, None) {
308 Ok(Supported::Specific(f)) => f,
309 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
310 };
311
312 for format in format_iter {
313 println!("format: {format:#?}");
314 }
315 }
316
317 #[test]
318 fn video_decoder() {
319 let codec = decoder::find(Id::H264).expect("can find H264 decoder");
320
321 assert!(supported_sample_rates(codec, None).is_err());
322 assert!(matches!(
323 supported_color_spaces(codec, None),
324 Ok(Supported::All)
325 ));
326 }
327
328 #[test]
329 fn video_encoder() {
330 let codec = encoder::find(Id::VP9).expect("can find VP9 encoder");
331
332 let color_ranges = match supported_color_ranges(codec, None) {
333 Ok(Supported::Specific(c)) => c,
334 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
335 };
336
337 for range in color_ranges {
338 println!("{range:#?}");
339 }
340
341 assert!(matches!(
342 supported_pixel_formats(codec, None),
343 Ok(Supported::Specific(_))
344 ));
345
346 assert!(matches!(
347 supported_frame_rates(codec, None),
348 Ok(Supported::All)
349 ));
350 }
351
352 #[cfg(feature = "ffmpeg_8_1")]
353 #[test]
354 fn alpha_modes() {
355 let codec = encoder::find(Id::PNG).expect("can find PNG encoder");
356
357 let alpha_modes = match supported_alpha_modes(codec, None) {
358 Ok(Supported::Specific(c)) => c,
359 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
360 };
361
362 for mode in alpha_modes {
363 println!("{mode:?}");
364 }
365 }
366
367 #[test]
368 fn supports() {
369 let codec = encoder::find(Id::FFV1).expect("can find FFV1 encoder");
370
371 assert!(supported_color_ranges(codec, None)
372 .expect("can check color range support")
373 .supports(Range::MPEG));
374
375 assert!(!supported_pixel_formats(codec, None)
376 .expect("can check color range support")
377 .supports(Pixel::GRAY16));
378
379 assert!(supported_frame_rates(codec, None)
380 .expect("can check frame rate support")
381 .supports(Rational(123, 456)));
382
383 supported_sample_formats(codec, None)
384 .expect_err("can NOT check sample format support (video codec)");
385 }
386
387 #[test]
388 fn with_context() {
389 let codec = encoder::find(Id::MJPEG).expect("can find MJPEG encoder");
390
391 let mut ctx = unsafe {
392 let avctx = crate::ffi::avcodec_alloc_context3(codec.as_ptr());
393 crate::codec::Context::wrap(avctx, None)
394 };
395
396 ctx.compliance(Compliance::Strict);
397
398 assert!(!supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
399 .expect("can check color range support")
400 .supports(Range::MPEG));
401
402 ctx.compliance(Compliance::Unofficial);
403
404 assert!(supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
408 .expect("can check color range support")
409 .supports(Range::MPEG));
410 }
411}