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