Skip to main content

vapoursynth4_rs/
node.rs

1/*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6
7mod 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    /// # Errors
54    ///
55    /// Return the internal error message if the frame is not ready.
56    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    // TODO: Find a better way to handle callbacks
69    /// # Safety
70    ///
71    /// The caller must ensure that:
72    /// - `data` is a valid pointer to the data needed by the callback
73    /// - `callback` is a valid function pointer that safely handles the frame data
74    /// - The callback and data remain valid until the frame processing is complete
75    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    /// Name of the function that created this filter.
82    ///
83    /// `level` 0 is the creating function itself; higher levels walk up to the
84    /// function that invoked it. Returns [`None`] for a non-existent level.
85    ///
86    /// Only meaningful on a core built with
87    /// [`enable_graph_inspection()`](crate::core::CoreBuilder::enable_graph_inspection),
88    /// and not safe to call concurrently with frame requests.
89    #[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    /// Identifier of the plugin that created this filter.
101    ///
102    /// See [`creation_function_name()`](Self::creation_function_name) for `level`
103    /// and for the constraints on when this may be called.
104    #[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    /// Namespace of the plugin that created this filter.
111    ///
112    /// See [`creation_function_name()`](Self::creation_function_name) for `level`
113    /// and for the constraints on when this may be called.
114    #[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    /// Arguments passed to the function that created this filter.
121    ///
122    /// See [`creation_function_name()`](Self::creation_function_name) for `level`
123    /// and for the constraints on when this may be called.
124    #[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/// The graph inspection functions return strings owned by the node, so the
133/// result borrows from `self` rather than being copied out.
134#[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    /// # Safety
174    ///
175    /// The caller must ensure that `ptr` is a valid pointer to a [`ffi::VSNode`] that represents a video node.
176    #[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        // SAFETY: `vi` is valid if the node is a video node
184        unsafe { &*(self.api.getVideoInfo)(self.as_ptr()) }
185    }
186
187    /// # Panics
188    ///
189    /// Panics if the the dependency index is larger than [`i32::MAX`].
190    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    /// # Safety
265    ///
266    /// The caller must ensure that `ptr` is a valid pointer to a [`ffi::VSNode`] that represents an audio node.
267    #[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        // SAFETY: `ai` is valid if the node is an audio node
275        unsafe { &*(self.api.getAudioInfo)(self.as_ptr()) }
276    }
277
278    /// # Panics
279    ///
280    /// Panics if the the dependency index is larger than [`i32::MAX`].
281    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;