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 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#[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}