1mod dependency;
8mod filter;
9pub(crate) mod internal;
10
11use std::ffi::{CStr, CString, c_void};
12
13use crate::{
14 AudioInfo, VideoInfo,
15 api::Api,
16 core::Core,
17 ffi,
18 frame::{AudioFrame, Frame, FrameContext, VideoFrame, internal::FrameFromPtr},
19 node::internal::FilterExtern,
20};
21
22pub use dependency::*;
23pub use filter::*;
24use vapoursynth4_sys::VSFrameDoneCallback;
25
26pub trait Node: Sized + Send + Sync + crate::_private::Sealed {
27 type FrameType: Frame;
28
29 fn api(&self) -> Api;
30
31 #[must_use]
32 fn as_ptr(&self) -> *mut ffi::VSNode;
33
34 #[must_use]
35 fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType;
36
37 fn set_linear_filter(&mut self) -> i32 {
38 unsafe { (self.api().setLinearFilter)(self.as_ptr()) }
39 }
40
41 fn set_cache_mode(&mut self, mode: CacheMode) {
42 unsafe {
43 (self.api().setCacheMode)(self.as_ptr(), mode);
44 }
45 }
46
47 fn set_cache_options(&mut self, fixed_size: i32, max_size: i32, max_history_size: i32) {
48 unsafe {
49 (self.api().setCacheOptions)(self.as_ptr(), fixed_size, max_size, max_history_size);
50 }
51 }
52
53 fn get_frame(&self, n: i32) -> Result<Self::FrameType, CString> {
57 let mut buf = vec![0; 1024];
58 let ptr = unsafe { (self.api().getFrame)(n, self.as_ptr(), buf.as_mut_ptr(), 1024) };
59
60 if ptr.is_null() {
61 let mut buf = std::mem::ManuallyDrop::new(buf);
62 Err(unsafe { CStr::from_ptr(buf.as_mut_ptr()).into() })
63 } else {
64 unsafe { Ok(Self::FrameType::from_ptr(ptr, self.api())) }
65 }
66 }
67
68 unsafe fn get_frame_async(&self, n: i32, data: *mut c_void, callback: VSFrameDoneCallback) {
76 unsafe {
77 (self.api().getFrameAsync)(n, self.as_ptr(), callback, data);
78 }
79 }
80
81 #[cfg(feature = "vs-graph")]
90 #[must_use]
91 fn creation_function_name(&self, level: i32) -> Option<&CStr> {
92 unsafe {
93 to_cstr((self.api().getNodeCreationFunctionName)(
94 self.as_ptr(),
95 level,
96 ))
97 }
98 }
99
100 #[cfg(feature = "vs-graph")]
105 #[must_use]
106 fn creation_plugin_id(&self, level: i32) -> Option<&CStr> {
107 unsafe { to_cstr((self.api().getNodeCreationPluginID)(self.as_ptr(), level)) }
108 }
109
110 #[cfg(feature = "vs-graph")]
115 #[must_use]
116 fn creation_plugin_namespace(&self, level: i32) -> Option<&CStr> {
117 unsafe { to_cstr((self.api().getNodeCreationPluginNS)(self.as_ptr(), level)) }
118 }
119
120 #[cfg(feature = "vs-graph")]
125 #[must_use]
126 fn creation_function_arguments(&self, level: i32) -> Option<crate::map::MapRef<'_>> {
127 let ptr = unsafe { (self.api().getNodeCreationFunctionArguments)(self.as_ptr(), level) };
128 (!ptr.is_null()).then(|| unsafe { crate::map::MapRef::from_ptr(ptr, self.api()) })
129 }
130}
131
132#[cfg(feature = "vs-graph")]
135unsafe fn to_cstr<'a>(ptr: *const std::ffi::c_char) -> Option<&'a CStr> {
136 (!ptr.is_null()).then(|| unsafe { CStr::from_ptr(ptr) })
137}
138
139#[derive(Debug, PartialEq, Eq, Hash)]
140pub struct VideoNode {
141 handle: *const ffi::VSNode,
142 api: Api,
143}
144
145impl crate::_private::Sealed for VideoNode {}
146unsafe impl Send for VideoNode {}
147unsafe impl Sync for VideoNode {}
148
149impl Node for VideoNode {
150 type FrameType = VideoFrame;
151
152 #[inline]
153 fn api(&self) -> Api {
154 self.api
155 }
156
157 #[inline]
158 fn as_ptr(&self) -> *mut ffi::VSNode {
159 self.handle.cast_mut()
160 }
161
162 fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType {
163 unsafe {
164 VideoFrame::from_ptr(
165 (self.api.getFrameFilter)(n, self.as_ptr(), ctx.as_ptr()),
166 self.api,
167 )
168 }
169 }
170}
171
172impl VideoNode {
173 #[must_use]
177 pub unsafe fn from_ptr(ptr: *mut ffi::VSNode, api: Api) -> Self {
178 Self { handle: ptr, api }
179 }
180
181 #[must_use]
182 pub fn info(&self) -> &VideoInfo {
183 unsafe { &*(self.api.getVideoInfo)(self.as_ptr()) }
185 }
186
187 pub fn new<F: Filter>(
191 name: &str,
192 info: &VideoInfo,
193 filter: F,
194 dependencies: &[ffi::VSFilterDependency],
195 core: impl AsRef<Core>,
196 ) -> Option<Self> {
197 let filter = Box::new(filter);
198 let name = CString::new(name).ok()?;
199 let core = core.as_ref();
200 let ptr = unsafe {
201 (core.api().createVideoFilter2)(
202 name.as_ptr(),
203 info,
204 F::filter_get_frame,
205 Some(F::filter_free),
206 F::FILTER_MODE,
207 dependencies.as_ptr(),
208 dependencies.len().try_into().unwrap(),
209 Box::into_raw(filter).cast(),
210 core.as_ptr(),
211 )
212 };
213 ptr.is_null()
214 .then_some(unsafe { Self::from_ptr(ptr, core.api()) })
215 }
216}
217
218impl Clone for VideoNode {
219 fn clone(&self) -> Self {
220 unsafe { Self::from_ptr((self.api.addNodeRef)(self.as_ptr()), self.api) }
221 }
222}
223
224impl Drop for VideoNode {
225 fn drop(&mut self) {
226 unsafe { (self.api.freeNode)(self.as_ptr()) }
227 }
228}
229
230#[derive(PartialEq, Eq, Hash, Debug)]
231pub struct AudioNode {
232 handle: *const ffi::VSNode,
233 api: Api,
234}
235
236impl crate::_private::Sealed for AudioNode {}
237unsafe impl Send for AudioNode {}
238unsafe impl Sync for AudioNode {}
239
240impl Node for AudioNode {
241 type FrameType = AudioFrame;
242
243 #[inline]
244 fn api(&self) -> Api {
245 self.api
246 }
247
248 #[inline]
249 fn as_ptr(&self) -> *mut ffi::VSNode {
250 self.handle.cast_mut()
251 }
252
253 fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType {
254 unsafe {
255 AudioFrame::from_ptr(
256 (self.api.getFrameFilter)(n, self.as_ptr(), ctx.as_ptr()),
257 self.api,
258 )
259 }
260 }
261}
262
263impl AudioNode {
264 #[must_use]
268 pub unsafe fn from_ptr(ptr: *mut ffi::VSNode, api: Api) -> Self {
269 Self { handle: ptr, api }
270 }
271
272 #[must_use]
273 pub fn info(&self) -> &AudioInfo {
274 unsafe { &*(self.api.getAudioInfo)(self.as_ptr()) }
276 }
277
278 pub fn new<F: Filter>(
282 name: &str,
283 info: &AudioInfo,
284 filter: F,
285 dependencies: &[ffi::VSFilterDependency],
286 core: impl AsRef<Core>,
287 ) -> Option<Self> {
288 let filter = Box::new(filter);
289 let name = CString::new(name).ok()?;
290 let core = core.as_ref();
291 let ptr = unsafe {
292 (core.api().createAudioFilter2)(
293 name.as_ptr(),
294 info,
295 F::filter_get_frame,
296 Some(F::filter_free),
297 F::FILTER_MODE,
298 dependencies.as_ptr(),
299 dependencies.len().try_into().unwrap(),
300 Box::into_raw(filter).cast(),
301 core.as_ptr(),
302 )
303 };
304 ptr.is_null()
305 .then_some(unsafe { Self::from_ptr(ptr, core.api()) })
306 }
307}
308
309impl Clone for AudioNode {
310 fn clone(&self) -> Self {
311 unsafe { Self::from_ptr((self.api.addNodeRef)(self.as_ptr()), self.api) }
312 }
313}
314
315impl Drop for AudioNode {
316 fn drop(&mut self) {
317 unsafe { (self.api.freeNode)(self.as_ptr()) }
318 }
319}
320
321pub type FilterMode = ffi::VSFilterMode;
322pub type CacheMode = ffi::VSCacheMode;