1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! VapourSynth script-related things.

#[cfg(not(feature = "gte-vsscript-api-32"))]
use std::sync::Mutex;
use std::sync::Once;
use vapoursynth_sys as ffi;

#[cfg(not(feature = "gte-vsscript-api-32"))]
lazy_static! {
    static ref FFI_CALL_MUTEX: Mutex<()> = Mutex::new(());
}

// Some `vsscript_*` function calls have threading issues. Protect them with a mutex.
// https://github.com/vapoursynth/vapoursynth/issues/367
macro_rules! call_vsscript {
    ($call:expr) => {{
        // Fixed in VSScript API 3.2.
        // TODO: also not needed when we're running API 3.2 even without a feature.
        #[cfg(not(feature = "gte-vsscript-api-32"))]
        let _lock = crate::vsscript::FFI_CALL_MUTEX.lock();

        $call
    }};
}

/// Ensures `vsscript_init()` has been called at least once.
// TODO: `vsscript_init()` is already thread-safe with `std::call_once()`, maybe this can be done
// differently to remove the thread protection on Rust's side? An idea is to have a special type
// which calls `vsscript_init()` in `new()` and `vsscript_finalize()` in `drop()` and have the rest
// of the API accessible through that type, however that could become somewhat unergonomic with
// having to store its lifetime everywhere and potentially pass it around the threads.
#[inline]
pub(crate) fn maybe_initialize() {
    static ONCE: Once = Once::new();

    ONCE.call_once(|| unsafe {
        ffi::vsscript_init();

        // Verify the VSScript API version.
        #[cfg(feature = "gte-vsscript-api-31")]
        {
            fn split_version(version: i32) -> (i32, i32) {
                (version >> 16, version & 0xFFFF)
            }

            let vsscript_version = ffi::vsscript_getApiVersion();
            let (major, minor) = split_version(vsscript_version);
            let (my_major, my_minor) = split_version(ffi::VSSCRIPT_API_VERSION);

            if my_major != major {
                panic!(
                    "Invalid VSScript major API version (expected: {}, got: {})",
                    my_major, major
                );
            } else if my_minor > minor {
                panic!(
                    "Invalid VSScript minor API version (expected: >= {}, got: {})",
                    my_minor, minor
                );
            }
        }
    });
}

mod errors;
pub use self::errors::{Error, VSScriptError};

mod environment;
pub use self::environment::{Environment, EvalFlags};