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};
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::*;
24
25pub trait Node: Sized + crate::_private::Sealed {
26    type FrameType: Frame;
27
28    fn api(&self) -> Api;
29
30    #[must_use]
31    fn as_ptr(&self) -> *mut ffi::VSNode;
32
33    #[must_use]
34    fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType;
35
36    fn set_linear_filter(&mut self) -> i32 {
37        unsafe { (self.api().setLinearFilter)(self.as_ptr()) }
38    }
39
40    fn set_cache_mode(&mut self, mode: CacheMode) {
41        unsafe {
42            (self.api().setCacheMode)(self.as_ptr(), mode);
43        }
44    }
45
46    fn set_cache_options(&mut self, fixed_size: i32, max_size: i32, max_history_size: i32) {
47        unsafe {
48            (self.api().setCacheOptions)(self.as_ptr(), fixed_size, max_size, max_history_size);
49        }
50    }
51
52    /// # Errors
53    ///
54    /// Return the internal error message if the frame is not ready.
55    fn get_frame(&self, n: i32) -> Result<Self::FrameType, CString> {
56        let mut buf = vec![0; 1024];
57        let ptr = unsafe { (self.api().getFrame)(n, self.as_ptr(), buf.as_mut_ptr(), 1024) };
58
59        if ptr.is_null() {
60            let mut buf = std::mem::ManuallyDrop::new(buf);
61            Err(unsafe { CStr::from_ptr(buf.as_mut_ptr()).into() })
62        } else {
63            unsafe { Ok(Self::FrameType::from_ptr(ptr, self.api())) }
64        }
65    }
66
67    // TODO: Find a better way to handle callbacks
68    fn get_frame_async<D, F, Fr>(&self, _n: i32, _data: &mut D)
69    where
70        F: Fn(D, Fr, i32) -> Result<(), String>,
71        Fr: Frame,
72    {
73        todo!()
74    }
75}
76
77#[derive(Debug, PartialEq, Eq, Hash)]
78pub struct VideoNode {
79    handle: *const ffi::VSNode,
80    api: Api,
81}
82
83impl crate::_private::Sealed for VideoNode {}
84impl Node for VideoNode {
85    type FrameType = VideoFrame;
86
87    #[inline]
88    fn api(&self) -> Api {
89        self.api
90    }
91
92    #[must_use]
93    #[inline]
94    fn as_ptr(&self) -> *mut ffi::VSNode {
95        self.handle.cast_mut()
96    }
97
98    #[must_use]
99    fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType {
100        unsafe {
101            VideoFrame::from_ptr(
102                (self.api.getFrameFilter)(n, self.as_ptr(), ctx.as_ptr()),
103                self.api,
104            )
105        }
106    }
107}
108
109impl VideoNode {
110    /// # Safety
111    ///
112    /// The caller must ensure that `ptr` is a valid pointer to a [`ffi::VSNode`] that represents a video node.
113    #[must_use]
114    pub unsafe fn from_ptr(ptr: *mut ffi::VSNode, api: Api) -> Self {
115        Self { handle: ptr, api }
116    }
117
118    #[must_use]
119    pub fn info(&self) -> &VideoInfo {
120        // SAFETY: `vi` is valid if the node is a video node
121        unsafe { &*(self.api.getVideoInfo)(self.as_ptr()) }
122    }
123
124    /// # Panics
125    ///
126    /// Panics if the the dependency index is larger than [`i32::MAX`].
127    pub fn new<F: Filter>(
128        name: &str,
129        info: &VideoInfo,
130        filter: F,
131        dependencies: &[ffi::VSFilterDependency],
132        core: impl AsRef<Core>,
133    ) -> Option<Self> {
134        let filter = Box::new(filter);
135        let name = CString::new(name).ok()?;
136        let core = core.as_ref();
137        let ptr = unsafe {
138            (core.api().createVideoFilter2)(
139                name.as_ptr(),
140                info,
141                F::filter_get_frame,
142                Some(F::filter_free),
143                F::FILTER_MODE,
144                dependencies.as_ptr(),
145                dependencies.len().try_into().unwrap(),
146                Box::into_raw(filter).cast(),
147                core.as_ptr(),
148            )
149        };
150        ptr.is_null()
151            .then_some(unsafe { Self::from_ptr(ptr, core.api()) })
152    }
153}
154
155impl Clone for VideoNode {
156    fn clone(&self) -> Self {
157        unsafe { Self::from_ptr((self.api.addNodeRef)(self.as_ptr()), self.api) }
158    }
159}
160
161impl Drop for VideoNode {
162    fn drop(&mut self) {
163        unsafe { (self.api.freeNode)(self.as_ptr()) }
164    }
165}
166
167#[derive(PartialEq, Eq, Hash, Debug)]
168pub struct AudioNode {
169    handle: *const ffi::VSNode,
170    api: Api,
171}
172
173impl crate::_private::Sealed for AudioNode {}
174impl Node for AudioNode {
175    type FrameType = AudioFrame;
176
177    #[inline]
178    fn api(&self) -> Api {
179        self.api
180    }
181
182    #[must_use]
183    #[inline]
184    fn as_ptr(&self) -> *mut ffi::VSNode {
185        self.handle.cast_mut()
186    }
187
188    fn get_frame_filter(&self, n: i32, ctx: &mut FrameContext) -> Self::FrameType {
189        unsafe {
190            AudioFrame::from_ptr(
191                (self.api.getFrameFilter)(n, self.as_ptr(), ctx.as_ptr()),
192                self.api,
193            )
194        }
195    }
196}
197
198impl AudioNode {
199    /// # Safety
200    ///
201    /// The caller must ensure that `ptr` is a valid pointer to a [`ffi::VSNode`] that represents an audio node.
202    #[must_use]
203    pub unsafe fn from_ptr(ptr: *mut ffi::VSNode, api: Api) -> Self {
204        Self { handle: ptr, api }
205    }
206
207    #[must_use]
208    pub fn info(&self) -> &AudioInfo {
209        // SAFETY: `ai` is valid if the node is an audio node
210        unsafe { &*(self.api.getAudioInfo)(self.as_ptr()) }
211    }
212
213    /// # Panics
214    ///
215    /// Panics if the the dependency index is larger than [`i32::MAX`].
216    pub fn new<F: Filter>(
217        name: &str,
218        info: &AudioInfo,
219        filter: F,
220        dependencies: &[ffi::VSFilterDependency],
221        core: impl AsRef<Core>,
222    ) -> Option<Self> {
223        let filter = Box::new(filter);
224        let name = CString::new(name).ok()?;
225        let core = core.as_ref();
226        let ptr = unsafe {
227            (core.api().createAudioFilter2)(
228                name.as_ptr(),
229                info,
230                F::filter_get_frame,
231                Some(F::filter_free),
232                F::FILTER_MODE,
233                dependencies.as_ptr(),
234                dependencies.len().try_into().unwrap(),
235                Box::into_raw(filter).cast(),
236                core.as_ptr(),
237            )
238        };
239        ptr.is_null()
240            .then_some(unsafe { Self::from_ptr(ptr, core.api()) })
241    }
242}
243
244impl Clone for AudioNode {
245    fn clone(&self) -> Self {
246        unsafe { Self::from_ptr((self.api.addNodeRef)(self.as_ptr()), self.api) }
247    }
248}
249
250impl Drop for AudioNode {
251    fn drop(&mut self) {
252        unsafe { (self.api.freeNode)(self.as_ptr()) }
253    }
254}
255
256pub type FilterMode = ffi::VSFilterMode;
257pub type CacheMode = ffi::VSCacheMode;