Skip to main content

aviutl2\output/
bridge.rs

1use std::num::NonZeroIsize;
2
3use crate::{
4    common::{AnyResult, LeakManager, format_file_filters},
5    output::{FromRawAudioSamples, OutputInfo, OutputPlugin},
6};
7
8use aviutl2_sys::common::{WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_PCM};
9
10pub struct InternalOutputPluginState<T: Send + Sync + OutputPlugin> {
11    leak_manager: LeakManager,
12    global_leak_manager: LeakManager,
13
14    instance: T,
15}
16
17impl<T: Send + Sync + OutputPlugin> InternalOutputPluginState<T> {
18    pub fn new(instance: T) -> Self {
19        Self {
20            leak_manager: LeakManager::new(),
21            global_leak_manager: LeakManager::new(),
22            instance,
23        }
24    }
25}
26
27impl FromRawAudioSamples for f32 {
28    const FORMAT: u32 = WAVE_FORMAT_IEEE_FLOAT;
29
30    unsafe fn from_raw(length: i32, num_channels: u32, frame_data_ptr: *const u8) -> Vec<Self> {
31        let frame_data_slice = unsafe {
32            std::slice::from_raw_parts(
33                frame_data_ptr as *const f32,
34                length as usize * num_channels as usize,
35            )
36        };
37        frame_data_slice.to_vec()
38    }
39}
40impl FromRawAudioSamples for i16 {
41    const FORMAT: u32 = WAVE_FORMAT_PCM;
42
43    unsafe fn from_raw(length: i32, num_channels: u32, frame_data_ptr: *const u8) -> Vec<Self> {
44        let frame_data_slice = unsafe {
45            std::slice::from_raw_parts(
46                frame_data_ptr as *const i16,
47                length as usize * num_channels as usize,
48            )
49        };
50        frame_data_slice.to_vec()
51    }
52}
53
54pub unsafe fn initialize_plugin_c<T: OutputSingleton>(version: u32) -> bool {
55    match initialize_plugin::<T>(version) {
56        Ok(_) => true,
57        Err(e) => {
58            tracing::error!("Failed to initialize plugin: {}", e);
59            let _ = crate::logger::write_error_log(&format!("{e}"));
60            false
61        }
62    }
63}
64
65pub unsafe fn initialize_plugin_c_unwind<T: OutputSingleton>(version: u32) -> bool {
66    match crate::utils::catch_unwind_with_panic_info(|| unsafe {
67        initialize_plugin_c::<T>(version)
68    }) {
69        Ok(result) => result,
70        Err(panic_info) => {
71            tracing::error!(
72                "Panic occurred during plugin initialization: {}",
73                panic_info
74            );
75            let _ = crate::logger::write_error_log(&panic_info);
76            false
77        }
78    }
79}
80
81pub(crate) fn initialize_plugin<T: OutputSingleton>(version: u32) -> AnyResult<()> {
82    crate::common::ensure_minimum_aviutl2_version(version.into())?;
83    let plugin_state = T::__get_singleton_state();
84    let info = crate::common::AviUtl2Info {
85        version: version.into(),
86    };
87    let internal = T::new(info)?;
88    let plugin = InternalOutputPluginState::new(internal);
89    *plugin_state.write().unwrap() = Some(plugin);
90
91    Ok(())
92}
93
94pub unsafe fn uninitialize_plugin<T: OutputSingleton>() {
95    let plugin_state = T::__get_singleton_state();
96    let mut plugin_state = plugin_state.write().unwrap();
97    *plugin_state = None;
98}
99
100pub unsafe fn uninitialize_plugin_c_unwind<T: OutputSingleton>() {
101    match crate::utils::catch_unwind_with_panic_info(|| unsafe { uninitialize_plugin::<T>() }) {
102        Ok(()) => {}
103        Err(panic_info) => {
104            tracing::error!(
105                "Panic occurred during plugin uninitialization: {}",
106                panic_info
107            );
108            let _ = crate::logger::write_error_log(&panic_info);
109        }
110    }
111}
112
113fn create_table_impl<T: OutputSingleton>(
114    unwind: bool,
115) -> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
116    let plugin_state = T::__get_singleton_state();
117    let mut plugin_state = plugin_state.write().unwrap();
118    let plugin_state = plugin_state.as_mut().expect("Plugin not initialized");
119    tracing::info!("Creating OUTPUT_PLUGIN_TABLE");
120    plugin_state.leak_manager.free_leaked_memory();
121    let plugin = &plugin_state.instance;
122    let plugin_info = plugin.plugin_info();
123    let filefilter = format_file_filters(&plugin_info.file_filters);
124
125    let name = plugin_info.name.clone();
126    let information = plugin_info.information.clone();
127
128    let func_output = if unwind {
129        func_output_unwind::<T>
130    } else {
131        func_output::<T>
132    };
133    let func_config = if unwind {
134        func_config_unwind::<T>
135    } else {
136        func_config::<T>
137    };
138    let func_get_config_text = if unwind {
139        func_get_config_text_unwind::<T>
140    } else {
141        func_get_config_text::<T>
142    };
143    let func_load_project_config = if unwind {
144        func_load_project_config_unwind::<T>
145    } else {
146        func_load_project_config::<T>
147    };
148    let func_save_project_config = if unwind {
149        func_save_project_config_unwind::<T>
150    } else {
151        func_save_project_config::<T>
152    };
153
154    // NOTE: プラグイン名などの文字列はAviUtlが終了するまで解放しない
155    let table = aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
156        flag: plugin_info.output_type.to_bits()
157            | if plugin_info.project_config {
158                aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE::FLAG_PROJECT_CONFIG
159            } else {
160                0
161            },
162        name: plugin_state.global_leak_manager.leak_as_wide_string(&name),
163        filefilter: plugin_state
164            .global_leak_manager
165            .leak_as_wide_string(&filefilter),
166        information: plugin_state
167            .global_leak_manager
168            .leak_as_wide_string(&information),
169        func_output: Some(func_output),
170        func_config: plugin_info.can_config.then_some(func_config),
171        func_get_config_text: Some(func_get_config_text),
172        func_load_project_config: plugin_info
173            .project_config
174            .then_some(func_load_project_config),
175        func_save_project_config: plugin_info
176            .project_config
177            .then_some(func_save_project_config),
178    };
179    let table = Box::new(table);
180    Box::leak(table)
181}
182
183pub unsafe fn create_table<T: OutputSingleton>() -> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
184    create_table_impl::<T>(false)
185}
186
187pub unsafe fn create_table_unwind<T: OutputSingleton>()
188-> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
189    match crate::utils::catch_unwind_with_panic_info(|| create_table_impl::<T>(true)) {
190        Ok(table) => table,
191        Err(panic_info) => {
192            tracing::error!("Panic occurred during create_table: {}", panic_info);
193            let _ = crate::logger::write_error_log(&panic_info);
194            std::ptr::null_mut()
195        }
196    }
197}
198
199extern "C" fn func_output<T: OutputSingleton>(oip: *mut aviutl2_sys::output2::OUTPUT_INFO) -> bool {
200    let plugin_state = T::__get_singleton_state();
201    let plugin_state = plugin_state.read().unwrap();
202    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
203    plugin_state.leak_manager.free_leaked_memory();
204    let plugin = &plugin_state.instance;
205    let oip = unsafe { &mut *oip };
206    let output_info = OutputInfo::from_raw(oip);
207    match plugin.output(output_info) {
208        Ok(()) => true,
209        Err(e) => {
210            tracing::error!("Error during func_output: {}", e);
211            let _ = crate::logger::write_error_log(&format!("{e}"));
212            false
213        }
214    }
215}
216extern "C" fn func_output_unwind<T: OutputSingleton>(
217    oip: *mut aviutl2_sys::output2::OUTPUT_INFO,
218) -> bool {
219    match crate::utils::catch_unwind_with_panic_info(|| func_output::<T>(oip)) {
220        Ok(result) => result,
221        Err(panic_info) => {
222            tracing::error!("Panic occurred during func_output: {}", panic_info);
223            let _ = crate::logger::write_error_log(&panic_info);
224            false
225        }
226    }
227}
228
229extern "C" fn func_config<T: OutputSingleton>(
230    hwnd: aviutl2_sys::output2::HWND,
231    dll_hinst: aviutl2_sys::output2::HINSTANCE,
232) -> bool {
233    let plugin_state = T::__get_singleton_state();
234    let plugin_state = plugin_state.read().unwrap();
235    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
236    plugin_state.leak_manager.free_leaked_memory();
237    let plugin = &plugin_state.instance;
238    let mut handle =
239        raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
240    handle.hinstance = Some(NonZeroIsize::new(dll_hinst as isize).unwrap());
241    match plugin.config(handle) {
242        Ok(()) => true,
243        Err(e) => {
244            tracing::error!("Error during func_config: {}", e);
245            let _ = crate::logger::write_error_log(&format!("{e}"));
246            false
247        }
248    }
249}
250extern "C" fn func_config_unwind<T: OutputSingleton>(
251    hwnd: aviutl2_sys::output2::HWND,
252    dll_hinst: aviutl2_sys::output2::HINSTANCE,
253) -> bool {
254    match crate::utils::catch_unwind_with_panic_info(|| func_config::<T>(hwnd, dll_hinst)) {
255        Ok(result) => result,
256        Err(panic_info) => {
257            tracing::error!("Panic occurred during func_config: {}", panic_info);
258            let _ = crate::logger::write_error_log(&panic_info);
259            false
260        }
261    }
262}
263
264extern "C" fn func_get_config_text<T: OutputSingleton>() -> *const u16 {
265    let plugin_state = T::__get_singleton_state();
266    let plugin_state = plugin_state.read().unwrap();
267    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
268    plugin_state.leak_manager.free_leaked_memory();
269    let plugin = &plugin_state.instance;
270    let text = plugin.config_text();
271    match text {
272        Ok(text) => plugin_state.leak_manager.leak_as_wide_string(&text),
273        Err(e) => {
274            tracing::error!("Error during func_get_config_text: {}", e);
275            plugin_state
276                .leak_manager
277                .leak_as_wide_string(format!("エラー:{}", e).as_str())
278        }
279    }
280}
281extern "C" fn func_get_config_text_unwind<T: OutputSingleton>() -> *const u16 {
282    match crate::utils::catch_unwind_with_panic_info(|| func_get_config_text::<T>()) {
283        Ok(text) => text,
284        Err(panic_info) => {
285            tracing::error!("Panic occurred during func_get_config_text: {}", panic_info);
286            let _ = crate::logger::write_error_log(&panic_info);
287            std::ptr::null()
288        }
289    }
290}
291
292extern "C" fn func_load_project_config<T: OutputSingleton>(
293    project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
294) -> bool {
295    let plugin_state = T::__get_singleton_state();
296    let plugin_state = plugin_state.read().unwrap();
297    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
298    plugin_state.leak_manager.free_leaked_memory();
299    let plugin = &plugin_state.instance;
300    let mut project = unsafe { crate::generic::ProjectFile::from_raw(project) };
301    match plugin.load_project_config(&mut project) {
302        Ok(()) => true,
303        Err(e) => {
304            tracing::error!("Error during func_load_project_config: {}", e);
305            let _ = crate::logger::write_error_log(&format!("{e}"));
306            false
307        }
308    }
309}
310
311extern "C" fn func_save_project_config<T: OutputSingleton>(
312    project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
313) -> bool {
314    let plugin_state = T::__get_singleton_state();
315    let plugin_state = plugin_state.read().unwrap();
316    let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
317    plugin_state.leak_manager.free_leaked_memory();
318    let plugin = &plugin_state.instance;
319    let mut project = unsafe { crate::generic::ProjectFile::from_raw(project) };
320    match plugin.save_project_config(&mut project) {
321        Ok(()) => true,
322        Err(e) => {
323            tracing::error!("Error during func_save_project_config: {}", e);
324            let _ = crate::logger::write_error_log(&format!("{e}"));
325            false
326        }
327    }
328}
329
330extern "C" fn func_load_project_config_unwind<T: OutputSingleton>(
331    project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
332) -> bool {
333    match crate::utils::catch_unwind_with_panic_info(|| func_load_project_config::<T>(project)) {
334        Ok(result) => result,
335        Err(panic_info) => {
336            tracing::error!(
337                "Panic occurred during func_load_project_config: {}",
338                panic_info
339            );
340            let _ = crate::logger::write_error_log(&panic_info);
341            false
342        }
343    }
344}
345
346extern "C" fn func_save_project_config_unwind<T: OutputSingleton>(
347    project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
348) -> bool {
349    match crate::utils::catch_unwind_with_panic_info(|| func_save_project_config::<T>(project)) {
350        Ok(result) => result,
351        Err(panic_info) => {
352            tracing::error!(
353                "Panic occurred during func_save_project_config: {}",
354                panic_info
355            );
356            let _ = crate::logger::write_error_log(&panic_info);
357            false
358        }
359    }
360}
361
362pub trait OutputSingleton
363where
364    Self: 'static + Send + Sync + crate::output::OutputPlugin,
365{
366    fn __get_singleton_state()
367    -> &'static std::sync::RwLock<Option<crate::output::__bridge::InternalOutputPluginState<Self>>>;
368    fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R {
369        let lock = Self::__get_singleton_state();
370        let guard = lock.read().unwrap();
371        let state = guard.as_ref().expect("Plugin not initialized");
372        f(&state.instance)
373    }
374    fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R {
375        let lock = Self::__get_singleton_state();
376        let mut guard = lock.write().unwrap();
377        let state = guard.as_mut().expect("Plugin not initialized");
378        f(&mut state.instance)
379    }
380}
381
382/// 出力プラグインを登録するマクロ。
383///
384/// # Arguments
385///
386/// - `unwind`: panic時にunwindするかどうか。デフォルトは`true`。
387#[macro_export]
388macro_rules! register_output_plugin {
389    ($struct:ident, $($key:ident = $value:expr),* $(,)?) => {
390        $crate::__internal_module! {
391            #[unsafe(no_mangle)]
392            unsafe extern "C" fn RequiredVersion() -> u32 {
393                $crate::MINIMUM_AVIUTL2_VERSION.into()
394            }
395
396            #[unsafe(no_mangle)]
397            unsafe extern "C" fn InitializeLogger(logger: *mut $crate::sys::logger2::LOG_HANDLE) {
398                $crate::comptime_if::comptime_if! {
399                    if unwind where (unwind = true, $( $key = $value ),* ) {
400                        $crate::logger::__initialize_logger_unwind(logger)
401                    } else {
402                        $crate::logger::__initialize_logger(logger)
403                    }
404                }
405            }
406
407            #[unsafe(no_mangle)]
408            unsafe extern "C" fn InitializeConfig(
409                config: *mut $crate::sys::config2::CONFIG_HANDLE
410            ) {
411                $crate::comptime_if::comptime_if! {
412                    if unwind where (unwind = true, $( $key = $value ),* ) {
413                        $crate::config::__initialize_config_handle_unwind(config)
414                    } else {
415                        $crate::config::__initialize_config_handle(config)
416                    }
417                }
418            }
419
420            #[unsafe(no_mangle)]
421            unsafe extern "C" fn InitializePlugin(version: u32) -> bool {
422                unsafe {
423                    $crate::comptime_if::comptime_if! {
424                        if unwind where (unwind = true, $( $key = $value ),* ) {
425                            $crate::output::__bridge::initialize_plugin_c_unwind::<$struct>(version)
426                        } else {
427                            $crate::output::__bridge::initialize_plugin_c::<$struct>(version)
428                        }
429                    }
430                }
431            }
432
433            #[unsafe(no_mangle)]
434            unsafe extern "C" fn UninitializePlugin() {
435                unsafe {
436                    $crate::comptime_if::comptime_if! {
437                        if unwind where (unwind = true, $( $key = $value ),* ) {
438                            $crate::output::__bridge::uninitialize_plugin_c_unwind::<$struct>()
439                        } else {
440                            $crate::output::__bridge::uninitialize_plugin::<$struct>()
441                        }
442                    }
443                }
444            }
445
446            #[unsafe(no_mangle)]
447            unsafe extern "C" fn GetOutputPluginTable()
448            -> *mut aviutl2::sys::output2::OUTPUT_PLUGIN_TABLE {
449                $crate::comptime_if::comptime_if! {
450                    if unwind where (unwind = true, $( $key = $value ),* ) {
451                        unsafe { $crate::output::__bridge::create_table_unwind::<$struct>() }
452                    } else {
453                        unsafe { $crate::output::__bridge::create_table::<$struct>() }
454                    }
455                }
456            }
457        }
458    };
459    ($struct:ident, $($key:ident),* $(,)?) => {
460        $crate::register_output_plugin!($struct, $( $key = true ),* );
461    };
462    ($struct:ident) => {
463        $crate::register_output_plugin!($struct, );
464    };
465}