From 2a3a4e61695c155774237c593f096e10ecc9b00c Mon Sep 17 00:00:00 2001 From: meh Date: Fri, 4 Sep 2015 16:36:04 +0200 Subject: [PATCH] filter: add avfilter wrapper --- src/filter/context/context.rs | 68 +++++++++++++ src/filter/context/mod.rs | 8 ++ src/filter/context/sink.rs | 34 +++++++ src/filter/context/source.rs | 30 ++++++ src/filter/filter.rs | 107 ++++++++++++++++++++ src/filter/flag.rs | 13 +++ src/filter/graph.rs | 180 ++++++++++++++++++++++++++++++++++ src/filter/mod.rs | 66 +++++++++++++ src/filter/pad.rs | 47 +++++++++ src/lib.rs | 18 +++- 10 files changed, 570 insertions(+), 1 deletion(-) create mode 100644 src/filter/context/context.rs create mode 100644 src/filter/context/mod.rs create mode 100644 src/filter/context/sink.rs create mode 100644 src/filter/context/source.rs create mode 100644 src/filter/filter.rs create mode 100644 src/filter/flag.rs create mode 100644 src/filter/graph.rs create mode 100644 src/filter/mod.rs create mode 100644 src/filter/pad.rs diff --git a/src/filter/context/context.rs b/src/filter/context/context.rs new file mode 100644 index 0000000..ba02db5 --- /dev/null +++ b/src/filter/context/context.rs @@ -0,0 +1,68 @@ +use std::marker::PhantomData; + +use ffi::*; +use libc::c_void; +use ::{option, format, Error, ChannelLayout}; +use super::{Source, Sink}; + +pub struct Context<'a> { + ptr: *mut AVFilterContext, + + _marker: PhantomData<&'a ()>, +} + +impl<'a> Context<'a> { + pub unsafe fn wrap(ptr: *mut AVFilterContext) -> Self { + Context { ptr: ptr, _marker: PhantomData } + } + + pub unsafe fn as_ptr(&self) -> *const AVFilterContext { + self.ptr as *const _ + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterContext { + self.ptr + } +} + +impl<'a> Context<'a> { + pub fn source(&'a mut self) -> Source<'a> { + unsafe { + Source::wrap(self) + } + } + + pub fn sink(&'a mut self) -> Sink<'a> { + unsafe { + Sink::wrap(self) + } + } + + pub fn set_pixel_format(&mut self, value: format::Pixel) -> Result<(), Error> { + option::Settable::set::(self, "pix_fmts", &value.into()) + } + + pub fn set_sample_format(&mut self, value: format::Sample) -> Result<(), Error> { + option::Settable::set::(self, "sample_fmts", &value.into()) + } + + pub fn set_sample_rate(&mut self, value: u32) -> Result<(), Error> { + option::Settable::set(self, "sample_rates", &(value as i64)) + } + + pub fn set_channel_layout(&mut self, value: ChannelLayout) -> Result<(), Error> { + option::Settable::set(self, "channel_layouts", &value.bits()) + } +} + +unsafe impl<'a> option::Target for Context<'a> { + fn as_ptr(&self) -> *const c_void { + self.ptr as *const _ + } + + fn as_mut_ptr(&mut self) -> *mut c_void { + self.ptr as *mut _ + } +} + +impl<'a> option::Settable for Context<'a> { } diff --git a/src/filter/context/mod.rs b/src/filter/context/mod.rs new file mode 100644 index 0000000..c70169c --- /dev/null +++ b/src/filter/context/mod.rs @@ -0,0 +1,8 @@ +mod context; +pub use self::context::Context; + +mod source; +pub use self::source::Source; + +mod sink; +pub use self::sink::Sink; diff --git a/src/filter/context/sink.rs b/src/filter/context/sink.rs new file mode 100644 index 0000000..d529a04 --- /dev/null +++ b/src/filter/context/sink.rs @@ -0,0 +1,34 @@ +use ffi::*; +use libc::c_int; +use ::{Frame, Error}; +use super::Context; + +pub struct Sink<'a> { + ctx: &'a mut Context<'a>, +} + +impl<'a> Sink<'a> { + pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Sink<'b> { + Sink { ctx: ctx } + } +} + +impl<'a> Sink<'a> { + pub fn frame(&mut self, frame: &mut Frame) -> Result<(), Error> { + unsafe { + match av_buffersink_get_frame(self.ctx.as_mut_ptr(), frame.as_mut_ptr()) { + n if n >= 0 => Ok(()), + e => Err(Error::from(e)), + } + } + } + + pub fn samples(&mut self, frame: &mut Frame, samples: usize) -> Result<(), Error> { + unsafe { + match av_buffersink_get_samples(self.ctx.as_mut_ptr(), frame.as_mut_ptr(), samples as c_int) { + n if n >= 0 => Ok(()), + e => Err(Error::from(e)), + } + } + } +} diff --git a/src/filter/context/source.rs b/src/filter/context/source.rs new file mode 100644 index 0000000..1f4e70f --- /dev/null +++ b/src/filter/context/source.rs @@ -0,0 +1,30 @@ +use ffi::*; +use ::{Error, Frame}; +use super::Context; + +pub struct Source<'a> { + ctx: &'a mut Context<'a>, +} + +impl<'a> Source<'a> { + pub unsafe fn wrap<'b>(ctx: &'b mut Context<'b>) -> Source<'b> { + Source { ctx: ctx } + } +} + +impl<'a> Source<'a> { + pub fn failed_requests(&self) -> usize { + unsafe { + av_buffersrc_get_nb_failed_requests(self.ctx.as_ptr()) as usize + } + } + + pub fn add(&mut self, frame: &Frame) -> Result<(), Error> { + unsafe { + match av_buffersrc_add_frame(self.ctx.as_mut_ptr(), frame.as_ptr() as *mut _) { + 0 => Ok(()), + e => Err(Error::from(e)), + } + } + } +} diff --git a/src/filter/filter.rs b/src/filter/filter.rs new file mode 100644 index 0000000..d1b33fb --- /dev/null +++ b/src/filter/filter.rs @@ -0,0 +1,107 @@ +use std::ffi::CStr; +use std::str::from_utf8_unchecked; +use std::marker::PhantomData; + +use ffi::*; +use super::{Pad, Flags}; + +pub struct Filter { + ptr: *mut AVFilter, +} + +impl Filter { + pub unsafe fn wrap(ptr: *mut AVFilter) -> Self { + Filter { ptr: ptr } + } + + pub unsafe fn as_ptr(&self) -> *const AVFilter { + self.ptr as *const _ + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilter { + self.ptr + } +} + +impl Filter { + pub fn name(&self) -> &str { + unsafe { + from_utf8_unchecked(CStr::from_ptr((*self.as_ptr()).name).to_bytes()) + } + } + + pub fn description(&self) -> Option<&str> { + unsafe { + let ptr = (*self.as_ptr()).description; + + if ptr.is_null() { + None + } + else { + Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + } + } + } + + pub fn inputs(&self) -> Option { + unsafe { + let ptr = (*self.as_ptr()).inputs; + + if ptr.is_null() { + None + } + else { + Some(PadIter::new((*self.as_ptr()).inputs)) + } + } + } + + pub fn outputs(&self) -> Option { + unsafe { + let ptr = (*self.as_ptr()).outputs; + + if ptr.is_null() { + None + } + else { + Some(PadIter::new((*self.as_ptr()).outputs)) + } + } + } + + pub fn flags(&self) -> Flags { + unsafe { + Flags::from_bits_truncate((*self.as_ptr()).flags) + } + } +} + +pub struct PadIter<'a> { + ptr: *const AVFilterPad, + cur: isize, + + _marker: PhantomData<&'a ()>, +} + +impl<'a> PadIter<'a> { + pub fn new(ptr: *const AVFilterPad) -> Self { + PadIter { ptr: ptr, cur: 0, _marker: PhantomData } + } +} + +impl<'a> Iterator for PadIter<'a> { + type Item = Pad<'a>; + + fn next(&mut self) -> Option { + unsafe { + if self.cur >= avfilter_pad_count(self.ptr) as isize { + return None; + } + + let pad = Pad::wrap(self.ptr.offset(self.cur)); + self.cur += 1; + + Some(pad) + } + } +} diff --git a/src/filter/flag.rs b/src/filter/flag.rs new file mode 100644 index 0000000..fb429e9 --- /dev/null +++ b/src/filter/flag.rs @@ -0,0 +1,13 @@ +use libc::c_int; +use ffi::*; + +bitflags! { + flags Flags: c_int { + const DYNAMIC_INPUTS = AVFILTER_FLAG_DYNAMIC_INPUTS, + const DYNAMIC_OUTPUTS = AVFILTER_FLAG_DYNAMIC_OUTPUTS, + const SLICE_THREADS = AVFILTER_FLAG_SLICE_THREADS, + const SUPPORT_TIMELINE_GENERIC = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + const SUPPORT_TIMELINE_INTERNAL = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + const SUPPORT_TIMELINE = AVFILTER_FLAG_SUPPORT_TIMELINE, + } +} diff --git a/src/filter/graph.rs b/src/filter/graph.rs new file mode 100644 index 0000000..8005843 --- /dev/null +++ b/src/filter/graph.rs @@ -0,0 +1,180 @@ +use std::ptr; +use std::ffi::CString; + +use ffi::*; +use libc::c_int; +use ::Error; +use super::{Context, Filter}; + +pub struct Graph { + ptr: *mut AVFilterGraph, +} + +impl Graph { + pub unsafe fn wrap(ptr: *mut AVFilterGraph) -> Self { + Graph { ptr: ptr } + } + + pub unsafe fn as_ptr(&self) -> *const AVFilterGraph { + self.ptr as *const _ + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterGraph { + self.ptr + } +} + +impl Graph { + pub fn new() -> Self { + unsafe { + let ptr = avfilter_graph_alloc(); + + if ptr.is_null() { + panic!("out of memory"); + } + + Graph::wrap(ptr) + } + } + + pub fn validate(&mut self) -> Result<(), Error> { + unsafe { + match avfilter_graph_config(self.as_mut_ptr(), ptr::null_mut()) { + 0 => Ok(()), + e => Err(Error::from(e)) + } + } + } + + pub fn add<'a, 'b>(&'a mut self, filter: &Filter, name: &str, args: &str) -> Result, Error> where 'a: 'b { + unsafe { + let mut context = ptr::null_mut(); + + match avfilter_graph_create_filter(&mut context as *mut *mut AVFilterContext, + filter.as_ptr(), + CString::new(name).unwrap().as_ptr(), + CString::new(args).unwrap().as_ptr(), + ptr::null_mut(), + self.as_mut_ptr()) + { + n if n >= 0 => Ok(Context::wrap(context)), + e => Err(Error::from(e)), + } + } + } + + pub fn get<'a, 'b>(&'b mut self, name: &str) -> Option> where 'a: 'b { + unsafe { + let ptr = avfilter_graph_get_filter(self.as_mut_ptr(), CString::new(name).unwrap().as_ptr()); + + if ptr.is_null() { + None + } + else { + Some(Context::wrap(ptr)) + } + } + } + + pub fn input(&mut self, name: &str, pad: usize) -> Result { + Parser::new(self).input(name, pad) + } + + pub fn output(&mut self, name: &str, pad: usize) -> Result { + Parser::new(self).output(name, pad) + } + + pub fn parse(&mut self, spec: &str) -> Result<(), Error> { + Parser::new(self).parse(spec) + } +} + +impl Drop for Graph { + fn drop(&mut self) { + unsafe { + avfilter_graph_free(&mut self.as_mut_ptr()); + } + } +} + +pub struct Parser<'a> { + graph: &'a mut Graph, + inputs: *mut AVFilterInOut, + outputs: *mut AVFilterInOut, +} + +impl<'a> Parser<'a> { + pub fn new(graph: &mut Graph) -> Parser { + Parser { + graph: graph, + inputs: ptr::null_mut(), + outputs: ptr::null_mut(), + } + } + + pub fn input(mut self, name: &str, pad: usize) -> Result { + unsafe { + let mut context = try!(self.graph.get(name).ok_or(Error::InvalidData)); + let mut input = avfilter_inout_alloc(); + + if input.is_null() { + panic!("out of memory"); + } + + (*input).name = av_strdup(CString::new(name).unwrap().as_ptr()); + (*input).filter_ctx = context.as_mut_ptr(); + (*input).pad_idx = pad as c_int; + (*input).next = ptr::null_mut(); + + if self.inputs.is_null() { + self.inputs = input; + } + else { + (*self.inputs).next = input; + } + } + + Ok(self) + } + + pub fn output(mut self, name: &str, pad: usize) -> Result { + unsafe { + let mut context = try!(self.graph.get(name).ok_or(Error::InvalidData)); + let mut output = avfilter_inout_alloc(); + + if output.is_null() { + panic!("out of memory"); + } + + (*output).name = av_strdup(CString::new(name).unwrap().as_ptr()); + (*output).filter_ctx = context.as_mut_ptr(); + (*output).pad_idx = pad as c_int; + (*output).next = ptr::null_mut(); + + if self.outputs.is_null() { + self.outputs = output; + } + else { + (*self.outputs).next = output; + } + } + + Ok(self) + } + + pub fn parse(mut self, spec: &str) -> Result<(), Error> { + unsafe { + let result = avfilter_graph_parse_ptr(self.graph.as_mut_ptr(), + CString::new(spec).unwrap().as_ptr(), &mut self.inputs, &mut self.outputs, + ptr::null_mut()); + + avfilter_inout_free(&mut self.inputs); + avfilter_inout_free(&mut self.outputs); + + match result { + n if n >= 0 => Ok(()), + e => Err(Error::from(e)) + } + } + } +} diff --git a/src/filter/mod.rs b/src/filter/mod.rs new file mode 100644 index 0000000..c23eb73 --- /dev/null +++ b/src/filter/mod.rs @@ -0,0 +1,66 @@ +pub mod flag; +pub use self::flag::Flags; + +pub mod pad; +pub use self::pad::Pad; + +pub mod filter; +pub use self::filter::Filter; + +pub mod context; +pub use self::context::{Context, Source, Sink}; + +pub mod graph; +pub use self::graph::Graph; + +use std::ffi::{CString, CStr}; +use std::str::from_utf8_unchecked; + +use ffi::*; +use ::Error; + +pub fn register_all() { + unsafe { + avfilter_register_all(); + } +} + +pub fn register(filter: &Filter) -> Result<(), Error> { + unsafe { + match avfilter_register(filter.as_ptr()) { + 0 => Ok(()), + _ => Err(Error::InvalidData), + } + } +} + +pub fn version() -> u32 { + unsafe { + avfilter_version() + } +} + +pub fn configuration() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(avfilter_configuration()).to_bytes()) + } +} + +pub fn license() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(avfilter_license()).to_bytes()) + } +} + +pub fn find(name: &str) -> Option { + unsafe { + let ptr = avfilter_get_by_name(CString::new(name).unwrap().as_ptr()); + + if ptr.is_null() { + None + } + else { + Some(Filter::wrap(ptr)) + } + } +} diff --git a/src/filter/pad.rs b/src/filter/pad.rs new file mode 100644 index 0000000..bdd20a2 --- /dev/null +++ b/src/filter/pad.rs @@ -0,0 +1,47 @@ +use std::ffi::CStr; +use std::str::from_utf8_unchecked; +use std::marker::PhantomData; + +use ffi::*; +use ::media; + +pub struct Pad<'a> { + ptr: *const AVFilterPad, + + _marker: PhantomData<&'a ()>, +} + +impl<'a> Pad<'a> { + pub unsafe fn wrap(ptr: *const AVFilterPad) -> Self { + Pad { ptr: ptr, _marker: PhantomData } + } + + pub unsafe fn as_ptr(&self) -> *const AVFilterPad { + self.ptr + } + + pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterPad { + self.ptr as *mut _ + } +} + +impl<'a> Pad<'a> { + pub fn name(&self) -> Option<&str> { + unsafe { + let ptr = avfilter_pad_get_name(self.ptr, 0); + + if ptr.is_null() { + None + } + else { + Some(from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())) + } + } + } + + pub fn medium(&self) -> media::Type { + unsafe { + media::Type::from(avfilter_pad_get_type(self.ptr, 0)) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9a34395..e0e94f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,12 @@ #![allow(raw_pointer_derive, non_camel_case_types)] extern crate libc; -extern crate ffmpeg_sys as ffi; +extern crate ffmpeg_sys as sys; #[macro_use] extern crate bitflags; #[cfg(feature = "image")] extern crate image; +pub use sys as ffi; + pub mod util; pub use util::error::Error; pub use util::dictionary::Dictionary; @@ -49,6 +51,11 @@ pub use codec::threading; #[cfg(feature = "device")] pub mod device; +#[cfg(feature = "filter")] +pub mod filter; +#[cfg(feature = "filter")] +pub use filter::Filter; + pub mod software; fn init_error() { @@ -71,10 +78,19 @@ fn init_device() { #[cfg(not(feature = "device"))] fn init_device() { } +#[cfg(feature = "filter")] +fn init_filter() { + filter::register_all(); +} + +#[cfg(not(feature = "filter"))] +fn init_filter() { } + pub fn init() -> Result<(), Error> { init_error(); init_format(); init_device(); + init_filter(); Ok(()) }