use crate::iters::TerminatedPtrIter;
#[cfg(feature = "ffmpeg_7_1")]
use std::ptr::NonNull;
#[cfg(feature = "ffmpeg_7_1")]
use crate::codec::Context;
#[cfg(feature = "ffmpeg_7_1")]
use crate::ffi::*;
#[cfg(feature = "ffmpeg_7_1")]
use crate::Codec;
#[cfg(feature = "ffmpeg_7_1")]
use crate::Error;
#[cfg(feature = "ffmpeg_7_1")]
#[derive(Debug, Clone)]
pub enum Supported<I> {
All,
Specific(I),
}
#[cfg(feature = "ffmpeg_7_1")]
impl<T, I> Supported<I>
where
T: PartialEq,
I: Iterator<Item = T>,
{
pub fn all(&self) -> bool {
matches!(self, Supported::All)
}
pub fn supports(self, t: T) -> bool {
match self {
Supported::All => true,
Supported::Specific(mut iter) => iter.any(|elem| elem == t),
}
}
}
#[cfg(feature = "ffmpeg_7_1")]
fn supported<WrapperType, AVType, CodecType, I>(
codec: Codec<CodecType>,
ctx: Option<&Context>,
cfg: AVCodecConfig,
) -> Result<Supported<I>, Error>
where
I: TerminatedPtrIter<AVType, WrapperType>,
AVType: Into<WrapperType>,
{
let mut out_ptr: *const libc::c_void = std::ptr::null();
unsafe {
let avctx = ctx.map_or(std::ptr::null(), |ctx| ctx.as_ptr());
let ret = avcodec_get_supported_config(
avctx,
codec.as_ptr(),
cfg,
0, &mut out_ptr,
std::ptr::null_mut(), );
if ret < 0 {
return Err(Error::from(ret));
}
match NonNull::new(out_ptr as *mut _) {
Some(ptr) => Ok(Supported::Specific(I::from_ptr(ptr))),
None => Ok(Supported::All),
}
}
}
macro_rules! impl_config_iter {
(
$fn_name:ident,
$codec_cfg:expr,
$iter:ident,
$ty:ty,
$av_ty:ty,
$terminator:expr
) => {
impl_config_iter_fn!($fn_name, $iter, $codec_cfg);
impl_config_iter_struct!($iter, $av_ty);
impl_config_iter_traits!($iter, $ty, $av_ty, $terminator);
};
}
macro_rules! impl_config_iter_struct {
($iter:ident, $av_ty:ty) => {
#[derive(Debug, Clone)]
pub struct $iter<'a> {
next: std::ptr::NonNull<$av_ty>,
_marker: std::marker::PhantomData<&'a $av_ty>,
}
};
}
macro_rules! impl_config_iter_fn {
($fn_name:ident, $iter:ident, $codec_cfg:expr) => {
#[cfg(feature = "ffmpeg_7_1")]
pub fn $fn_name<T>(
codec: Codec<T>,
ctx: Option<&Context>,
) -> Result<Supported<$iter<'_>>, Error> {
supported(codec, ctx, $codec_cfg)
}
};
}
macro_rules! impl_config_iter_traits {
($iter:ident, $ty:ty, $av_ty:ty, $terminator:expr) => {
impl<'a> TerminatedPtrIter<$av_ty, $ty> for $iter<'a> {
unsafe fn from_ptr(ptr: std::ptr::NonNull<$av_ty>) -> Self {
Self {
next: ptr,
_marker: std::marker::PhantomData,
}
}
}
impl<'a> std::iter::FusedIterator for $iter<'a> {}
impl<'a> Iterator for $iter<'a> {
type Item = $ty;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let curr = self.next.as_ptr();
if *curr == $terminator {
return None;
}
self.next = self.next.add(1);
Some((*curr).into())
}
}
}
};
}
impl_config_iter!(
supported_pixel_formats,
crate::ffi::AVCodecConfig::PIX_FORMAT,
PixelFormatIter,
crate::format::Pixel,
crate::ffi::AVPixelFormat,
crate::ffi::AVPixelFormat::NONE
);
impl_config_iter!(
supported_frame_rates,
crate::ffi::AVCodecConfig::FRAME_RATE,
FrameRateIter,
crate::Rational,
crate::ffi::AVRational,
crate::ffi::AVRational { num: 0, den: 0 }
);
impl_config_iter!(
supported_sample_rates,
crate::ffi::AVCodecConfig::SAMPLE_RATE,
SampleRateIter,
libc::c_int,
libc::c_int,
0 as libc::c_int
);
impl_config_iter!(
supported_sample_formats,
crate::ffi::AVCodecConfig::SAMPLE_FORMAT,
SampleFormatIter,
crate::format::Sample,
crate::ffi::AVSampleFormat,
crate::ffi::AVSampleFormat::NONE
);
#[cfg(feature = "ffmpeg_7_1")]
impl_config_iter!(
supported_color_ranges,
crate::ffi::AVCodecConfig::COLOR_RANGE,
ColorRangeIter,
crate::color::Range,
crate::ffi::AVColorRange,
crate::ffi::AVColorRange::UNSPECIFIED
);
#[cfg(feature = "ffmpeg_7_1")]
impl_config_iter!(
supported_color_spaces,
crate::ffi::AVCodecConfig::COLOR_SPACE,
ColorSpaceIter,
crate::color::Space,
crate::ffi::AVColorSpace,
crate::ffi::AVColorSpace::UNSPECIFIED
);
#[cfg(feature = "ffmpeg_8_1")]
impl_config_iter!(
supported_alpha_modes,
crate::ffi::AVCodecConfig::ALPHA_MODE,
AlphaModeIter,
crate::format::AlphaMode,
crate::ffi::AVAlphaMode,
crate::ffi::AVAlphaMode::UNSPECIFIED
);
#[cfg(test)]
#[cfg(feature = "ffmpeg_7_1")]
mod test {
use super::*;
use crate::codec::{decoder, encoder, Compliance, Id};
use crate::color::Range;
use crate::format::Pixel;
use crate::Rational;
#[test]
fn audio_decoder() {
let codec = decoder::find(Id::MP3).expect("can find mp3 decoder");
assert!(supported_color_ranges(codec, None).is_err());
let format_iter = match supported_sample_formats(codec, None) {
Ok(Supported::Specific(f)) => f,
sup => panic!("Should be Supported::Specific, got {sup:#?}"),
};
for format in format_iter {
println!("format: {format:#?}");
}
}
#[test]
fn audio_encoder() {
let codec = encoder::find(Id::OPUS).expect("can find opus encoder");
assert!(matches!(
supported_color_spaces(codec, None),
Ok(Supported::All)
));
let format_iter = match supported_sample_formats(codec, None) {
Ok(Supported::Specific(f)) => f,
sup => panic!("Should be Supported::Specific, got {sup:#?}"),
};
for format in format_iter {
println!("format: {format:#?}");
}
}
#[test]
fn video_decoder() {
let codec = decoder::find(Id::H264).expect("can find H264 decoder");
assert!(supported_sample_rates(codec, None).is_err());
assert!(matches!(
supported_color_spaces(codec, None),
Ok(Supported::All)
));
}
#[test]
fn video_encoder() {
let codec = encoder::find(Id::VP9).expect("can find VP9 encoder");
let color_ranges = match supported_color_ranges(codec, None) {
Ok(Supported::Specific(c)) => c,
sup => panic!("Should be Supported::Specific, got {sup:#?}"),
};
for range in color_ranges {
println!("{range:#?}");
}
assert!(matches!(
supported_pixel_formats(codec, None),
Ok(Supported::Specific(_))
));
assert!(matches!(
supported_frame_rates(codec, None),
Ok(Supported::All)
));
}
#[cfg(feature = "ffmpeg_8_1")]
#[test]
fn alpha_modes() {
let codec = encoder::find(Id::PNG).expect("can find PNG encoder");
let alpha_modes = match supported_alpha_modes(codec, None) {
Ok(Supported::Specific(c)) => c,
sup => panic!("Should be Supported::Specific, got {sup:#?}"),
};
for mode in alpha_modes {
println!("{mode:?}");
}
}
#[test]
fn supports() {
let codec = encoder::find(Id::FFV1).expect("can find FFV1 encoder");
assert!(supported_color_ranges(codec, None)
.expect("can check color range support")
.supports(Range::MPEG));
assert!(!supported_pixel_formats(codec, None)
.expect("can check color range support")
.supports(Pixel::GRAY16));
assert!(supported_frame_rates(codec, None)
.expect("can check frame rate support")
.supports(Rational(123, 456)));
supported_sample_formats(codec, None)
.expect_err("can NOT check sample format support (video codec)");
}
#[test]
fn with_context() {
let codec = encoder::find(Id::MJPEG).expect("can find MJPEG encoder");
let mut ctx = unsafe {
let avctx = crate::ffi::avcodec_alloc_context3(codec.as_ptr());
crate::codec::Context::wrap(avctx, None)
};
ctx.compliance(Compliance::Strict);
assert!(!supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
.expect("can check color range support")
.supports(Range::MPEG));
ctx.compliance(Compliance::Unofficial);
assert!(supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
.expect("can check color range support")
.supports(Range::MPEG));
}
}