Refactor crate::codec (#78)
* Refactor AVCodec wrapper * Implement new config interface * Move ProfileIter to codec::profile * Add CodecDescriptor
This commit is contained in:
parent
f1e978b58d
commit
f1c6b442b5
@ -20,7 +20,7 @@ fn main() {
|
|||||||
println!("\t profiles: none");
|
println!("\t profiles: none");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(video) = codec.video() {
|
if let Some(video) = codec.video() {
|
||||||
if let Some(rates) = video.rates() {
|
if let Some(rates) = video.rates() {
|
||||||
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
||||||
} else {
|
} else {
|
||||||
@ -34,7 +34,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(audio) = codec.audio() {
|
if let Some(audio) = codec.audio() {
|
||||||
if let Some(rates) = audio.rates() {
|
if let Some(rates) = audio.rates() {
|
||||||
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
||||||
} else {
|
} else {
|
||||||
@ -71,7 +71,7 @@ fn main() {
|
|||||||
println!("\t profiles: {:?}", profiles.collect::<Vec<_>>());
|
println!("\t profiles: {:?}", profiles.collect::<Vec<_>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(video) = codec.video() {
|
if let Some(video) = codec.video() {
|
||||||
if let Some(rates) = video.rates() {
|
if let Some(rates) = video.rates() {
|
||||||
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
||||||
} else {
|
} else {
|
||||||
@ -85,7 +85,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(audio) = codec.audio() {
|
if let Some(audio) = codec.audio() {
|
||||||
if let Some(rates) = audio.rates() {
|
if let Some(rates) = audio.rates() {
|
||||||
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
println!("\t rates: {:?}", rates.collect::<Vec<_>>());
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,7 +83,8 @@ fn transcoder<P: AsRef<Path>>(
|
|||||||
let mut decoder = context.decoder().audio()?;
|
let mut decoder = context.decoder().audio()?;
|
||||||
let codec = ffmpeg::encoder::find(octx.format().codec(path, media::Type::Audio))
|
let codec = ffmpeg::encoder::find(octx.format().codec(path, media::Type::Audio))
|
||||||
.expect("failed to find encoder")
|
.expect("failed to find encoder")
|
||||||
.audio()?;
|
.audio()
|
||||||
|
.expect("encoder is not audio encoder");
|
||||||
let global = octx
|
let global = octx
|
||||||
.format()
|
.format()
|
||||||
.flags()
|
.flags()
|
||||||
|
@ -1,215 +0,0 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use super::codec::Codec;
|
|
||||||
use crate::ffi::*;
|
|
||||||
use crate::format;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ffmpeg_7_0"))]
|
|
||||||
use crate::ChannelLayoutMask;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
|
||||||
pub struct Audio {
|
|
||||||
codec: Codec,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Audio {
|
|
||||||
pub unsafe fn new(codec: Codec) -> Audio {
|
|
||||||
Audio { codec }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Audio {
|
|
||||||
pub fn rates(&self) -> Option<RateIter> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.as_ptr()).supported_samplerates.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(RateIter::new((*self.codec.as_ptr()).supported_samplerates))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn formats(&self) -> Option<FormatIter> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.codec.as_ptr()).sample_fmts.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FormatIter::new((*self.codec.as_ptr()).sample_fmts))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ffmpeg_7_0"))]
|
|
||||||
pub fn channel_layouts(&self) -> Option<ChannelLayoutMaskIter> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.codec.as_ptr()).channel_layouts.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ChannelLayoutMaskIter::new(
|
|
||||||
(*self.codec.as_ptr()).channel_layouts,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ffmpeg_5_1")]
|
|
||||||
pub fn ch_layouts(&self) -> Option<ChannelLayoutIter> {
|
|
||||||
unsafe { ChannelLayoutIter::from_raw((*self.codec.as_ptr()).ch_layouts) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Audio {
|
|
||||||
type Target = Codec;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.codec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RateIter {
|
|
||||||
ptr: *const i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RateIter {
|
|
||||||
pub fn new(ptr: *const i32) -> Self {
|
|
||||||
RateIter { ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for RateIter {
|
|
||||||
type Item = i32;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
||||||
unsafe {
|
|
||||||
if *self.ptr == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rate = *self.ptr;
|
|
||||||
self.ptr = self.ptr.offset(1);
|
|
||||||
|
|
||||||
Some(rate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FormatIter {
|
|
||||||
ptr: *const AVSampleFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormatIter {
|
|
||||||
pub fn new(ptr: *const AVSampleFormat) -> Self {
|
|
||||||
FormatIter { ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for FormatIter {
|
|
||||||
type Item = format::Sample;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
||||||
unsafe {
|
|
||||||
if *self.ptr == AVSampleFormat::AV_SAMPLE_FMT_NONE {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let format = (*self.ptr).into();
|
|
||||||
self.ptr = self.ptr.offset(1);
|
|
||||||
|
|
||||||
Some(format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ffmpeg_7_0"))]
|
|
||||||
pub struct ChannelLayoutMaskIter {
|
|
||||||
ptr: *const u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ffmpeg_7_0"))]
|
|
||||||
impl ChannelLayoutMaskIter {
|
|
||||||
pub fn new(ptr: *const u64) -> Self {
|
|
||||||
ChannelLayoutMaskIter { ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn best(self, max: i32) -> ChannelLayoutMask {
|
|
||||||
self.fold(ChannelLayoutMask::MONO, |acc, cur| {
|
|
||||||
if cur.channels() > acc.channels() && cur.channels() <= max {
|
|
||||||
cur
|
|
||||||
} else {
|
|
||||||
acc
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ffmpeg_7_0"))]
|
|
||||||
impl Iterator for ChannelLayoutMaskIter {
|
|
||||||
type Item = ChannelLayoutMask;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
||||||
unsafe {
|
|
||||||
if *self.ptr == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let layout = ChannelLayoutMask::from_bits_truncate(*self.ptr);
|
|
||||||
self.ptr = self.ptr.offset(1);
|
|
||||||
|
|
||||||
Some(layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ffmpeg_5_1")]
|
|
||||||
pub use ch_layout::ChannelLayoutIter;
|
|
||||||
|
|
||||||
#[cfg(feature = "ffmpeg_5_1")]
|
|
||||||
mod ch_layout {
|
|
||||||
use super::*;
|
|
||||||
use crate::ChannelLayout;
|
|
||||||
|
|
||||||
pub struct ChannelLayoutIter<'a> {
|
|
||||||
next: &'a AVChannelLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ChannelLayoutIter<'a> {
|
|
||||||
pub unsafe fn from_raw(ptr: *const AVChannelLayout) -> Option<Self> {
|
|
||||||
ptr.as_ref().map(|next| Self { next })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ChannelLayoutIter<'a> {
|
|
||||||
pub fn best(self, max: u32) -> ChannelLayout<'a> {
|
|
||||||
self.fold(ChannelLayout::MONO, |acc, cur| {
|
|
||||||
if cur.channels() > acc.channels() && cur.channels() <= max {
|
|
||||||
cur
|
|
||||||
} else {
|
|
||||||
acc
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for ChannelLayoutIter<'a> {
|
|
||||||
type Item = ChannelLayout<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
unsafe {
|
|
||||||
let curr = self.next;
|
|
||||||
if *curr == zeroed_layout() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: We trust that there is always an initialized layout up until
|
|
||||||
// the zeroed-out AVChannelLayout, which signals the end of iteration.
|
|
||||||
self.next = (curr as *const AVChannelLayout).add(1).as_ref().unwrap();
|
|
||||||
Some(ChannelLayout::from(curr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this with a const variable when zeroed() is const (1.75.0)
|
|
||||||
unsafe fn zeroed_layout() -> AVChannelLayout {
|
|
||||||
std::mem::zeroed()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +1,58 @@
|
|||||||
use super::{Audio, Capabilities, Id, Profile, Video};
|
use std::marker::PhantomData;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use super::config::{
|
||||||
|
FrameRateIter, PixelFormatIter, SampleFormatIter, SampleRateIter, TerminatedPtrIter,
|
||||||
|
};
|
||||||
|
use super::descriptor::{CodecDescriptor, CodecDescriptorIter};
|
||||||
|
use super::profile::ProfileIter;
|
||||||
|
use super::{Capabilities, Id};
|
||||||
use crate::ffi::*;
|
use crate::ffi::*;
|
||||||
use crate::{media, utils, Error};
|
use crate::{media, utils};
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
use crate::codec::config::{ColorRangeIter, ColorSpaceIter, Supported};
|
||||||
|
|
||||||
|
pub fn list_descriptors() -> CodecDescriptorIter {
|
||||||
|
CodecDescriptorIter::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Audio = Codec<AudioType>;
|
||||||
|
pub type Video = Codec<VideoType>;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
pub struct Codec {
|
pub struct Codec<Type = UnknownType> {
|
||||||
ptr: *mut AVCodec,
|
ptr: NonNull<AVCodec>,
|
||||||
|
_marker: PhantomData<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Codec {}
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
unsafe impl Sync for Codec {}
|
pub struct UnknownType;
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct AudioType;
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct VideoType;
|
||||||
|
|
||||||
impl Codec {
|
unsafe impl<T> Send for Codec<T> {}
|
||||||
pub unsafe fn wrap(ptr: *mut AVCodec) -> Self {
|
unsafe impl<T> Sync for Codec<T> {}
|
||||||
Codec { ptr }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const AVCodec {
|
impl Codec<UnknownType> {
|
||||||
self.ptr as *const _
|
/// Create a new reference to a codec from a raw pointer.
|
||||||
}
|
///
|
||||||
|
/// Returns `None` if `ptr` is null.
|
||||||
pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodec {
|
pub unsafe fn from_raw(ptr: *const AVCodec) -> Option<Self> {
|
||||||
self.ptr
|
NonNull::new(ptr as *mut _).map(|ptr| Self {
|
||||||
|
ptr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Codec {
|
impl<T> Codec<T> {
|
||||||
|
pub fn as_ptr(&self) -> *const AVCodec {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_encoder(&self) -> bool {
|
pub fn is_encoder(&self) -> bool {
|
||||||
unsafe { av_codec_is_encoder(self.as_ptr()) != 0 }
|
unsafe { av_codec_is_encoder(self.as_ptr()) != 0 }
|
||||||
}
|
}
|
||||||
@ -53,13 +81,14 @@ impl Codec {
|
|||||||
self.medium() == media::Type::Video
|
self.medium() == media::Type::Video
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn video(self) -> Result<Video, Error> {
|
pub fn video(self) -> Option<Video> {
|
||||||
unsafe {
|
|
||||||
if self.medium() == media::Type::Video {
|
if self.medium() == media::Type::Video {
|
||||||
Ok(Video::new(self))
|
Some(Codec {
|
||||||
|
ptr: self.ptr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InvalidData)
|
None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +96,14 @@ impl Codec {
|
|||||||
self.medium() == media::Type::Audio
|
self.medium() == media::Type::Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn audio(self) -> Result<Audio, Error> {
|
pub fn audio(self) -> Option<Audio> {
|
||||||
unsafe {
|
|
||||||
if self.medium() == media::Type::Audio {
|
if self.medium() == media::Type::Audio {
|
||||||
Ok(Audio::new(self))
|
Some(Codec {
|
||||||
|
ptr: self.ptr,
|
||||||
|
_marker: PhantomData,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InvalidData)
|
None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,32 +124,217 @@ impl Codec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ProfileIter {
|
pub fn descriptor(self) -> Option<CodecDescriptor> {
|
||||||
id: Id,
|
unsafe {
|
||||||
ptr: *const AVProfile,
|
let ptr = avcodec_descriptor_get(self.id().into());
|
||||||
|
CodecDescriptor::from_raw(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProfileIter {
|
|
||||||
pub fn new(id: Id, ptr: *const AVProfile) -> Self {
|
|
||||||
ProfileIter { id, ptr }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for ProfileIter {
|
impl Codec<AudioType> {
|
||||||
type Item = Profile;
|
/// Checks if the given sample rate is supported by this audio codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_rate(self, rate: libc::c_int) -> bool {
|
||||||
|
self.supported_rates().supports(rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported sample rates.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_rates(self) -> Supported<SampleRateIter<'static>> {
|
||||||
|
use super::config::supported_sample_rates;
|
||||||
|
supported_sample_rates(self, None).expect("audio codec returns supported sample rates")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rates(&self) -> Option<SampleRateIter> {
|
||||||
|
unsafe { SampleRateIter::from_raw((*self.as_ptr()).supported_samplerates) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given sample format is supported by this audio codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_format(self, format: crate::format::Sample) -> bool {
|
||||||
|
self.supported_formats().supports(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported sample formats.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_formats(self) -> Supported<SampleFormatIter<'static>> {
|
||||||
|
use super::config::supported_sample_formats;
|
||||||
|
supported_sample_formats(self, None).expect("audio codec returns supported sample formats")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn formats(&self) -> Option<SampleFormatIter> {
|
||||||
|
unsafe { SampleFormatIter::from_raw((*self.as_ptr()).sample_fmts) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ffmpeg_7_0"))]
|
||||||
|
pub fn channel_layouts(&self) -> Option<ChannelLayoutMaskIter> {
|
||||||
|
unsafe { ChannelLayoutMaskIter::from_raw((*self.as_ptr()).channel_layouts) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_5_1")]
|
||||||
|
pub fn ch_layouts(&self) -> Option<ChannelLayoutIter> {
|
||||||
|
unsafe { ChannelLayoutIter::from_raw((*self.as_ptr()).ch_layouts) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Codec<VideoType> {
|
||||||
|
/// Checks if the given frame rate is supported by this video codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_rate(self, rate: crate::Rational) -> bool {
|
||||||
|
self.supported_rates().supports(rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported frame rates.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_rates(self) -> Supported<FrameRateIter<'static>> {
|
||||||
|
use crate::codec::config::supported_frame_rates;
|
||||||
|
supported_frame_rates(self, None).expect("video codec returns supported frame rates")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rates(&self) -> Option<FrameRateIter> {
|
||||||
|
unsafe { FrameRateIter::from_raw((*self.as_ptr()).supported_framerates) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given pixel format is supported by this video codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_format(self, format: crate::format::Pixel) -> bool {
|
||||||
|
self.supported_formats().supports(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported pixel formats.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_formats(self) -> Supported<PixelFormatIter<'static>> {
|
||||||
|
use crate::codec::config::supported_pixel_formats;
|
||||||
|
supported_pixel_formats(self, None).expect("video codec returns supported pixel formats")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn formats(&self) -> Option<PixelFormatIter> {
|
||||||
|
unsafe { PixelFormatIter::from_raw((*self.as_ptr()).pix_fmts) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given color space is supported by this video codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_color_space(self, space: crate::color::Space) -> bool {
|
||||||
|
self.supported_color_spaces().supports(space)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported color spaces.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_color_spaces(self) -> Supported<ColorSpaceIter<'static>> {
|
||||||
|
use crate::codec::config::supported_color_spaces;
|
||||||
|
supported_color_spaces(self, None).expect("video codec returns supported color spaces")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given color range is supported by this video codec.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supports_color_range(self, range: crate::color::Range) -> bool {
|
||||||
|
self.supported_color_ranges().supports(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Supported`] representing the supported color ranges.
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
pub fn supported_color_ranges(self) -> Supported<ColorRangeIter<'static>> {
|
||||||
|
use crate::codec::config::supported_color_ranges;
|
||||||
|
supported_color_ranges(self, None).expect("video codec returns supported color ranges")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ffmpeg_7_0"))]
|
||||||
|
use crate::ChannelLayoutMask;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ffmpeg_7_0"))]
|
||||||
|
pub struct ChannelLayoutMaskIter {
|
||||||
|
ptr: NonNull<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ffmpeg_7_0"))]
|
||||||
|
impl ChannelLayoutMaskIter {
|
||||||
|
pub fn from_raw(ptr: *const u64) -> Option<Self> {
|
||||||
|
NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn best(self, max: i32) -> ChannelLayoutMask {
|
||||||
|
self.fold(ChannelLayoutMask::MONO, |acc, cur| {
|
||||||
|
if cur.channels() > acc.channels() && cur.channels() <= max {
|
||||||
|
cur
|
||||||
|
} else {
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ffmpeg_7_0"))]
|
||||||
|
impl Iterator for ChannelLayoutMaskIter {
|
||||||
|
type Item = ChannelLayoutMask;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if (*self.ptr).profile == FF_PROFILE_UNKNOWN {
|
let ptr = self.ptr.as_ptr();
|
||||||
|
if *ptr == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let profile = Profile::from((self.id, (*self.ptr).profile));
|
let layout = ChannelLayoutMask::from_bits_truncate(*ptr);
|
||||||
self.ptr = self.ptr.offset(1);
|
self.ptr = NonNull::new_unchecked(ptr.add(1));
|
||||||
|
|
||||||
Some(profile)
|
Some(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_5_1")]
|
||||||
|
pub use ch_layout::ChannelLayoutIter;
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_5_1")]
|
||||||
|
mod ch_layout {
|
||||||
|
use super::*;
|
||||||
|
use crate::ChannelLayout;
|
||||||
|
|
||||||
|
pub struct ChannelLayoutIter<'a> {
|
||||||
|
next: &'a AVChannelLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ChannelLayoutIter<'a> {
|
||||||
|
pub unsafe fn from_raw(ptr: *const AVChannelLayout) -> Option<Self> {
|
||||||
|
ptr.as_ref().map(|next| Self { next })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ChannelLayoutIter<'a> {
|
||||||
|
pub fn best(self, max: u32) -> ChannelLayout<'a> {
|
||||||
|
self.fold(ChannelLayout::MONO, |acc, cur| {
|
||||||
|
if cur.channels() > acc.channels() && cur.channels() <= max {
|
||||||
|
cur
|
||||||
|
} else {
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ChannelLayoutIter<'a> {
|
||||||
|
type Item = ChannelLayout<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
let curr = self.next;
|
||||||
|
if *curr == zeroed_layout() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: We trust that there is always an initialized layout up until
|
||||||
|
// the zeroed-out AVChannelLayout, which signals the end of iteration.
|
||||||
|
self.next = (curr as *const AVChannelLayout).add(1).as_ref().unwrap();
|
||||||
|
Some(ChannelLayout::from(curr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this with a const variable when zeroed() is const (1.75.0)
|
||||||
|
unsafe fn zeroed_layout() -> AVChannelLayout {
|
||||||
|
std::mem::zeroed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
397
src/codec/config.rs
Normal file
397
src/codec/config.rs
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
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>,
|
||||||
|
{
|
||||||
|
/// Check if all possible configuration values are supported.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ffmpeg_the_third::codec::{encoder, Id};
|
||||||
|
///
|
||||||
|
/// let codec = encoder::find(Id::VP9)
|
||||||
|
/// .expect("Can find a VP9 encoder")
|
||||||
|
/// .video()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// let supported = codec.supported_rates();
|
||||||
|
/// assert!(supported.all())
|
||||||
|
/// ```
|
||||||
|
pub fn all(&self) -> bool {
|
||||||
|
matches!(self, Supported::All)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a specific configuration value is supported.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ffmpeg_the_third::codec::{encoder, Id};
|
||||||
|
/// use ffmpeg_the_third::format::sample::{Sample, Type};
|
||||||
|
///
|
||||||
|
/// let codec = encoder::find(Id::MP3)
|
||||||
|
/// .expect("Can find an MP3 encoder")
|
||||||
|
/// .audio()
|
||||||
|
/// .unwrap();
|
||||||
|
///
|
||||||
|
/// let supported = codec.supported_formats();
|
||||||
|
/// assert!(supported.supports(Sample::F32(Type::Planar)));
|
||||||
|
/// ```
|
||||||
|
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, // flags: unused as of 7.1, set to zero
|
||||||
|
&mut out_ptr,
|
||||||
|
std::ptr::null_mut(), // out_num_configs: optional, we don't support it currently
|
||||||
|
);
|
||||||
|
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(Error::from(ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
match NonNull::new(out_ptr as *mut _) {
|
||||||
|
// non-nullptr -> Specific list of values is supported.
|
||||||
|
Some(ptr) => Ok(Supported::Specific(I::from_ptr(ptr))),
|
||||||
|
// nullptr -> Everything is supported
|
||||||
|
None => Ok(Supported::All),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pointer-based iterator, stepped through via pointer arithmetic and ended
|
||||||
|
/// when a dereferenced pointer equals a terminator value.
|
||||||
|
pub(crate) trait TerminatedPtrIter<AVType, WrapperType>:
|
||||||
|
Sized + Iterator<Item = WrapperType>
|
||||||
|
{
|
||||||
|
/// Create a new iterator from a non-null pointer to any value in the iteration.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `ptr` and all following pointers must be dereferenceable until the terminator is reached.
|
||||||
|
unsafe fn from_ptr(ptr: NonNull<AVType>) -> Self;
|
||||||
|
|
||||||
|
/// Create a new iterator from a pointer to any value in the iteration.
|
||||||
|
///
|
||||||
|
/// Returns `None` if `ptr` is null. See also [`from_ptr`][TerminatedPtrIter::from_ptr].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// See [`from_ptr`][TerminatedPtrIter::from_ptr].
|
||||||
|
unsafe fn from_raw(ptr: *const AVType) -> Option<Self> {
|
||||||
|
unsafe { NonNull::new(ptr as *mut _).map(|ptr| Self::from_ptr(ptr)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
/// Low-level function interacting with the FFmpeg API via
|
||||||
|
/// `avcodec_get_supported_config()`. Consider using one of the convenience methods
|
||||||
|
/// on the codecs or codec contexts instead.
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We make sure that this is true by not incrementing self.ptr after the
|
||||||
|
// terminator has been reached.
|
||||||
|
impl<'a> std::iter::FusedIterator for $iter<'a> {}
|
||||||
|
|
||||||
|
// TODO: Maybe add ExactSizeIterator? This would require using the out_num_configs
|
||||||
|
// parameter and storing it inside $iter. Not sure it's too important unless
|
||||||
|
// many people want to use .collect() or something else that benefits from
|
||||||
|
// ExactSizeIterator.
|
||||||
|
|
||||||
|
impl<'a> Iterator for $iter<'a> {
|
||||||
|
type Item = $ty;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// SAFETY: The FFmpeg API guarantees that the pointer is safe to deref and
|
||||||
|
// increment until the terminator is reached.
|
||||||
|
unsafe {
|
||||||
|
let curr = self.next.as_ptr();
|
||||||
|
if *curr == $terminator {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with the following if MSRV >= 1.80:
|
||||||
|
// self.next = NonNull::from(self.next).add(1).as_ref();
|
||||||
|
self.next = std::ptr::NonNull::new_unchecked(curr.add(1));
|
||||||
|
|
||||||
|
Some((*curr).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_pixel_formats,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_PIX_FORMAT,
|
||||||
|
PixelFormatIter,
|
||||||
|
crate::format::Pixel,
|
||||||
|
crate::ffi::AVPixelFormat,
|
||||||
|
crate::ffi::AVPixelFormat::AV_PIX_FMT_NONE
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_frame_rates,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_FRAME_RATE,
|
||||||
|
FrameRateIter,
|
||||||
|
crate::Rational,
|
||||||
|
crate::ffi::AVRational,
|
||||||
|
crate::ffi::AVRational { num: 0, den: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_sample_rates,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_RATE,
|
||||||
|
SampleRateIter,
|
||||||
|
libc::c_int,
|
||||||
|
libc::c_int,
|
||||||
|
0 as libc::c_int
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_sample_formats,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_SAMPLE_FORMAT,
|
||||||
|
SampleFormatIter,
|
||||||
|
crate::format::Sample,
|
||||||
|
crate::ffi::AVSampleFormat,
|
||||||
|
crate::ffi::AVSampleFormat::AV_SAMPLE_FMT_NONE
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_color_ranges,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_COLOR_RANGE,
|
||||||
|
ColorRangeIter,
|
||||||
|
crate::color::Range,
|
||||||
|
crate::ffi::AVColorRange,
|
||||||
|
crate::ffi::AVColorRange::AVCOL_RANGE_UNSPECIFIED
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "ffmpeg_7_1")]
|
||||||
|
impl_config_iter!(
|
||||||
|
supported_color_spaces,
|
||||||
|
crate::ffi::AVCodecConfig::AV_CODEC_CONFIG_COLOR_SPACE,
|
||||||
|
ColorSpaceIter,
|
||||||
|
crate::color::Space,
|
||||||
|
crate::ffi::AVColorSpace,
|
||||||
|
crate::ffi::AVColorSpace::AVCOL_SPC_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;
|
||||||
|
|
||||||
|
// These tests can fail if the FFmpeg build does not contain the required de/encoder.
|
||||||
|
// TODO: Check if tests can be hidden behind feature flags.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn audio_decoder() {
|
||||||
|
let codec = decoder::find(Id::MP3).expect("can find mp3 decoder");
|
||||||
|
|
||||||
|
// Audio decoder does not have color ranges
|
||||||
|
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::MP3).expect("can find mp3 encoder");
|
||||||
|
|
||||||
|
// looks like every codec returns Supported::All for color space.
|
||||||
|
// might change in a future FFmpeg release
|
||||||
|
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)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn supports() {
|
||||||
|
let codec = encoder::find(Id::VP9).expect("can find VP9 encoder");
|
||||||
|
|
||||||
|
assert!(supported_color_ranges(codec, None)
|
||||||
|
.expect("can check color range support")
|
||||||
|
.supports(Range::JPEG));
|
||||||
|
|
||||||
|
assert!(!supported_pixel_formats(codec, None)
|
||||||
|
.expect("can check color range support")
|
||||||
|
.supports(Pixel::BGR8));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Note that we check for NOT supported above, and YES supported here
|
||||||
|
// MJPEG encoder only supports MPEG color range if compliance is
|
||||||
|
// Unofficial or lower (less strict)
|
||||||
|
assert!(supported_color_ranges(ctx.codec().unwrap(), Some(&ctx))
|
||||||
|
.expect("can check color range support")
|
||||||
|
.supports(Range::MPEG));
|
||||||
|
}
|
||||||
|
}
|
@ -62,13 +62,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn codec(&self) -> Option<Codec> {
|
pub fn codec(&self) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe { Codec::from_raw((*self.as_ptr()).codec) }
|
||||||
if (*self.as_ptr()).codec.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap((*self.as_ptr()).codec as *mut _))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn medium(&self) -> media::Type {
|
pub fn medium(&self) -> media::Type {
|
||||||
|
@ -18,7 +18,7 @@ impl Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as<D: traits::Decoder>(mut self, codec: D) -> Result<Opened, Error> {
|
pub fn open_as<T, D: traits::Decoder<T>>(mut self, codec: D) -> Result<Opened, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(codec) = codec.decoder() {
|
if let Some(codec) = codec.decoder() {
|
||||||
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
||||||
@ -31,7 +31,7 @@ impl Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as_with<D: traits::Decoder>(
|
pub fn open_as_with<T, D: traits::Decoder<T>>(
|
||||||
mut self,
|
mut self,
|
||||||
codec: D,
|
codec: D,
|
||||||
options: Dictionary,
|
options: Dictionary,
|
||||||
|
@ -34,25 +34,16 @@ pub fn new() -> Decoder {
|
|||||||
|
|
||||||
pub fn find(id: Id) -> Option<Codec> {
|
pub fn find(id: Id) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = avcodec_find_decoder(id.into()) as *mut AVCodec;
|
let ptr = avcodec_find_decoder(id.into());
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_name(name: &str) -> Option<Codec> {
|
pub fn find_by_name(name: &str) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CString::new(name).unwrap();
|
let name = CString::new(name).unwrap();
|
||||||
let ptr = avcodec_find_decoder_by_name(name.as_ptr()) as *mut AVCodec;
|
let ptr = avcodec_find_decoder_by_name(name.as_ptr());
|
||||||
|
|
||||||
if ptr.is_null() {
|
Codec::from_raw(ptr)
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
158
src/codec/descriptor.rs
Normal file
158
src/codec/descriptor.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use std::ffi::CStr;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::str::from_utf8_unchecked;
|
||||||
|
|
||||||
|
use crate::ffi::*;
|
||||||
|
use crate::media;
|
||||||
|
|
||||||
|
use super::profile::ProfileIter;
|
||||||
|
use super::{CodecProperties, Id};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct CodecDescriptor {
|
||||||
|
ptr: NonNull<AVCodecDescriptor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodecDescriptor {
|
||||||
|
pub unsafe fn from_raw(ptr: *const AVCodecDescriptor) -> Option<Self> {
|
||||||
|
NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(self) -> *const AVCodecDescriptor {
|
||||||
|
self.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(self) -> Id {
|
||||||
|
unsafe { (*self.as_ptr()).id.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kind(self) -> media::Type {
|
||||||
|
unsafe { (*self.as_ptr()).type_.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self) -> &'static str {
|
||||||
|
unsafe { from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(self) -> Option<&'static str> {
|
||||||
|
unsafe {
|
||||||
|
let long_name = (*self.as_ptr()).long_name;
|
||||||
|
if long_name.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(from_utf8_unchecked(CStr::from_ptr(long_name).to_bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn props(self) -> CodecProperties {
|
||||||
|
unsafe { CodecProperties::from_bits_truncate((*self.as_ptr()).props) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mime_types(self) -> Option<MimeTypeIter> {
|
||||||
|
unsafe { MimeTypeIter::from_raw((*self.as_ptr()).mime_types) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn profiles(self) -> Option<ProfileIter> {
|
||||||
|
unsafe {
|
||||||
|
if (*self.as_ptr()).profiles.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ProfileIter::new(self.id(), (*self.as_ptr()).profiles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CodecDescriptorIter {
|
||||||
|
ptr: *const AVCodecDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodecDescriptorIter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: std::ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CodecDescriptorIter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for CodecDescriptorIter {
|
||||||
|
type Item = CodecDescriptor;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
let next = avcodec_descriptor_next(self.ptr);
|
||||||
|
if let Some(desc) = CodecDescriptor::from_raw(next) {
|
||||||
|
self.ptr = next;
|
||||||
|
Some(desc)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MimeTypeIter {
|
||||||
|
ptr: NonNull<*const libc::c_char>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MimeTypeIter {
|
||||||
|
pub unsafe fn from_raw(ptr: *const *const libc::c_char) -> Option<Self> {
|
||||||
|
NonNull::new(ptr as *mut _).map(|ptr| Self { ptr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for MimeTypeIter {
|
||||||
|
type Item = &'static str;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
unsafe {
|
||||||
|
let next = *self.ptr.as_ptr();
|
||||||
|
if next.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ptr = NonNull::new_unchecked(self.ptr.as_ptr().add(1));
|
||||||
|
Some(from_utf8_unchecked(CStr::from_ptr(next).to_bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::decoder::find;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn descriptor() {
|
||||||
|
let targa = find(Id::TARGA).expect("can find targa decoder");
|
||||||
|
let desc = targa.descriptor().expect("targa has descriptor");
|
||||||
|
assert_eq!(desc.id(), Id::TARGA);
|
||||||
|
assert_eq!(desc.kind(), media::Type::Video);
|
||||||
|
assert_eq!(desc.name(), "targa");
|
||||||
|
|
||||||
|
// --enable-small will remove all `long_name`s. So this can either be null/None
|
||||||
|
// or the correct description
|
||||||
|
assert!(matches!(
|
||||||
|
desc.description(),
|
||||||
|
None | Some("Truevision Targa image")
|
||||||
|
));
|
||||||
|
|
||||||
|
let props = desc.props();
|
||||||
|
assert!(
|
||||||
|
props.contains(CodecProperties::INTRA_ONLY)
|
||||||
|
&& props.contains(CodecProperties::LOSSLESS)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut mime_types = desc.mime_types().expect("has mime types");
|
||||||
|
assert_eq!(mime_types.next(), Some("image/x-targa"));
|
||||||
|
assert_eq!(mime_types.next(), Some("image/x-tga"));
|
||||||
|
assert_eq!(mime_types.next(), None);
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as<E: traits::Encoder>(mut self, codec: E) -> Result<Encoder, Error> {
|
pub fn open_as<T, E: traits::Encoder<T>>(mut self, codec: E) -> Result<Encoder, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(codec) = codec.encoder() {
|
if let Some(codec) = codec.encoder() {
|
||||||
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
||||||
@ -57,7 +57,7 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as_with<E: traits::Encoder>(
|
pub fn open_as_with<T, E: traits::Encoder<T>>(
|
||||||
mut self,
|
mut self,
|
||||||
codec: E,
|
codec: E,
|
||||||
options: Dictionary,
|
options: Dictionary,
|
||||||
|
@ -37,25 +37,15 @@ pub fn new() -> Encoder {
|
|||||||
|
|
||||||
pub fn find(id: Id) -> Option<Codec> {
|
pub fn find(id: Id) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = avcodec_find_encoder(id.into()) as *mut AVCodec;
|
let ptr = avcodec_find_encoder(id.into());
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_name(name: &str) -> Option<Codec> {
|
pub fn find_by_name(name: &str) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CString::new(name).unwrap();
|
let name = CString::new(name).unwrap();
|
||||||
let ptr = avcodec_find_encoder_by_name(name.as_ptr()) as *mut AVCodec;
|
let ptr = avcodec_find_encoder_by_name(name.as_ptr());
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ impl Subtitle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as<E: traits::Encoder>(mut self, codec: E) -> Result<Encoder, Error> {
|
pub fn open_as<T, E: traits::Encoder<T>>(mut self, codec: E) -> Result<Encoder, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(codec) = codec.encoder() {
|
if let Some(codec) = codec.encoder() {
|
||||||
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
||||||
@ -33,7 +33,7 @@ impl Subtitle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as_with<E: traits::Encoder>(
|
pub fn open_as_with<T, E: traits::Encoder<T>>(
|
||||||
mut self,
|
mut self,
|
||||||
codec: E,
|
codec: E,
|
||||||
options: Dictionary,
|
options: Dictionary,
|
||||||
|
@ -27,7 +27,7 @@ impl Video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn open_as<E: traits::Encoder>(mut self, codec: E) -> Result<Encoder, Error> {
|
pub fn open_as<T, E: traits::Encoder<T>>(mut self, codec: E) -> Result<Encoder, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(codec) = codec.encoder() {
|
if let Some(codec) = codec.encoder() {
|
||||||
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) {
|
||||||
@ -56,7 +56,7 @@ impl Video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn open_as_with<E: traits::Encoder>(
|
pub fn open_as_with<T, E: traits::Encoder<T>>(
|
||||||
mut self,
|
mut self,
|
||||||
codec: E,
|
codec: E,
|
||||||
options: Dictionary,
|
options: Dictionary,
|
||||||
|
@ -11,8 +11,13 @@ pub mod subtitle;
|
|||||||
#[cfg(not(feature = "ffmpeg_5_0"))]
|
#[cfg(not(feature = "ffmpeg_5_0"))]
|
||||||
pub mod picture;
|
pub mod picture;
|
||||||
|
|
||||||
|
pub mod descriptor;
|
||||||
|
pub use self::descriptor::CodecDescriptor;
|
||||||
|
|
||||||
pub mod discard;
|
pub mod discard;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub use self::context::Context;
|
pub use self::context::Context;
|
||||||
|
|
||||||
@ -20,16 +25,11 @@ pub mod capabilities;
|
|||||||
pub use self::capabilities::Capabilities;
|
pub use self::capabilities::Capabilities;
|
||||||
|
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
|
pub use self::codec::{Audio, Codec, Video};
|
||||||
|
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
pub use self::parameters::Parameters;
|
pub use self::parameters::Parameters;
|
||||||
|
|
||||||
pub mod video;
|
|
||||||
pub use self::video::Video;
|
|
||||||
|
|
||||||
pub mod audio;
|
|
||||||
pub use self::audio::Audio;
|
|
||||||
|
|
||||||
pub mod audio_service;
|
pub mod audio_service;
|
||||||
pub mod field_order;
|
pub mod field_order;
|
||||||
|
|
||||||
@ -42,6 +42,9 @@ pub use self::debug::Debug;
|
|||||||
pub mod profile;
|
pub mod profile;
|
||||||
pub use self::profile::Profile;
|
pub use self::profile::Profile;
|
||||||
|
|
||||||
|
pub mod props;
|
||||||
|
pub use self::props::CodecProperties;
|
||||||
|
|
||||||
pub mod threading;
|
pub mod threading;
|
||||||
|
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
|
@ -387,3 +387,31 @@ impl From<Profile> for c_int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ProfileIter {
|
||||||
|
id: Id,
|
||||||
|
ptr: *const AVProfile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfileIter {
|
||||||
|
pub fn new(id: Id, ptr: *const AVProfile) -> Self {
|
||||||
|
ProfileIter { id, ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ProfileIter {
|
||||||
|
type Item = Profile;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
||||||
|
unsafe {
|
||||||
|
if (*self.ptr).profile == FF_PROFILE_UNKNOWN {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let profile = Profile::from((self.id, (*self.ptr).profile));
|
||||||
|
self.ptr = self.ptr.offset(1);
|
||||||
|
|
||||||
|
Some(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
17
src/codec/props.rs
Normal file
17
src/codec/props.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use crate::ffi::*;
|
||||||
|
use libc::c_int;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct CodecProperties: c_int {
|
||||||
|
const INTRA_ONLY = AV_CODEC_PROP_INTRA_ONLY;
|
||||||
|
const LOSSY = AV_CODEC_PROP_LOSSY;
|
||||||
|
const LOSSLESS = AV_CODEC_PROP_LOSSLESS;
|
||||||
|
const REORDER = AV_CODEC_PROP_REORDER;
|
||||||
|
#[cfg(feature = "ffmpeg_6_1")]
|
||||||
|
const FIELDS = AV_CODEC_PROP_FIELDS;
|
||||||
|
|
||||||
|
const BITMAP_SUB = AV_CODEC_PROP_BITMAP_SUB;
|
||||||
|
const TEXT_SUB = AV_CODEC_PROP_TEXT_SUB;
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,26 @@
|
|||||||
|
use super::codec::UnknownType;
|
||||||
use super::{decoder, encoder};
|
use super::{decoder, encoder};
|
||||||
use crate::codec::{Audio, Id, Video};
|
use crate::codec::Id;
|
||||||
use crate::Codec;
|
use crate::Codec;
|
||||||
|
|
||||||
pub trait Decoder {
|
pub trait Decoder<T> {
|
||||||
fn decoder(self) -> Option<Codec>;
|
fn decoder(self) -> Option<Codec<T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Decoder for &'a str {
|
impl<'a> Decoder<UnknownType> for &'a str {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn decoder(self) -> Option<Codec<UnknownType>> {
|
||||||
decoder::find_by_name(self)
|
decoder::find_by_name(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Id {
|
impl Decoder<UnknownType> for Id {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn decoder(self) -> Option<Codec<UnknownType>> {
|
||||||
decoder::find(self)
|
decoder::find(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Codec {
|
impl<T> Decoder<T> for Codec<T> {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn decoder(self) -> Option<Codec<T>> {
|
||||||
if self.is_decoder() {
|
if self.is_decoder() {
|
||||||
Some(self)
|
Some(self)
|
||||||
} else {
|
} else {
|
||||||
@ -28,50 +29,30 @@ impl Decoder for Codec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Option<Codec> {
|
impl<T> Decoder<T> for Option<Codec<T>> {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn decoder(self) -> Option<Codec<T>> {
|
||||||
self.and_then(|c| c.decoder())
|
self.and_then(|c| c.decoder())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Audio {
|
pub trait Encoder<T> {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn encoder(self) -> Option<Codec<T>>;
|
||||||
if self.is_decoder() {
|
|
||||||
Some(*self)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for Video {
|
impl<'a> Encoder<UnknownType> for &'a str {
|
||||||
fn decoder(self) -> Option<Codec> {
|
fn encoder(self) -> Option<Codec<UnknownType>> {
|
||||||
if self.is_decoder() {
|
|
||||||
Some(*self)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Encoder {
|
|
||||||
fn encoder(self) -> Option<Codec>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Encoder for &'a str {
|
|
||||||
fn encoder(self) -> Option<Codec> {
|
|
||||||
encoder::find_by_name(self)
|
encoder::find_by_name(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Id {
|
impl Encoder<UnknownType> for Id {
|
||||||
fn encoder(self) -> Option<Codec> {
|
fn encoder(self) -> Option<Codec<UnknownType>> {
|
||||||
encoder::find(self)
|
encoder::find(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Codec {
|
impl<T> Encoder<T> for Codec<T> {
|
||||||
fn encoder(self) -> Option<Codec> {
|
fn encoder(self) -> Option<Codec<T>> {
|
||||||
if self.is_encoder() {
|
if self.is_encoder() {
|
||||||
Some(self)
|
Some(self)
|
||||||
} else {
|
} else {
|
||||||
@ -80,28 +61,8 @@ impl Encoder for Codec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Option<Codec> {
|
impl<T> Encoder<T> for Option<Codec<T>> {
|
||||||
fn encoder(self) -> Option<Codec> {
|
fn encoder(self) -> Option<Codec<T>> {
|
||||||
self.and_then(|c| c.encoder())
|
self.and_then(|c| c.encoder())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Audio {
|
|
||||||
fn encoder(self) -> Option<Codec> {
|
|
||||||
if self.is_encoder() {
|
|
||||||
Some(*self)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encoder for Video {
|
|
||||||
fn encoder(self) -> Option<Codec> {
|
|
||||||
if self.is_encoder() {
|
|
||||||
Some(*self)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use super::codec::Codec;
|
|
||||||
use crate::ffi::*;
|
|
||||||
use crate::{format, Rational};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
|
||||||
pub struct Video {
|
|
||||||
codec: Codec,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Video {
|
|
||||||
pub unsafe fn new(codec: Codec) -> Video {
|
|
||||||
Video { codec }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Video {
|
|
||||||
pub fn rates(&self) -> Option<RateIter> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.codec.as_ptr()).supported_framerates.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(RateIter::new((*self.codec.as_ptr()).supported_framerates))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn formats(&self) -> Option<FormatIter> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.codec.as_ptr()).pix_fmts.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FormatIter::new((*self.codec.as_ptr()).pix_fmts))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Video {
|
|
||||||
type Target = Codec;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.codec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RateIter {
|
|
||||||
ptr: *const AVRational,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RateIter {
|
|
||||||
pub fn new(ptr: *const AVRational) -> Self {
|
|
||||||
RateIter { ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for RateIter {
|
|
||||||
type Item = Rational;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
||||||
unsafe {
|
|
||||||
if (*self.ptr).num == 0 && (*self.ptr).den == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rate = (*self.ptr).into();
|
|
||||||
self.ptr = self.ptr.offset(1);
|
|
||||||
|
|
||||||
Some(rate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FormatIter {
|
|
||||||
ptr: *const AVPixelFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormatIter {
|
|
||||||
pub fn new(ptr: *const AVPixelFormat) -> Self {
|
|
||||||
FormatIter { ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for FormatIter {
|
|
||||||
type Item = format::Pixel;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
|
|
||||||
unsafe {
|
|
||||||
if *self.ptr == AVPixelFormat::AV_PIX_FMT_NONE {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let format = (*self.ptr).into();
|
|
||||||
self.ptr = self.ptr.offset(1);
|
|
||||||
|
|
||||||
Some(format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,12 +42,7 @@ impl Input {
|
|||||||
pub fn video_codec(&self) -> Option<Codec> {
|
pub fn video_codec(&self) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (*self.as_ptr()).video_codec;
|
let ptr = (*self.as_ptr()).video_codec;
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +50,7 @@ impl Input {
|
|||||||
pub fn audio_codec(&self) -> Option<Codec> {
|
pub fn audio_codec(&self) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (*self.as_ptr()).audio_codec;
|
let ptr = (*self.as_ptr()).audio_codec;
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,12 +58,7 @@ impl Input {
|
|||||||
pub fn subtitle_codec(&self) -> Option<Codec> {
|
pub fn subtitle_codec(&self) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (*self.as_ptr()).subtitle_codec;
|
let ptr = (*self.as_ptr()).subtitle_codec;
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,12 +66,7 @@ impl Input {
|
|||||||
pub fn data_codec(&self) -> Option<Codec> {
|
pub fn data_codec(&self) -> Option<Codec> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = (*self.as_ptr()).data_codec;
|
let ptr = (*self.as_ptr()).data_codec;
|
||||||
|
Codec::from_raw(ptr)
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Codec::wrap(ptr))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ impl Output {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_stream<E: traits::Encoder>(&mut self, codec: E) -> Result<StreamMut, Error> {
|
pub fn add_stream<T, E: traits::Encoder<T>>(&mut self, codec: E) -> Result<StreamMut, Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let codec = codec.encoder();
|
let codec = codec.encoder();
|
||||||
let codec = codec.map_or(ptr::null(), |c| c.as_ptr());
|
let codec = codec.map_or(ptr::null(), |c| c.as_ptr());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user