vapoursynth/
api.rs

1//! Most general VapourSynth API functions.
2
3use 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/// A wrapper for the VapourSynth API.
12#[derive(Debug, Clone, Copy)]
13pub struct API {
14    // Note that this is *const, not *mut.
15    handle: NonNull<ffi::VSAPI>,
16}
17
18unsafe impl Send for API {}
19unsafe impl Sync for API {}
20
21/// A cached API pointer. Note that this is `*const ffi::VSAPI`, not `*mut`.
22static RAW_API: AtomicPtr<ffi::VSAPI> = AtomicPtr::new(ptr::null_mut());
23
24/// VapourSynth log message types.
25#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub enum MessageType {
27    Debug,
28    Warning,
29    Critical,
30
31    /// The process will `abort()` after the message handler returns.
32    Fatal,
33}
34
35// Macros for implementing repetitive functions.
36macro_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/// ID of a unique, registered VapourSynth message handler.
67///
68/// Note: In VapourSynth v4, the message handler registration system has been removed.
69/// This type is kept for backward compatibility but is now a dummy type.
70#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
71pub struct MessageHandlerId(());
72
73impl API {
74    /// Retrieves the VapourSynth API.
75    ///
76    /// Returns `None` on error, for example if the requested API version (selected with features,
77    /// see the crate-level docs) is not supported.
78    // If we're linking to VSScript anyway, use the VSScript function.
79    #[inline]
80    pub fn get() -> Option<Self> {
81        // Check if we already have the API.
82        let handle = RAW_API.load(Ordering::Relaxed);
83
84        let handle = if handle.is_null() {
85            // Attempt retrieving it otherwise.
86            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                // If we successfully retrieved the API, cache it.
93                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    /// Returns the cached API.
110    ///
111    /// # Safety
112    /// This function assumes the cache contains a valid API pointer.
113    #[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    /// Stores the API in the cache.
121    ///
122    /// # Safety
123    /// The given pointer should be valid.
124    #[inline]
125    pub(crate) unsafe fn set(handle: *const ffi::VSAPI) {
126        RAW_API.store(handle as *mut _, Ordering::Relaxed);
127    }
128
129    /// Sends a message through VapourSynth’s logging framework.
130    #[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(), // No specific core in v4
138            );
139        }
140        Ok(())
141    }
142
143    /// Frees `node`.
144    ///
145    /// # Safety
146    /// The caller must ensure `node` is valid.
147    #[inline]
148    pub(crate) unsafe fn free_node(self, node: *mut ffi::VSNode) {
149        (self.handle.as_ref().freeNode.unwrap())(node);
150    }
151
152    /// Clones `node`.
153    ///
154    /// # Safety
155    /// The caller must ensure `node` is valid.
156    #[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    /// Returns a pointer to the video info associated with `node`. The pointer is valid as long as
162    /// the node lives.
163    ///
164    /// # Safety
165    /// The caller must ensure `node` is valid.
166    #[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    /// Generates a frame directly.
172    ///
173    /// # Safety
174    /// The caller must ensure `node` is valid.
175    ///
176    /// # Panics
177    /// Panics if `err_msg` is larger than `i32::MAX`.
178    #[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    /// Generates a frame directly.
193    ///
194    /// # Safety
195    /// The caller must ensure `node` and `callback` are valid.
196    #[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    /// Frees `frame`.
208    ///
209    /// # Safety
210    /// The caller must ensure `frame` is valid.
211    #[inline]
212    pub(crate) unsafe fn free_frame(self, frame: &ffi::VSFrame) {
213        (self.handle.as_ref().freeFrame.unwrap())(frame);
214    }
215
216    /// Clones `frame`.
217    ///
218    /// # Safety
219    /// The caller must ensure `frame` is valid.
220    #[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    /// Retrieves the format of a frame.
226    ///
227    /// # Safety
228    /// The caller must ensure `frame` is valid.
229    #[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    /// Returns the width of a plane of a given frame, in pixels.
235    ///
236    /// # Safety
237    /// The caller must ensure `frame` is valid and `plane` is valid for the given `frame`.
238    #[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    /// Returns the height of a plane of a given frame, in pixels.
244    ///
245    /// # Safety
246    /// The caller must ensure `frame` is valid and `plane` is valid for the given `frame`.
247    #[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    /// Returns the distance in bytes between two consecutive lines of a plane of a frame.
253    ///
254    /// # Safety
255    /// The caller must ensure `frame` is valid and `plane` is valid for the given `frame`.
256    #[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    /// Returns a read-only pointer to a plane of a frame.
262    ///
263    /// # Safety
264    /// The caller must ensure `frame` is valid and `plane` is valid for the given `frame`.
265    #[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    /// Returns a read-write pointer to a plane of a frame.
271    ///
272    /// # Safety
273    /// The caller must ensure `frame` is valid and `plane` is valid for the given `frame`.
274    #[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    /// Returns a read-only pointer to a frame's properties.
284    ///
285    /// # Safety
286    /// The caller must ensure `frame` is valid and the correct lifetime is assigned to the
287    /// returned map (it can't outlive `frame`).
288    #[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    /// Returns a read-write pointer to a frame's properties.
294    ///
295    /// # Safety
296    /// The caller must ensure `frame` is valid and the correct lifetime is assigned to the
297    /// returned map (it can't outlive `frame`).
298    #[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    /// Creates a new `VSMap`.
304    #[inline]
305    pub(crate) fn create_map(self) -> *mut ffi::VSMap {
306        unsafe { (self.handle.as_ref().createMap.unwrap())() }
307    }
308
309    /// Clears `map`.
310    ///
311    /// # Safety
312    /// The caller must ensure `map` is valid.
313    #[inline]
314    pub(crate) unsafe fn clear_map(self, map: &mut ffi::VSMap) {
315        (self.handle.as_ref().clearMap.unwrap())(map);
316    }
317
318    /// Frees `map`.
319    ///
320    /// # Safety
321    /// The caller must ensure `map` is valid.
322    #[inline]
323    pub(crate) unsafe fn free_map(self, map: &mut ffi::VSMap) {
324        (self.handle.as_ref().freeMap.unwrap())(map);
325    }
326
327    /// Returns a pointer to the error message contained in the map, or NULL if there is no error
328    /// message.
329    ///
330    /// # Safety
331    /// The caller must ensure `map` is valid.
332    #[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    /// Adds an error message to a map. The map is cleared first. The error message is copied.
338    ///
339    /// # Safety
340    /// The caller must ensure `map` and `errorMessage` are valid.
341    #[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    /// Returns the number of keys contained in a map.
347    ///
348    /// # Safety
349    /// The caller must ensure `map` is valid.
350    #[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    /// Returns a key from a property map.
356    ///
357    /// # Safety
358    /// The caller must ensure `map` is valid and `index` is valid for `map`.
359    #[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    /// Removes the key from a property map.
365    ///
366    /// # Safety
367    /// The caller must ensure `map` and `key` are valid.
368    #[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    /// Returns the number of elements associated with a key in a property map.
374    ///
375    /// # Safety
376    /// The caller must ensure `map` and `key` are valid.
377    #[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    /// Returns the type of the elements associated with the given key in a property map.
383    ///
384    /// # Safety
385    /// The caller must ensure `map` and `key` are valid.
386    #[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    /// Returns the size in bytes of a property of type ptData.
392    ///
393    /// # Safety
394    /// The caller must ensure `map` and `key` are valid.
395    #[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    /// Retrieves an array of integers from a map.
420    ///
421    /// # Safety
422    /// The caller must ensure `map` and `key` are valid.
423    #[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    /// Retrieves an array of floating point numbers from a map.
434    ///
435    /// # Safety
436    /// The caller must ensure `map` and `key` are valid.
437    #[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    /// Adds a data property to the map.
448    ///
449    /// # Safety
450    /// The caller must ensure `map` and `key` are valid.
451    ///
452    /// # Panics
453    /// Panics if `value.len()` can't fit in an `i32`.
454    #[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, // type hint
472            append as i32,
473        )
474    }
475
476    /// Adds an array of integers to the map.
477    ///
478    /// # Safety
479    /// The caller must ensure `map` and `key` are valid.
480    ///
481    /// # Panics
482    /// Panics if `value.len()` can't fit in an `i32`.
483    #[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    /// Adds an array of floating point numbers to the map.
498    ///
499    /// # Safety
500    /// The caller must ensure `map` and `key` are valid.
501    ///
502    /// # Panics
503    /// Panics if `value.len()` can't fit in an `i32`.
504    #[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    /// Frees `function`.
519    ///
520    /// # Safety
521    /// The caller must ensure `function` is valid.
522    #[inline]
523    pub(crate) unsafe fn free_func(self, function: *mut ffi::VSFunction) {
524        (self.handle.as_ref().freeFunction.unwrap())(function);
525    }
526
527    /// Clones `function`.
528    ///
529    /// # Safety
530    /// The caller must ensure `function` is valid.
531    #[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    /// Returns information about the VapourSynth core.
537    ///
538    /// # Safety
539    /// The caller must ensure `core` is valid.
540    #[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    /// Returns a VSFormat structure from a video format identifier.
550    ///
551    /// # Safety
552    /// The caller must ensure `core` is valid.
553    #[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        // V4 API uses output parameters, so we need to allocate and box the format
562        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    /// Registers a custom video format.
577    ///
578    /// # Safety
579    /// The caller must ensure `core` is valid.
580    #[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        // V4 API uses queryVideoFormat which fills in the format struct
593        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    /// Creates a new video filter node.
612    ///
613    /// # Safety
614    /// The caller must ensure all pointers are valid.
615    #[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    /// Adds an error message to a frame context, replacing the existing message, if any.
645    ///
646    /// This is the way to report errors in a filter's "get frame" function. Such errors are not
647    /// necessarily fatal, i.e. the caller can try to request the same frame again.
648    ///
649    /// # Safety
650    /// The caller must ensure all pointers are valid.
651    #[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    /// Requests a frame from a node and returns immediately.
661    ///
662    /// This is only used in filters' "get frame" functions.
663    ///
664    /// # Safety
665    /// The caller must ensure all pointers are valid and this is called from a filter "get frame"
666    /// function.
667    #[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    /// Retrieves a frame that was previously requested with `request_frame_filter()`.
678    ///
679    /// This is only used in filters' "get frame" functions.
680    ///
681    /// # Safety
682    /// The caller must ensure all pointers are valid and this is called from a filter "get frame"
683    /// function.
684    #[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    /// Duplicates the frame (not just the reference). As the frame buffer is shared in a
695    /// copy-on-write fashion, the frame content is not really duplicated until a write operation
696    /// occurs. This is transparent for the user.
697    ///
698    /// # Safety
699    /// The caller must ensure all pointers are valid.
700    #[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    /// Creates a new frame, optionally copying the properties attached to another frame. The new
710    /// frame contains uninitialised memory.
711    ///
712    /// # Safety
713    /// The caller must ensure all pointers are valid and that the uninitialized plane data of the
714    /// returned frame is handled carefully.
715    #[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    /// Queries a video format ID from format properties.
728    ///
729    /// # Safety
730    /// The caller must ensure the core pointer is valid.
731    #[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    /// Gets the printable name of a video format.
752    ///
753    /// # Safety
754    /// The caller must ensure pointers are valid and buffer is large enough.
755    #[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    /// Returns a pointer to the plugin with the given identifier, or a null pointer if not found.
765    ///
766    /// # Safety
767    /// The caller must ensure all pointers are valid.
768    #[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    /// Returns a pointer to the plugin with the given namespace, or a null pointer if not found.
778    ///
779    /// # Safety
780    /// The caller must ensure all pointers are valid.
781    #[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    /// Returns the absolute path to the plugin, including the plugin's file name. This is the real
791    /// location of the plugin, i.e. there are no symbolic links in the path.
792    ///
793    /// Path elements are always delimited with forward slashes.
794    ///
795    /// VapourSynth retains ownership of the returned pointer.
796    ///
797    /// # Safety
798    /// The caller must ensure all pointers are valid.
799    // This was introduced in R25 without bumping the API version (R3) but we must be sure it's
800    // there, so require R3.1.
801    #[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    /// Invokes a filter.
807    ///
808    /// # Safety
809    /// The caller must ensure all pointers are valid.
810    #[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    /// Creates a user-defined function.
821    ///
822    /// # Safety
823    /// The caller must ensure all pointers are valid.
824    #[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    /// Calls a function. If the call fails out will have an error set.
836    ///
837    /// # Safety
838    /// The caller must ensure all pointers are valid.
839    #[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    /// Registers a filter exported by the plugin. A plugin can export any number of filters.
850    ///
851    /// # Safety
852    /// The caller must ensure all pointers are valid.
853    #[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    /// Sets the maximum size of the framebuffer cache. Returns the new maximum size.
874    ///
875    /// # Safety
876    /// The caller must ensure all pointers are valid. On VapourSynth API below 3.6, the caller
877    /// must ensure there are no concurrent accesses to the core info.
878    #[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    /// Sets the number of worker threads for the given core.
884    ///
885    /// If the requested number of threads is zero or lower, the number of hardware threads will be
886    /// detected and used.
887    ///
888    /// Returns the new thread count.
889    ///
890    /// # Safety
891    /// The caller must ensure all pointers are valid. On VapourSynth API below 3.6, the caller
892    /// must ensure there are no concurrent accesses to the core info.
893    #[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    /// Creates and returns a new core.
899    ///
900    /// Note that there's currently no safe way of freeing the returned core, and the lifetime is
901    /// unbounded, because it can live for an arbitrary long time. You may use the (unsafe)
902    /// `vapoursynth_sys::VSAPI::freeCore()` after ensuring that all frame requests have completed
903    /// and all objects belonging to the core have been released.
904    #[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    /// Returns a pointer to a plugin function with the given name, or a null pointer if not found.
914    ///
915    /// # Safety
916    /// The caller must ensure all pointers are valid.
917    #[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    /// Returns the name of a plugin function.
927    ///
928    /// # Safety
929    /// The caller must ensure the function pointer is valid.
930    #[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    /// Returns the argument string of a plugin function.
939    ///
940    /// # Safety
941    /// The caller must ensure the function pointer is valid.
942    #[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    /// Returns the return type string of a plugin function.
951    ///
952    /// # Safety
953    /// The caller must ensure the function pointer is valid.
954    #[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}