diff --git a/Cargo.toml b/Cargo.toml index ed2cc76..0147571 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["audio", "video"] [features] default = ["codec", "device", "filter", "format", "resampling", "software-scaling"] +unstable = [] static = ["ffmpeg-sys/static"] # Currently the version built by ffmpeg-sys is 4.1 diff --git a/examples/metadata.rs b/examples/metadata.rs index 8f6375b..c937770 100644 --- a/examples/metadata.rs +++ b/examples/metadata.rs @@ -1,11 +1,11 @@ -extern crate ffmpeg; - -use std::env; +use std::{env, fs::File}; fn main() { + env_logger::init(); ffmpeg::init().unwrap(); - match ffmpeg::format::input(&env::args().nth(1).expect("missing file")) { + let file = File::open(&env::args().nth(1).expect("missing file")).unwrap(); + match ffmpeg::format::io::input(file) { Ok(context) => { for (k, v) in context.metadata().iter() { println!("{}: {}", k, v); diff --git a/shell.nix b/shell.nix index 024bb7b..ad876ab 100644 --- a/shell.nix +++ b/shell.nix @@ -11,7 +11,7 @@ mkShell { buildInputs = [ # For building. - clang rustChannels.stable.rust pkg-config ffmpeg + clang rustChannels.nightly.rust pkg-config ffmpeg ]; RUST_BACKTRACE = 1; diff --git a/src/format/context/input.rs b/src/format/context/input.rs index 0fef6b5..7e365b1 100644 --- a/src/format/context/input.rs +++ b/src/format/context/input.rs @@ -11,7 +11,7 @@ use crate::{ffi::*, format::{self, io::Io}, util::range::Range, Codec, Error, Pa pub struct Input { ptr: *mut AVFormatContext, ctx: Context, - io: Option + _io: Option } unsafe impl Send for Input {} @@ -21,7 +21,7 @@ impl Input { Input { ptr, ctx: Context::wrap(ptr, destructor::Mode::Input), - io: None, + _io: None, } } @@ -29,7 +29,7 @@ impl Input { Input { ptr, ctx: Context::wrap(ptr, destructor::Mode::Input), - io: Some(io), + _io: Some(io), } } diff --git a/src/format/context/output.rs b/src/format/context/output.rs index cdcaa8e..71fd899 100644 --- a/src/format/context/output.rs +++ b/src/format/context/output.rs @@ -8,12 +8,11 @@ use std::{ use libc; use super::{common::Context, destructor}; -use crate::{codec::traits, ffi::*, format::{self, io::Io}, ChapterMut, Dictionary, Error, Rational, StreamMut}; +use crate::{codec::traits, ffi::*, format, ChapterMut, Dictionary, Error, Rational, StreamMut}; pub struct Output { ptr: *mut AVFormatContext, ctx: Context, - io: Option, } unsafe impl Send for Output {} @@ -23,17 +22,9 @@ 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), - } - } pub unsafe fn as_ptr(&self) -> *const AVFormatContext { self.ptr as *const _ } diff --git a/src/format/io.rs b/src/format/io.rs index 6032582..66b67b2 100644 --- a/src/format/io.rs +++ b/src/format/io.rs @@ -1,24 +1,51 @@ -use std::{slice, convert::TryInto, io::{self, Read, Write, Seek, SeekFrom}, ptr}; +use std::{slice, convert::TryInto, io::{Read, Write, Seek, SeekFrom}, ptr}; use libc::{c_void, c_int, SEEK_CUR, SEEK_END, SEEK_SET}; -use crate::{ffi::*, Format, format::context::{self, Context}, Error}; +use crate::{ffi::*, format::context, Error}; -pub trait Stream: Read + Write + Seek { - fn size(&self) -> io::Result { - Ok(0) +pub enum Proxy { + Input(Box), + Stream(Box), + Output(Box), +} + +impl Proxy { + pub fn as_read(&mut self) -> &mut dyn Read { + match self { + Proxy::Input(ref mut inner) => inner, + Proxy::Stream(ref mut inner) => inner, + Proxy::Output(_) => unreachable!(), + } + } + + pub fn as_seek(&mut self) -> &mut dyn Seek { + match self { + Proxy::Input(ref mut inner) => inner, + Proxy::Stream(_) | Proxy::Output(_) => unreachable!(), + } + } + + pub fn as_write(&mut self) -> &mut dyn Write { + match self { + Proxy::Output(ref mut inner) => inner, + Proxy::Stream(_) | Proxy::Input(_) => unreachable!(), + } } } #[derive(Debug)] pub struct Io { ptr: *mut AVIOContext, - proxy: *mut Box, + proxy: *mut Proxy, } -impl From for Io { - fn from(value: F) -> Io { - Io::new(Box::new(value)) - } -} +pub trait InputIo: Read + Seek { } +impl InputIo for T { } + +pub trait StreamIo: Read { } +impl StreamIo for T { } + +pub trait OutputIo: Write { } +impl OutputIo for T { } impl Io { pub unsafe fn as_ptr(&self) -> *const AVIOContext { @@ -30,12 +57,11 @@ impl Io { } } -#[no_mangle] -unsafe extern fn read_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { - let mut proxy = Box::>::from_raw(opaque.cast()); +unsafe extern "C" fn read_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { + let mut proxy = Box::::from_raw(opaque.cast()); let buffer = slice::from_raw_parts_mut(buf, size.try_into().unwrap()); - let result = match proxy.read(buffer) { + let result = match proxy.as_read().read(buffer) { Ok(0) => AVERROR_EOF, Ok(size) => size.try_into().unwrap(), Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), @@ -45,40 +71,42 @@ unsafe extern fn read_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> result.try_into().unwrap() } -#[no_mangle] -unsafe extern fn write_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { - let mut proxy = Box::>::from_raw(opaque.cast()); - let buffer = slice::from_raw_parts(buf, size.try_into().unwrap()); +unsafe extern "C" fn seek(opaque: *mut c_void, offset: i64, whence: c_int) -> i64 { + let mut proxy = Box::::from_raw(opaque.cast()); - let result = match proxy.write(buffer) { - Ok(size) => size, - Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + let result = if whence == AVSEEK_SIZE { + #[cfg(feature = "unstable")] + match proxy.as_seek().stream_len() { + Ok(size) => size, + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + } + + #[cfg(not(feature = "unstable"))] + 0 + } + else { + let whence = match whence { + SEEK_SET => SeekFrom::Start(offset.try_into().unwrap()), + SEEK_END => SeekFrom::End(offset.try_into().unwrap()), + SEEK_CUR => SeekFrom::Current(offset.try_into().unwrap()), + _ => unreachable!(), + }; + + match proxy.as_seek().seek(whence) { + Ok(size) => size, + Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), + } }; Box::into_raw(proxy); result.try_into().unwrap() } -#[no_mangle] -unsafe extern fn seek(opaque: *mut c_void, offset: i64, whence: c_int) -> i64 { - let mut proxy = Box::>::from_raw(opaque.cast()); - let whence = match whence { - AVSEEK_SIZE => { - let result = match proxy.size() { - Ok(size) => size, - Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), - }; - Box::into_raw(proxy); - return result.try_into().unwrap(); - } +unsafe extern "C" fn write_packet(opaque: *mut c_void, buf: *mut u8, size: c_int) -> c_int { + let mut proxy = Box::::from_raw(opaque.cast()); + let buffer = slice::from_raw_parts(buf, size.try_into().unwrap()); - SEEK_SET => SeekFrom::Start(offset.try_into().unwrap()), - SEEK_END => SeekFrom::End(offset.try_into().unwrap()), - SEEK_CUR => SeekFrom::Current(offset.try_into().unwrap()), - _ => unreachable!(), - }; - - let result = match proxy.seek(whence) { + let result = match proxy.as_write().write(buffer) { Ok(size) => size, Err(err) => err.raw_os_error().unwrap().try_into().unwrap(), }; @@ -88,15 +116,31 @@ unsafe extern fn seek(opaque: *mut c_void, offset: i64, whence: c_int) -> i64 { } impl Io { - pub fn new(proxy: Box) -> Self { - Io::with_capacity(4096, proxy) + pub fn input(value: impl Read + Seek + 'static) -> Self { + unsafe { + let proxy = Box::into_raw(Box::new(Proxy::Input(Box::new(value)))); + let ptr = avio_alloc_context(ptr::null_mut(), 0, AVIO_FLAG_READ & AVIO_FLAG_DIRECT, proxy.cast(), + Some(read_packet), None, Some(seek)); + + Io { proxy, ptr } + } } - pub fn with_capacity(size: usize, proxy: Box) -> Self { + pub fn stream(value: impl Read + 'static) -> Self { unsafe { - let proxy = Box::into_raw(Box::new(proxy)); + let proxy = Box::into_raw(Box::new(Proxy::Stream(Box::new(value)))); + let ptr = avio_alloc_context(ptr::null_mut(), 0, AVIO_FLAG_READ & AVIO_FLAG_DIRECT, proxy.cast(), + Some(read_packet), None, None); + + Io { proxy, ptr } + } + } + + pub fn output(value: impl Write + 'static) -> Self { + unsafe { + let proxy = Box::into_raw(Box::new(Proxy::Output(Box::new(value)))); let ptr = avio_alloc_context(ptr::null_mut(), 0, AVIO_FLAG_WRITE & AVIO_FLAG_DIRECT, proxy.cast(), - Some(read_packet), Some(write_packet), Some(seek)); + None, Some(write_packet), None); Io { proxy, ptr } } @@ -107,56 +151,23 @@ impl Drop for Io { fn drop(&mut self) { unsafe { avio_context_free(&mut self.ptr); + Box::from_raw(self.proxy); } } } -pub fn open>(io: I, format: &Format) -> Result { +pub fn input(io: impl Read + Seek + 'static) -> Result { unsafe { let mut ps = avformat_alloc_context(); - let mut io = io.into(); - (*ps).pb = io.as_mut_ptr(); - - match *format { - Format::Input(ref format) => match avformat_open_input( - &mut ps, - ptr::null_mut(), - format.as_ptr() as *mut _, - ptr::null_mut(), - ) { - 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { - r if r >= 0 => Ok(Context::Input(context::Input::wrap_with(ps, io))), - e => { - avformat_close_input(&mut ps); - Err(Error::from(e)) - } - }, - - e => Err(Error::from(e)), - }, - - Format::Output(ref format) => match avformat_alloc_output_context2( - &mut ps, - format.as_ptr() as *mut _, - ptr::null(), - ptr::null(), - ) { - 0 => Ok(Context::Output(context::Output::wrap_with(ps, io))), - e => Err(Error::from(e)), - }, - } - } -} - -pub fn input>(io: I) -> Result { - unsafe { - let mut ps = avformat_alloc_context(); - let mut io = io.into(); + let mut io = Io::input(io); (*ps).pb = io.as_mut_ptr(); match avformat_open_input(&mut ps, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()) { 0 => match avformat_find_stream_info(ps, ptr::null_mut()) { - r if r >= 0 => Ok(context::Input::wrap_with(ps, io)), + r if r >= 0 => { + Ok(context::Input::wrap_with(ps, io)) + } + e => { avformat_close_input(&mut ps); Err(Error::from(e)) @@ -167,16 +178,3 @@ pub fn input>(io: I) -> Result { } } } - -pub fn output>(io: I) -> Result { - unsafe { - let mut ps = avformat_alloc_context(); - let mut io = io.into(); - (*ps).pb = io.as_mut_ptr(); - - match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), ptr::null_mut()) { - 0 => 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 c14720c..89a0690 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -2,8 +2,6 @@ pub use crate::util::format::{pixel, sample, Pixel, Sample}; use crate::util::interrupt; pub mod io; -pub use self::io::Io; - pub mod stream; pub mod chapter; diff --git a/src/lib.rs b/src/lib.rs index ff1c344..b8a6142 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![allow(non_camel_case_types)] +#![cfg_attr(feature = "unstable", feature(seek_stream_len))] #![cfg_attr(feature = "cargo-clippy", allow(inline_always))] #[macro_use]