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 = std::ptr::NonNull::new_unchecked(curr.add(1));
183
184 Some((*curr).into())
185 }
186 }
187 }
188 };
189}
190
191impl_config_iter!(
192 supported_pixel_formats,
193 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_PIX_FORMAT,
194 PixelFormatIter,
195 crate::format::Pixel,
196 crate::ffi::AVPixelFormat,
197 crate::ffi::AVPixelFormat::AV_PIX_FMT_NONE
198);
199
200impl_config_iter!(
201 supported_frame_rates,
202 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_FRAME_RATE,
203 FrameRateIter,
204 crate::Rational,
205 crate::ffi::AVRational,
206 crate::ffi::AVRational { num: 0, den: 0 }
207);
208
209impl_config_iter!(
210 supported_sample_rates,
211 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_RATE,
212 SampleRateIter,
213 libc::c_int,
214 libc::c_int,
215 0 as libc::c_int
216);
217
218impl_config_iter!(
219 supported_sample_formats,
220 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_FORMAT,
221 SampleFormatIter,
222 crate::format::Sample,
223 crate::ffi::AVSampleFormat,
224 crate::ffi::AVSampleFormat::AV_SAMPLE_FMT_NONE
225);
226
227#[cfg(feature = "ffmpeg_7_1")]
228impl_config_iter!(
229 supported_color_ranges,
230 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_COLOR_RANGE,
231 ColorRangeIter,
232 crate::color::Range,
233 crate::ffi::AVColorRange,
234 crate::ffi::AVColorRange::AVCOL_RANGE_UNSPECIFIED
235);
236
237#[cfg(feature = "ffmpeg_7_1")]
238impl_config_iter!(
239 supported_color_spaces,
240 crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_COLOR_SPACE,
241 ColorSpaceIter,
242 crate::color::Space,
243 crate::ffi::AVColorSpace,
244 crate::ffi::AVColorSpace::AVCOL_SPC_UNSPECIFIED
245);
246
247#[cfg(test)]
248#[cfg(feature = "ffmpeg_7_1")]
249mod test {
250 use super::*;
251
252 use crate::codec::{decoder, encoder, Compliance, Id};
253 use crate::color::Range;
254 use crate::format::Pixel;
255 use crate::Rational;
256
257 #[test]
261 fn audio_decoder() {
262 let codec = decoder::find(Id::MP3).expect("can find mp3 decoder");
263
264 assert!(supported_color_ranges(codec, None).is_err());
266
267 let format_iter = match supported_sample_formats(codec, None) {
268 Ok(Supported::Specific(f)) => f,
269 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
270 };
271
272 for format in format_iter {
273 println!("format: {format:#?}");
274 }
275 }
276
277 #[test]
278 fn audio_encoder() {
279 let codec = encoder::find(Id::OPUS).expect("can find opus encoder");
280
281 assert!(matches!(
284 supported_color_spaces(codec, None),
285 Ok(Supported::All)
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 video_decoder() {
299 let codec = decoder::find(Id::H264).expect("can find H264 decoder");
300
301 assert!(supported_sample_rates(codec, None).is_err());
302 assert!(matches!(
303 supported_color_spaces(codec, None),
304 Ok(Supported::All)
305 ));
306 }
307
308 #[test]
309 fn video_encoder() {
310 let codec = encoder::find(Id::VP9).expect("can find VP9 encoder");
311
312 let color_ranges = match supported_color_ranges(codec, None) {
313 Ok(Supported::Specific(c)) => c,
314 sup => panic!("Should be Supported::Specific, got {sup:#?}"),
315 };
316
317 for range in color_ranges {
318 println!("{range:#?}");
319 }
320
321 assert!(matches!(
322 supported_pixel_formats(codec, None),
323 Ok(Supported::Specific(_))
324 ));
325
326 assert!(matches!(
327 supported_frame_rates(codec, None),
328 Ok(Supported::All)
329 ));
330 }
331
332 #[test]
333 fn supports() {
334 let codec = encoder::find(Id::FFV1).expect("can find FFV1 encoder");
335
336 assert!(supported_color_ranges(codec, None)
337 .expect("can check color range support")
338 .supports(Range::MPEG));
339
340 assert!(!supported_pixel_formats(codec, None)
341 .expect("can check color range support")
342 .supports(Pixel::GRAY16));
343
344 assert!(supported_frame_rates(codec, None)
345 .expect("can check frame rate support")
346 .supports(Rational(123, 456)));
347
348 supported_sample_formats(codec, None)
349 .expect_err("can NOT check sample format support (video codec)");
350 }
351
352 #[test]
353 fn with_context() {
354 let codec = encoder::find(Id::MJPEG).expect("can find MJPEG encoder");
355
356 let mut ctx = unsafe {
357 let avctx = crate::ffi::avcodec_alloc_context3(codec.as_ptr());
358 crate::codec::Context::wrap(avctx, None)
359 };
360
361 ctx.compliance(Compliance::Strict);
362
363 assert!(!supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
364 .expect("can check color range support")
365 .supports(Range::MPEG));
366
367 ctx.compliance(Compliance::Unofficial);
368
369 assert!(supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
373 .expect("can check color range support")
374 .supports(Range::MPEG));
375 }
376}