diff --git a/Cargo.lock b/Cargo.lock index 4e19376..3e72d61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ dependencies = [ "anyhow", "ffmpeg-sys-the-third", "libc", + "slimbox", ] [[package]] @@ -246,6 +247,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slimbox" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dfcf7e4fe830e4b9245b9e0def30d3df9ea194aca707e9a78b079d2b646b1a" + [[package]] name = "syn" version = "2.0.82" diff --git a/Cargo.toml b/Cargo.toml index a73bc83..e12bd76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ crate-type = ["lib", "cdylib"] anyhow = "1.0.91" ffmpeg-sys-the-third = { git = "https://github.com/shssoichiro/ffmpeg-the-third.git", branch = "master", package = "ffmpeg-sys-the-third" } libc = { version = "0.2.160" } +slimbox = "0.1.0" diff --git a/examples/main.rs b/examples/main.rs new file mode 100644 index 0000000..9fe77cc --- /dev/null +++ b/examples/main.rs @@ -0,0 +1,54 @@ +use ffmpeg_rs_raw::{Decoder, Demuxer}; +use ffmpeg_sys_the_third::{av_frame_free, av_packet_free}; +use std::env::args; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +struct DropTest { + inner: File, +} + +impl Read for DropTest { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.read(buf) + } +} + +impl Drop for DropTest { + fn drop(&mut self) { + println!("Dropped!"); + } +} + +fn main() { + let name = args().next().unwrap_or("main".to_string()); + let path = if let Some(path) = args().skip(1).next() { + PathBuf::from(path) + } else { + eprintln!("Usage: {} ", name); + std::process::exit(1); + }; + + let file = File::open(path).unwrap(); + let mut demuxer = Demuxer::new_custom_io(DropTest { inner: file }); + unsafe { + let info = demuxer.probe_input().expect("demuxer failed"); + println!("{}", info); + + let mut decoder = Decoder::new(); + loop { + let (mut pkt, stream) = demuxer.get_packet().expect("demuxer failed"); + if pkt.is_null() { + break; // EOF + } + if let Ok(frames) = decoder.decode_pkt(pkt, stream) { + for (mut frame, stream) in frames { + // do nothing but decode entire stream + av_frame_free(&mut frame); + } + } + av_packet_free(&mut pkt); + } + } +} diff --git a/src/demux.rs b/src/demux.rs index a850527..dbeb02e 100644 --- a/src/demux.rs +++ b/src/demux.rs @@ -1,40 +1,30 @@ +use anyhow::Error; +use ffmpeg_sys_the_third::*; use std::ffi::CStr; use std::time::Instant; use std::{ptr, slice}; -use anyhow::Error; -use ffmpeg_sys_the_third::*; - use crate::get_ffmpeg_error_msg; use crate::return_ffmpeg_error; +use slimbox::{slimbox_unsize, SlimBox, SlimMut}; use std::fmt::{Display, Formatter}; use std::io::{Read, Write}; use std::mem::transmute; +#[no_mangle] unsafe extern "C" fn read_data( opaque: *mut libc::c_void, dst_buffer: *mut libc::c_uchar, size: libc::c_int, ) -> libc::c_int { - let buffer: *mut Box = opaque.cast(); - /// we loop until there is enough data to fill [size] - let mut dst_slice: &mut [u8] = slice::from_raw_parts_mut(dst_buffer, size as usize); - let mut w_total = 0usize; - loop { - return match (*buffer).read(dst_slice) { - Ok(v) => { - w_total += v; - if w_total != size as usize { - dst_slice = &mut dst_slice[v..]; - continue; - } - size - } - Err(e) => { - eprintln!("read_data {}", e); - AVERROR_EOF - } - }; + let mut buffer: SlimMut<'_, dyn Read + 'static> = SlimMut::from_raw(opaque); + let dst_slice: &mut [u8] = slice::from_raw_parts_mut(dst_buffer, size as usize); + match buffer.read_exact(dst_slice) { + Ok(_) => size, + Err(e) => { + eprintln!("read_data {}", e); + AVERROR_EOF + } } } @@ -160,18 +150,14 @@ impl Display for StreamInfoChannel { } } -pub struct Demuxer<'a> { +pub struct Demuxer { ctx: *mut AVFormatContext, input: String, started: Instant, - buffer: Option>, + buffer: Option>, } -unsafe impl Send for Demuxer<'_> {} - -unsafe impl Sync for Demuxer<'_> {} - -impl Demuxer<'_> { +impl Demuxer { pub fn new(input: &str) -> Self { unsafe { let ps = avformat_alloc_context(); @@ -184,33 +170,33 @@ impl Demuxer<'_> { } } - pub fn new_custom_io(reader: T) -> Self { + pub fn new_custom_io(reader: R) -> Self { unsafe { let ps = avformat_alloc_context(); (*ps).flags |= AVFMT_FLAG_CUSTOM_IO; - let buffer = Box::new(reader); Self { ctx: ps, input: String::new(), started: Instant::now(), - buffer: Some(buffer), + buffer: Some(slimbox_unsize!(reader)), } } } unsafe fn open_input(&mut self) -> libc::c_int { - if let Some(mut buffer) = self.buffer.take() { + if let Some(buffer) = self.buffer.take() { const BUFFER_SIZE: usize = 4096; let pb = avio_alloc_context( av_mallocz(BUFFER_SIZE) as *mut libc::c_uchar, BUFFER_SIZE as libc::c_int, 0, - ptr::addr_of_mut!(buffer) as _, + buffer.into_raw(), Some(read_data), None, None, ); + (*self.ctx).pb = pb; avformat_open_input( &mut self.ctx, @@ -235,7 +221,7 @@ impl Demuxer<'_> { if avformat_find_stream_info(self.ctx, ptr::null_mut()) < 0 { return Err(Error::msg("Could not find stream info")); } - av_dump_format(self.ctx, 0, ptr::null_mut(), 0); + //av_dump_format(self.ctx, 0, ptr::null_mut(), 0); let mut channel_infos = vec![]; @@ -308,9 +294,13 @@ impl Demuxer<'_> { } } -impl Drop for Demuxer<'_> { +impl Drop for Demuxer { fn drop(&mut self) { unsafe { + if !(*(*self.ctx).pb).opaque.is_null() { + let ptr: SlimBox = SlimBox::from_raw((*(*self.ctx).pb).opaque); + drop(ptr) + } avformat_free_context(self.ctx); self.ctx = ptr::null_mut(); } diff --git a/src/lib.rs b/src/lib.rs index c4d55c3..4a64b3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,3 +23,8 @@ fn get_ffmpeg_error_msg(ret: libc::c_int) -> String { String::from(CStr::from_ptr(buf.as_ptr()).to_str().unwrap()) } } + +pub use decode::*; +pub use demux::*; +pub use resample::*; +pub use scale::*; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}