use std::ffi::CString;
use std::ptr::NonNull;
use ffmpeg_sys_next::*;
use crate::error::{FfmpegError, AVERROR_EAGAIN};
use crate::frame::Frame;
use crate::smart_object::SmartPtr;
pub struct FilterGraph(SmartPtr<AVFilterGraph>);
unsafe impl Send for FilterGraph {}
impl FilterGraph {
pub fn new() -> Result<Self, FfmpegError> {
unsafe { Self::wrap(avfilter_graph_alloc()) }
}
unsafe fn wrap(ptr: *mut AVFilterGraph) -> Result<Self, FfmpegError> {
Ok(Self(
SmartPtr::wrap_non_null(ptr, |ptr| unsafe { avfilter_graph_free(ptr) }).ok_or(FfmpegError::Alloc)?,
))
}
pub fn as_ptr(&self) -> *const AVFilterGraph {
self.0.as_ptr()
}
pub fn as_mut_ptr(&mut self) -> *mut AVFilterGraph {
self.0.as_mut_ptr()
}
pub fn add(&mut self, filter: Filter, name: &str, args: &str) -> Result<FilterContext<'_>, FfmpegError> {
let name = CString::new(name).expect("failed to convert name to CString");
let args = CString::new(args).expect("failed to convert args to CString");
let mut filter_context = std::ptr::null_mut();
let ret = unsafe {
avfilter_graph_create_filter(
&mut filter_context,
filter.as_ptr(),
name.as_ptr(),
args.as_ptr(),
std::ptr::null_mut(),
self.as_mut_ptr(),
)
};
if ret < 0 {
Err(FfmpegError::Code(ret.into()))
} else {
Ok(FilterContext(unsafe {
NonNull::new(filter_context).ok_or(FfmpegError::Alloc)?.as_mut()
}))
}
}
pub fn get(&mut self, name: &str) -> Option<FilterContext<'_>> {
let name = CString::new(name).unwrap();
let mut ptr = NonNull::new(unsafe { avfilter_graph_get_filter(self.as_mut_ptr(), name.as_ptr()) })?;
Some(FilterContext(unsafe { ptr.as_mut() }))
}
pub fn validate(&mut self) -> Result<(), FfmpegError> {
let ret = unsafe { avfilter_graph_config(self.as_mut_ptr(), std::ptr::null_mut()) };
if ret < 0 {
Err(FfmpegError::Code(ret.into()))
} else {
Ok(())
}
}
pub fn dump(&mut self) -> Option<String> {
unsafe {
let c_str = SmartPtr::wrap_non_null(avfilter_graph_dump(self.as_mut_ptr(), std::ptr::null_mut()), |ptr| {
av_free(*ptr as *mut libc::c_void);
*ptr = std::ptr::null_mut();
})?;
let c_str = std::ffi::CStr::from_ptr(c_str.as_ptr());
Some(c_str.to_str().ok()?.to_owned())
}
}
pub fn set_thread_count(&mut self, threads: i32) {
self.0.as_deref_mut_except().nb_threads = threads;
}
pub fn input(&mut self, name: &str, pad: i32) -> Result<FilterGraphParser<'_>, FfmpegError> {
FilterGraphParser::new(self).input(name, pad)
}
pub fn output(&mut self, name: &str, pad: i32) -> Result<FilterGraphParser<'_>, FfmpegError> {
FilterGraphParser::new(self).output(name, pad)
}
}
pub struct FilterGraphParser<'a> {
graph: &'a mut FilterGraph,
inputs: SmartPtr<AVFilterInOut>,
outputs: SmartPtr<AVFilterInOut>,
}
unsafe impl Send for FilterGraphParser<'_> {}
impl<'a> FilterGraphParser<'a> {
fn new(graph: &'a mut FilterGraph) -> Self {
Self {
graph,
inputs: unsafe { SmartPtr::wrap(std::ptr::null_mut(), |ptr| avfilter_inout_free(ptr)) },
outputs: unsafe { SmartPtr::wrap(std::ptr::null_mut(), |ptr| avfilter_inout_free(ptr)) },
}
}
pub fn input(self, name: &str, pad: i32) -> Result<Self, FfmpegError> {
self.inout_impl(name, pad, false)
}
pub fn output(self, name: &str, pad: i32) -> Result<Self, FfmpegError> {
self.inout_impl(name, pad, true)
}
pub fn parse(mut self, spec: &str) -> Result<(), FfmpegError> {
let spec = CString::new(spec).unwrap();
unsafe {
match avfilter_graph_parse_ptr(
self.graph.as_mut_ptr(),
spec.as_ptr(),
self.inputs.as_mut(),
self.outputs.as_mut(),
std::ptr::null_mut(),
) {
n if n >= 0 => Ok(()),
e => Err(FfmpegError::Code(e.into())),
}
}
}
fn inout_impl(mut self, name: &str, pad: i32, output: bool) -> Result<Self, FfmpegError> {
let context = self.graph.get(name).ok_or(FfmpegError::Arguments("unknown name"))?;
let mut inout = unsafe { SmartPtr::wrap_non_null(avfilter_inout_alloc(), |ptr| avfilter_inout_free(ptr)) }
.ok_or(FfmpegError::Alloc)?;
let name = CString::new(name).unwrap();
inout.as_deref_mut_except().name = name.into_raw();
inout.as_deref_mut_except().filter_ctx = context.0;
inout.as_deref_mut_except().pad_idx = pad;
if output {
inout.as_deref_mut_except().next = self.outputs.into_inner();
self.outputs = inout;
} else {
inout.as_deref_mut_except().next = self.inputs.into_inner();
self.inputs = inout;
}
Ok(self)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Filter(*const AVFilter);
impl Filter {
pub fn get(name: &str) -> Option<Self> {
let name = std::ffi::CString::new(name).ok()?;
let filter = unsafe { avfilter_get_by_name(name.as_ptr()) };
if filter.is_null() {
None
} else {
Some(Self(filter))
}
}
pub fn as_ptr(&self) -> *const AVFilter {
self.0
}
pub unsafe fn wrap(ptr: *const AVFilter) -> Self {
Self(ptr)
}
}
unsafe impl Send for Filter {}
pub struct FilterContext<'a>(&'a mut AVFilterContext);
unsafe impl Send for FilterContext<'_> {}
impl<'a> FilterContext<'a> {
pub fn source(self) -> FilterContextSource<'a> {
FilterContextSource(self.0)
}
pub fn sink(self) -> FilterContextSink<'a> {
FilterContextSink(self.0)
}
}
pub struct FilterContextSource<'a>(&'a mut AVFilterContext);
unsafe impl Send for FilterContextSource<'_> {}
impl FilterContextSource<'_> {
pub fn send_frame(&mut self, frame: &Frame) -> Result<(), FfmpegError> {
unsafe {
match av_buffersrc_write_frame(self.0, frame.as_ptr()) {
0 => Ok(()),
e => Err(FfmpegError::Code(e.into())),
}
}
}
pub fn send_eof(&mut self, pts: Option<i64>) -> Result<(), FfmpegError> {
unsafe {
match if let Some(pts) = pts {
av_buffersrc_close(self.0, pts, 0)
} else {
av_buffersrc_write_frame(self.0, std::ptr::null())
} {
0 => Ok(()),
e => Err(FfmpegError::Code(e.into())),
}
}
}
}
pub struct FilterContextSink<'a>(&'a mut AVFilterContext);
unsafe impl Send for FilterContextSink<'_> {}
impl FilterContextSink<'_> {
pub fn receive_frame(&mut self) -> Result<Option<Frame>, FfmpegError> {
let mut frame = Frame::new()?;
unsafe {
match av_buffersink_get_frame(self.0, frame.as_mut_ptr()) {
0 => Ok(Some(frame)),
AVERROR_EAGAIN | AVERROR_EOF => Ok(None),
e => Err(FfmpegError::Code(e.into())),
}
}
}
}