playa_ffmpeg/filter/
graph.rs

1use std::{
2    ffi::{CStr, CString},
3    ptr,
4    str::from_utf8_unchecked,
5};
6
7use super::{Context, Filter};
8use crate::{Error, ffi::*};
9use libc::c_int;
10
11pub struct Graph {
12    ptr: *mut AVFilterGraph,
13}
14
15unsafe impl Send for Graph {}
16unsafe impl Sync for Graph {}
17
18impl Graph {
19    pub unsafe fn wrap(ptr: *mut AVFilterGraph) -> Self {
20        Graph { ptr }
21    }
22
23    pub unsafe fn as_ptr(&self) -> *const AVFilterGraph {
24        self.ptr as *const _
25    }
26
27    pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFilterGraph {
28        self.ptr
29    }
30}
31
32impl Graph {
33    pub fn new() -> Self {
34        unsafe {
35            let ptr = avfilter_graph_alloc();
36
37            if ptr.is_null() {
38                panic!("out of memory");
39            }
40
41            Graph::wrap(ptr)
42        }
43    }
44
45    pub fn validate(&mut self) -> Result<(), Error> {
46        unsafe {
47            match avfilter_graph_config(self.as_mut_ptr(), ptr::null_mut()) {
48                0 => Ok(()),
49                e => Err(Error::from(e)),
50            }
51        }
52    }
53
54    pub fn add(&mut self, filter: &Filter, name: &str, args: &str) -> Result<Context, Error> {
55        unsafe {
56            let name = CString::new(name).unwrap();
57            let args = CString::new(args).unwrap();
58            let mut context = ptr::null_mut();
59
60            match avfilter_graph_create_filter(&mut context as *mut *mut AVFilterContext, filter.as_ptr(), name.as_ptr(), args.as_ptr(), ptr::null_mut(), self.as_mut_ptr()) {
61                n if n >= 0 => Ok(Context::wrap(context)),
62                e => Err(Error::from(e)),
63            }
64        }
65    }
66
67    pub fn get(&mut self, name: &str) -> Option<Context> {
68        unsafe {
69            let name = CString::new(name).unwrap();
70            let ptr = avfilter_graph_get_filter(self.as_mut_ptr(), name.as_ptr());
71
72            if ptr.is_null() { None } else { Some(Context::wrap(ptr)) }
73        }
74    }
75
76    pub fn dump(&self) -> String {
77        unsafe {
78            let ptr = avfilter_graph_dump(self.as_ptr() as *mut _, ptr::null());
79            let cstr = from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes());
80            let string = cstr.to_owned();
81
82            av_free(ptr as *mut _);
83
84            string
85        }
86    }
87
88    pub fn input(&mut self, name: &str, pad: usize) -> Result<Parser<'_>, Error> {
89        Parser::new(self).input(name, pad)
90    }
91
92    pub fn output(&mut self, name: &str, pad: usize) -> Result<Parser<'_>, Error> {
93        Parser::new(self).output(name, pad)
94    }
95
96    pub fn parse(&mut self, spec: &str) -> Result<(), Error> {
97        Parser::new(self).parse(spec)
98    }
99}
100
101impl Drop for Graph {
102    fn drop(&mut self) {
103        unsafe {
104            avfilter_graph_free(&mut self.as_mut_ptr());
105        }
106    }
107}
108
109pub struct Parser<'a> {
110    graph: &'a mut Graph,
111    inputs: *mut AVFilterInOut,
112    outputs: *mut AVFilterInOut,
113}
114
115impl<'a> Parser<'a> {
116    pub fn new(graph: &mut Graph) -> Parser<'_> {
117        Parser { graph, inputs: ptr::null_mut(), outputs: ptr::null_mut() }
118    }
119
120    pub fn input(mut self, name: &str, pad: usize) -> Result<Self, Error> {
121        unsafe {
122            let mut context = self.graph.get(name).ok_or(Error::InvalidData)?;
123            let input = avfilter_inout_alloc();
124
125            if input.is_null() {
126                panic!("out of memory");
127            }
128
129            let name = CString::new(name).unwrap();
130
131            (*input).name = av_strdup(name.as_ptr());
132            (*input).filter_ctx = context.as_mut_ptr();
133            (*input).pad_idx = pad as c_int;
134            (*input).next = ptr::null_mut();
135
136            if self.inputs.is_null() {
137                self.inputs = input;
138            } else {
139                (*self.inputs).next = input;
140            }
141        }
142
143        Ok(self)
144    }
145
146    pub fn output(mut self, name: &str, pad: usize) -> Result<Self, Error> {
147        unsafe {
148            let mut context = self.graph.get(name).ok_or(Error::InvalidData)?;
149            let output = avfilter_inout_alloc();
150
151            if output.is_null() {
152                panic!("out of memory");
153            }
154
155            let name = CString::new(name).unwrap();
156
157            (*output).name = av_strdup(name.as_ptr());
158            (*output).filter_ctx = context.as_mut_ptr();
159            (*output).pad_idx = pad as c_int;
160            (*output).next = ptr::null_mut();
161
162            if self.outputs.is_null() {
163                self.outputs = output;
164            } else {
165                (*self.outputs).next = output;
166            }
167        }
168
169        Ok(self)
170    }
171
172    pub fn parse(mut self, spec: &str) -> Result<(), Error> {
173        unsafe {
174            let spec = CString::new(spec).unwrap();
175
176            let result = avfilter_graph_parse_ptr(self.graph.as_mut_ptr(), spec.as_ptr(), &mut self.inputs, &mut self.outputs, ptr::null_mut());
177
178            avfilter_inout_free(&mut self.inputs);
179            avfilter_inout_free(&mut self.outputs);
180
181            match result {
182                n if n >= 0 => Ok(()),
183                e => Err(Error::from(e)),
184            }
185        }
186    }
187}
188
189impl Default for Graph {
190    fn default() -> Self {
191        Self::new()
192    }
193}