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, InputPixelFormat::Bgra => 4, InputPixelFormat::Yuy2 => 2, InputPixelFormat::Pa64 => 8, InputPixelFormat::Hf64 => 8, InputPixelFormat::Yc48 => 6, }
21 }
22}
23
24impl AudioFormat {
25 fn bytes_per_sample(&self) -> usize {
26 match self {
27 AudioFormat::IeeeFloat32 => 4, AudioFormat::Pcm16 => 2, }
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 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, biCompression: bi_compression,
75 biSizeImage: u32::try_from(self.buffer_size()).expect("Video image size overflow"),
76 biXPelsPerMeter: 0, biYPelsPerMeter: 0, biClrUsed: 0, biClrImportant: 0, }
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, }
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 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; (*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 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 }
641 }
642 Some(Err(e)) => {
643 tracing::error!("Error occurred while getting track count: {}", e);
644 -1 }
646 None => {
647 unreachable!("Track count should have been initialized before this point");
648 }
649 }
650 } else {
651 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, }
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#[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}