use std::borrow::Cow;
use std::ffi::CStr;
use std::{mem, panic};
use std::os::raw::{c_char, c_void};
use std::process;
use vapoursynth_sys as ffi;
use api::API;
use frame::Frame;
use video_info::VideoInfo;
mod errors;
pub use self::errors::GetFrameError;
bitflags! {
pub struct Flags: i32 {
const NO_CACHE = ffi::VSNodeFlags_nfNoCache.0;
const IS_CACHE = ffi::VSNodeFlags_nfIsCache.0;
#[cfg(feature = "gte-vapoursynth-api-33")]
const MAKE_LINEAR = ffi::VSNodeFlags_nfMakeLinear.0;
}
}
impl From<ffi::VSNodeFlags> for Flags {
#[inline]
fn from(flags: ffi::VSNodeFlags) -> Self {
Self::from_bits_truncate(flags.0)
}
}
#[derive(Debug)]
pub struct Node {
handle: *mut ffi::VSNodeRef,
}
unsafe impl Send for Node {}
unsafe impl Sync for Node {}
impl Drop for Node {
#[inline]
fn drop(&mut self) {
unsafe {
API::get_cached().free_node(self.handle);
}
}
}
impl Clone for Node {
#[inline]
fn clone(&self) -> Self {
let handle = unsafe { API::get_cached().clone_node(self.handle) };
Self { handle }
}
}
impl Node {
#[inline]
pub(crate) unsafe fn from_ptr(handle: *mut ffi::VSNodeRef) -> Self {
Self { handle }
}
#[inline]
pub(crate) fn ptr(&self) -> *mut ffi::VSNodeRef {
self.handle
}
#[inline]
pub fn info(&self) -> VideoInfo {
unsafe {
let ptr = API::get_cached().get_video_info(self.handle);
VideoInfo::from_ptr(ptr)
}
}
pub fn get_frame(&self, n: usize) -> Result<Frame, GetFrameError<'static>> {
assert!(n <= i32::max_value() as usize);
let n = n as i32;
const ERROR_BUF_CAPACITY: usize = 32 * 1024;
let mut err_buf = Vec::with_capacity(ERROR_BUF_CAPACITY);
err_buf.resize(ERROR_BUF_CAPACITY, 0);
let mut err_buf = err_buf.into_boxed_slice();
let handle = unsafe { API::get_cached().get_frame(n, self.handle, &mut *err_buf) };
if handle.is_null() {
let error = unsafe { CStr::from_ptr(err_buf.as_ptr()) }.to_owned();
Err(GetFrameError::new(Cow::Owned(error)))
} else {
Ok(unsafe { Frame::from_ptr(handle) })
}
}
pub fn get_frame_async<F>(&self, n: usize, callback: F)
where
F: FnOnce(Result<Frame, GetFrameError>, usize, Node) + Send + 'static,
{
struct CallbackData {
callback: Box<CallbackFn>,
}
trait CallbackFn {
fn call(self: Box<Self>, frame: Result<Frame, GetFrameError>, n: usize, node: Node);
}
impl<F> CallbackFn for F
where
F: FnOnce(Result<Frame, GetFrameError>, usize, Node),
{
#[cfg_attr(feature = "cargo-clippy", allow(boxed_local))]
fn call(self: Box<Self>, frame: Result<Frame, GetFrameError>, n: usize, node: Node) {
(self)(frame, n, node)
}
}
unsafe extern "system" fn c_callback(
user_data: *mut c_void,
frame: *const ffi::VSFrameRef,
n: i32,
node: *mut ffi::VSNodeRef,
error_msg: *const c_char,
) {
let user_data = Box::from_raw(user_data as *mut CallbackData);
let closure = panic::AssertUnwindSafe(move || {
let frame = if frame.is_null() {
debug_assert!(!error_msg.is_null());
let error_msg = Cow::Borrowed(CStr::from_ptr(error_msg));
Err(GetFrameError::new(error_msg))
} else {
debug_assert!(error_msg.is_null());
Ok(Frame::from_ptr(frame))
};
let node = Node::from_ptr(node);
debug_assert!(n >= 0);
let n = n as usize;
user_data.callback.call(frame, n, node);
});
if panic::catch_unwind(closure).is_err() {
eprintln!("panic in the get_frame_async() callback, aborting");
process::abort();
}
}
assert!(n <= i32::max_value() as usize);
let n = n as i32;
let user_data = Box::new(CallbackData {
callback: Box::new(callback),
});
let new_node = self.clone();
unsafe {
API::get_cached().get_frame_async(
n,
new_node.handle,
Some(c_callback),
Box::into_raw(user_data) as *mut c_void,
);
}
mem::forget(new_node);
}
}