diff --git a/examples/remux.rs b/examples/remux.rs index 59ec8270..f9b3e975 100644 --- a/examples/remux.rs +++ b/examples/remux.rs @@ -29,7 +29,9 @@ fn main() { stream_mapping[ist_index] = ost_index; ist_time_bases[ist_index] = ist.time_base(); ost_index += 1; - let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); + let mut ost = octx + .add_stream(encoder::find(codec::Id::None).expect("ID_NONE encoder exists")) + .unwrap(); ost.set_parameters(ist.parameters()); // We need to set codec_tag to 0 lest we run into incompatible codec tag // issues when muxing into a different container format. Unfortunately diff --git a/examples/transcode-x264.rs b/examples/transcode-x264.rs index f969dc8e..20ecf22d 100644 --- a/examples/transcode-x264.rs +++ b/examples/transcode-x264.rs @@ -53,10 +53,10 @@ impl Transcoder { .decoder() .video()?; - let codec = encoder::find(codec::Id::H264); + let codec = encoder::find(codec::Id::H264).ok_or(ffmpeg::Error::EncoderNotFound)?; let mut ost = octx.add_stream(codec)?; - let mut encoder = codec::context::Context::new_with_codec(codec.unwrap()) + let mut encoder = codec::context::Context::new_with_codec(codec) .encoder() .video()?; ost.set_parameters(Parameters::from(&encoder)); @@ -221,7 +221,9 @@ fn main() { ); } else { // Set up for stream copy for non-video stream. - let mut ost = octx.add_stream(encoder::find(codec::Id::None)).unwrap(); + let mut ost = octx + .add_stream(encoder::find(codec::Id::None).expect("ID_NONE encoder exists")) + .unwrap(); ost.set_parameters(ist.parameters()); // We need to set codec_tag to 0 lest we run into incompatible codec tag // issues when muxing into a different container format. Unfortunately diff --git a/src/codec/codec.rs b/src/codec/codec.rs index a30449c9..3051fb3a 100644 --- a/src/codec/codec.rs +++ b/src/codec/codec.rs @@ -8,6 +8,7 @@ use super::descriptor::{CodecDescriptor, CodecDescriptorIter}; use super::profile::ProfileIter; use super::{Capabilities, Id}; use crate::ffi::*; +use crate::AsPtr; use crate::{media, utils}; #[cfg(feature = "ffmpeg_7_1")] @@ -17,18 +18,41 @@ pub fn list_descriptors() -> CodecDescriptorIter { CodecDescriptorIter::new() } -pub type Audio = Codec; -pub type Video = Codec; -pub type Data = Codec; -pub type Subtitle = Codec; -pub type Attachment = Codec; +pub type Audio = Codec; +pub type Video = Codec; +pub type Data = Codec; +pub type Subtitle = Codec; +pub type Attachment = Codec; + +pub type Decoder = Codec; +pub type UnknownDecoder = Codec; +pub type AudioDecoder = Codec; +pub type VideoDecoder = Codec; +pub type DataDecoder = Codec; +pub type SubtitleDecoder = Codec; +pub type AttachmentDecoder = Codec; + +pub type Encoder = Codec; +pub type UnknownEncoder = Codec; +pub type AudioEncoder = Codec; +pub type VideoEncoder = Codec; +pub type DataEncoder = Codec; +pub type SubtitleEncoder = Codec; +pub type AttachmentEncoder = Codec; #[derive(PartialEq, Eq, Copy, Clone)] -pub struct Codec { +pub struct Codec { ptr: NonNull, - _marker: PhantomData, + _marker: PhantomData<(Action, Type)>, } +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct UnknownAction; +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct DecodingAction; +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct EncodingAction; + #[derive(PartialEq, Eq, Copy, Clone)] pub struct UnknownType; #[derive(PartialEq, Eq, Copy, Clone)] @@ -42,10 +66,10 @@ pub struct SubtitleType; #[derive(PartialEq, Eq, Copy, Clone)] pub struct AttachmentType; -unsafe impl Send for Codec {} -unsafe impl Sync for Codec {} +unsafe impl Send for Codec {} +unsafe impl Sync for Codec {} -impl Codec { +impl Codec { /// Create a new reference to a codec from a raw pointer. /// /// Returns `None` if `ptr` is null. @@ -59,84 +83,18 @@ impl Codec { // Helper function to easily convert to another codec type. // TODO: Does this need to be unsafe? /// Ensure that `self.medium()` is correct for `Codec`. - fn as_other_codec(self) -> Codec { + fn as_other_codec(&self) -> Codec { Codec { ptr: self.ptr, _marker: PhantomData, } } - pub fn is_video(&self) -> bool { - self.medium() == media::Type::Video - } - - pub fn video(self) -> Option Codec { + pub fn is_video(self) -> bool { + self.medium() == media::Type::Video + } + + pub fn is_audio(self) -> bool { + self.medium() == media::Type::Audio + } + + pub fn is_data(self) -> bool { + self.medium() == media::Type::Data + } + + pub fn is_subtitle(self) -> bool { + self.medium() == media::Type::Subtitle + } + + pub fn is_attachment(self) -> bool { + self.medium() == media::Type::Attachment + } +} + +impl Codec { + pub fn video(self) -> Option> { + self.is_video().then(|| self.as_other_codec()) + } + + pub fn audio(self) -> Option> { + self.is_audio().then(|| self.as_other_codec()) + } + + pub fn data(self) -> Option> { + self.is_data().then(|| self.as_other_codec()) + } + + pub fn subtitle(self) -> Option> { + self.is_subtitle().then(|| self.as_other_codec()) + } + + pub fn attachment(self) -> Option> { + self.is_attachment().then(|| self.as_other_codec()) + } +} + +impl Codec { /// 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 { @@ -228,7 +240,7 @@ impl Codec { } } -impl Codec { +impl Codec { /// 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 { @@ -387,3 +399,9 @@ mod ch_layout { std::mem::zeroed() } } + +impl AsPtr for Codec { + fn as_ptr(&self) -> *const AVCodec { + self.ptr.as_ptr() + } +} diff --git a/src/codec/config.rs b/src/codec/config.rs index d1d3d168..96425436 100644 --- a/src/codec/config.rs +++ b/src/codec/config.rs @@ -5,6 +5,8 @@ use crate::codec::Context; #[cfg(feature = "ffmpeg_7_1")] use crate::ffi::*; #[cfg(feature = "ffmpeg_7_1")] +use crate::AsPtr; +#[cfg(feature = "ffmpeg_7_1")] use crate::Codec; #[cfg(feature = "ffmpeg_7_1")] use crate::Error; @@ -66,12 +68,13 @@ where } #[cfg(feature = "ffmpeg_7_1")] -fn supported( - codec: Codec, +fn supported( + codec: C, ctx: Option<&Context>, cfg: AVCodecConfig, ) -> Result, Error> where + C: AsPtr, I: TerminatedPtrIter, AVType: Into, { @@ -157,8 +160,8 @@ macro_rules! impl_config_iter_fn { /// `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( - codec: Codec, + pub fn $fn_name( + codec: Codec, ctx: Option<&Context>, ) -> Result, Error> { supported(codec, ctx, $codec_cfg) diff --git a/src/codec/context.rs b/src/codec/context.rs index dd67b77a..9bbb00ee 100644 --- a/src/codec/context.rs +++ b/src/codec/context.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use super::decoder::Decoder; use super::encoder::Encoder; use super::{threading, Compliance, Debug, Flags, Id}; +use crate::codec::codec::{UnknownAction, UnknownType}; use crate::ffi::*; use crate::media; use crate::option; @@ -43,7 +44,7 @@ impl Context { } } - pub fn new_with_codec(codec: Codec) -> Self { + pub fn new_with_codec(codec: Codec) -> Self { unsafe { Context { ptr: avcodec_alloc_context3(codec.as_ptr()), @@ -71,7 +72,7 @@ impl Context { Encoder(self) } - pub fn codec(&self) -> Option { + pub fn codec(&self) -> Option> { unsafe { Codec::from_raw((*self.as_ptr()).codec) } } diff --git a/src/codec/decoder/decoder.rs b/src/codec/decoder/decoder.rs index 6118a93c..d5b03285 100644 --- a/src/codec/decoder/decoder.rs +++ b/src/codec/decoder/decoder.rs @@ -2,8 +2,10 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use super::{Audio, Check, Conceal, Opened, Subtitle, Video}; -use crate::codec::{traits, Context}; +use crate::codec::codec; +use crate::codec::Context; use crate::ffi::*; +use crate::AsPtr; use crate::{Dictionary, Discard, Error, Rational}; pub struct Decoder(pub Context); @@ -18,37 +20,29 @@ impl Decoder { } } - pub fn open_as>(mut self, codec: D) -> Result { + pub fn open_as(mut self, codec: codec::Decoder) -> Result { unsafe { - if let Some(codec) = codec.decoder() { - match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { - 0 => Ok(Opened(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::DecoderNotFound) + match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { + 0 => Ok(Opened(self)), + e => Err(Error::from(e)), } } } - pub fn open_as_with>( + pub fn open_as_with( mut self, - codec: D, + codec: codec::Decoder, options: Dictionary, ) -> Result { unsafe { - if let Some(codec) = codec.decoder() { - let mut opts = options.disown(); - let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); - - Dictionary::own(opts); - - match res { - 0 => Ok(Opened(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::DecoderNotFound) + let mut opts = options.disown(); + let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); + + Dictionary::own(opts); + + match res { + 0 => Ok(Opened(self)), + e => Err(Error::from(e)), } } } diff --git a/src/codec/decoder/mod.rs b/src/codec/decoder/mod.rs index c83489ce..295fb4e9 100644 --- a/src/codec/decoder/mod.rs +++ b/src/codec/decoder/mod.rs @@ -23,6 +23,7 @@ pub use self::opened::Opened; use std::ffi::CString; +use crate::codec::codec::UnknownDecoder; use crate::codec::Context; use crate::codec::Id; use crate::ffi::*; @@ -32,14 +33,14 @@ pub fn new() -> Decoder { Context::new().decoder() } -pub fn find(id: Id) -> Option { +pub fn find(id: Id) -> Option { unsafe { let ptr = avcodec_find_decoder(id.into()); Codec::from_raw(ptr) } } -pub fn find_by_name(name: &str) -> Option { +pub fn find_by_name(name: &str) -> Option { unsafe { let name = CString::new(name).unwrap(); let ptr = avcodec_find_decoder_by_name(name.as_ptr()); diff --git a/src/codec/encoder/audio.rs b/src/codec/encoder/audio.rs index c0afb6e3..b1894528 100644 --- a/src/codec/encoder/audio.rs +++ b/src/codec/encoder/audio.rs @@ -2,11 +2,12 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use crate::ffi::*; +use crate::AsPtr; #[cfg(not(feature = "ffmpeg_5_0"))] use libc::c_int; use super::Encoder as Super; -use crate::codec::{traits, Context}; +use crate::codec::{codec, Context}; use crate::util::format; #[cfg(not(feature = "ffmpeg_5_0"))] use crate::{frame, packet}; @@ -30,15 +31,11 @@ impl Audio { } } - pub fn open_as>(mut self, codec: E) -> Result { + pub fn open_as(mut self, codec: codec::Encoder) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } @@ -57,24 +54,20 @@ impl Audio { } } - pub fn open_as_with>( + pub fn open_as_with( mut self, - codec: E, + codec: codec::Encoder, options: Dictionary, ) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - let mut opts = options.disown(); - let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); - - Dictionary::own(opts); - - match res { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + let mut opts = options.disown(); + let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); + + Dictionary::own(opts); + + match res { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } diff --git a/src/codec/encoder/mod.rs b/src/codec/encoder/mod.rs index d7667a40..fd518221 100644 --- a/src/codec/encoder/mod.rs +++ b/src/codec/encoder/mod.rs @@ -26,6 +26,7 @@ pub use self::decision::Decision; use std::ffi::CString; +use crate::codec::codec::UnknownEncoder; use crate::codec::Context; use crate::codec::Id; use crate::ffi::*; @@ -35,14 +36,14 @@ pub fn new() -> Encoder { Context::new().encoder() } -pub fn find(id: Id) -> Option { +pub fn find(id: Id) -> Option { unsafe { let ptr = avcodec_find_encoder(id.into()); Codec::from_raw(ptr) } } -pub fn find_by_name(name: &str) -> Option { +pub fn find_by_name(name: &str) -> Option { unsafe { let name = CString::new(name).unwrap(); let ptr = avcodec_find_encoder_by_name(name.as_ptr()); diff --git a/src/codec/encoder/subtitle.rs b/src/codec/encoder/subtitle.rs index 7239aff4..b1d264cf 100644 --- a/src/codec/encoder/subtitle.rs +++ b/src/codec/encoder/subtitle.rs @@ -2,10 +2,11 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use crate::ffi::*; +use crate::AsPtr; use libc::c_int; use super::Encoder as Super; -use crate::codec::{traits, Context}; +use crate::codec::{codec, Context}; use crate::{Dictionary, Error}; pub struct Subtitle(pub Super); @@ -20,37 +21,29 @@ impl Subtitle { } } - pub fn open_as>(mut self, codec: E) -> Result { + pub fn open_as(mut self, codec: codec::Encoder) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } - pub fn open_as_with>( + pub fn open_as_with( mut self, - codec: E, + codec: codec::Encoder, options: Dictionary, ) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - let mut opts = options.disown(); - let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); - - Dictionary::own(opts); - - match res { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + let mut opts = options.disown(); + let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); + + Dictionary::own(opts); + + match res { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } diff --git a/src/codec/encoder/video.rs b/src/codec/encoder/video.rs index daecf5cb..a6e64536 100644 --- a/src/codec/encoder/video.rs +++ b/src/codec/encoder/video.rs @@ -2,13 +2,14 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use crate::ffi::*; +use crate::AsPtr; use libc::{c_float, c_int}; use super::Encoder as Super; use super::{Comparison, Decision}; #[cfg(not(feature = "ffmpeg_5_0"))] use super::{MotionEstimation, Prediction}; -use crate::codec::{traits, Context}; +use crate::codec::{codec, Context}; use crate::{color, format, Dictionary, Error, Rational}; #[cfg(not(feature = "ffmpeg_5_0"))] use crate::{frame, packet}; @@ -27,15 +28,11 @@ impl Video { } #[inline] - pub fn open_as>(mut self, codec: E) -> Result { + pub fn open_as(mut self, codec: codec::Encoder) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + match avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), ptr::null_mut()) { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } @@ -56,24 +53,20 @@ impl Video { } #[inline] - pub fn open_as_with>( + pub fn open_as_with( mut self, - codec: E, + codec: codec::Encoder, options: Dictionary, ) -> Result { unsafe { - if let Some(codec) = codec.encoder() { - let mut opts = options.disown(); - let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); + let mut opts = options.disown(); + let res = avcodec_open2(self.as_mut_ptr(), codec.as_ptr(), &mut opts); - Dictionary::own(opts); + Dictionary::own(opts); - match res { - 0 => Ok(Encoder(self)), - e => Err(Error::from(e)), - } - } else { - Err(Error::EncoderNotFound) + match res { + 0 => Ok(Encoder(self)), + e => Err(Error::from(e)), } } } diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 80fa8893..ebe9d69c 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -49,7 +49,6 @@ pub mod threading; pub mod decoder; pub mod encoder; -pub mod traits; use crate::ffi::*; use crate::utils; diff --git a/src/codec/traits.rs b/src/codec/traits.rs deleted file mode 100644 index 3cccf3fc..00000000 --- a/src/codec/traits.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::codec::UnknownType; -use super::{decoder, encoder}; -use crate::codec::Id; -use crate::Codec; - -pub trait Decoder { - fn decoder(self) -> Option>; -} - -impl Decoder for &str { - fn decoder(self) -> Option> { - decoder::find_by_name(self) - } -} - -impl Decoder for Id { - fn decoder(self) -> Option> { - decoder::find(self) - } -} - -impl Decoder for Codec { - fn decoder(self) -> Option> { - if self.is_decoder() { - Some(self) - } else { - None - } - } -} - -impl Decoder for Option> { - fn decoder(self) -> Option> { - self.and_then(|c| c.decoder()) - } -} - -pub trait Encoder { - fn encoder(self) -> Option>; -} - -impl Encoder for &str { - fn encoder(self) -> Option> { - encoder::find_by_name(self) - } -} - -impl Encoder for Id { - fn encoder(self) -> Option> { - encoder::find(self) - } -} - -impl Encoder for Codec { - fn encoder(self) -> Option> { - if self.is_encoder() { - Some(self) - } else { - None - } - } -} - -impl Encoder for Option> { - fn encoder(self) -> Option> { - self.and_then(|c| c.encoder()) - } -} diff --git a/src/format/context/input.rs b/src/format/context/input.rs index ac817030..d4fe51ee 100644 --- a/src/format/context/input.rs +++ b/src/format/context/input.rs @@ -4,9 +4,9 @@ use std::ops::{Bound, Deref, DerefMut, RangeBounds}; use super::common::Context; use super::destructor; -use crate::ffi::*; #[cfg(not(feature = "ffmpeg_5_0"))] -use crate::Codec; +use crate::codec::codec::*; +use crate::ffi::*; use crate::{format, Error, Packet, Stream}; pub struct Input { @@ -38,8 +38,12 @@ impl Input { unsafe { format::Input::from_raw((*self.as_ptr()).iformat).expect("iformat is non-null") } } + // The following codec fields (e.g. video_codec) are set by the user, so we cannot + // assume they are actually "correct" (e.g. a video codec with decoding capabilities). + // Because of this, we just return unknown codecs here. + #[cfg(not(feature = "ffmpeg_5_0"))] - pub fn video_codec(&self) -> Option { + pub fn video_codec(&self) -> Option> { unsafe { let ptr = (*self.as_ptr()).video_codec; Codec::from_raw(ptr) @@ -47,7 +51,7 @@ impl Input { } #[cfg(not(feature = "ffmpeg_5_0"))] - pub fn audio_codec(&self) -> Option { + pub fn audio_codec(&self) -> Option> { unsafe { let ptr = (*self.as_ptr()).audio_codec; Codec::from_raw(ptr) @@ -55,7 +59,7 @@ impl Input { } #[cfg(not(feature = "ffmpeg_5_0"))] - pub fn subtitle_codec(&self) -> Option { + pub fn subtitle_codec(&self) -> Option> { unsafe { let ptr = (*self.as_ptr()).subtitle_codec; Codec::from_raw(ptr) @@ -63,7 +67,7 @@ impl Input { } #[cfg(not(feature = "ffmpeg_5_0"))] - pub fn data_codec(&self) -> Option { + pub fn data_codec(&self) -> Option> { unsafe { let ptr = (*self.as_ptr()).data_codec; Codec::from_raw(ptr) diff --git a/src/format/context/output.rs b/src/format/context/output.rs index b03f2d2b..ec4036dc 100644 --- a/src/format/context/output.rs +++ b/src/format/context/output.rs @@ -5,8 +5,9 @@ use std::ptr; use super::common::Context; use super::destructor; -use crate::codec::traits; +use crate::codec::codec; use crate::ffi::*; +use crate::AsPtr; use crate::{format, ChapterMut, Dictionary, Error, Rational, StreamMut}; pub struct Output { @@ -68,11 +69,9 @@ impl Output { } } - pub fn add_stream>(&mut self, codec: E) -> Result { + pub fn add_stream(&mut self, codec: codec::Encoder) -> Result { unsafe { - let codec = codec.encoder(); - let codec = codec.map_or(ptr::null(), |c| c.as_ptr()); - let ptr = avformat_new_stream(self.as_mut_ptr(), codec); + let ptr = avformat_new_stream(self.as_mut_ptr(), codec.as_ptr()); if ptr.is_null() { return Err(Error::Unknown);