aviutl2\filter/
binding.rs

1use parking_lot::lock_api::RawRwLock;
2use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
3
4use super::{ErasedFilterConfigData, config};
5use crate::common::{AnyResult, AviUtl2Info, Rational32};
6
7/// 入力プラグインの情報を表す構造体。
8#[derive(Debug, Clone)]
9pub struct FilterPluginTable {
10    /// プラグインの名前。
11    pub name: String,
12    /// ラベルの初期値。
13    /// Noneの場合、デフォルトのラベルになります
14    pub label: Option<String>,
15    /// プラグインの情報。
16    /// 「プラグイン情報」ダイアログで表示されます。
17    pub information: String,
18
19    /// 対応している機能のフラグ。
20    pub flags: FilterPluginFlags,
21
22    /// 設定項目。
23    pub config_items: Vec<config::FilterConfigItem>,
24}
25
26define_bitflag! {
27    /// フィルタプラグインのフラグ。
28    ///
29    /// # See Also
30    ///
31    /// - [`crate::bitflag!`]
32    #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
33    #[non_exhaustive]
34    pub struct FilterPluginFlags: i32 {
35        /// 画像フィルタをサポートするかどうか。
36        video: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_VIDEO,
37
38        /// 音声フィルタをサポートするかどうか。
39        audio: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_AUDIO,
40
41        /// カスタムオブジェクトにするかどうか。
42        /// `true` の場合、カスタムオブジェクトとして動作します。
43        /// `false` の場合、フィルタ効果として動作します。
44        as_object: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_INPUT,
45
46        /// フィルタオブジェクトをサポートするかどうか。
47        /// `true` の場合、フィルタオブジェクトとして使えるようになります。
48        as_filter: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_FILTER,
49    }
50}
51
52/// フィルタプラグインのトレイト。
53/// このトレイトを実装し、[`crate::register_filter_plugin!`] マクロを使用してプラグインを登録します。
54pub trait FilterPlugin: Send + Sync + Sized {
55    /// プラグインを初期化する。
56    fn new(info: AviUtl2Info) -> AnyResult<Self>;
57
58    /// プラグインの情報を返す。
59    fn plugin_info(&self) -> FilterPluginTable;
60
61    /// 画像フィルタ処理関数。
62    ///
63    /// # Note
64    ///
65    /// フィルタオブジェクトの場合、画像サイズは変更できません。
66    fn proc_video(
67        &self,
68        _config: &[config::FilterConfigItem],
69        _video: &mut FilterProcVideo,
70    ) -> AnyResult<()> {
71        anyhow::bail!("proc_video is not implemented");
72    }
73
74    /// 音声フィルタ処理関数。
75    fn proc_audio(
76        &self,
77        _config: &[config::FilterConfigItem],
78        _audio: &mut FilterProcAudio,
79    ) -> AnyResult<()> {
80        anyhow::bail!("proc_audio is not implemented");
81    }
82
83    /// シングルトンインスタンスを参照するためのヘルパーメソッド。
84    ///
85    /// # Panics
86    ///
87    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
88    fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R
89    where
90        Self: crate::filter::__bridge::FilterSingleton,
91    {
92        <Self as crate::filter::__bridge::FilterSingleton>::with_instance(f)
93    }
94
95    /// シングルトンインスタンスを可変参照するためのヘルパーメソッド。
96    ///
97    /// # Panics
98    ///
99    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
100    fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R
101    where
102        Self: crate::filter::__bridge::FilterSingleton,
103    {
104        <Self as crate::filter::__bridge::FilterSingleton>::with_instance_mut(f)
105    }
106}
107
108/// シーン情報。
109#[derive(Debug, Clone, Copy)]
110pub struct SceneInfo {
111    /// 解像度(幅)。
112    pub width: u32,
113    /// 解像度(高さ)。
114    pub height: u32,
115    /// フレームレート。
116    pub frame_rate: Rational32,
117    /// サンプリングレート。
118    pub sample_rate: u32,
119}
120
121/// オブジェクト情報。
122#[derive(Debug, Clone, Copy)]
123pub struct ObjectInfo {
124    /// ID。
125    /// アプリ起動ごとの固有IDです。
126    pub id: i64,
127    /// オブジェクトの内の対象エフェクトのID。
128    /// アプリ起動ごとの固有IDです。
129    pub effect_id: i64,
130    /// オブジェクトの現在のフレーム番号。
131    pub frame: u32,
132    /// オブジェクトの総フレーム数。
133    pub frame_total: u32,
134    /// オブジェクトの現在の時間(秒)。
135    pub time: f64,
136    /// オブジェクトの総時間(秒)。
137    pub time_total: f64,
138}
139
140/// 画像フィルタのオブジェクト情報。
141#[derive(Debug, Clone, Copy)]
142pub struct VideoObjectInfo {
143    /// オブジェクトの現在の画像サイズの幅。
144    pub width: u32,
145    /// オブジェクトの現在の画像サイズの高さ。
146    pub height: u32,
147}
148
149/// 音声フィルタのオブジェクト情報。
150#[derive(Debug, Clone, Copy)]
151pub struct AudioObjectInfo {
152    /// オブジェクトの現在の音声サンプル位置。
153    pub sample_index: u64,
154    /// オブジェクトの総サンプル数。
155    pub sample_total: u64,
156    /// オブジェクトの現在の音声サンプル数。
157    pub sample_num: u32,
158    /// オブジェクトの現在の音声チャンネル数。
159    /// 通常2になります。
160    pub channel_num: u32,
161}
162
163/// RGBAのピクセル。
164#[derive(
165    Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
166)]
167pub struct RgbaPixel {
168    /// 赤。
169    pub r: u8,
170    /// 緑。
171    pub g: u8,
172    /// 青。
173    pub b: u8,
174    /// アルファ。
175    pub a: u8,
176}
177
178/// 画像フィルタ処理のための構造体。
179#[derive(Debug)]
180pub struct FilterProcVideo {
181    /// シーン情報。
182    pub scene: SceneInfo,
183    /// オブジェクト情報。
184    pub object: ObjectInfo,
185    /// 画像フィルタ特有のオブジェクト情報。
186    pub video_object: VideoObjectInfo,
187
188    pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_VIDEO,
189}
190unsafe impl Send for FilterProcVideo {}
191unsafe impl Sync for FilterProcVideo {}
192
193impl FilterProcVideo {
194    /// 現在の画像のデータを取得する。
195    /// RGBA32bit で取得されます。
196    ///
197    /// # Panics
198    ///
199    /// `buffer` をバイト列に変換した際の長さが `width * height * 4` と一致しない場合、パニックします。
200    /// 例えば[`u8`] の場合、`buffer` の長さは `width * height * 4` と一致する必要があり、
201    /// [`RgbaPixel`] の場合、`buffer` の長さは `width * height` と一致する必要があります。
202    ///
203    /// # Note
204    ///
205    /// [`FilterPluginFlags::video`] が `true` の場合、この関数は何もせずに 0 を返します。
206    pub fn get_image_data<T>(&mut self, buffer: &mut [T]) -> usize
207    where
208        T: Copy + FromBytes + Immutable,
209    {
210        if self.video_object.width == 0 || self.video_object.height == 0 {
211            log::warn!("width or height is 0, perhaps the filter plugin is a custom object");
212            return 0;
213        }
214        assert_eq!(
215            std::mem::size_of_val(buffer),
216            (self.video_object.width * self.video_object.height * 4) as usize,
217            "buffer length as bytes does not match width * height * 4"
218        );
219        assert!(
220            std::mem::align_of::<T>() >= std::mem::align_of::<aviutl2_sys::filter2::PIXEL_RGBA>(),
221            "buffer alignment is not sufficient"
222        );
223        let width = self.video_object.width as usize;
224        let height = self.video_object.height as usize;
225        let inner = unsafe { &*self.inner };
226        unsafe {
227            (inner.get_image_data)(
228                buffer.as_mut_ptr() as *mut u8 as *mut aviutl2_sys::filter2::PIXEL_RGBA
229            )
230        };
231
232        width * height * 4
233    }
234
235    /// 現在の画像のデータを設定する。
236    ///
237    /// # Panics
238    ///
239    /// `data` をバイト列に変換した際の長さが `width * height * 4` と一致しない場合、パニックします。
240    pub fn set_image_data<T: IntoBytes + Immutable>(
241        &mut self,
242        data: &[T],
243        width: u32,
244        height: u32,
245    ) {
246        let bytes = &data.as_bytes();
247        assert_eq!(
248            bytes.len(),
249            (width * height * 4) as usize,
250            "data length does not match width * height * 4"
251        );
252        let inner = unsafe { &*self.inner };
253        unsafe {
254            (inner.set_image_data)(
255                bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
256                width as i32,
257                height as i32,
258            )
259        };
260    }
261
262    /// 現在のオブジェクトの画像データのポインタをID3D11Texture2Dのポインタとして取得する。
263    ///
264    /// # Warning
265    ///
266    /// [`Self::set_image_data`] によって現在の画像が変更されるかフィルタ処理の終了まで有効です。
267    pub fn get_image_texture2d(&mut self) -> *mut std::ffi::c_void {
268        let inner = unsafe { &*self.inner };
269        unsafe { (inner.get_image_texture2d)() }
270    }
271
272    /// 現在のフレームバッファの画像データのポインタをID3D11Texture2Dのポインタとして取得する。
273    ///
274    /// # Warning
275    ///
276    /// フィルタ処理の終了まで有効です。
277    pub fn get_framebuffer_texture2d(&mut self) -> *mut std::ffi::c_void {
278        let inner = unsafe { &*self.inner };
279        unsafe { (inner.get_framebuffer_texture2d)() }
280    }
281}
282
283/// 音声フィルタ処理のための構造体。
284#[derive(Debug)]
285pub struct FilterProcAudio {
286    /// シーン情報。
287    pub scene: SceneInfo,
288    /// オブジェクト情報。
289    pub object: ObjectInfo,
290    /// 音声フィルタ特有のオブジェクト情報。
291    pub audio_object: AudioObjectInfo,
292
293    pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_AUDIO,
294}
295
296unsafe impl Send for FilterProcAudio {}
297unsafe impl Sync for FilterProcAudio {}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq)]
300pub enum AudioChannel {
301    Left,
302    Right,
303    Any(i32),
304}
305impl From<i32> for AudioChannel {
306    fn from(value: i32) -> Self {
307        match value {
308            0 => AudioChannel::Left,
309            1 => AudioChannel::Right,
310            v => AudioChannel::Any(v),
311        }
312    }
313}
314impl From<AudioChannel> for i32 {
315    fn from(value: AudioChannel) -> Self {
316        match value {
317            AudioChannel::Left => 0,
318            AudioChannel::Right => 1,
319            AudioChannel::Any(v) => v,
320        }
321    }
322}
323
324impl FilterProcAudio {
325    /// 現在の音声のデータを取得する。
326    /// `channel` は 0 が左チャンネル、1 が右チャンネルです。
327    ///
328    /// # Panics
329    ///
330    /// `buffer` の長さが `sample_num` と一致しない場合、パニックします。
331    pub fn get_sample_data(&mut self, channel: AudioChannel, buffer: &mut [f32]) -> usize {
332        let sample_num = self.audio_object.sample_num as usize;
333        assert_eq!(
334            buffer.len(),
335            sample_num,
336            "buffer length does not match sample_num"
337        );
338        let inner = unsafe { &*self.inner };
339        unsafe { (inner.get_sample_data)(buffer.as_mut_ptr(), channel.into()) };
340        sample_num
341    }
342
343    /// 現在の音声のデータを設定する。
344    /// `channel` は 0 が左チャンネル、1 が右チャンネルです。
345    ///
346    /// # Panics
347    ///
348    /// `data` の長さが `sample_num` と一致しない場合、パニックします。
349    pub fn set_sample_data(&mut self, channel: AudioChannel, data: &[f32]) {
350        let sample_num = self.audio_object.sample_num as usize;
351        assert_eq!(
352            data.len(),
353            sample_num,
354            "data length does not match sample_num"
355        );
356        let inner = unsafe { &*self.inner };
357        unsafe { (inner.set_sample_data)(data.as_ptr(), channel.into()) };
358    }
359}
360
361/// フィルタプラグインでのデータを使うためのハンドル。
362/// RwLockのような仕組みで安全にデータを扱うことができます。
363#[derive(Debug)]
364pub struct FilterConfigDataHandle<T: Copy> {
365    pub(crate) inner: *mut T,
366}
367
368unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataHandle<T> {}
369unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataHandle<T> {}
370
371static HANDLES: std::sync::LazyLock<dashmap::DashMap<usize, parking_lot::RawRwLock>> =
372    std::sync::LazyLock::new(dashmap::DashMap::new);
373static OWNED_REFERENCES: std::sync::LazyLock<
374    std::sync::Arc<dashmap::DashMap<usize, std::sync::atomic::AtomicUsize>>,
375> = std::sync::LazyLock::new(|| std::sync::Arc::new(dashmap::DashMap::new()));
376
377impl<T: Copy> Clone for FilterConfigDataHandle<T> {
378    fn clone(&self) -> Self {
379        if !self.inner.is_null() {
380            let addr = self.inner as usize;
381            if OWNED_REFERENCES.contains_key(&addr) {
382                let entry = OWNED_REFERENCES.get(&addr).unwrap();
383                entry.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
384            }
385        }
386        Self { inner: self.inner }
387    }
388}
389impl<T: Copy> Drop for FilterConfigDataHandle<T> {
390    fn drop(&mut self) {
391        if !self.inner.is_null() {
392            let addr = self.inner as usize;
393            if let Some(entry) = OWNED_REFERENCES.get(&addr) {
394                let prev = entry.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
395                if prev == 1 {
396                    unsafe {
397                        let _boxed = Box::from_raw(self.inner);
398                    }
399                    // NOTE: ここでdropしないとdeadlockする
400                    drop(entry);
401                    OWNED_REFERENCES.remove(&addr);
402                }
403            }
404        }
405    }
406}
407
408impl<T: Copy> FilterConfigDataHandle<T> {
409    #[doc(hidden)]
410    pub fn __generics_default_value() -> T
411    where
412        T: Default,
413    {
414        T::default()
415    }
416
417    #[doc(hidden)]
418    pub fn __from_erased(erased: &ErasedFilterConfigData) -> Self {
419        Self {
420            inner: erased.value.map_or(std::ptr::null_mut(), |v| v.as_ptr()) as *mut T,
421        }
422    }
423
424    #[doc(hidden)]
425    pub fn __new_owned(value: T) -> Self {
426        let boxed = Box::new(value);
427        let pointer = Box::into_raw(boxed);
428        let addr = pointer as *mut () as usize;
429        OWNED_REFERENCES.insert(addr, std::sync::atomic::AtomicUsize::new(1));
430        Self { inner: pointer }
431    }
432
433    /// データを読み取るためのロックを取得する。
434    pub fn read<'handle>(&'handle self) -> FilterConfigDataReadGuard<'handle, T> {
435        let addr = self.inner as *mut () as usize;
436        let lock = HANDLES
437            .entry(addr)
438            .or_insert_with(|| parking_lot::RawRwLock::INIT);
439        let lock = lock.value();
440
441        lock.lock_shared();
442        FilterConfigDataReadGuard::new(self.inner)
443    }
444
445    /// データを読み取るためのロックの取得を試みる。
446    /// ロックが取得できなかった場合は `None` を返します。
447    pub fn try_read<'handle>(&'handle self) -> Option<FilterConfigDataReadGuard<'handle, T>> {
448        let addr = self.inner as *mut () as usize;
449        let lock = HANDLES
450            .entry(addr)
451            .or_insert_with(|| parking_lot::RawRwLock::INIT);
452        let lock = lock.value();
453
454        if lock.try_lock_shared() {
455            Some(FilterConfigDataReadGuard::new(self.inner))
456        } else {
457            None
458        }
459    }
460
461    /// データを書き込むためのロックを取得する。
462    pub fn write<'handle>(&'handle self) -> FilterConfigDataWriteGuard<'handle, T> {
463        let addr = self.inner as *mut () as usize;
464        let lock = HANDLES
465            .entry(addr)
466            .or_insert_with(|| parking_lot::RawRwLock::INIT);
467        let lock = lock.value();
468        lock.lock_exclusive();
469        FilterConfigDataWriteGuard::new(self.inner)
470    }
471
472    /// データを書き込むためのロックの取得を試みる。
473    /// ロックが取得できなかった場合は `None` を返します。
474    pub fn try_write<'handle>(&'handle self) -> Option<FilterConfigDataWriteGuard<'handle, T>> {
475        let addr = self.inner as *mut () as usize;
476        let lock = HANDLES
477            .entry(addr)
478            .or_insert_with(|| parking_lot::RawRwLock::INIT);
479        let lock = lock.value();
480        if lock.try_lock_exclusive() {
481            Some(FilterConfigDataWriteGuard::new(self.inner))
482        } else {
483            None
484        }
485    }
486
487    /// 内部のポインタを取得する。
488    ///
489    /// # Warning
490    ///
491    /// このポインタを直接操作するとデータ競合が発生する可能性があります。
492    pub fn as_ptr(&self) -> *mut T {
493        self.inner
494    }
495}
496
497/// フィルタプラグインのデータを読み取るためのガード。
498pub struct FilterConfigDataReadGuard<'handle, T: Copy> {
499    pub(crate) inner: *mut T,
500    _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
501}
502unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataReadGuard<'_, T> {}
503unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataReadGuard<'_, T> {}
504impl<T: Copy> FilterConfigDataReadGuard<'_, T> {
505    fn new<'handle>(inner: *mut T) -> FilterConfigDataReadGuard<'handle, T> {
506        FilterConfigDataReadGuard {
507            inner,
508            _handle: std::marker::PhantomData,
509        }
510    }
511}
512impl<T: Copy> Drop for FilterConfigDataReadGuard<'_, T> {
513    fn drop(&mut self) {
514        let addr = self.inner as *mut () as usize;
515        if let Some(entry) = HANDLES.get(&addr) {
516            let lock = entry.value();
517            unsafe { lock.unlock_shared() };
518        }
519    }
520}
521impl<T: Copy> std::convert::AsRef<T> for FilterConfigDataReadGuard<'_, T> {
522    fn as_ref(&self) -> &T {
523        unsafe { &*self.inner }
524    }
525}
526impl<T: Copy> std::ops::Deref for FilterConfigDataReadGuard<'_, T> {
527    type Target = T;
528
529    fn deref(&self) -> &Self::Target {
530        self.as_ref()
531    }
532}
533
534/// フィルタプラグインのデータを書き込むためのガード。
535pub struct FilterConfigDataWriteGuard<'handle, T: Copy> {
536    pub(crate) inner: *mut T,
537    _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
538}
539
540unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataWriteGuard<'_, T> {}
541unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataWriteGuard<'_, T> {}
542impl<T: Copy> FilterConfigDataWriteGuard<'_, T> {
543    fn new<'handle>(inner: *mut T) -> FilterConfigDataWriteGuard<'handle, T> {
544        FilterConfigDataWriteGuard {
545            inner,
546            _handle: std::marker::PhantomData,
547        }
548    }
549}
550impl<T: Copy> Drop for FilterConfigDataWriteGuard<'_, T> {
551    fn drop(&mut self) {
552        let addr = self.inner as *mut () as usize;
553        if let Some(entry) = HANDLES.get(&addr) {
554            let lock = entry.value();
555            unsafe { lock.unlock_exclusive() };
556        }
557    }
558}
559impl<T: Copy> std::convert::AsMut<T> for FilterConfigDataWriteGuard<'_, T> {
560    fn as_mut(&mut self) -> &mut T {
561        unsafe { &mut *self.inner }
562    }
563}
564impl<T: Copy> std::ops::Deref for FilterConfigDataWriteGuard<'_, T> {
565    type Target = T;
566    fn deref(&self) -> &Self::Target {
567        unsafe { &*self.inner }
568    }
569}
570impl<T: Copy> std::ops::DerefMut for FilterConfigDataWriteGuard<'_, T> {
571    fn deref_mut(&mut self) -> &mut Self::Target {
572        self.as_mut()
573    }
574}
575
576#[cfg(test)]
577mod tests {
578    use super::*;
579
580    #[test]
581    fn filter_config_data_handle_reads_initial_value() {
582        let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
583        let read_guard = handle.read();
584        assert_eq!(*read_guard, 42);
585    }
586
587    #[test]
588    fn filter_config_data_handle_writes_and_reads_updated_value() {
589        let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
590        {
591            let mut write_guard = handle.write();
592            *write_guard = 100;
593        }
594        let read_guard = handle.read();
595        assert_eq!(*read_guard, 100);
596    }
597
598    #[test]
599    fn filter_config_data_handle_try_read_fails_when_locked_for_write() {
600        let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
601        let _write_guard = handle.write();
602        let try_read_guard = handle.try_read();
603        assert!(try_read_guard.is_none());
604    }
605
606    #[test]
607    fn filter_config_data_handle_try_write_fails_when_locked_for_read() {
608        let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
609        let _read_guard = handle.read();
610        let try_write_guard = handle.try_write();
611        assert!(try_write_guard.is_none());
612    }
613
614    #[test]
615    fn filter_config_data_handle_clone_shares_state() {
616        let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
617        let cloned_handle = handle.clone();
618        {
619            let mut write_guard = handle.write();
620            *write_guard = 100;
621        }
622        let read_guard = cloned_handle.read();
623        assert_eq!(*read_guard, 100);
624    }
625
626    #[test]
627    fn filter_config_data_handle_never_drops_data_for_borrowed() {
628        let mut data =
629            crate::filter::ErasedFilterConfigData::with_default_value("test".to_string(), 42);
630        let data_ptr = Box::into_raw(Box::new(42u32));
631        data.value = Some(std::ptr::NonNull::new(data_ptr as _).unwrap());
632        let handle = FilterConfigDataHandle::<u32>::__from_erased(&data);
633        drop(handle);
634
635        assert_eq!(unsafe { *data_ptr }, 42);
636    }
637
638    #[test]
639    fn filter_config_data_handle_reads_value_from_erased_data() {
640        let boxed = Box::new(77u32);
641        let ptr = std::ptr::NonNull::from(boxed.as_ref());
642        let data = crate::filter::FilterConfigData {
643            name: "test".to_string(),
644            value: Some(ptr),
645            default_value: 0,
646        };
647        let erased = data.erase_type();
648        let handle = FilterConfigDataHandle::<u32>::__from_erased(&erased);
649        let read_guard = handle.read();
650
651        assert_eq!(*read_guard, 77);
652        assert_eq!(erased.value.unwrap().as_ptr() as *mut u32, ptr.as_ptr());
653
654        drop(read_guard);
655        drop(handle);
656        drop(boxed);
657    }
658}