From f1c6b442b58400c15df8d156f888a073bbbda747 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Sun, 3 Nov 2024 01:31:30 +0100 Subject: [PATCH] Refactor crate::codec (#78) * Refactor AVCodec wrapper * Implement new config interface * Move ProfileIter to codec::profile * Add CodecDescriptor --- examples/codec-info.rs | 8 +- examples/transcode-audio.rs | 3 +- src/codec/audio.rs | 215 ------------------ src/codec/codec.rs | 325 +++++++++++++++++++++++----- src/codec/config.rs | 397 ++++++++++++++++++++++++++++++++++ src/codec/context.rs | 8 +- src/codec/decoder/decoder.rs | 4 +- src/codec/decoder/mod.rs | 17 +- src/codec/descriptor.rs | 158 ++++++++++++++ src/codec/encoder/audio.rs | 4 +- src/codec/encoder/mod.rs | 18 +- src/codec/encoder/subtitle.rs | 4 +- src/codec/encoder/video.rs | 4 +- src/codec/mod.rs | 15 +- src/codec/profile.rs | 28 +++ src/codec/props.rs | 17 ++ src/codec/traits.rs | 83 ++----- src/codec/video.rs | 100 --------- src/format/context/input.rs | 28 +-- src/format/context/output.rs | 2 +- 20 files changed, 929 insertions(+), 509 deletions(-) delete mode 100644 src/codec/audio.rs create mode 100644 src/codec/config.rs create mode 100644 src/codec/descriptor.rs create mode 100644 src/codec/props.rs delete mode 100644 src/codec/video.rs diff --git a/examples/codec-info.rs b/examples/codec-info.rs index 6210cf7..2445d90 100644 --- a/examples/codec-info.rs +++ b/examples/codec-info.rs @@ -20,7 +20,7 @@ fn main() { println!("\t profiles: none"); } - if let Ok(video) = codec.video() { + if let Some(video) = codec.video() { if let Some(rates) = video.rates() { println!("\t rates: {:?}", rates.collect::>()); } 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() { println!("\t rates: {:?}", rates.collect::>()); } else { @@ -71,7 +71,7 @@ fn main() { println!("\t profiles: {:?}", profiles.collect::>()); } - if let Ok(video) = codec.video() { + if let Some(video) = codec.video() { if let Some(rates) = video.rates() { println!("\t rates: {:?}", rates.collect::>()); } 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() { println!("\t rates: {:?}", rates.collect::>()); } else { diff --git a/examples/transcode-audio.rs b/examples/transcode-audio.rs index 4a49160..b79b5ed 100644 --- a/examples/transcode-audio.rs +++ b/examples/transcode-audio.rs @@ -83,7 +83,8 @@ fn transcoder>( let mut decoder = context.decoder().audio()?; let codec = ffmpeg::encoder::find(octx.format().codec(path, media::Type::Audio)) .expect("failed to find encoder") - .audio()?; + .audio() + .expect("encoder is not audio encoder"); let global = octx .format() .flags() diff --git a/src/codec/audio.rs b/src/codec/audio.rs deleted file mode 100644 index 0e7988a..0000000 --- a/src/codec/audio.rs +++ /dev/null @@ -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 { - 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 { - 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 { - 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 { - 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<::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<::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<::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 { - 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 { - 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() - } -} diff --git a/src/codec/codec.rs b/src/codec/codec.rs index 61fad53..4279663 100644 --- a/src/codec/codec.rs +++ b/src/codec/codec.rs @@ -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::{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; +pub type Video = Codec; #[derive(PartialEq, Eq, Copy, Clone)] -pub struct Codec { - ptr: *mut AVCodec, +pub struct Codec { + ptr: NonNull, + _marker: PhantomData, } -unsafe impl Send for Codec {} -unsafe impl Sync for Codec {} +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct UnknownType; +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct AudioType; +#[derive(PartialEq, Eq, Copy, Clone)] +pub struct VideoType; -impl Codec { - pub unsafe fn wrap(ptr: *mut AVCodec) -> Self { - Codec { ptr } - } +unsafe impl Send for Codec {} +unsafe impl Sync for Codec {} - pub unsafe fn as_ptr(&self) -> *const AVCodec { - self.ptr as *const _ - } - - pub unsafe fn as_mut_ptr(&mut self) -> *mut AVCodec { - self.ptr +impl Codec { + /// Create a new reference to a codec from a raw pointer. + /// + /// Returns `None` if `ptr` is null. + pub unsafe fn from_raw(ptr: *const AVCodec) -> Option { + NonNull::new(ptr as *mut _).map(|ptr| Self { + ptr, + _marker: PhantomData, + }) } } -impl Codec { +impl Codec { + pub fn as_ptr(&self) -> *const AVCodec { + self.ptr.as_ptr() + } + pub fn is_encoder(&self) -> bool { unsafe { av_codec_is_encoder(self.as_ptr()) != 0 } } @@ -53,13 +81,14 @@ impl Codec { self.medium() == media::Type::Video } - pub fn video(self) -> Result { - unsafe { - if self.medium() == media::Type::Video { - Ok(Video::new(self)) - } else { - Err(Error::InvalidData) - } + pub fn video(self) -> Option