1use parking_lot::lock_api::RawRwLock;
2use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
3
4use super::{ErasedFilterConfigData, config};
5use crate::common::Rational32;
6
7#[derive(Debug, Clone)]
9pub struct FilterPluginTable {
10 pub name: String,
12 pub label: Option<String>,
15 pub information: String,
18
19 pub flags: FilterPluginFlags,
21
22 pub config_items: Vec<config::FilterConfigItem>,
24}
25
26define_bitflag! {
27 #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
33 #[non_exhaustive]
34 pub struct FilterPluginFlags: i32 {
35 video: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_VIDEO,
37
38 audio: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_AUDIO,
40
41 input: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_INPUT,
45
46 filter: aviutl2_sys::filter2::FILTER_PLUGIN_TABLE::FLAG_FILTER,
49 }
50}
51
52pub trait FilterPlugin: Send + Sync + Sized {
55 fn new(info: crate::common::AviUtl2Info) -> crate::common::AnyResult<Self>;
57
58 fn plugin_info(&self) -> crate::filter::FilterPluginTable;
60
61 fn proc_video(
67 &self,
68 _config: &[crate::filter::FilterConfigItem],
69 _video: &mut crate::filter::FilterProcVideo,
70 ) -> crate::common::AnyResult<()> {
71 anyhow::bail!("proc_video is not implemented");
72 }
73
74 fn proc_audio(
76 &self,
77 _config: &[crate::filter::FilterConfigItem],
78 _audio: &mut crate::filter::FilterProcAudio,
79 ) -> crate::common::AnyResult<()> {
80 anyhow::bail!("proc_audio is not implemented");
81 }
82
83 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 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#[derive(Debug, Clone, Copy)]
110pub struct SceneInfo {
111 pub width: u32,
113 pub height: u32,
115 pub frame_rate: Rational32,
117 pub sample_rate: u32,
119}
120
121#[derive(Debug, Clone, Copy)]
123pub struct ObjectInfo {
124 pub id: i64,
127 pub effect_id: i64,
130 pub frame: u32,
132 pub frame_total: u32,
134 pub time: f64,
136 pub time_total: f64,
138 pub is_filter_object: bool,
140}
141
142#[derive(Debug, Clone, Copy)]
144pub struct VideoObjectInfo {
145 pub width: u32,
147 pub height: u32,
149}
150
151#[derive(Debug, Clone, Copy)]
153pub struct AudioObjectInfo {
154 pub sample_index: u64,
156 pub sample_total: u64,
158 pub sample_num: u32,
160 pub channel_num: u32,
163}
164
165#[derive(
167 Debug, Default, Clone, Copy, PartialEq, Eq, IntoBytes, FromBytes, Immutable, KnownLayout,
168)]
169pub struct RgbaPixel {
170 pub r: u8,
172 pub g: u8,
174 pub b: u8,
176 pub a: u8,
178}
179
180#[derive(Debug)]
182pub struct FilterProcVideo {
183 pub scene: SceneInfo,
185 pub object: ObjectInfo,
187 pub video_object: VideoObjectInfo,
189
190 pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_VIDEO,
191}
192unsafe impl Send for FilterProcVideo {}
193unsafe impl Sync for FilterProcVideo {}
194
195impl FilterProcVideo {
196 pub fn get_image_data<T>(&mut self, buffer: &mut [T]) -> usize
209 where
210 T: Copy + FromBytes + Immutable,
211 {
212 if self.video_object.width == 0 || self.video_object.height == 0 {
213 tracing::warn!("width or height is 0, perhaps the filter plugin is a custom object");
214 return 0;
215 }
216 assert_eq!(
217 std::mem::size_of_val(buffer),
218 (self.video_object.width * self.video_object.height * 4) as usize,
219 "buffer length as bytes does not match width * height * 4"
220 );
221 assert!(
222 std::mem::align_of::<T>() >= std::mem::align_of::<aviutl2_sys::filter2::PIXEL_RGBA>(),
223 "buffer alignment is not sufficient"
224 );
225 let width = self.video_object.width as usize;
226 let height = self.video_object.height as usize;
227 let inner = unsafe { &*self.inner };
228 unsafe {
229 (inner.get_image_data)(
230 buffer.as_mut_ptr() as *mut u8 as *mut aviutl2_sys::filter2::PIXEL_RGBA
231 )
232 };
233
234 width * height * 4
235 }
236
237 pub fn set_image_data<T: IntoBytes + Immutable>(
243 &mut self,
244 data: &[T],
245 width: u32,
246 height: u32,
247 ) {
248 let bytes = &data.as_bytes();
249 assert_eq!(
250 bytes.len(),
251 (width * height * 4) as usize,
252 "data length does not match width * height * 4"
253 );
254 let inner = unsafe { &*self.inner };
255 unsafe {
256 (inner.set_image_data)(
257 bytes.as_ptr() as *const aviutl2_sys::filter2::PIXEL_RGBA,
258 width as i32,
259 height as i32,
260 )
261 };
262 }
263
264 pub fn get_image_texture2d(&mut self) -> *mut std::ffi::c_void {
270 let inner = unsafe { &*self.inner };
271 unsafe { (inner.get_image_texture2d)() }
272 }
273
274 pub fn get_framebuffer_texture2d(&mut self) -> *mut std::ffi::c_void {
280 let inner = unsafe { &*self.inner };
281 unsafe { (inner.get_framebuffer_texture2d)() }
282 }
283}
284
285#[derive(Debug)]
287pub struct FilterProcAudio {
288 pub scene: SceneInfo,
290 pub object: ObjectInfo,
292 pub audio_object: AudioObjectInfo,
294
295 pub(crate) inner: *const aviutl2_sys::filter2::FILTER_PROC_AUDIO,
296}
297
298unsafe impl Send for FilterProcAudio {}
299unsafe impl Sync for FilterProcAudio {}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302pub enum AudioChannel {
303 Left,
304 Right,
305 Any(i32),
306}
307impl From<i32> for AudioChannel {
308 fn from(value: i32) -> Self {
309 match value {
310 0 => AudioChannel::Left,
311 1 => AudioChannel::Right,
312 v => AudioChannel::Any(v),
313 }
314 }
315}
316impl From<AudioChannel> for i32 {
317 fn from(value: AudioChannel) -> Self {
318 match value {
319 AudioChannel::Left => 0,
320 AudioChannel::Right => 1,
321 AudioChannel::Any(v) => v,
322 }
323 }
324}
325
326impl FilterProcAudio {
327 pub fn get_sample_data(&mut self, channel: AudioChannel, buffer: &mut [f32]) -> usize {
334 let sample_num = self.audio_object.sample_num as usize;
335 assert_eq!(
336 buffer.len(),
337 sample_num,
338 "buffer length does not match sample_num"
339 );
340 let inner = unsafe { &*self.inner };
341 unsafe { (inner.get_sample_data)(buffer.as_mut_ptr(), channel.into()) };
342 sample_num
343 }
344
345 pub fn set_sample_data(&mut self, channel: AudioChannel, data: &[f32]) {
352 let sample_num = self.audio_object.sample_num as usize;
353 assert_eq!(
354 data.len(),
355 sample_num,
356 "data length does not match sample_num"
357 );
358 let inner = unsafe { &*self.inner };
359 unsafe { (inner.set_sample_data)(data.as_ptr(), channel.into()) };
360 }
361}
362
363#[derive(Debug)]
366pub struct FilterConfigDataHandle<T: Copy> {
367 pub(crate) inner: *mut T,
368}
369
370unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataHandle<T> {}
371unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataHandle<T> {}
372
373static HANDLES: std::sync::LazyLock<dashmap::DashMap<usize, parking_lot::RawRwLock>> =
374 std::sync::LazyLock::new(dashmap::DashMap::new);
375static OWNED_REFERENCES: std::sync::LazyLock<
376 std::sync::Arc<dashmap::DashMap<usize, std::sync::atomic::AtomicUsize>>,
377> = std::sync::LazyLock::new(|| std::sync::Arc::new(dashmap::DashMap::new()));
378
379impl<T: Copy> Clone for FilterConfigDataHandle<T> {
380 fn clone(&self) -> Self {
381 if !self.inner.is_null() {
382 let addr = self.inner as usize;
383 if OWNED_REFERENCES.contains_key(&addr) {
384 let entry = OWNED_REFERENCES.get(&addr).unwrap();
385 entry.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
386 }
387 }
388 Self { inner: self.inner }
389 }
390}
391impl<T: Copy> Drop for FilterConfigDataHandle<T> {
392 fn drop(&mut self) {
393 if !self.inner.is_null() {
394 let addr = self.inner as usize;
395 if let Some(entry) = OWNED_REFERENCES.get(&addr) {
396 let prev = entry.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
397 if prev == 1 {
398 unsafe {
399 let _boxed = Box::from_raw(self.inner);
400 }
401 drop(entry);
403 OWNED_REFERENCES.remove(&addr);
404 }
405 }
406 }
407 }
408}
409
410impl<T: Copy> FilterConfigDataHandle<T> {
411 #[doc(hidden)]
412 pub fn __generics_default_value() -> T
413 where
414 T: Default,
415 {
416 T::default()
417 }
418
419 #[doc(hidden)]
420 pub fn __from_erased(erased: &ErasedFilterConfigData) -> Self {
421 Self {
422 inner: erased.value.map_or(std::ptr::null_mut(), |v| v.as_ptr()) as *mut T,
423 }
424 }
425
426 #[doc(hidden)]
427 pub fn __new_owned(value: T) -> Self {
428 let boxed = Box::new(value);
429 let pointer = Box::into_raw(boxed);
430 let addr = pointer as *mut () as usize;
431 OWNED_REFERENCES.insert(addr, std::sync::atomic::AtomicUsize::new(1));
432 Self { inner: pointer }
433 }
434
435 pub fn read<'handle>(&'handle self) -> FilterConfigDataReadGuard<'handle, T> {
437 let addr = self.inner as *mut () as usize;
438 let lock = HANDLES
439 .entry(addr)
440 .or_insert_with(|| parking_lot::RawRwLock::INIT);
441 let lock = lock.value();
442
443 lock.lock_shared();
444 FilterConfigDataReadGuard::new(self.inner)
445 }
446
447 pub fn try_read<'handle>(&'handle self) -> Option<FilterConfigDataReadGuard<'handle, T>> {
450 let addr = self.inner as *mut () as usize;
451 let lock = HANDLES
452 .entry(addr)
453 .or_insert_with(|| parking_lot::RawRwLock::INIT);
454 let lock = lock.value();
455
456 if lock.try_lock_shared() {
457 Some(FilterConfigDataReadGuard::new(self.inner))
458 } else {
459 None
460 }
461 }
462
463 pub fn write<'handle>(&'handle self) -> FilterConfigDataWriteGuard<'handle, T> {
465 let addr = self.inner as *mut () as usize;
466 let lock = HANDLES
467 .entry(addr)
468 .or_insert_with(|| parking_lot::RawRwLock::INIT);
469 let lock = lock.value();
470 lock.lock_exclusive();
471 FilterConfigDataWriteGuard::new(self.inner)
472 }
473
474 pub fn try_write<'handle>(&'handle self) -> Option<FilterConfigDataWriteGuard<'handle, T>> {
477 let addr = self.inner as *mut () as usize;
478 let lock = HANDLES
479 .entry(addr)
480 .or_insert_with(|| parking_lot::RawRwLock::INIT);
481 let lock = lock.value();
482 if lock.try_lock_exclusive() {
483 Some(FilterConfigDataWriteGuard::new(self.inner))
484 } else {
485 None
486 }
487 }
488
489 pub fn as_ptr(&self) -> *mut T {
495 self.inner
496 }
497}
498
499#[doc(hidden)]
500#[expect(private_bounds)]
501pub fn __string_to_pathbuf_or_option_pathbuf<T: StringToPathBufOrOptionPathBuf>(s: &str) -> T {
502 T::__string_to_pathbuf_or_option_pathbuf(s)
503}
504
505trait StringToPathBufOrOptionPathBuf: Sized {
506 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self;
507}
508impl StringToPathBufOrOptionPathBuf for std::path::PathBuf {
509 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self {
510 std::path::PathBuf::from(s)
511 }
512}
513impl StringToPathBufOrOptionPathBuf for Option<std::path::PathBuf> {
514 fn __string_to_pathbuf_or_option_pathbuf(s: &str) -> Self {
515 if s.is_empty() {
516 None
517 } else {
518 Some(std::path::PathBuf::from(s))
519 }
520 }
521}
522
523pub struct FilterConfigDataReadGuard<'handle, T: Copy> {
525 pub(crate) inner: *mut T,
526 _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
527}
528unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataReadGuard<'_, T> {}
529unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataReadGuard<'_, T> {}
530impl<T: Copy> FilterConfigDataReadGuard<'_, T> {
531 fn new<'handle>(inner: *mut T) -> FilterConfigDataReadGuard<'handle, T> {
532 FilterConfigDataReadGuard {
533 inner,
534 _handle: std::marker::PhantomData,
535 }
536 }
537}
538impl<T: Copy> Drop for FilterConfigDataReadGuard<'_, T> {
539 fn drop(&mut self) {
540 let addr = self.inner as *mut () as usize;
541 if let Some(entry) = HANDLES.get(&addr) {
542 let lock = entry.value();
543 unsafe { lock.unlock_shared() };
544 }
545 }
546}
547impl<T: Copy> std::convert::AsRef<T> for FilterConfigDataReadGuard<'_, T> {
548 fn as_ref(&self) -> &T {
549 unsafe { &*self.inner }
550 }
551}
552impl<T: Copy> std::ops::Deref for FilterConfigDataReadGuard<'_, T> {
553 type Target = T;
554
555 fn deref(&self) -> &Self::Target {
556 self.as_ref()
557 }
558}
559
560pub struct FilterConfigDataWriteGuard<'handle, T: Copy> {
562 pub(crate) inner: *mut T,
563 _handle: std::marker::PhantomData<&'handle FilterConfigDataHandle<T>>,
564}
565
566unsafe impl<T: Send + Sync + Copy> Send for FilterConfigDataWriteGuard<'_, T> {}
567unsafe impl<T: Send + Sync + Copy> Sync for FilterConfigDataWriteGuard<'_, T> {}
568impl<T: Copy> FilterConfigDataWriteGuard<'_, T> {
569 fn new<'handle>(inner: *mut T) -> FilterConfigDataWriteGuard<'handle, T> {
570 FilterConfigDataWriteGuard {
571 inner,
572 _handle: std::marker::PhantomData,
573 }
574 }
575}
576impl<T: Copy> Drop for FilterConfigDataWriteGuard<'_, T> {
577 fn drop(&mut self) {
578 let addr = self.inner as *mut () as usize;
579 if let Some(entry) = HANDLES.get(&addr) {
580 let lock = entry.value();
581 unsafe { lock.unlock_exclusive() };
582 }
583 }
584}
585impl<T: Copy> std::convert::AsMut<T> for FilterConfigDataWriteGuard<'_, T> {
586 fn as_mut(&mut self) -> &mut T {
587 unsafe { &mut *self.inner }
588 }
589}
590impl<T: Copy> std::ops::Deref for FilterConfigDataWriteGuard<'_, T> {
591 type Target = T;
592 fn deref(&self) -> &Self::Target {
593 unsafe { &*self.inner }
594 }
595}
596impl<T: Copy> std::ops::DerefMut for FilterConfigDataWriteGuard<'_, T> {
597 fn deref_mut(&mut self) -> &mut Self::Target {
598 self.as_mut()
599 }
600}
601
602#[cfg(test)]
603mod tests {
604 use super::*;
605
606 #[test]
607 fn filter_config_data_handle_reads_initial_value() {
608 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
609 let read_guard = handle.read();
610 assert_eq!(*read_guard, 42);
611 }
612
613 #[test]
614 fn filter_config_data_handle_writes_and_reads_updated_value() {
615 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
616 {
617 let mut write_guard = handle.write();
618 *write_guard = 100;
619 }
620 let read_guard = handle.read();
621 assert_eq!(*read_guard, 100);
622 }
623
624 #[test]
625 fn filter_config_data_handle_try_read_fails_when_locked_for_write() {
626 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
627 let _write_guard = handle.write();
628 let try_read_guard = handle.try_read();
629 assert!(try_read_guard.is_none());
630 }
631
632 #[test]
633 fn filter_config_data_handle_try_write_fails_when_locked_for_read() {
634 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
635 let _read_guard = handle.read();
636 let try_write_guard = handle.try_write();
637 assert!(try_write_guard.is_none());
638 }
639
640 #[test]
641 fn filter_config_data_handle_clone_shares_state() {
642 let handle = FilterConfigDataHandle::<u32>::__new_owned(42);
643 let cloned_handle = handle.clone();
644 {
645 let mut write_guard = handle.write();
646 *write_guard = 100;
647 }
648 let read_guard = cloned_handle.read();
649 assert_eq!(*read_guard, 100);
650 }
651
652 #[test]
653 fn filter_config_data_handle_never_drops_data_for_borrowed() {
654 let mut data =
655 crate::filter::ErasedFilterConfigData::with_default_value("test".to_string(), 42);
656 let data_ptr = Box::into_raw(Box::new(42u32));
657 data.value = Some(std::ptr::NonNull::new(data_ptr as _).unwrap());
658 let handle = FilterConfigDataHandle::<u32>::__from_erased(&data);
659 drop(handle);
660
661 assert_eq!(unsafe { *data_ptr }, 42);
662 }
663
664 #[test]
665 fn filter_config_data_handle_reads_value_from_erased_data() {
666 let boxed = Box::new(77u32);
667 let ptr = std::ptr::NonNull::from(boxed.as_ref());
668 let data = crate::filter::FilterConfigData {
669 name: "test".to_string(),
670 value: Some(ptr),
671 default_value: 0,
672 };
673 let erased = data.erase_type();
674 let handle = FilterConfigDataHandle::<u32>::__from_erased(&erased);
675 let read_guard = handle.read();
676
677 assert_eq!(*read_guard, 77);
678 assert_eq!(erased.value.unwrap().as_ptr() as *mut u32, ptr.as_ptr());
679
680 drop(read_guard);
681 drop(handle);
682 drop(boxed);
683 }
684}