1use std::ffi::{CString, NulError};
4use std::os::raw::{c_char, c_int, c_void};
5use std::ptr::{self, NonNull};
6use std::sync::atomic::{AtomicPtr, Ordering};
7use vapoursynth_sys as ffi;
8
9use crate::core::CoreRef;
10
11#[derive(Debug, Clone, Copy)]
13pub struct API {
14 handle: NonNull<ffi::VSAPI>,
16}
17
18unsafe impl Send for API {}
19unsafe impl Sync for API {}
20
21static RAW_API: AtomicPtr<ffi::VSAPI> = AtomicPtr::new(ptr::null_mut());
23
24#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub enum MessageType {
27 Debug,
28 Warning,
29 Critical,
30
31 Fatal,
33}
34
35macro_rules! prop_get_something {
37 ($name:ident, $func:ident, $rv:ty) => {
38 #[inline]
39 pub(crate) unsafe fn $name(
40 self,
41 map: &$crate::ffi::VSMap,
42 key: *const c_char,
43 index: i32,
44 error: &mut i32,
45 ) -> $rv {
46 (self.handle.as_ref().$func.unwrap())(map, key, index, error)
47 }
48 };
49}
50
51macro_rules! prop_set_something {
52 ($name:ident, $func:ident, $type:ty) => {
53 #[inline]
54 pub(crate) unsafe fn $name(
55 self,
56 map: &mut $crate::ffi::VSMap,
57 key: *const c_char,
58 value: $type,
59 append: $crate::ffi::VSMapAppendMode,
60 ) -> i32 {
61 (self.handle.as_ref().$func.unwrap())(map, key, value, append as i32)
62 }
63 };
64}
65
66#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
71pub struct MessageHandlerId(());
72
73impl API {
74 #[inline]
80 pub fn get() -> Option<Self> {
81 let handle = RAW_API.load(Ordering::Relaxed);
83
84 let handle = if handle.is_null() {
85 let vsscript_api = crate::vsscript::VSScriptAPI::get()?;
87 let version = (ffi::VAPOURSYNTH_API_MAJOR << 16 | ffi::VAPOURSYNTH_API_MINOR) as i32;
88 let handle =
89 unsafe { (vsscript_api.handle().getVSAPI.unwrap())(version) } as *mut ffi::VSAPI;
90
91 if !handle.is_null() {
92 RAW_API.store(handle, Ordering::Relaxed);
94 }
95 handle
96 } else {
97 handle
98 };
99
100 if handle.is_null() {
101 None
102 } else {
103 Some(Self {
104 handle: unsafe { NonNull::new_unchecked(handle) },
105 })
106 }
107 }
108
109 #[inline]
114 pub(crate) unsafe fn get_cached() -> Self {
115 Self {
116 handle: NonNull::new_unchecked(RAW_API.load(Ordering::Relaxed)),
117 }
118 }
119
120 #[inline]
125 pub(crate) unsafe fn set(handle: *const ffi::VSAPI) {
126 RAW_API.store(handle as *mut _, Ordering::Relaxed);
127 }
128
129 #[inline]
131 pub fn log(self, message_type: MessageType, message: &str) -> Result<(), NulError> {
132 let message = CString::new(message)?;
133 unsafe {
134 (self.handle.as_ref().logMessage.unwrap())(
135 message_type.ffi_type(),
136 message.as_ptr(),
137 ptr::null_mut(), );
139 }
140 Ok(())
141 }
142
143 #[inline]
148 pub(crate) unsafe fn free_node(self, node: *mut ffi::VSNode) {
149 (self.handle.as_ref().freeNode.unwrap())(node);
150 }
151
152 #[inline]
157 pub(crate) unsafe fn clone_node(self, node: *mut ffi::VSNode) -> *mut ffi::VSNode {
158 (self.handle.as_ref().addNodeRef.unwrap())(node)
159 }
160
161 #[inline]
167 pub(crate) unsafe fn get_video_info(self, node: *mut ffi::VSNode) -> *const ffi::VSVideoInfo {
168 (self.handle.as_ref().getVideoInfo.unwrap())(node)
169 }
170
171 #[inline]
179 pub(crate) unsafe fn get_frame(
180 self,
181 n: i32,
182 node: *mut ffi::VSNode,
183 err_msg: &mut [c_char],
184 ) -> *const ffi::VSFrame {
185 let len = err_msg.len();
186 assert!(len <= i32::MAX as usize);
187 let len = len as i32;
188
189 (self.handle.as_ref().getFrame.unwrap())(n, node, err_msg.as_mut_ptr(), len)
190 }
191
192 #[inline]
197 pub(crate) unsafe fn get_frame_async(
198 self,
199 n: i32,
200 node: *mut ffi::VSNode,
201 callback: ffi::VSFrameDoneCallback,
202 user_data: *mut c_void,
203 ) {
204 (self.handle.as_ref().getFrameAsync.unwrap())(n, node, callback, user_data);
205 }
206
207 #[inline]
212 pub(crate) unsafe fn free_frame(self, frame: &ffi::VSFrame) {
213 (self.handle.as_ref().freeFrame.unwrap())(frame);
214 }
215
216 #[inline]
221 pub(crate) unsafe fn clone_frame(self, frame: &ffi::VSFrame) -> *const ffi::VSFrame {
222 (self.handle.as_ref().addFrameRef.unwrap())(frame)
223 }
224
225 #[inline]
230 pub(crate) unsafe fn get_frame_format(self, frame: &ffi::VSFrame) -> *const ffi::VSVideoFormat {
231 (self.handle.as_ref().getVideoFrameFormat.unwrap())(frame)
232 }
233
234 #[inline]
239 pub(crate) unsafe fn get_frame_width(self, frame: &ffi::VSFrame, plane: i32) -> i32 {
240 (self.handle.as_ref().getFrameWidth.unwrap())(frame, plane)
241 }
242
243 #[inline]
248 pub(crate) unsafe fn get_frame_height(self, frame: &ffi::VSFrame, plane: i32) -> i32 {
249 (self.handle.as_ref().getFrameHeight.unwrap())(frame, plane)
250 }
251
252 #[inline]
257 pub(crate) unsafe fn get_frame_stride(self, frame: &ffi::VSFrame, plane: i32) -> isize {
258 (self.handle.as_ref().getStride.unwrap())(frame, plane)
259 }
260
261 #[inline]
266 pub(crate) unsafe fn get_frame_read_ptr(self, frame: &ffi::VSFrame, plane: i32) -> *const u8 {
267 (self.handle.as_ref().getReadPtr.unwrap())(frame, plane)
268 }
269
270 #[inline]
275 pub(crate) unsafe fn get_frame_write_ptr(
276 self,
277 frame: &mut ffi::VSFrame,
278 plane: i32,
279 ) -> *mut u8 {
280 (self.handle.as_ref().getWritePtr.unwrap())(frame, plane)
281 }
282
283 #[inline]
289 pub(crate) unsafe fn get_frame_props_ro(self, frame: &ffi::VSFrame) -> *const ffi::VSMap {
290 (self.handle.as_ref().getFramePropertiesRO.unwrap())(frame)
291 }
292
293 #[inline]
299 pub(crate) unsafe fn get_frame_props_rw(self, frame: &mut ffi::VSFrame) -> *mut ffi::VSMap {
300 (self.handle.as_ref().getFramePropertiesRW.unwrap())(frame)
301 }
302
303 #[inline]
305 pub(crate) fn create_map(self) -> *mut ffi::VSMap {
306 unsafe { (self.handle.as_ref().createMap.unwrap())() }
307 }
308
309 #[inline]
314 pub(crate) unsafe fn clear_map(self, map: &mut ffi::VSMap) {
315 (self.handle.as_ref().clearMap.unwrap())(map);
316 }
317
318 #[inline]
323 pub(crate) unsafe fn free_map(self, map: &mut ffi::VSMap) {
324 (self.handle.as_ref().freeMap.unwrap())(map);
325 }
326
327 #[inline]
333 pub(crate) unsafe fn get_error(self, map: &ffi::VSMap) -> *const c_char {
334 (self.handle.as_ref().mapGetError.unwrap())(map)
335 }
336
337 #[inline]
342 pub(crate) unsafe fn set_error(self, map: &mut ffi::VSMap, error_message: *const c_char) {
343 (self.handle.as_ref().mapSetError.unwrap())(map, error_message)
344 }
345
346 #[inline]
351 pub(crate) unsafe fn prop_num_keys(self, map: &ffi::VSMap) -> i32 {
352 (self.handle.as_ref().mapNumKeys.unwrap())(map)
353 }
354
355 #[inline]
360 pub(crate) unsafe fn prop_get_key(self, map: &ffi::VSMap, index: i32) -> *const c_char {
361 (self.handle.as_ref().mapGetKey.unwrap())(map, index)
362 }
363
364 #[inline]
369 pub(crate) unsafe fn prop_delete_key(self, map: &mut ffi::VSMap, key: *const c_char) -> i32 {
370 (self.handle.as_ref().mapDeleteKey.unwrap())(map, key)
371 }
372
373 #[inline]
378 pub(crate) unsafe fn prop_num_elements(self, map: &ffi::VSMap, key: *const c_char) -> i32 {
379 (self.handle.as_ref().mapNumElements.unwrap())(map, key)
380 }
381
382 #[inline]
387 pub(crate) unsafe fn prop_get_type(self, map: &ffi::VSMap, key: *const c_char) -> i32 {
388 (self.handle.as_ref().mapGetType.unwrap())(map, key)
389 }
390
391 #[inline]
396 pub(crate) unsafe fn prop_get_data_size(
397 self,
398 map: &ffi::VSMap,
399 key: *const c_char,
400 index: i32,
401 error: &mut i32,
402 ) -> i32 {
403 (self.handle.as_ref().mapGetDataSize.unwrap())(map, key, index, error)
404 }
405
406 prop_get_something!(prop_get_int, mapGetInt, i64);
407 prop_get_something!(prop_get_float, mapGetFloat, f64);
408 prop_get_something!(prop_get_data, mapGetData, *const c_char);
409 prop_get_something!(prop_get_node, mapGetNode, *mut ffi::VSNode);
410 prop_get_something!(prop_get_frame, mapGetFrame, *const ffi::VSFrame);
411 prop_get_something!(prop_get_func, mapGetFunction, *mut ffi::VSFunction);
412
413 prop_set_something!(prop_set_int, mapSetInt, i64);
414 prop_set_something!(prop_set_float, mapSetFloat, f64);
415 prop_set_something!(prop_set_node, mapSetNode, *mut ffi::VSNode);
416 prop_set_something!(prop_set_frame, mapSetFrame, *const ffi::VSFrame);
417 prop_set_something!(prop_set_func, mapSetFunction, *mut ffi::VSFunction);
418
419 #[inline]
424 pub(crate) unsafe fn prop_get_int_array(
425 self,
426 map: &ffi::VSMap,
427 key: *const c_char,
428 error: &mut i32,
429 ) -> *const i64 {
430 (self.handle.as_ref().mapGetIntArray.unwrap())(map, key, error)
431 }
432
433 #[inline]
438 pub(crate) unsafe fn prop_get_float_array(
439 self,
440 map: &ffi::VSMap,
441 key: *const c_char,
442 error: &mut i32,
443 ) -> *const f64 {
444 (self.handle.as_ref().mapGetFloatArray.unwrap())(map, key, error)
445 }
446
447 #[inline]
455 pub(crate) unsafe fn prop_set_data(
456 self,
457 map: &mut ffi::VSMap,
458 key: *const c_char,
459 value: &[u8],
460 append: ffi::VSMapAppendMode,
461 ) -> i32 {
462 let length = value.len();
463 assert!(length <= i32::MAX as usize);
464 let length = length as i32;
465
466 (self.handle.as_ref().mapSetData.unwrap())(
467 map,
468 key,
469 value.as_ptr() as _,
470 length,
471 ffi::VSDataTypeHint_dtUnknown, append as i32,
473 )
474 }
475
476 #[inline]
484 pub(crate) unsafe fn prop_set_int_array(
485 self,
486 map: &mut ffi::VSMap,
487 key: *const c_char,
488 value: &[i64],
489 ) -> i32 {
490 let length = value.len();
491 assert!(length <= i32::MAX as usize);
492 let length = length as i32;
493
494 (self.handle.as_ref().mapSetIntArray.unwrap())(map, key, value.as_ptr(), length)
495 }
496
497 #[inline]
505 pub(crate) unsafe fn prop_set_float_array(
506 self,
507 map: &mut ffi::VSMap,
508 key: *const c_char,
509 value: &[f64],
510 ) -> i32 {
511 let length = value.len();
512 assert!(length <= i32::MAX as usize);
513 let length = length as i32;
514
515 (self.handle.as_ref().mapSetFloatArray.unwrap())(map, key, value.as_ptr(), length)
516 }
517
518 #[inline]
523 pub(crate) unsafe fn free_func(self, function: *mut ffi::VSFunction) {
524 (self.handle.as_ref().freeFunction.unwrap())(function);
525 }
526
527 #[inline]
532 pub(crate) unsafe fn clone_func(self, function: *mut ffi::VSFunction) -> *mut ffi::VSFunction {
533 (self.handle.as_ref().addFunctionRef.unwrap())(function)
534 }
535
536 #[inline]
541 pub(crate) unsafe fn get_core_info(self, core: *mut ffi::VSCore) -> ffi::VSCoreInfo {
542 use std::mem::MaybeUninit;
543
544 let mut core_info = MaybeUninit::uninit();
545 (self.handle.as_ref().getCoreInfo.unwrap())(core, core_info.as_mut_ptr());
546 core_info.assume_init()
547 }
548
549 #[inline]
554 pub(crate) unsafe fn get_format_preset(
555 self,
556 id: i32,
557 core: *mut ffi::VSCore,
558 ) -> *const ffi::VSVideoFormat {
559 use std::mem::MaybeUninit;
560
561 let mut format = Box::new(MaybeUninit::<ffi::VSVideoFormat>::uninit());
563 let result = (self.handle.as_ref().getVideoFormatByID.unwrap())(
564 format.as_mut_ptr(),
565 id as u32,
566 core,
567 );
568
569 if result != 0 {
570 Box::into_raw(format) as *const ffi::VSVideoFormat
571 } else {
572 ptr::null()
573 }
574 }
575
576 #[inline]
581 pub(crate) unsafe fn register_format(
582 self,
583 color_family: ffi::VSColorFamily,
584 sample_type: ffi::VSSampleType,
585 bits_per_sample: i32,
586 sub_sampling_w: i32,
587 sub_sampling_h: i32,
588 core: *mut ffi::VSCore,
589 ) -> *const ffi::VSVideoFormat {
590 use std::mem::MaybeUninit;
591
592 let mut format = Box::new(MaybeUninit::<ffi::VSVideoFormat>::uninit());
594 let result = (self.handle.as_ref().queryVideoFormat.unwrap())(
595 format.as_mut_ptr(),
596 color_family as i32,
597 sample_type as i32,
598 bits_per_sample,
599 sub_sampling_w,
600 sub_sampling_h,
601 core,
602 );
603
604 if result != 0 {
605 Box::into_raw(format) as *const ffi::VSVideoFormat
606 } else {
607 ptr::null()
608 }
609 }
610
611 #[allow(clippy::too_many_arguments)]
616 #[inline]
617 pub(crate) unsafe fn create_video_filter(
618 self,
619 out: *mut ffi::VSMap,
620 name: *const c_char,
621 vi: *const ffi::VSVideoInfo,
622 get_frame: ffi::VSFilterGetFrame,
623 free: ffi::VSFilterFree,
624 filter_mode: i32,
625 dependencies: *const ffi::VSFilterDependency,
626 num_deps: i32,
627 instance_data: *mut c_void,
628 core: *mut ffi::VSCore,
629 ) {
630 (self.handle.as_ref().createVideoFilter.unwrap())(
631 out,
632 name,
633 vi,
634 get_frame,
635 free,
636 filter_mode,
637 dependencies,
638 num_deps,
639 instance_data,
640 core,
641 );
642 }
643
644 #[inline]
652 pub(crate) unsafe fn set_filter_error(
653 self,
654 message: *const c_char,
655 frame_ctx: *mut ffi::VSFrameContext,
656 ) {
657 (self.handle.as_ref().setFilterError.unwrap())(message, frame_ctx);
658 }
659
660 #[inline]
668 pub(crate) unsafe fn request_frame_filter(
669 self,
670 n: i32,
671 node: *mut ffi::VSNode,
672 frame_ctx: *mut ffi::VSFrameContext,
673 ) {
674 (self.handle.as_ref().requestFrameFilter.unwrap())(n, node, frame_ctx);
675 }
676
677 #[inline]
685 pub(crate) unsafe fn get_frame_filter(
686 self,
687 n: i32,
688 node: *mut ffi::VSNode,
689 frame_ctx: *mut ffi::VSFrameContext,
690 ) -> *const ffi::VSFrame {
691 (self.handle.as_ref().getFrameFilter.unwrap())(n, node, frame_ctx)
692 }
693
694 #[inline]
701 pub(crate) unsafe fn copy_frame(
702 self,
703 f: &ffi::VSFrame,
704 core: *mut ffi::VSCore,
705 ) -> *mut ffi::VSFrame {
706 (self.handle.as_ref().copyFrame.unwrap())(f, core)
707 }
708
709 #[inline]
716 pub(crate) unsafe fn new_video_frame(
717 self,
718 format: &ffi::VSVideoFormat,
719 width: i32,
720 height: i32,
721 prop_src: *const ffi::VSFrame,
722 core: *mut ffi::VSCore,
723 ) -> *mut ffi::VSFrame {
724 (self.handle.as_ref().newVideoFrame.unwrap())(format, width, height, prop_src, core)
725 }
726
727 #[inline]
732 pub(crate) unsafe fn query_video_format_id(
733 self,
734 color_family: i32,
735 sample_type: i32,
736 bits_per_sample: i32,
737 sub_sampling_w: i32,
738 sub_sampling_h: i32,
739 core: *mut ffi::VSCore,
740 ) -> u32 {
741 (self.handle.as_ref().queryVideoFormatID.unwrap())(
742 color_family,
743 sample_type,
744 bits_per_sample,
745 sub_sampling_w,
746 sub_sampling_h,
747 core,
748 )
749 }
750
751 #[inline]
756 pub(crate) unsafe fn get_video_format_name(
757 self,
758 format: *const ffi::VSVideoFormat,
759 buffer: *mut c_char,
760 ) -> i32 {
761 (self.handle.as_ref().getVideoFormatName.unwrap())(format, buffer)
762 }
763
764 #[inline]
769 pub(crate) unsafe fn get_plugin_by_id(
770 self,
771 identifier: *const c_char,
772 core: *mut ffi::VSCore,
773 ) -> *mut ffi::VSPlugin {
774 (self.handle.as_ref().getPluginByID.unwrap())(identifier, core)
775 }
776
777 #[inline]
782 pub(crate) unsafe fn get_plugin_by_ns(
783 self,
784 namespace: *const c_char,
785 core: *mut ffi::VSCore,
786 ) -> *mut ffi::VSPlugin {
787 (self.handle.as_ref().getPluginByNamespace.unwrap())(namespace, core)
788 }
789
790 #[inline]
802 pub(crate) unsafe fn get_plugin_path(self, plugin: *mut ffi::VSPlugin) -> *const c_char {
803 (self.handle.as_ref().getPluginPath.unwrap())(plugin)
804 }
805
806 #[inline]
811 pub(crate) unsafe fn invoke(
812 self,
813 plugin: *mut ffi::VSPlugin,
814 name: *const c_char,
815 args: *const ffi::VSMap,
816 ) -> *mut ffi::VSMap {
817 (self.handle.as_ref().invoke.unwrap())(plugin, name, args)
818 }
819
820 #[inline]
825 pub(crate) unsafe fn create_func(
826 self,
827 func: ffi::VSPublicFunction,
828 user_data: *mut c_void,
829 free: ffi::VSFreeFunctionData,
830 core: *mut ffi::VSCore,
831 ) -> *mut ffi::VSFunction {
832 (self.handle.as_ref().createFunction.unwrap())(func, user_data, free, core)
833 }
834
835 #[inline]
840 pub(crate) unsafe fn call_func(
841 self,
842 func: *mut ffi::VSFunction,
843 in_: *const ffi::VSMap,
844 out: *mut ffi::VSMap,
845 ) {
846 (self.handle.as_ref().callFunction.unwrap())(func, in_, out);
847 }
848
849 #[inline]
854 pub(crate) unsafe fn register_function(
855 self,
856 name: *const c_char,
857 args: *const c_char,
858 return_type: *const c_char,
859 args_func: ffi::VSPublicFunction,
860 function_data: *mut c_void,
861 plugin: *mut ffi::VSPlugin,
862 ) {
863 (self.handle.as_ref().registerFunction.unwrap())(
864 name,
865 args,
866 return_type,
867 args_func,
868 function_data,
869 plugin,
870 );
871 }
872
873 #[inline]
879 pub(crate) unsafe fn set_max_cache_size(self, bytes: i64, core: *mut ffi::VSCore) -> i64 {
880 (self.handle.as_ref().setMaxCacheSize.unwrap())(bytes, core)
881 }
882
883 #[inline]
894 pub(crate) unsafe fn set_thread_count(self, threads: c_int, core: *mut ffi::VSCore) -> c_int {
895 (self.handle.as_ref().setThreadCount.unwrap())(threads, core)
896 }
897
898 #[inline]
905 pub fn create_core<'core>(self, threads: i32) -> CoreRef<'core> {
906 unsafe {
907 let handle = (self.handle.as_ref().createCore.unwrap())(0);
908 (self.handle.as_ref().setThreadCount.unwrap())(threads, handle);
909 CoreRef::from_ptr(handle)
910 }
911 }
912
913 #[inline]
918 pub(crate) unsafe fn get_plugin_function_by_name(
919 self,
920 name: *const c_char,
921 plugin: *mut ffi::VSPlugin,
922 ) -> *mut ffi::VSPluginFunction {
923 (self.handle.as_ref().getPluginFunctionByName.unwrap())(name, plugin)
924 }
925
926 #[inline]
931 pub(crate) unsafe fn get_plugin_function_name(
932 self,
933 func: *mut ffi::VSPluginFunction,
934 ) -> *const c_char {
935 (self.handle.as_ref().getPluginFunctionName.unwrap())(func)
936 }
937
938 #[inline]
943 pub(crate) unsafe fn get_plugin_function_arguments(
944 self,
945 func: *mut ffi::VSPluginFunction,
946 ) -> *const c_char {
947 (self.handle.as_ref().getPluginFunctionArguments.unwrap())(func)
948 }
949
950 #[inline]
955 pub(crate) unsafe fn get_plugin_function_return_type(
956 self,
957 func: *mut ffi::VSPluginFunction,
958 ) -> *const c_char {
959 (self.handle.as_ref().getPluginFunctionReturnType.unwrap())(func)
960 }
961}
962
963impl MessageType {
964 #[inline]
965 fn ffi_type(self) -> c_int {
966 let rv = match self {
967 MessageType::Debug => ffi::VSMessageType_mtDebug,
968 MessageType::Warning => ffi::VSMessageType_mtWarning,
969 MessageType::Critical => ffi::VSMessageType_mtCritical,
970 MessageType::Fatal => ffi::VSMessageType_mtFatal,
971 };
972 rv as c_int
973 }
974
975 #[inline]
976 #[expect(dead_code)]
977 fn from_ffi_type(x: c_int) -> Option<Self> {
978 match x {
979 x if x == ffi::VSMessageType_mtDebug as c_int => Some(MessageType::Debug),
980 x if x == ffi::VSMessageType_mtWarning as c_int => Some(MessageType::Warning),
981 x if x == ffi::VSMessageType_mtCritical as c_int => Some(MessageType::Critical),
982 x if x == ffi::VSMessageType_mtFatal as c_int => Some(MessageType::Fatal),
983 _ => None,
984 }
985 }
986}