1use std::borrow::Cow;
4use std::ffi::{CStr, CString};
5use std::marker::PhantomData;
6use std::os::raw::{c_char, c_void};
7use std::process;
8use std::ptr::NonNull;
9use std::{mem, panic};
10use vapoursynth_sys as ffi;
11
12use crate::api::API;
13use crate::frame::FrameRef;
14use crate::plugins::FrameContext;
15use crate::video_info::VideoInfo;
16
17mod errors;
18pub use self::errors::GetFrameError;
19
20#[derive(Debug)]
22pub struct Node<'core> {
23 handle: NonNull<ffi::VSNode>,
24 _owner: PhantomData<&'core ()>,
25}
26
27unsafe impl<'core> Send for Node<'core> {}
28unsafe impl<'core> Sync for Node<'core> {}
29
30impl<'core> Drop for Node<'core> {
31 #[inline]
32 fn drop(&mut self) {
33 unsafe {
34 API::get_cached().free_node(self.handle.as_ptr());
35 }
36 }
37}
38
39impl<'core> Clone for Node<'core> {
40 #[inline]
41 fn clone(&self) -> Self {
42 let handle = unsafe { API::get_cached().clone_node(self.handle.as_ptr()) };
43 Self {
44 handle: unsafe { NonNull::new_unchecked(handle) },
45 _owner: PhantomData,
46 }
47 }
48}
49
50impl<'core> Node<'core> {
51 #[inline]
56 pub(crate) unsafe fn from_ptr(handle: *mut ffi::VSNode) -> Self {
57 Self {
58 handle: NonNull::new_unchecked(handle),
59 _owner: PhantomData,
60 }
61 }
62
63 #[inline]
65 pub(crate) fn ptr(&self) -> *mut ffi::VSNode {
66 self.handle.as_ptr()
67 }
68
69 #[inline]
73 pub fn info(&self) -> VideoInfo<'core> {
74 unsafe {
75 let ptr = API::get_cached().get_video_info(self.handle.as_ptr());
76 VideoInfo::from_ptr(ptr)
77 }
78 }
79
80 pub fn get_frame<'error>(&self, n: usize) -> Result<FrameRef<'core>, GetFrameError<'error>> {
87 assert!(n <= i32::MAX as usize);
88
89 let vi = &self.info();
90
91 if n >= vi.num_frames {
92 let err_cstring = CString::new("Requested frame number beyond the last one").unwrap();
93 return Err(GetFrameError::new(Cow::Owned(err_cstring)));
94 }
95
96 const ERROR_BUF_CAPACITY: usize = 32 * 1024;
98
99 let mut err_buf = vec![0; ERROR_BUF_CAPACITY].into_boxed_slice();
100
101 let handle =
102 unsafe { API::get_cached().get_frame(n as i32, self.handle.as_ptr(), &mut err_buf) };
103
104 if handle.is_null() {
105 let error = unsafe { CStr::from_ptr(err_buf.as_ptr()) }.to_owned();
107 Err(GetFrameError::new(Cow::Owned(error)))
108 } else {
109 Ok(unsafe { FrameRef::from_ptr(handle) })
110 }
111 }
112
113 pub fn get_frame_async<F>(&self, n: usize, callback: F)
129 where
130 F: FnOnce(Result<FrameRef<'core>, GetFrameError>, usize, Node<'core>) + Send + 'core,
131 {
132 struct CallbackData<'core> {
133 callback: Box<dyn CallbackFn<'core> + 'core>,
134 }
135
136 trait CallbackFn<'core> {
138 fn call(
139 self: Box<Self>,
140 frame: Result<FrameRef<'core>, GetFrameError>,
141 n: usize,
142 node: Node<'core>,
143 );
144 }
145
146 impl<'core, F> CallbackFn<'core> for F
147 where
148 F: FnOnce(Result<FrameRef<'core>, GetFrameError>, usize, Node<'core>),
149 {
150 #[allow(clippy::boxed_local)]
151 fn call(
152 self: Box<Self>,
153 frame: Result<FrameRef<'core>, GetFrameError>,
154 n: usize,
155 node: Node<'core>,
156 ) {
157 (self)(frame, n, node)
158 }
159 }
160
161 unsafe extern "C" fn c_callback(
162 user_data: *mut c_void,
163 frame: *const ffi::VSFrame,
164 n: i32,
165 node: *mut ffi::VSNode,
166 error_msg: *const c_char,
167 ) {
168 let user_data = Box::from_raw(user_data as *mut CallbackData<'static>);
171
172 let closure = panic::AssertUnwindSafe(move || {
173 let frame = if frame.is_null() {
174 debug_assert!(!error_msg.is_null());
175 let error_msg = Cow::Borrowed(CStr::from_ptr(error_msg));
176 Err(GetFrameError::new(error_msg))
177 } else {
178 debug_assert!(error_msg.is_null());
179 Ok(FrameRef::from_ptr(frame))
180 };
181
182 let node = Node::from_ptr(node);
183
184 debug_assert!(n >= 0);
185 let n = n as usize;
186
187 user_data.callback.call(frame, n, node);
188 });
189
190 if panic::catch_unwind(closure).is_err() {
191 process::abort();
192 }
193 }
194
195 assert!(n <= i32::MAX as usize);
196 let n = n as i32;
197
198 let user_data = Box::new(CallbackData {
199 callback: Box::new(callback),
200 });
201
202 let new_node = self.clone();
203
204 unsafe {
205 API::get_cached().get_frame_async(
206 n,
207 new_node.handle.as_ptr(),
208 Some(c_callback),
209 Box::into_raw(user_data) as *mut c_void,
210 );
211 }
212
213 mem::forget(new_node);
215 }
216
217 pub fn request_frame_filter(&self, context: FrameContext, n: usize) {
233 assert!(n <= i32::MAX as usize);
234 let n = n as i32;
235
236 unsafe {
237 API::get_cached().request_frame_filter(n, self.ptr(), context.ptr());
238 }
239 }
240
241 pub fn get_frame_filter(&self, context: FrameContext, n: usize) -> Option<FrameRef<'core>> {
249 assert!(n <= i32::MAX as usize);
250 let n = n as i32;
251
252 let ptr = unsafe { API::get_cached().get_frame_filter(n, self.ptr(), context.ptr()) };
253 if ptr.is_null() {
254 None
255 } else {
256 Some(unsafe { FrameRef::from_ptr(ptr) })
257 }
258 }
259}