vapoursynth4_rs/
core.rs

1/*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6
7use std::{
8    ffi::CStr,
9    marker::PhantomData,
10    mem::MaybeUninit,
11    ops::{Deref, DerefMut},
12    ptr::{NonNull, null_mut},
13};
14
15use bon::bon;
16use core_builder::State;
17
18use crate::{
19    AudioInfo, ColorFamily, SampleType, VideoInfo,
20    api::Api,
21    ffi,
22    frame::{
23        AudioFormat, AudioFrame, FormatName, Frame, VideoFormat, VideoFrame, internal::FrameFromPtr,
24    },
25    function::Function,
26    map::{Map, MapRef},
27    node::{Dependencies, Filter, internal::FilterExtern},
28    plugin::{Plugin, Plugins},
29};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub struct CoreRef<'c> {
33    handle: *const ffi::VSCore,
34    api: Api,
35    marker: PhantomData<&'c ()>,
36}
37
38impl CoreRef<'_> {
39    #[must_use]
40    pub(crate) unsafe fn from_ptr(ptr: *const ffi::VSCore, api: Api) -> Self {
41        Self {
42            handle: ptr.cast_mut(),
43            api,
44            marker: PhantomData,
45        }
46    }
47}
48
49impl AsRef<Core> for CoreRef<'_> {
50    fn as_ref(&self) -> &Core {
51        unsafe { &*std::ptr::from_ref(self).cast() }
52    }
53}
54
55impl Deref for CoreRef<'_> {
56    type Target = Core;
57
58    fn deref(&self) -> &Self::Target {
59        unsafe { &*std::ptr::from_ref(self).cast() }
60    }
61}
62
63impl DerefMut for CoreRef<'_> {
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        unsafe { &mut *std::ptr::from_mut(self).cast() }
66    }
67}
68
69#[derive(Debug, PartialEq, Eq, Hash)]
70pub struct Core {
71    handle: *const ffi::VSCore,
72    api: Api,
73}
74
75impl Core {
76    #[must_use]
77    pub fn as_ptr(&self) -> *mut ffi::VSCore {
78        self.handle.cast_mut()
79    }
80
81    pub fn set_max_cache_size(&mut self, size: i64) {
82        unsafe {
83            (self.api.setMaxCacheSize)(size, self.as_ptr());
84        }
85    }
86
87    pub fn set_thread_count(&mut self, count: i32) {
88        unsafe {
89            (self.api.setThreadCount)(count, self.as_ptr());
90        }
91    }
92
93    #[must_use]
94    pub fn get_info(&self) -> ffi::VSCoreInfo {
95        unsafe {
96            let mut info = MaybeUninit::uninit();
97            (self.api.getCoreInfo)(self.as_ptr(), info.as_mut_ptr());
98            info.assume_init()
99        }
100    }
101
102    /// # Panics
103    ///
104    /// Panic if the `dependencies` has more item than [`i32::MAX`]
105    pub fn create_video_filter<F: Filter>(
106        &mut self,
107        out: MapRef,
108        name: &CStr,
109        info: &VideoInfo,
110        filter: Box<F>,
111        dependencies: &Dependencies,
112    ) {
113        debug_assert!(!out.as_ptr().is_null());
114        unsafe {
115            (self.api.createVideoFilter)(
116                out.as_ptr(),
117                name.as_ptr(),
118                info,
119                F::filter_get_frame,
120                Some(F::filter_free),
121                F::FILTER_MODE,
122                dependencies.as_ptr(),
123                dependencies.len().try_into().unwrap(),
124                Box::into_raw(filter).cast(),
125                self.as_ptr(),
126            );
127        }
128    }
129
130    /// # Panics
131    ///
132    /// Panic if the `dependencies` has more item than [`i32::MAX`]
133    pub fn create_audio_filter<F: Filter>(
134        &mut self,
135        out: &mut MapRef,
136        name: &CStr,
137        info: &AudioInfo,
138        filter: F,
139        dependencies: &Dependencies,
140    ) {
141        let filter = Box::new(filter);
142        unsafe {
143            (self.api.createAudioFilter)(
144                out.as_ptr(),
145                name.as_ptr(),
146                info,
147                F::filter_get_frame,
148                Some(F::filter_free),
149                F::FILTER_MODE,
150                dependencies.as_ptr(),
151                dependencies.len().try_into().unwrap(),
152                Box::into_raw(filter).cast(),
153                self.as_ptr(),
154            );
155        }
156    }
157
158    #[must_use]
159    pub fn new_video_frame(
160        &self,
161        format: &VideoFormat,
162        width: i32,
163        height: i32,
164        prop_src: Option<&VideoFrame>,
165    ) -> VideoFrame {
166        unsafe {
167            let ptr = (self.api.newVideoFrame)(
168                format,
169                width,
170                height,
171                prop_src.map_or(null_mut(), |f| f.as_ptr().cast()),
172                self.as_ptr(),
173            );
174            VideoFrame::from_ptr(ptr, self.api)
175        }
176    }
177
178    #[must_use]
179    pub fn new_video_frame2(
180        &self,
181        format: &VideoFormat,
182        width: i32,
183        height: i32,
184        plane_src: &[*const ffi::VSFrame],
185        planes: &[i32],
186        prop_src: Option<&VideoFrame>,
187    ) -> VideoFrame {
188        unsafe {
189            let ptr = (self.api.newVideoFrame2)(
190                format,
191                width,
192                height,
193                plane_src.as_ptr(),
194                planes.as_ptr(),
195                prop_src.map_or(null_mut(), |f| f.as_ptr().cast()),
196                self.as_ptr(),
197            );
198            VideoFrame::from_ptr(ptr, self.api)
199        }
200    }
201
202    #[must_use]
203    pub fn new_audio_frame(
204        &self,
205        format: &AudioFormat,
206        num_samples: i32,
207        prop_src: Option<&AudioFrame>,
208    ) -> AudioFrame {
209        unsafe {
210            let ptr = (self.api.newAudioFrame)(
211                format,
212                num_samples,
213                prop_src.map_or(null_mut(), |f| f.as_ptr().cast()),
214                self.as_ptr(),
215            );
216            AudioFrame::from_ptr(ptr, self.api)
217        }
218    }
219
220    #[must_use]
221    pub fn new_audio_frame2(
222        &self,
223        format: &AudioFormat,
224        num_samples: i32,
225        channel_src: &[*const ffi::VSFrame],
226        channels: &[i32],
227        prop_src: Option<&AudioFrame>,
228    ) -> AudioFrame {
229        unsafe {
230            let ptr = (self.api.newAudioFrame2)(
231                format,
232                num_samples,
233                channel_src.as_ptr(),
234                channels.as_ptr(),
235                prop_src.map_or(null_mut(), |f| f.as_ptr().cast()),
236                self.as_ptr(),
237            );
238            AudioFrame::from_ptr(ptr, self.api)
239        }
240    }
241
242    #[must_use]
243    pub fn copy_frame<F: Frame>(&self, frame: &F) -> F {
244        unsafe {
245            F::from_ptr(
246                (self.api.copyFrame)(frame.as_ptr(), self.as_ptr()),
247                self.api,
248            )
249        }
250    }
251
252    #[must_use]
253    pub fn query_video_format(
254        &self,
255        color_family: ColorFamily,
256        sample_type: SampleType,
257        bits_per_sample: i32,
258        subsampling_w: i32,
259        subsampling_h: i32,
260    ) -> VideoFormat {
261        unsafe {
262            let mut format = MaybeUninit::uninit();
263            (self.api.queryVideoFormat)(
264                format.as_mut_ptr(),
265                color_family,
266                sample_type,
267                bits_per_sample,
268                subsampling_w,
269                subsampling_h,
270                self.as_ptr(),
271            );
272            format.assume_init()
273        }
274    }
275
276    #[must_use]
277    pub fn get_video_format_name(&self, format: &VideoFormat) -> Option<String> {
278        let mut buffer = FormatName::new();
279        if 0 == unsafe { (self.api.getVideoFormatName)(format, buffer.as_mut_ptr().cast()) } {
280            None
281        } else {
282            Some(buffer.to_string())
283        }
284    }
285
286    #[must_use]
287    pub fn query_audio_format(
288        &self,
289        sample_type: SampleType,
290        bits_per_sample: i32,
291        channel_layout: u64,
292    ) -> AudioFormat {
293        unsafe {
294            let mut format = MaybeUninit::uninit();
295            (self.api.queryAudioFormat)(
296                format.as_mut_ptr(),
297                sample_type,
298                bits_per_sample,
299                channel_layout,
300                self.as_ptr(),
301            );
302            format.assume_init()
303        }
304    }
305
306    #[must_use]
307    pub fn get_audio_format_name(&self, format: &AudioFormat) -> Option<String> {
308        let mut buffer = FormatName::new();
309        if 0 == unsafe { (self.api.getAudioFormatName)(format, buffer.as_mut_ptr().cast()) } {
310            None
311        } else {
312            Some(buffer.to_string())
313        }
314    }
315
316    #[must_use]
317    pub fn query_video_format_id(
318        &self,
319        color_family: ColorFamily,
320        sample_type: SampleType,
321        bits_per_sample: i32,
322        subsampling_w: i32,
323        subsampling_h: i32,
324    ) -> u32 {
325        unsafe {
326            (self.api.queryVideoFormatID)(
327                color_family,
328                sample_type,
329                bits_per_sample,
330                subsampling_w,
331                subsampling_h,
332                self.as_ptr(),
333            )
334        }
335    }
336
337    #[must_use]
338    pub fn get_video_format_by_id(&self, id: u32) -> VideoFormat {
339        unsafe {
340            let mut format = MaybeUninit::uninit();
341            (self.api.getVideoFormatByID)(format.as_mut_ptr(), id, self.as_ptr());
342            format.assume_init()
343        }
344    }
345
346    pub fn create_function<T>(
347        &mut self,
348        func: ffi::VSPublicFunction,
349        data: Box<T>,
350        free: ffi::VSFreeFunctionData,
351    ) -> Function {
352        unsafe {
353            Function::from_ptr(
354                (self.api.createFunction)(func, Box::into_raw(data).cast(), free, self.as_ptr()),
355                self.api,
356            )
357        }
358    }
359
360    #[must_use]
361    pub fn get_plugin_by_id(&self, id: &CStr) -> Option<Plugin> {
362        unsafe {
363            NonNull::new((self.api.getPluginByID)(id.as_ptr(), self.as_ptr()))
364                .map(|p| Plugin::new(p, self.api))
365        }
366    }
367
368    #[must_use]
369    pub fn get_plugin_by_namespace(&self, ns: &CStr) -> Option<Plugin> {
370        unsafe {
371            NonNull::new((self.api.getPluginByNamespace)(ns.as_ptr(), self.as_ptr()))
372                .map(|p| Plugin::new(p, self.api))
373        }
374    }
375
376    #[must_use]
377    pub fn plugins(&self) -> Plugins<'_> {
378        Plugins::new(self)
379    }
380
381    pub fn log(&mut self, level: ffi::VSMessageType, msg: &CStr) {
382        unsafe {
383            (self.api.logMessage)(level, msg.as_ptr(), self.as_ptr());
384        }
385    }
386}
387
388impl Drop for Core {
389    fn drop(&mut self) {
390        unsafe {
391            (self.api.freeCore)(self.handle.cast_mut());
392        }
393    }
394}
395
396// MARK: Helper
397
398impl Core {
399    unsafe fn new_with(flags: i32, api: Api) -> Self {
400        let core = unsafe { (api.createCore)(flags) };
401        Self { handle: core, api }
402    }
403
404    #[must_use]
405    pub fn api(&self) -> Api {
406        self.api
407    }
408
409    #[must_use]
410    pub fn create_map(&self) -> Map {
411        unsafe {
412            let ptr = (self.api.createMap)();
413            Map::from_ptr(ptr, self.api)
414        }
415    }
416}
417
418// MARK: Builder
419
420#[bon]
421impl Core {
422    #[builder]
423    pub fn new(
424        #[builder(field)] flags: i32,
425        max_cache_size: Option<i64>,
426        thread_count: Option<i32>,
427        #[cfg(feature = "link-library")]
428        #[builder(default)]
429        api: Api,
430        #[cfg(not(feature = "link-library"))] api: Api,
431    ) -> Self {
432        let mut core = unsafe { Core::new_with(flags, api) };
433        if let Some(size) = max_cache_size {
434            core.set_max_cache_size(size);
435        }
436        if let Some(count) = thread_count {
437            core.set_thread_count(count);
438        }
439
440        core
441    }
442}
443
444impl<S: State> CoreBuilder<S> {
445    pub fn enable_graph_inspection(mut self) -> Self {
446        self.flags |= ffi::VSCoreCreationFlags::EnableGraphInspection as i32;
447        self
448    }
449
450    pub fn disable_auto_loading(mut self) -> Self {
451        self.flags |= ffi::VSCoreCreationFlags::DisableAutoLoading as i32;
452        self
453    }
454
455    pub fn disable_library_unloading(mut self) -> Self {
456        self.flags |= ffi::VSCoreCreationFlags::DisableLibraryUnloading as i32;
457        self
458    }
459}
460
461#[cfg(test)]
462#[cfg(feature = "link-library")]
463mod tests {
464    use super::*;
465
466    #[test]
467    fn builder() {
468        let api = Api::default();
469        let core = Core::builder()
470            .api(api)
471            .enable_graph_inspection()
472            .disable_auto_loading()
473            .disable_library_unloading()
474            .max_cache_size(1024)
475            .thread_count(4)
476            .build();
477        assert_eq!(core.get_info().max_framebuffer_size, 1024);
478        assert_eq!(core.get_info().num_threads, 4);
479    }
480}