1use std::ffi::CString;
3use std::fmt::Write;
4use std::ops::{Deref, DerefMut};
5use std::os::raw::c_void;
6use std::ptr::{self};
7use std::{mem, panic, process};
8
9use vapoursynth_sys as ffi;
10
11use crate::api::API;
12use crate::core::CoreRef;
13use crate::map::{MapRef, MapRefMut};
14use crate::plugins::{Filter, FilterFunction, FrameContext, Metadata};
15use crate::video_info::VideoInfo;
16
17pub(crate) struct FilterFunctionData<F: FilterFunction> {
19 pub filter_function: F,
20 pub name: CString,
23}
24
25unsafe extern "C" fn free(
27 instance_data: *mut c_void,
28 _core: *mut ffi::VSCore,
29 _vsapi: *const ffi::VSAPI,
30) {
31 let closure = move || {
32 let filter = Box::from_raw(instance_data as *mut Box<dyn Filter<'static> + 'static>);
35 drop(filter);
36 };
37
38 if panic::catch_unwind(closure).is_err() {
39 process::abort();
40 }
41}
42
43unsafe extern "C" fn get_frame(
45 n: i32,
46 activation_reason: i32,
47 instance_data: *mut c_void,
48 _frame_data: *mut *mut c_void,
49 frame_ctx: *mut ffi::VSFrameContext,
50 core: *mut ffi::VSCore,
51 _vsapi: *const ffi::VSAPI,
52) -> *const ffi::VSFrame {
53 let closure = move || {
54 let api = API::get_cached();
55 let core = CoreRef::from_ptr(core);
56 let context = FrameContext::from_ptr(frame_ctx);
57
58 let filter = Box::from_raw(instance_data as *mut Box<dyn Filter<'static> + 'static>);
61
62 debug_assert!(n >= 0);
63 let n = n as usize;
64
65 let rv = match activation_reason {
66 x if x == ffi::VSActivationReason_arInitial as _ => {
67 match filter.get_frame_initial(api, core, context, n) {
68 Ok(Some(frame)) => {
69 let ptr = frame.deref().deref() as *const _;
70 mem::forget(frame);
72 ptr
73 }
74 Ok(None) => ptr::null(),
75 Err(err) => {
76 let mut buf = String::with_capacity(64);
77
78 write!(buf, "Error in Filter::get_frame_initial(): {}", err).unwrap();
79
80 write!(buf, "{}", err).unwrap();
81
82 let buf = CString::new(buf.replace('\0', "\\0")).unwrap();
83 api.set_filter_error(buf.as_ptr(), frame_ctx);
84
85 ptr::null()
86 }
87 }
88 }
89 x if x == ffi::VSActivationReason_arAllFramesReady as _ => {
90 match filter.get_frame(api, core, context, n) {
91 Ok(frame) => {
92 let ptr = frame.deref().deref() as *const _;
93 mem::forget(frame);
95 ptr
96 }
97 Err(err) => {
98 let buf = format!("{}", err);
99 let buf = CString::new(buf.replace('\0', "\\0")).unwrap();
100 api.set_filter_error(buf.as_ptr(), frame_ctx);
101
102 ptr::null()
103 }
104 }
105 }
106 _ => ptr::null(),
107 };
108
109 mem::forget(filter);
110
111 rv
112 };
113
114 match panic::catch_unwind(closure) {
115 Ok(frame) => frame,
116 Err(_) => process::abort(),
117 }
118}
119
120pub(crate) unsafe extern "C" fn create<F: FilterFunction>(
122 in_: *const ffi::VSMap,
123 out: *mut ffi::VSMap,
124 user_data: *mut c_void,
125 core: *mut ffi::VSCore,
126 api: *const ffi::VSAPI,
127) {
128 let closure = move || {
129 API::set(api);
130
131 let args = MapRef::from_ptr(in_);
132 let mut out = MapRefMut::from_ptr(out);
133 let core = CoreRef::from_ptr(core);
134 let data = Box::from_raw(user_data as *mut FilterFunctionData<F>);
135
136 let filter = match data.filter_function.create(API::get_cached(), core, &args) {
137 Ok(Some(filter)) => Some(Box::new(filter)),
138 Ok(None) => None,
139 Err(err) => {
140 let mut buf = String::with_capacity(64);
141
142 write!(
143 buf,
144 "Error in Filter::create() of {}: {}",
145 data.name.to_str().unwrap(),
146 err
147 )
148 .unwrap();
149
150 write!(buf, "{}", err).unwrap();
151
152 out.set_error(&buf.replace('\0', "\\0")).unwrap();
153 None
154 }
155 };
156
157 if let Some(filter) = filter {
158 let vi = filter
160 .video_info(API::get_cached(), core)
161 .into_iter()
162 .map(VideoInfo::ffi_type)
163 .collect::<Vec<_>>();
164
165 let vi_ptr = if !vi.is_empty() {
168 vi.as_ptr()
169 } else {
170 ptr::null()
171 };
172
173 API::get_cached().create_video_filter(
174 out.deref_mut().deref_mut(),
175 data.name.as_ptr(),
176 vi_ptr,
177 Some(get_frame),
178 Some(free),
179 ffi::VSFilterMode_fmParallel as i32,
180 ptr::null(), 0, Box::into_raw(filter) as *mut _,
183 core.ptr(),
184 );
185
186 mem::forget(vi);
188 }
189
190 mem::forget(data);
191 };
192
193 if panic::catch_unwind(closure).is_err() {
194 process::abort();
196 }
197}
198
199#[inline]
206pub unsafe fn call_config_func(
207 vspapi: *const ffi::VSPLUGINAPI,
208 plugin: *mut ffi::VSPlugin,
209 metadata: Metadata,
210) {
211 let identifier_cstring = CString::new(metadata.identifier)
212 .expect("Couldn't convert the plugin identifier to a CString");
213 let namespace_cstring = CString::new(metadata.namespace)
214 .expect("Couldn't convert the plugin namespace to a CString");
215 let name_cstring =
216 CString::new(metadata.name).expect("Couldn't convert the plugin name to a CString");
217
218 let api_version = (ffi::VAPOURSYNTH_API_MAJOR << 16 | ffi::VAPOURSYNTH_API_MINOR) as i32;
219 let flags = if metadata.read_only {
220 0 } else {
222 ffi::VSPluginConfigFlags_pcModifiable as i32
223 };
224
225 ((*vspapi).configPlugin.unwrap())(
226 identifier_cstring.as_ptr(),
227 namespace_cstring.as_ptr(),
228 name_cstring.as_ptr(),
229 1, api_version,
231 flags,
232 plugin,
233 );
234}
235
236#[inline]
243pub unsafe fn call_register_func<F: FilterFunction>(
244 vspapi: *const ffi::VSPLUGINAPI,
245 plugin: *mut ffi::VSPlugin,
246 filter_function: F,
247) {
248 let name_cstring = CString::new(filter_function.name())
249 .expect("Couldn't convert the filter name to a CString");
250 let args_cstring = CString::new(filter_function.args())
251 .expect("Couldn't convert the filter args to a CString");
252 let return_type_cstring =
253 CString::new("clip:vnode;").expect("Couldn't convert return type to a CString");
254
255 let data = Box::new(FilterFunctionData {
256 filter_function,
257 name: name_cstring,
258 });
259
260 ((*vspapi).registerFunction.unwrap())(
261 data.name.as_ptr(),
262 args_cstring.as_ptr(),
263 return_type_cstring.as_ptr(),
264 Some(create::<F>),
265 Box::into_raw(data) as _,
266 plugin,
267 );
268}
269
270#[macro_export]
293macro_rules! export_vapoursynth_plugin {
294 ($metadata:expr, [$($filter:expr),*$(,)*]) => (
295 #[allow(non_snake_case)]
296 #[unsafe(no_mangle)]
297 pub unsafe extern "C" fn VapourSynthPluginInit2(
298 plugin: *mut $crate::ffi::VSPlugin,
299 vspapi: *const $crate::ffi::VSPLUGINAPI,
300 ) {
301 use ::std::{panic, process};
302 use $crate::plugins::ffi::{call_config_func, call_register_func};
303
304 let closure = move || {
305 call_config_func(vspapi, plugin, $metadata);
306
307 $(
308 call_register_func(vspapi, plugin, $filter);
309 )*
310 };
311
312 if panic::catch_unwind(closure).is_err() {
313 process::abort();
314 }
315 }
316 )
317}