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