Skip to main content

aviutl2\input/
bridge.rs

1use std::num::NonZeroIsize;
2
3use crate::{
4    common::{AnyResult, LeakManager, format_file_filters, load_wide_string},
5    input::{
6        AudioFormat, AudioInputInfo, AudioReturner, ImageReturner, InputInfo, InputPixelFormat,
7        InputPlugin, InputPluginTable, VideoInputInfo,
8    },
9};
10
11impl InputPixelFormat {
12    fn bytes_count_per_pixel(&self) -> usize {
13        match self {
14            InputPixelFormat::Bgr => 3,  // RGB format
15            InputPixelFormat::Bgra => 4, // RGBA format
16            InputPixelFormat::Yuy2 => 2, // YUY2 format (packed YUV 4:2:2, 4 bytes per 2 pixels)
17            InputPixelFormat::Pa64 => 8, // DXGI_FORMAT_R16G16B16A16_UNORM (packed 16-bit per channel)
18            InputPixelFormat::Hf64 => 8, // DXGI_FORMAT_R16G16B16A16_FLOAT (half-float)
19            InputPixelFormat::Yc48 => 6, // YC48 (AviUtl1)
20        }
21    }
22}
23
24impl AudioFormat {
25    fn bytes_per_sample(&self) -> usize {
26        match self {
27            AudioFormat::IeeeFloat32 => 4, // 32-bit float
28            AudioFormat::Pcm16 => 2,       // 16-bit PCM
29        }
30    }
31}
32
33impl VideoInputInfo {
34    fn into_raw(self) -> aviutl2_sys::input2::BITMAPINFOHEADER {
35        let bi_compression = match self.format {
36            InputPixelFormat::Bgr | InputPixelFormat::Bgra => aviutl2_sys::common::BI_RGB,
37            InputPixelFormat::Yuy2 => aviutl2_sys::common::BI_YUY2,
38            InputPixelFormat::Pa64 => aviutl2_sys::common::BI_PA64,
39            InputPixelFormat::Hf64 => aviutl2_sys::common::BI_HF64,
40            InputPixelFormat::Yc48 => aviutl2_sys::common::BI_YC48,
41        };
42
43        // NOTE:
44        // biHeightをマイナスにしてBI_RGBでも上からにするやつは使えない(AviUtが落ちる)
45        aviutl2_sys::input2::BITMAPINFOHEADER {
46            biSize: std::mem::size_of::<aviutl2_sys::input2::BITMAPINFOHEADER>() as u32,
47            biWidth: self.width as i32,
48            biHeight: self.height as i32,
49            biPlanes: 1,
50            biBitCount: (self.format.bytes_count_per_pixel() * 8) as u16, // Bits per pixel
51            biCompression: bi_compression,
52            biSizeImage: (self.width * self.height * self.format.bytes_count_per_pixel() as u32),
53            biXPelsPerMeter: 0, // Not used
54            biYPelsPerMeter: 0, // Not used
55            biClrUsed: 0,       // Not used
56            biClrImportant: 0,  // Not used
57        }
58    }
59}
60
61impl AudioInputInfo {
62    fn into_raw(self) -> aviutl2_sys::input2::WAVEFORMATEX {
63        let format = match self.format {
64            AudioFormat::IeeeFloat32 => aviutl2_sys::common::WAVE_FORMAT_IEEE_FLOAT,
65            AudioFormat::Pcm16 => aviutl2_sys::common::WAVE_FORMAT_PCM,
66        };
67        let bytes_per_sample = self.format.bytes_per_sample();
68        aviutl2_sys::input2::WAVEFORMATEX {
69            wFormatTag: format as u16,
70            nChannels: self.channels,
71            nSamplesPerSec: self.sample_rate,
72            nAvgBytesPerSec: (self.sample_rate
73                * (self.channels as u32)
74                * (bytes_per_sample as u32)),
75            nBlockAlign: (self.channels * bytes_per_sample as u16),
76            wBitsPerSample: u16::try_from(bytes_per_sample * 8usize)
77                .expect("Invalid bits per sample"),
78            cbSize: 0, // No extra data
79        }
80    }
81}
82
83#[doc(hidden)]
84pub struct InternalInputPluginState<T: Send + Sync + InputPlugin> {
85    plugin_info: InputPluginTable,
86    global_leak_manager: LeakManager,
87    leak_manager: LeakManager,
88
89    instance: T,
90}
91
92impl<T: Send + Sync + InputPlugin> InternalInputPluginState<T> {
93    pub fn new(instance: T) -> Self {
94        let plugin_info = instance.plugin_info();
95        Self {
96            plugin_info,
97            global_leak_manager: LeakManager::new(),
98            leak_manager: LeakManager::new(),
99            instance,
100        }
101    }
102}
103
104struct InternalInputHandle<T: Send + Sync> {
105    input_info: Option<InputInfo>,
106    num_tracks: std::sync::Mutex<Option<AnyResult<(u32, u32)>>>,
107    current_video_track: std::sync::OnceLock<u32>,
108    current_audio_track: std::sync::OnceLock<u32>,
109
110    handle: T,
111}
112
113pub unsafe fn initialize_plugin_c<T: InputSingleton>(version: u32) -> bool {
114    match initialize_plugin::<T>(version) {
115        Ok(_) => true,
116        Err(e) => {
117            tracing::error!("Failed to initialize plugin: {}", e);
118            let _ = crate::logger::write_error_log(&format!("{e}"));
119            false
120        }
121    }
122}
123
124pub unsafe fn initialize_plugin_c_unwind<T: InputSingleton>(version: u32) -> bool {
125    match crate::utils::catch_unwind_with_panic_info(|| unsafe {
126        initialize_plugin_c::<T>(version)
127    }) {
128        Ok(result) => result,
129        Err(panic_info) => {
130            tracing::error!(
131                "Panic occurred during plugin initialization: {}",
132                panic_info
133            );
134            let _ = crate::logger::write_error_log(&panic_info);
135            false
136        }
137    }
138}
139
140pub(crate) fn initialize_plugin<T: InputSingleton>(version: u32) -> AnyResult<()> {
141    crate::common::ensure_minimum_aviutl2_version(version.into())?;
142    let plugin_state = T::__get_singleton_state();
143    let info = crate::common::AviUtl2Info {
144        version: version.into(),
145    };
146    let internal = T::new(info)?;
147    let plugin = InternalInputPluginState::new(internal);
148    *plugin_state.write().unwrap() = Some(plugin);
149
150    Ok(())
151}
152
153pub unsafe fn uninitialize_plugin<T: InputSingleton>() {
154    let plugin_state = T::__get_singleton_state();
155    let mut plugin_state = plugin_state.write().unwrap();
156    *plugin_state = None;
157}
158
159pub unsafe fn uninitialize_plugin_c_unwind<T: InputSingleton>() {
160    match crate::utils::catch_unwind_with_panic_info(|| unsafe { uninitialize_plugin::<T>() }) {
161        Ok(()) => {}
162        Err(panic_info) => {
163            tracing::error!(
164                "Panic occurred during plugin uninitialization: {}",
165                panic_info
166            );
167            let _ = crate::logger::write_error_log(&panic_info);
168        }
169    }
170}
171
172fn create_table_impl<T: InputSingleton>(
173    unwind: bool,
174) -> *mut aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
175    let plugin_state = T::__get_singleton_state();
176    let mut plugin_state = plugin_state.write().unwrap();
177    let plugin_state = plugin_state.as_mut().expect("Plugin not initialized");
178    let plugin_info = &plugin_state.plugin_info;
179    let file_filter = format_file_filters(&plugin_info.file_filters);
180
181    let name = plugin_info.name.clone();
182    let information = plugin_info.information.clone();
183
184    let mut flag = plugin_info.input_type.to_bits();
185    if plugin_info.concurrent {
186        flag |= aviutl2_sys::input2::INPUT_PLUGIN_TABLE::FLAG_CONCURRENT;
187    }
188    flag |= aviutl2_sys::input2::INPUT_PLUGIN_TABLE::FLAG_MULTI_TRACK;
189
190    let func_open = if unwind {
191        func_open_unwind::<T>
192    } else {
193        func_open::<T>
194    };
195    let func_close = if unwind {
196        func_close_unwind::<T>
197    } else {
198        func_close::<T>
199    };
200    let func_info_get = if unwind {
201        func_info_get_unwind::<T>
202    } else {
203        func_info_get::<T>
204    };
205    let func_read_video = if unwind {
206        func_read_video_unwind::<T>
207    } else {
208        func_read_video::<T>
209    };
210    let func_read_audio = if unwind {
211        func_read_audio_unwind::<T>
212    } else {
213        func_read_audio::<T>
214    };
215    let func_config = if unwind {
216        func_config_unwind::<T>
217    } else {
218        func_config::<T>
219    };
220    let func_set_track = if unwind {
221        func_set_track_unwind::<T>
222    } else {
223        func_set_track::<T>
224    };
225    let func_time_to_frame = if unwind {
226        func_time_to_frame_unwind::<T>
227    } else {
228        func_time_to_frame::<T>
229    };
230
231    // NOTE: プラグイン名などの文字列はAviUtlが終了するまで解放しない
232    let table = aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
233        flag,
234        name: plugin_state.global_leak_manager.leak_as_wide_string(&name),
235        filefilter: plugin_state
236            .global_leak_manager
237            .leak_as_wide_string(&file_filter),
238        information: plugin_state
239            .global_leak_manager
240            .leak_as_wide_string(&information),
241        func_open: Some(func_open),
242        func_close: Some(func_close),
243        func_info_get: Some(func_info_get),
244        func_read_video: Some(func_read_video),
245        func_read_audio: Some(func_read_audio),
246        func_config: plugin_info.can_config.then_some(func_config),
247        func_set_track: Some(func_set_track),
248        func_time_to_frame: Some(func_time_to_frame),
249    };
250    let table = Box::new(table);
251    Box::leak(table)
252}
253
254pub unsafe fn create_table<T: InputSingleton>() -> *mut aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
255    create_table_impl::<T>(false)
256}
257
258pub unsafe fn create_table_unwind<T: InputSingleton>()
259-> *mut aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
260    match crate::utils::catch_unwind_with_panic_info(|| create_table_impl::<T>(true)) {
261        Ok(table) => table,
262        Err(panic_info) => {
263            tracing::error!("Panic occurred during create_table: {}", panic_info);
264            let _ = crate::logger::write_error_log(&panic_info);
265            std::ptr::null_mut()
266        }
267    }
268}
269
270extern "C" fn func_open<T: InputSingleton>(
271    file: aviutl2_sys::common::LPCWSTR,
272) -> aviutl2_sys::input2::INPUT_HANDLE {
273    let plugin_state = T::__get_singleton_state();
274    let plugin_state = plugin_state.read().unwrap();
275    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
276    plugin_state.leak_manager.free_leaked_memory();
277    let path = unsafe { load_wide_string(file) };
278    tracing::info!("func_open called with path: {}", path);
279    let plugin = &plugin_state.instance;
280    match plugin.open(std::path::PathBuf::from(path)) {
281        Ok(handle) => {
282            let boxed_handle: Box<InternalInputHandle<T::InputHandle>> =
283                Box::new(InternalInputHandle {
284                    input_info: None,
285                    num_tracks: std::sync::Mutex::new(None),
286                    current_video_track: std::sync::OnceLock::new(),
287                    current_audio_track: std::sync::OnceLock::new(),
288                    handle,
289                });
290            Box::into_raw(boxed_handle) as aviutl2_sys::input2::INPUT_HANDLE
291        }
292        Err(e) => {
293            tracing::error!("Error during func_open: {}", e);
294            std::ptr::null_mut()
295        }
296    }
297}
298extern "C" fn func_open_unwind<T: InputSingleton>(
299    file: aviutl2_sys::common::LPCWSTR,
300) -> aviutl2_sys::input2::INPUT_HANDLE {
301    match crate::utils::catch_unwind_with_panic_info(|| func_open::<T>(file)) {
302        Ok(handle) => handle,
303        Err(panic_info) => {
304            tracing::error!("Panic occurred during func_open: {}", panic_info);
305            let _ = crate::logger::write_error_log(&panic_info);
306            std::ptr::null_mut()
307        }
308    }
309}
310extern "C" fn func_close<T: InputSingleton>(ih: aviutl2_sys::input2::INPUT_HANDLE) -> bool {
311    let plugin_state = T::__get_singleton_state();
312    let plugin_state = plugin_state.read().unwrap();
313    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
314    plugin_state.leak_manager.free_leaked_memory();
315    let handle = unsafe { Box::from_raw(ih as *mut InternalInputHandle<T::InputHandle>) };
316    let plugin = &plugin_state.instance;
317    match plugin.close(handle.handle) {
318        Ok(()) => true,
319        Err(e) => {
320            tracing::error!("Error during func_close: {}", e);
321            false
322        }
323    }
324}
325extern "C" fn func_close_unwind<T: InputSingleton>(ih: aviutl2_sys::input2::INPUT_HANDLE) -> bool {
326    match crate::utils::catch_unwind_with_panic_info(|| func_close::<T>(ih)) {
327        Ok(result) => result,
328        Err(panic_info) => {
329            tracing::error!("Panic occurred during func_close: {}", panic_info);
330            let _ = crate::logger::write_error_log(&panic_info);
331            false
332        }
333    }
334}
335extern "C" fn func_info_get<T: InputSingleton>(
336    ih: aviutl2_sys::input2::INPUT_HANDLE,
337    iip: *mut aviutl2_sys::input2::INPUT_INFO,
338) -> bool {
339    let plugin_state = T::__get_singleton_state();
340    let plugin_state = plugin_state.read().unwrap();
341    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
342    plugin_state.leak_manager.free_leaked_memory();
343    let handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
344    let video_track = {
345        *handle
346            .current_video_track
347            .get()
348            .expect("unreachable: func_set_track should have been called before func_info_get")
349    };
350    let audio_track = {
351        *handle
352            .current_audio_track
353            .get()
354            .expect("unreachable: func_set_track should have been called before func_info_get")
355    };
356    let plugin = &plugin_state.instance;
357
358    match T::get_input_info(plugin, &mut handle.handle, video_track, audio_track) {
359        Ok(info) => {
360            handle.input_info = Some(info.clone());
361            if let Some(video_info) = info.video {
362                let fps = video_info.fps;
363                let num_frames = video_info.num_frames;
364                let manual_frame_index = video_info.manual_frame_index;
365                let width = video_info.width;
366                let height = video_info.height;
367                let image_format = video_info.into_raw();
368                unsafe {
369                    (*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_VIDEO;
370                    if manual_frame_index {
371                        (*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_TIME_TO_FRAME;
372                    }
373                    (*iip).rate = *fps.numer();
374                    (*iip).scale = *fps.denom();
375                    (*iip).n = num_frames as _;
376                    (*iip).format = plugin_state.leak_manager.leak(image_format);
377                    (*iip).format_size = (4 * width * height) as i32; // 4 bytes per pixel for RGBA
378                    (*iip).audio_n = 0;
379                    (*iip).audio_format = std::ptr::null_mut();
380                    (*iip).audio_format_size = 0;
381                }
382            }
383
384            if let Some(audio_info) = info.audio {
385                let num_samples = audio_info.num_samples;
386                let audio_format = audio_info.into_raw();
387                let audio_format_size = std::mem::size_of_val(&audio_format) as i32;
388                unsafe {
389                    (*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_AUDIO;
390                    (*iip).audio_n = num_samples as _;
391                    (*iip).audio_format = plugin_state.leak_manager.leak(audio_format);
392                    (*iip).audio_format_size = audio_format_size;
393                }
394            }
395
396            true
397        }
398        Err(e) => {
399            tracing::error!("Error during func_info_get: {}", e);
400            false
401        }
402    }
403}
404extern "C" fn func_info_get_unwind<T: InputSingleton>(
405    ih: aviutl2_sys::input2::INPUT_HANDLE,
406    iip: *mut aviutl2_sys::input2::INPUT_INFO,
407) -> bool {
408    match crate::utils::catch_unwind_with_panic_info(|| func_info_get::<T>(ih, iip)) {
409        Ok(result) => result,
410        Err(panic_info) => {
411            tracing::error!("Panic occurred during func_info_get: {}", panic_info);
412            let _ = crate::logger::write_error_log(&panic_info);
413            false
414        }
415    }
416}
417extern "C" fn func_read_video<T: InputSingleton>(
418    ih: aviutl2_sys::input2::INPUT_HANDLE,
419    frame: i32,
420    buf: *mut std::ffi::c_void,
421) -> i32 {
422    let plugin_state = T::__get_singleton_state();
423    let plugin_state = plugin_state.read().unwrap();
424    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
425    plugin_state.leak_manager.free_leaked_memory();
426    let handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
427    let plugin = &plugin_state.instance;
428    let frame = frame as u32;
429    let mut returner = unsafe { ImageReturner::new(buf as *mut u8) };
430    let read_result = if plugin_state.plugin_info.concurrent {
431        T::read_video(plugin, &handle.handle, frame, &mut returner)
432    } else {
433        T::read_video_mut(plugin, &mut handle.handle, frame, &mut returner)
434    };
435    match read_result {
436        Ok(()) => {
437            #[cfg(debug_assertions)]
438            {
439                let video_format = handle
440                    .input_info
441                    .as_ref()
442                    .expect("Unreachable: Input info not set")
443                    .video
444                    .as_ref()
445                    .expect("Unreachable: Video format not set");
446                assert_eq!(
447                    returner.written,
448                    ((video_format.width * video_format.height) as usize
449                        * video_format.format.bytes_count_per_pixel()),
450                    "Image data size does not match expected size"
451                );
452            }
453            returner.written as i32
454        }
455        Err(e) => {
456            tracing::error!("Error during func_read_video: {}", e);
457            0
458        }
459    }
460}
461
462extern "C" fn func_read_video_unwind<T: InputSingleton>(
463    ih: aviutl2_sys::input2::INPUT_HANDLE,
464    frame: i32,
465    buf: *mut std::ffi::c_void,
466) -> i32 {
467    match crate::utils::catch_unwind_with_panic_info(|| func_read_video::<T>(ih, frame, buf)) {
468        Ok(result) => result,
469        Err(panic_info) => {
470            tracing::error!("Panic occurred during func_read_video: {}", panic_info);
471            let _ = crate::logger::write_error_log(&panic_info);
472            0
473        }
474    }
475}
476
477extern "C" fn func_read_audio<T: InputSingleton>(
478    ih: aviutl2_sys::input2::INPUT_HANDLE,
479    start: i32,
480    length: i32,
481    buf: *mut std::ffi::c_void,
482) -> i32 {
483    let plugin_state = T::__get_singleton_state();
484    let plugin_state = plugin_state.read().unwrap();
485    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
486    plugin_state.leak_manager.free_leaked_memory();
487    let handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
488    let plugin = &plugin_state.instance;
489    let mut returner = unsafe { AudioReturner::new(buf as *mut u8) };
490    let read_result = if plugin_state.plugin_info.concurrent {
491        T::read_audio(plugin, &handle.handle, start, length, &mut returner)
492    } else {
493        T::read_audio_mut(plugin, &mut handle.handle, start, length, &mut returner)
494    };
495    match read_result {
496        Ok(()) => {
497            #[cfg(debug_assertions)]
498            {
499                let audio_format = handle
500                    .input_info
501                    .as_ref()
502                    .expect("Unreachable: Input info not set")
503                    .audio
504                    .as_ref()
505                    .expect("Unreachable: Audio format not set");
506                assert_eq!(
507                    returner.written,
508                    ((length as usize)
509                        * (audio_format.channels as usize)
510                        * audio_format.format.bytes_per_sample()),
511                    "Audio data size does not match expected size"
512                );
513            }
514            returner.written as i32
515        }
516        Err(e) => {
517            tracing::error!("Error during func_read_audio: {}", e);
518            0
519        }
520    }
521}
522
523extern "C" fn func_read_audio_unwind<T: InputSingleton>(
524    ih: aviutl2_sys::input2::INPUT_HANDLE,
525    start: i32,
526    length: i32,
527    buf: *mut std::ffi::c_void,
528) -> i32 {
529    match crate::utils::catch_unwind_with_panic_info(|| {
530        func_read_audio::<T>(ih, start, length, buf)
531    }) {
532        Ok(result) => result,
533        Err(panic_info) => {
534            tracing::error!("Panic occurred during func_read_audio: {}", panic_info);
535            let _ = crate::logger::write_error_log(&panic_info);
536            0
537        }
538    }
539}
540
541extern "C" fn func_config<T: InputSingleton>(
542    hwnd: aviutl2_sys::input2::HWND,
543    dll_hinst: aviutl2_sys::input2::HINSTANCE,
544) -> bool {
545    let plugin_state = T::__get_singleton_state();
546    let plugin_state = plugin_state.read().unwrap();
547    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
548    plugin_state.leak_manager.free_leaked_memory();
549    let mut handle =
550        raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
551    handle.hinstance = Some(NonZeroIsize::new(dll_hinst as isize).unwrap());
552    let plugin = &plugin_state.instance;
553    match plugin.config(handle) {
554        Ok(()) => true,
555        Err(e) => {
556            tracing::error!("Error during func_config: {}", e);
557            let _ = crate::logger::write_error_log(&format!("{e}"));
558            false
559        }
560    }
561}
562extern "C" fn func_config_unwind<T: InputSingleton>(
563    hwnd: aviutl2_sys::input2::HWND,
564    dll_hinst: aviutl2_sys::input2::HINSTANCE,
565) -> bool {
566    match crate::utils::catch_unwind_with_panic_info(|| func_config::<T>(hwnd, dll_hinst)) {
567        Ok(result) => result,
568        Err(panic_info) => {
569            tracing::error!("Panic occurred during func_config: {}", panic_info);
570            let _ = crate::logger::write_error_log(&panic_info);
571            false
572        }
573    }
574}
575extern "C" fn func_set_track<T: InputSingleton>(
576    ih: aviutl2_sys::input2::INPUT_HANDLE,
577    track_type: i32,
578    track: i32,
579) -> i32 {
580    let plugin_state = T::__get_singleton_state();
581    let plugin_state = plugin_state.read().unwrap();
582    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
583    plugin_state.leak_manager.free_leaked_memory();
584    let handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
585    let plugin = &plugin_state.instance;
586    if track == -1 {
587        // track == -1:トラック数取得
588        if handle.num_tracks.lock().unwrap().is_none() {
589            let num_tracks = plugin.get_track_count(&mut handle.handle).map_err(|e| {
590                tracing::error!("Failed to get track count: {}", e);
591                e
592            });
593
594            if matches!(num_tracks, Ok((0, _))) {
595                handle
596                    .current_video_track
597                    .set(0)
598                    .expect("unreachable: func_set_track should only be called once per handle");
599            }
600            if matches!(num_tracks, Ok((_, 0))) {
601                handle
602                    .current_audio_track
603                    .set(0)
604                    .expect("unreachable: func_set_track should only be called once per handle");
605            }
606            *handle.num_tracks.lock().unwrap() = Some(num_tracks);
607        }
608        match &*handle.num_tracks.lock().unwrap() {
609            Some(Ok((video_tracks, audio_tracks))) => {
610                if track_type == aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_VIDEO {
611                    *video_tracks as i32
612                } else if track_type == aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_AUDIO {
613                    *audio_tracks as i32
614                } else {
615                    tracing::error!("Invalid track type: {}", track_type);
616                    -1 // Invalid track type
617                }
618            }
619            Some(Err(e)) => {
620                tracing::error!("Error occurred while getting track count: {}", e);
621                -1 // Error occurred
622            }
623            None => {
624                unreachable!("Track count should have been initialized before this point");
625            }
626        }
627    } else {
628        // track != -1:トラック設定
629        match track_type {
630            aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_VIDEO => {
631                let new_track = plugin
632                    .can_set_video_track(&mut handle.handle, track as u32)
633                    .map_or_else(
634                        |e| {
635                            tracing::debug!("Failed to set video track: {}", e);
636                            -1
637                        },
638                        |t| t as i32,
639                    );
640                handle
641                    .current_video_track
642                    .set(new_track as u32)
643                    .expect("unreachable: func_set_track should only be called once per handle");
644                new_track
645            }
646            aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_AUDIO => {
647                let new_track = plugin
648                    .can_set_audio_track(&mut handle.handle, track as u32)
649                    .map_or_else(
650                        |e| {
651                            tracing::debug!("Failed to set audio track: {}", e);
652                            -1
653                        },
654                        |t| t as i32,
655                    );
656                handle
657                    .current_audio_track
658                    .set(new_track as u32)
659                    .expect("unreachable: func_set_track should only be called once per handle");
660                new_track
661            }
662            _ => -1, // Invalid track type
663        }
664    }
665}
666extern "C" fn func_set_track_unwind<T: InputSingleton>(
667    ih: aviutl2_sys::input2::INPUT_HANDLE,
668    track_type: i32,
669    track: i32,
670) -> i32 {
671    match crate::utils::catch_unwind_with_panic_info(|| func_set_track::<T>(ih, track_type, track))
672    {
673        Ok(result) => result,
674        Err(panic_info) => {
675            tracing::error!("Panic occurred during func_set_track: {}", panic_info);
676            let _ = crate::logger::write_error_log(&panic_info);
677            -1
678        }
679    }
680}
681extern "C" fn func_time_to_frame<T: InputSingleton>(
682    ih: aviutl2_sys::input2::INPUT_HANDLE,
683    time: f64,
684) -> i32 {
685    let plugin_state = T::__get_singleton_state();
686    let plugin_state = plugin_state.read().unwrap();
687    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
688    plugin_state.leak_manager.free_leaked_memory();
689    let handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
690    let video_track = {
691        *handle
692            .current_video_track
693            .get()
694            .expect("unreachable: func_set_track should have been called before func_time_to_frame")
695    };
696    let plugin = &plugin_state.instance;
697    match T::time_to_frame(plugin, &mut handle.handle, video_track, time) {
698        Ok(frame) => frame as i32,
699        Err(e) => {
700            tracing::error!("Error during func_time_to_frame: {}", e);
701            0
702        }
703    }
704}
705extern "C" fn func_time_to_frame_unwind<T: InputSingleton>(
706    ih: aviutl2_sys::input2::INPUT_HANDLE,
707    time: f64,
708) -> i32 {
709    match crate::utils::catch_unwind_with_panic_info(|| func_time_to_frame::<T>(ih, time)) {
710        Ok(result) => result,
711        Err(panic_info) => {
712            tracing::error!("Panic occurred during func_time_to_frame: {}", panic_info);
713            let _ = crate::logger::write_error_log(&panic_info);
714            0
715        }
716    }
717}
718
719pub trait InputSingleton
720where
721    Self: 'static + Send + Sync + crate::input::InputPlugin,
722{
723    fn __get_singleton_state()
724    -> &'static std::sync::RwLock<Option<crate::input::__bridge::InternalInputPluginState<Self>>>;
725    fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R {
726        let lock = Self::__get_singleton_state();
727        let guard = lock.read().unwrap();
728        let state = guard.as_ref().expect("Plugin not initialized");
729        f(&state.instance)
730    }
731    fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R {
732        let lock = Self::__get_singleton_state();
733        let mut guard = lock.write().unwrap();
734        let state = guard.as_mut().expect("Plugin not initialized");
735        f(&mut state.instance)
736    }
737}
738
739/// 入力プラグインを登録するマクロ。
740///
741/// # Example
742///
743/// ```rs
744/// # use aviutl2::register_input_plugin;
745/// # struct MyInputPlugin;
746/// # impl aviutl2::input::InputPlugin for MyInputPlugin {
747/// #     type InputHandle = ();
748/// #     fn new(info: aviutl2::common::AviUtl2Info) -> aviutl2::common::AnyResult<Self> {
749/// #         let _ = info;
750/// #         unimplemented!()
751/// #     }
752/// #     fn plugin_info(&self) -> aviutl2::input::InputPluginTable {
753/// #         unimplemented!()
754/// #     }
755/// #     fn open(&self, path: std::path::PathBuf) -> aviutl2::common::AnyResult<Self::InputHandle> {
756/// #         let _ = path;
757/// #         unimplemented!()
758/// #     }
759/// #     fn close(&self, handle: Self::InputHandle) -> aviutl2::common::AnyResult<()> {
760/// #         let _ = handle;
761/// #         unimplemented!()
762/// #     }
763/// # }
764/// aviutl2::register_input_plugin!(MyInputPlugin, unwind = false);
765/// ```
766///
767/// # Arguments
768///
769/// - `unwind`: panic時にunwindするかどうか。デフォルトは`true`。
770#[macro_export]
771macro_rules! register_input_plugin {
772    ($struct:ident, $($key:ident = $value:expr),* $(,)?) => {
773        $crate::__internal_module! {
774            #[unsafe(no_mangle)]
775            unsafe extern "C" fn RequiredVersion() -> u32 {
776                $crate::MINIMUM_AVIUTL2_VERSION.into()
777            }
778
779            #[unsafe(no_mangle)]
780            unsafe extern "C" fn InitializeLogger(logger: *mut $crate::sys::logger2::LOG_HANDLE) {
781                $crate::comptime_if::comptime_if! {
782                    if unwind where (unwind = true, $( $key = $value ),* ) {
783                        $crate::logger::__initialize_logger_unwind(logger)
784                    } else {
785                        $crate::logger::__initialize_logger(logger)
786                    }
787                }
788            }
789
790            #[unsafe(no_mangle)]
791            unsafe extern "C" fn InitializeConfig(
792                config: *mut $crate::sys::config2::CONFIG_HANDLE
793            ) {
794                $crate::comptime_if::comptime_if! {
795                    if unwind where (unwind = true, $( $key = $value ),* ) {
796                        $crate::config::__initialize_config_handle_unwind(config)
797                    } else {
798                        $crate::config::__initialize_config_handle(config)
799                    }
800                }
801            }
802
803            #[unsafe(no_mangle)]
804            unsafe extern "C" fn InitializePlugin(version: u32) -> bool {
805                unsafe {
806                    $crate::comptime_if::comptime_if! {
807                        if unwind where (unwind = true, $( $key = $value ),* ) {
808                            $crate::input::__bridge::initialize_plugin_c_unwind::<$struct>(version)
809                        } else {
810                            $crate::input::__bridge::initialize_plugin_c::<$struct>(version)
811                        }
812                    }
813                }
814            }
815
816            #[unsafe(no_mangle)]
817            unsafe extern "C" fn UninitializePlugin() {
818                unsafe {
819                    $crate::comptime_if::comptime_if! {
820                        if unwind where (unwind = true, $( $key = $value ),* ) {
821                            $crate::input::__bridge::uninitialize_plugin_c_unwind::<$struct>()
822                        } else {
823                            $crate::input::__bridge::uninitialize_plugin::<$struct>()
824                        }
825                    }
826                }
827            }
828
829            #[unsafe(no_mangle)]
830            unsafe extern "C" fn GetInputPluginTable()
831            -> *mut aviutl2::sys::input2::INPUT_PLUGIN_TABLE {
832                $crate::comptime_if::comptime_if! {
833                    if unwind where (unwind = true, $( $key = $value ),* ) {
834                        unsafe { $crate::input::__bridge::create_table_unwind::<$struct>() }
835                    } else {
836                        unsafe { $crate::input::__bridge::create_table::<$struct>() }
837                    }
838                }
839            }
840        }
841    };
842    ($struct:ident, $($key:ident),* $(,)?) => {
843        $crate::register_input_plugin!($struct, $( $key = true ),* );
844    };
845    ($struct:ident) => {
846        $crate::register_input_plugin!($struct, );
847    };
848}