diff --git a/src/format/context/output.rs b/src/format/context/output.rs index 9b3813a..3da8386 100644 --- a/src/format/context/output.rs +++ b/src/format/context/output.rs @@ -8,11 +8,17 @@ use std::{ use libc; use super::{common::Context, destructor}; -use crate::{codec::traits, ffi::*, format, ChapterMut, Dictionary, Error, Rational, StreamMut}; +use crate::{ + codec::traits, + ffi::*, + format::{self, io::Io}, + ChapterMut, Dictionary, Error, Rational, StreamMut, +}; pub struct Output { ptr: *mut AVFormatContext, ctx: Context, + _io: Option, } unsafe impl Send for Output {} @@ -22,6 +28,15 @@ impl Output { Output { ptr, ctx: Context::wrap(ptr, destructor::Mode::Output), + _io: None, + } + } + + pub unsafe fn wrap_with(ptr: *mut AVFormatContext, io: Io) -> Self { + Output { + ptr, + ctx: Context::wrap(ptr, destructor::Mode::Output), + _io: Some(io), } } diff --git a/src/format/format/input.rs b/src/format/format/input.rs index d084cd2..e64aefe 100644 --- a/src/format/format/input.rs +++ b/src/format/format/input.rs @@ -1,7 +1,8 @@ -use std::{ffi::CStr, str::from_utf8_unchecked}; +use std::{ffi::CStr, ptr, str::from_utf8_unchecked}; use crate::ffi::*; +#[derive(Copy, Clone)] pub struct Input { ptr: *mut AVInputFormat, } @@ -59,3 +60,58 @@ impl Input { } } } + +pub struct Iter { + input: *mut AVInputFormat, +} + +impl Iter { + pub fn new() -> Self { + Iter { + input: ptr::null_mut(), + } + } +} + +impl Default for Iter { + fn default() -> Self { + Self::new() + } +} + +impl Iterator for Iter { + type Item = Input; + + fn next(&mut self) -> Option<::Item> { + unsafe { + let ptr = av_iformat_next(self.input); + + if ptr.is_null() && !self.input.is_null() { + None + } + else { + self.input = ptr; + Some(Input::wrap(ptr)) + } + } + } +} + +pub fn all() -> Iter { + Iter::new() +} + +pub fn by_name(name: impl Into) -> impl Iterator { + let name = name.into(); + all().filter(move |i| i.name() == name) +} + +pub fn by_mime(mime: impl Into) -> impl Iterator { + let mime = mime.into(); + all().filter(move |i| i.mime_types().contains(&mime.as_ref())) +} + +pub fn by_extension(ext: impl Into) -> impl Iterator { + let ext = ext.into(); + all().filter(move |i| i.extensions().contains(&ext.as_ref())) +} diff --git a/src/format/format/iter.rs b/src/format/format/iter.rs deleted file mode 100644 index f8c4322..0000000 --- a/src/format/format/iter.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::ptr; - -use super::{Format, Input, Output}; -use crate::ffi::*; - -pub struct Iter { - input: *mut AVInputFormat, - output: *mut AVOutputFormat, - step: Step, -} - -enum Step { - Input, - Output, - Done, -} - -impl Iter { - pub fn new() -> Self { - Iter { - input: ptr::null_mut(), - output: ptr::null_mut(), - step: Step::Input, - } - } -} - -impl Default for Iter { - fn default() -> Self { - Self::new() - } -} - -impl Iterator for Iter { - type Item = Format; - - fn next(&mut self) -> Option<::Item> { - unsafe { - match self.step { - Step::Input => { - let ptr = av_iformat_next(self.input); - - if ptr.is_null() && !self.input.is_null() { - self.step = Step::Output; - - self.next() - } - else { - self.input = ptr; - - Some(Format::Input(Input::wrap(ptr))) - } - } - - Step::Output => { - let ptr = av_oformat_next(self.output); - - if ptr.is_null() && !self.output.is_null() { - self.step = Step::Done; - - self.next() - } - else { - self.output = ptr; - - Some(Format::Output(Output::wrap(ptr))) - } - } - - Step::Done => None, - } - } - } -} diff --git a/src/format/format/mod.rs b/src/format/format/mod.rs index b8a3737..9471500 100644 --- a/src/format/format/mod.rs +++ b/src/format/format/mod.rs @@ -1,15 +1,13 @@ pub mod flag; pub use self::flag::Flags; -mod input; +pub mod input; pub use self::input::Input; -mod output; +pub mod output; pub use self::output::Output; -mod iter; -pub use self::iter::Iter; - +#[derive(Copy, Clone)] pub enum Format { Input(Input), Output(Output), @@ -45,6 +43,8 @@ impl Format { } } -pub fn list() -> Iter { - Iter::new() +pub fn all() -> impl Iterator { + input::all() + .map(|i| Format::Input(i)) + .chain(output::all().map(|o| Format::Output(o))) } diff --git a/src/format/format/output.rs b/src/format/format/output.rs index 2a89689..b6388f5 100644 --- a/src/format/format/output.rs +++ b/src/format/format/output.rs @@ -8,6 +8,7 @@ use std::{ use super::Flags; use crate::{codec, ffi::*, media}; +#[derive(Copy, Clone)] pub struct Output { ptr: *mut AVOutputFormat, } @@ -84,3 +85,58 @@ impl Output { unsafe { Flags::from_bits_truncate((*self.as_ptr()).flags) } } } + +pub struct Iter { + output: *mut AVOutputFormat, +} + +impl Iter { + pub fn new() -> Self { + Iter { + output: ptr::null_mut(), + } + } +} + +impl Default for Iter { + fn default() -> Self { + Self::new() + } +} + +impl Iterator for Iter { + type Item = Output; + + fn next(&mut self) -> Option<::Item> { + unsafe { + let ptr = av_oformat_next(self.output); + + if ptr.is_null() && !self.output.is_null() { + None + } + else { + self.output = ptr; + Some(Output::wrap(ptr)) + } + } + } +} + +pub fn all() -> Iter { + Iter::new() +} + +pub fn by_name(name: impl Into) -> impl Iterator { + let name = name.into(); + all().filter(move |i| i.name() == name) +} + +pub fn by_mime(mime: impl Into) -> impl Iterator { + let mime = mime.into(); + all().filter(move |i| i.mime_types().contains(&mime.as_ref())) +} + +pub fn by_extension(ext: impl Into) -> impl Iterator { + let ext = ext.into(); + all().filter(move |i| i.extensions().contains(&ext.as_ref())) +} diff --git a/src/format/io.rs b/src/format/io.rs index 8f4b3c7..7dc2714 100644 --- a/src/format/io.rs +++ b/src/format/io.rs @@ -6,7 +6,11 @@ use std::{ use libc::{c_int, c_void, EINVAL, SEEK_CUR, SEEK_END, SEEK_SET}; -use crate::{ffi::*, format::context, Error}; +use crate::{ + ffi::*, + format::{context, format}, + Error, +}; pub enum Proxy { Input(Box), @@ -212,3 +216,56 @@ pub fn input(io: impl Read + Seek + 'static) -> Result { } } } + +pub fn input_as( + io: impl Read + Seek + 'static, + mut format: format::Input, +) -> Result { + unsafe { + let mut ps = avformat_alloc_context(); + let mut io = Io::input(io); + (*ps).pb = io.as_mut_ptr(); + + match avformat_open_input( + &mut ps, + ptr::null_mut(), + format.as_mut_ptr(), + ptr::null_mut(), + ) { + 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { + r if r >= 0 => Ok(context::Input::wrap_with(ps, io)), + + e => { + avformat_close_input(&mut ps); + Err(Error::from(e)) + } + }, + + e => Err(Error::from(e)), + } + } +} + +pub fn output( + io: impl Write + 'static, + mut format: format::Output, +) -> Result { + unsafe { + let mut ps = ptr::null_mut(); + let mut io = Io::output(io); + + match avformat_alloc_output_context2( + &mut ps, + format.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + ) { + n if n >= 0 => { + (*ps).pb = io.as_mut_ptr(); + Ok(context::Output::wrap_with(ps, io)) + } + + e => Err(Error::from(e)), + } + } +} diff --git a/src/format/mod.rs b/src/format/mod.rs index c1bb873..8f03ef1 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -10,17 +10,17 @@ pub mod context; pub use self::context::Context; pub mod format; -pub use self::format::{flag, list, Flags, Input, Output}; +pub use self::format::{all, flag, input, output, Flags, Input, Output}; pub mod network; use std::{ - ffi::{CStr, CString, OsStr}, + ffi::{CStr, OsStr}, ptr, str::from_utf8_unchecked, }; -use crate::{ffi::*, Dictionary, Error, Format}; +use crate::{ffi::*, util::from_os_str, Dictionary, Error, Format}; pub fn register_all() { unsafe { @@ -52,7 +52,7 @@ pub fn license() -> &'static str { unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_license()).to_bytes()) } } -pub fn open>(path_or_url: P, format: &Format) -> Result { +pub fn open(path_or_url: impl AsRef, format: &Format) -> Result { unsafe { let mut ps = ptr::null_mut(); let path = from_os_str(path_or_url); @@ -89,8 +89,8 @@ pub fn open>(path_or_url: P, format: &Format) -> Result>( - path_or_url: P, +pub fn open_with( + path_or_url: impl AsRef, format: &Format, options: Dictionary<'_>, ) -> Result { @@ -132,7 +132,7 @@ pub fn open_with>( } } -pub fn input>(path_or_url: P) -> Result { +pub fn input(path_or_url: impl AsRef) -> Result { unsafe { let mut ps = ptr::null_mut(); let path = from_os_str(path_or_url); @@ -151,8 +151,8 @@ pub fn input>(path_or_url: P) -> Result { } } -pub fn input_with_dictionary>( - path_or_url: P, +pub fn input_with_dictionary( + path_or_url: impl AsRef, options: Dictionary<'_>, ) -> Result { unsafe { @@ -200,7 +200,7 @@ pub fn input_with_interrupt>( } } -pub fn output>(path_or_url: P) -> Result { +pub fn output(path_or_url: impl AsRef) -> Result { unsafe { let mut ps = ptr::null_mut(); let path = from_os_str(path_or_url); @@ -216,8 +216,8 @@ pub fn output>(path_or_url: P) -> Result } } -pub fn output_with>( - path_or_url: P, +pub fn output_with( + path_or_url: impl AsRef, options: Dictionary<'_>, ) -> Result { unsafe { @@ -248,13 +248,20 @@ pub fn output_with>( } } -pub fn output_as>(path_or_url: P, format: &str) -> Result { +pub fn output_as( + path_or_url: impl AsRef, + mut format: format::Output, +) -> Result { unsafe { let mut ps = ptr::null_mut(); let path = from_os_str(path_or_url); - let format = CString::new(format).unwrap(); - match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), format.as_ptr(), path.as_ptr()) { + match avformat_alloc_output_context2( + &mut ps, + format.as_mut_ptr(), + ptr::null_mut(), + path.as_ptr(), + ) { 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) { 0 => Ok(context::Output::wrap(ps)), e => Err(Error::from(e)), @@ -265,18 +272,22 @@ pub fn output_as>(path_or_url: P, format: &str) -> Result>( - path_or_url: P, - format: &str, +pub fn output_as_with( + path_or_url: impl AsRef, + mut format: format::Output, options: Dictionary<'_>, ) -> Result { unsafe { let mut ps = ptr::null_mut(); let path = from_os_str(path_or_url); - let format = CString::new(format).unwrap(); let mut opts = options.disown(); - match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), format.as_ptr(), path.as_ptr()) { + match avformat_alloc_output_context2( + &mut ps, + format.as_mut_ptr(), + ptr::null_mut(), + path.as_ptr(), + ) { 0 => { let res = avio_open2( &mut (*ps).pb, @@ -298,14 +309,3 @@ pub fn output_as_with>( } } } - -#[cfg(unix)] -fn from_os_str(path_or_url: impl AsRef) -> CString { - use std::os::unix::ffi::OsStrExt; - CString::new(path_or_url.as_ref().as_bytes()).unwrap() -} - -#[cfg(not(unix))] -fn from_os_str(path_or_url: impl AsRef) -> CString { - CString::new(path_or_url.as_ref().to_str().unwrap()).unwrap() -} diff --git a/src/util/mod.rs b/src/util/mod.rs index d32faab..228b925 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -18,7 +18,10 @@ pub mod time; #[cfg(feature = "log")] pub mod log; -use std::{ffi::CStr, str::from_utf8_unchecked}; +use std::{ + ffi::{CStr, CString, OsStr}, + str::from_utf8_unchecked, +}; use crate::ffi::*; @@ -36,3 +39,14 @@ pub fn configuration() -> &'static str { pub fn license() -> &'static str { unsafe { from_utf8_unchecked(CStr::from_ptr(avutil_license()).to_bytes()) } } + +#[cfg(unix)] +pub fn from_os_str(path_or_url: impl AsRef) -> CString { + use std::os::unix::ffi::OsStrExt; + CString::new(path_or_url.as_ref().as_bytes()).unwrap() +} + +#[cfg(not(unix))] +pub fn from_os_str(path_or_url: impl AsRef) -> CString { + CString::new(path_or_url.as_ref().to_str().unwrap()).unwrap() +}