1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
2
3use super::config;
4use crate::common::{AnyResult, AviUtl2Info, Rational32};
5
6#[derive(Debug, Clone)]
8pub struct FilterPluginTable {
9 pub name: String,
11 pub label: Option<String>,
14 pub information: String,
17
18 pub filter_type: FilterType,
20
21 pub as_object: bool,
25
26 pub config_items: Vec<config::FilterConfigItem>,
28}
29#[derive(Debug, Clone)]
32pub enum FilterType {
33 Video,
35 Audio,
37 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
54pub trait FilterPlugin: Send + Sync + Sized {
57 fn new(info: AviUtl2Info) -> AnyResult<Self>;
59
60 fn plugin_info(&self) -> FilterPluginTable;
62
63 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 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 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 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#[derive(Debug, Clone, Copy)]
108pub struct SceneInfo {
109 pub width: u32,
111 pub height: u32,
113 pub frame_rate: Rational32,
115 pub sample_rate: u32,
117}
118
119#[derive(Debug, Clone, Copy)]
121pub struct ObjectInfo {
122 pub id: i64,
125 pub effect_id: i64,
128 pub frame: u32,
130 pub frame_total: u32,
132 pub time: f64,
134 pub time_total: f64,
136}
137
138#[derive(Debug, Clone, Copy)]
140pub struct VideoObjectInfo {
141 pub width: u32,
143 pub height: u32,
145}
146
147#[derive(Debug, Clone, Copy)]
149pub struct AudioObjectInfo {
150 pub sample_index: u64,
152 pub sample_total: u64,
154 pub sample_num: u32,
156 pub channel_num: u32,
159}
160
161#[derive(
163 Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
164)]
165pub struct RgbaPixel {
166 pub r: u8,
168 pub g: u8,
170 pub b: u8,
172 pub a: u8,
174}
175
176#[derive(Debug)]
178pub struct FilterProcVideo {
179 pub scene: SceneInfo,
181 pub object: ObjectInfo,
183 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 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 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#[derive(Debug)]
263pub struct FilterProcAudio {
264 pub scene: SceneInfo,
266 pub object: ObjectInfo,
268 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 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 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}