aviutl2\filter/
binding.rs

1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
2
3use super::config;
4use crate::common::{AnyResult, AviUtl2Info, Rational32};
5
6/// 入力プラグインの情報を表す構造体。
7#[derive(Debug, Clone)]
8pub struct FilterPluginTable {
9    /// プラグインの名前。
10    pub name: String,
11    /// ラベルの初期値。
12    /// Noneの場合、デフォルトのラベルになります
13    pub label: Option<String>,
14    /// プラグインの情報。
15    /// 「プラグイン情報」ダイアログで表示されます。
16    pub information: String,
17
18    /// 入力の種類。
19    pub filter_type: FilterType,
20
21    /// カスタムオブジェクトにするかどうか。
22    /// `true` の場合、カスタムオブジェクトとして動作します。
23    /// `false` の場合、フィルタ効果として動作します。
24    pub as_object: bool,
25
26    /// 設定項目。
27    pub config_items: Vec<config::FilterConfigItem>,
28}
29/// 動画・画像と音声の入力情報をまとめた構造体。
30/// 入力の種類を表す列挙型。
31#[derive(Debug, Clone)]
32pub enum FilterType {
33    /// 動画のみ。
34    Video,
35    /// 音声のみ。
36    Audio,
37    /// 動画と音声の両方。
38    Both,
39}
40
41impl FilterType {
42    pub(crate) fn to_bits(&self) -> i32 {
43        match self {
44            FilterType::Video => aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_VIDEO,
45            FilterType::Audio => aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_AUDIO,
46            FilterType::Both => {
47                aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_VIDEO
48                    | aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_AUDIO
49            }
50        }
51    }
52}
53
54/// フィルタプラグインのトレイト。
55/// このトレイトを実装し、[`crate::register_filter_plugin!`] マクロを使用してプラグインを登録します。
56pub trait FilterPlugin: Send + Sync + Sized {
57    /// プラグインを初期化する。
58    fn new(info: AviUtl2Info) -> AnyResult<Self>;
59
60    /// プラグインの情報を返す。
61    fn plugin_info(&self) -> FilterPluginTable;
62
63    /// 画像フィルタ処理関数。
64    fn proc_video(
65        &self,
66        _config: &[config::FilterConfigItem],
67        _video: &mut FilterProcVideo,
68    ) -> AnyResult<()> {
69        anyhow::bail!("proc_video is not implemented");
70    }
71
72    /// 音声フィルタ処理関数。
73    fn proc_audio(
74        &self,
75        _config: &[config::FilterConfigItem],
76        _audio: &mut FilterProcAudio,
77    ) -> AnyResult<()> {
78        anyhow::bail!("proc_audio is not implemented");
79    }
80
81    /// シングルトンインスタンスを参照するためのヘルパーメソッド。
82    ///
83    /// # Panics
84    ///
85    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
86    fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R
87    where
88        Self: crate::filter::__bridge::FilterSingleton,
89    {
90        <Self as crate::filter::__bridge::FilterSingleton>::with_instance(f)
91    }
92
93    /// シングルトンインスタンスを可変参照するためのヘルパーメソッド。
94    ///
95    /// # Panics
96    ///
97    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
98    fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R
99    where
100        Self: crate::filter::__bridge::FilterSingleton,
101    {
102        <Self as crate::filter::__bridge::FilterSingleton>::with_instance_mut(f)
103    }
104}
105
106/// シーン情報。
107#[derive(Debug, Clone, Copy)]
108pub struct SceneInfo {
109    /// 解像度(幅)。
110    pub width: u32,
111    /// 解像度(高さ)。
112    pub height: u32,
113    /// フレームレート。
114    pub frame_rate: Rational32,
115    /// サンプリングレート。
116    pub sample_rate: u32,
117}
118
119/// オブジェクト情報。
120#[derive(Debug, Clone, Copy)]
121pub struct ObjectInfo {
122    /// ID。
123    /// アプリ起動ごとの固有IDです。
124    pub id: i64,
125    /// オブジェクトの内の対象エフェクトのID。
126    /// アプリ起動ごとの固有IDです。
127    pub effect_id: i64,
128    /// オブジェクトの現在のフレーム番号。
129    pub frame: u32,
130    /// オブジェクトの総フレーム数。
131    pub frame_total: u32,
132    /// オブジェクトの現在の時間(秒)。
133    pub time: f64,
134    /// オブジェクトの総時間(秒)。
135    pub time_total: f64,
136}
137
138/// 画像フィルタのオブジェクト情報。
139#[derive(Debug, Clone, Copy)]
140pub struct VideoObjectInfo {
141    /// オブジェクトの現在の画像サイズの幅。
142    pub width: u32,
143    /// オブジェクトの現在の画像サイズの高さ。
144    pub height: u32,
145}
146
147/// 音声フィルタのオブジェクト情報。
148#[derive(Debug, Clone, Copy)]
149pub struct AudioObjectInfo {
150    /// オブジェクトの現在の音声サンプル位置。
151    pub sample_index: u64,
152    /// オブジェクトの総サンプル数。
153    pub sample_total: u64,
154    /// オブジェクトの現在の音声サンプル数。
155    pub sample_num: u32,
156    /// オブジェクトの現在の音声チャンネル数。
157    /// 通常2になります。
158    pub channel_num: u32,
159}
160
161/// RGBAのピクセル。
162#[derive(
163    Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
164)]
165pub struct RgbaPixel {
166    /// 赤。
167    pub r: u8,
168    /// 緑。
169    pub g: u8,
170    /// 青。
171    pub b: u8,
172    /// アルファ。
173    pub a: u8,
174}
175
176/// 画像フィルタ処理のための構造体。
177#[derive(Debug)]
178pub struct FilterProcVideo {
179    /// シーン情報。
180    pub scene: SceneInfo,
181    /// オブジェクト情報。
182    pub object: ObjectInfo,
183    /// 画像フィルタ特有のオブジェクト情報。
184    pub video_object: VideoObjectInfo,
185
186    pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_VIDEO,
187}
188unsafe impl Send for FilterProcVideo {}
189unsafe impl Sync for FilterProcVideo {}
190
191impl FilterProcVideo {
192    /// 現在の画像のデータを取得する。
193    /// RGBA32bit で取得されます。
194    ///
195    /// # Panics
196    ///
197    /// `buffer` をバイト列に変換した際の長さが `width * height * 4` と一致しない場合、パニックします。
198    /// 例えば[`u8`] の場合、`buffer` の長さは `width * height * 4` と一致する必要があり、
199    /// [`RgbaPixel`] の場合、`buffer` の長さは `width * height` と一致する必要があります。
200    ///
201    /// # Note
202    ///
203    /// [`FilterPluginTable::as_object`] が `true` の場合、この関数は何もせずに 0 を返します。
204    pub fn get_image_data<T>(&mut self, buffer: &mut [T]) -> usize
205    where
206        T: Copy + FromBytes + Immutable,
207    {
208        if self.video_object.width == 0 || self.video_object.height == 0 {
209            log::warn!("width or height is 0, perhaps the filter plugin is a custom object");
210            return 0;
211        }
212        assert_eq!(
213            std::mem::size_of_val(buffer),
214            (self.video_object.width * self.video_object.height * 4) as usize,
215            "buffer length as bytes does not match width * height * 4"
216        );
217        assert!(
218            std::mem::align_of::<T>() >= std::mem::align_of::<aviutl2_sys::filter2::PIXEL_RGBA>(),
219            "buffer alignment is not sufficient"
220        );
221        let width = self.video_object.width as usize;
222        let height = self.video_object.height as usize;
223        let inner = unsafe { &*self.inner };
224        unsafe {
225            (inner.get_image_data)(
226                buffer.as_mut_ptr() as *mut u8 as *mut aviutl2_sys::filter2::PIXEL_RGBA
227            )
228        };
229
230        width * height * 4
231    }
232
233    /// 現在の画像のデータを設定する。
234    ///
235    /// # Panics
236    ///
237    /// `data` をバイト列に変換した際の長さが `width * height * 4` と一致しない場合、パニックします。
238    pub fn set_image_data<T: IntoBytes + Immutable>(
239        &mut self,
240        data: &[T],
241        width: u32,
242        height: u32,
243    ) {
244        let bytes = &data.as_bytes();
245        assert_eq!(
246            bytes.len(),
247            (width * height * 4) as usize,
248            "data length does not match width * height * 4"
249        );
250        let inner = unsafe { &*self.inner };
251        unsafe {
252            (inner.set_image_data)(
253                bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
254                width as i32,
255                height as i32,
256            )
257        };
258    }
259}
260
261/// 音声フィルタ処理のための構造体。
262#[derive(Debug)]
263pub struct FilterProcAudio {
264    /// シーン情報。
265    pub scene: SceneInfo,
266    /// オブジェクト情報。
267    pub object: ObjectInfo,
268    /// 音声フィルタ特有のオブジェクト情報。
269    pub audio_object: AudioObjectInfo,
270
271    pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_AUDIO,
272}
273
274unsafe impl Send for FilterProcAudio {}
275unsafe impl Sync for FilterProcAudio {}
276
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278pub enum AudioChannel {
279    Left,
280    Right,
281    Any(i32),
282}
283impl From<i32> for AudioChannel {
284    fn from(value: i32) -> Self {
285        match value {
286            0 => AudioChannel::Left,
287            1 => AudioChannel::Right,
288            v => AudioChannel::Any(v),
289        }
290    }
291}
292impl From<AudioChannel> for i32 {
293    fn from(value: AudioChannel) -> Self {
294        match value {
295            AudioChannel::Left => 0,
296            AudioChannel::Right => 1,
297            AudioChannel::Any(v) => v,
298        }
299    }
300}
301
302impl FilterProcAudio {
303    /// 現在の音声のデータを取得する。
304    /// `channel` は 0 が左チャンネル、1 が右チャンネルです。
305    ///
306    /// # Panics
307    ///
308    /// `buffer` の長さが `sample_num` と一致しない場合、パニックします。
309    pub fn get_sample_data(&mut self, channel: AudioChannel, buffer: &mut [f32]) -> usize {
310        let sample_num = self.audio_object.sample_num as usize;
311        assert_eq!(
312            buffer.len(),
313            sample_num,
314            "buffer length does not match sample_num"
315        );
316        let inner = unsafe { &*self.inner };
317        unsafe { (inner.get_sample_data)(buffer.as_mut_ptr(), channel.into()) };
318        sample_num
319    }
320
321    /// 現在の音声のデータを設定する。
322    /// `channel` は 0 が左チャンネル、1 が右チャンネルです。
323    ///
324    /// # Panics
325    ///
326    /// `data` の長さが `sample_num` と一致しない場合、パニックします。
327    pub fn set_sample_data(&mut self, channel: AudioChannel, data: &[f32]) {
328        let sample_num = self.audio_object.sample_num as usize;
329        assert_eq!(
330            data.len(),
331            sample_num,
332            "data length does not match sample_num"
333        );
334        let inner = unsafe { &*self.inner };
335        unsafe { (inner.set_sample_data)(data.as_ptr(), channel.into()) };
336    }
337}