use std::num::NonZeroIsize;
use crate::{
common::{AnyResult, LeakManager, format_file_filters},
output::{FromRawAudioSamples, OutputInfo, OutputPlugin},
};
use aviutl2_sys::common::{WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_PCM};
pub struct InternalOutputPluginState<T: Send + Sync + OutputPlugin> {
leak_manager: LeakManager,
global_leak_manager: LeakManager,
instance: T,
}
impl<T: Send + Sync + OutputPlugin> InternalOutputPluginState<T> {
pub fn new(instance: T) -> Self {
Self {
leak_manager: LeakManager::new(),
global_leak_manager: LeakManager::new(),
instance,
}
}
}
impl FromRawAudioSamples for f32 {
const FORMAT: u32 = WAVE_FORMAT_IEEE_FLOAT;
unsafe fn from_raw(length: i32, num_channels: u32, frame_data_ptr: *const u8) -> Vec<Self> {
let frame_data_slice = unsafe {
std::slice::from_raw_parts(
frame_data_ptr as *const f32,
length as usize * num_channels as usize,
)
};
frame_data_slice.to_vec()
}
}
impl FromRawAudioSamples for i16 {
const FORMAT: u32 = WAVE_FORMAT_PCM;
unsafe fn from_raw(length: i32, num_channels: u32, frame_data_ptr: *const u8) -> Vec<Self> {
let frame_data_slice = unsafe {
std::slice::from_raw_parts(
frame_data_ptr as *const i16,
length as usize * num_channels as usize,
)
};
frame_data_slice.to_vec()
}
}
pub unsafe fn initialize_plugin_c<T: OutputSingleton>(version: u32) -> bool {
match initialize_plugin::<T>(version) {
Ok(_) => true,
Err(e) => {
tracing::error!("Failed to initialize plugin: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
pub unsafe fn initialize_plugin_c_unwind<T: OutputSingleton>(version: u32) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| unsafe {
initialize_plugin_c::<T>(version)
}) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!(
"Panic occurred during plugin initialization: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
pub(crate) fn initialize_plugin<T: OutputSingleton>(version: u32) -> AnyResult<()> {
crate::common::ensure_minimum_aviutl2_version(version.into())?;
let plugin_state = T::__get_singleton_state();
let info = crate::common::AviUtl2Info {
version: version.into(),
};
let internal = T::new(info)?;
let plugin = InternalOutputPluginState::new(internal);
*plugin_state.write().unwrap() = Some(plugin);
Ok(())
}
pub unsafe fn uninitialize_plugin<T: OutputSingleton>() {
let plugin_state = T::__get_singleton_state();
let mut plugin_state = plugin_state.write().unwrap();
*plugin_state = None;
}
pub unsafe fn uninitialize_plugin_c_unwind<T: OutputSingleton>() {
match crate::utils::catch_unwind_with_panic_info(|| unsafe { uninitialize_plugin::<T>() }) {
Ok(()) => {}
Err(panic_info) => {
tracing::error!(
"Panic occurred during plugin uninitialization: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
}
}
}
fn create_table_impl<T: OutputSingleton>(
unwind: bool,
) -> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
let plugin_state = T::__get_singleton_state();
let mut plugin_state = plugin_state.write().unwrap();
let plugin_state = plugin_state.as_mut().expect("Plugin not initialized");
tracing::info!("Creating OUTPUT_PLUGIN_TABLE");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let plugin_info = plugin.plugin_info();
let filefilter = format_file_filters(&plugin_info.file_filters);
let name = plugin_info.name.clone();
let information = plugin_info.information.clone();
let func_output = if unwind {
func_output_unwind::<T>
} else {
func_output::<T>
};
let func_config = if unwind {
func_config_unwind::<T>
} else {
func_config::<T>
};
let func_get_config_text = if unwind {
func_get_config_text_unwind::<T>
} else {
func_get_config_text::<T>
};
let func_load_project_config = if unwind {
func_load_project_config_unwind::<T>
} else {
func_load_project_config::<T>
};
let func_save_project_config = if unwind {
func_save_project_config_unwind::<T>
} else {
func_save_project_config::<T>
};
let table = aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
flag: plugin_info.output_type.to_bits()
| if plugin_info.project_config {
aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE::FLAG_PROJECT_CONFIG
} else {
0
},
name: plugin_state.global_leak_manager.leak_as_wide_string(&name),
filefilter: plugin_state
.global_leak_manager
.leak_as_wide_string(&filefilter),
information: plugin_state
.global_leak_manager
.leak_as_wide_string(&information),
func_output: Some(func_output),
func_config: plugin_info.can_config.then_some(func_config),
func_get_config_text: Some(func_get_config_text),
func_load_project_config: plugin_info
.project_config
.then_some(func_load_project_config),
func_save_project_config: plugin_info
.project_config
.then_some(func_save_project_config),
};
let table = Box::new(table);
Box::leak(table)
}
pub unsafe fn create_table<T: OutputSingleton>() -> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
create_table_impl::<T>(false)
}
pub unsafe fn create_table_unwind<T: OutputSingleton>()
-> *mut aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE {
match crate::utils::catch_unwind_with_panic_info(|| create_table_impl::<T>(true)) {
Ok(table) => table,
Err(panic_info) => {
tracing::error!("Panic occurred during create_table: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
std::ptr::null_mut()
}
}
}
extern "C" fn func_output<T: OutputSingleton>(oip: *mut aviutl2_sys::output2::OUTPUT_INFO) -> bool {
let plugin_state = T::__get_singleton_state();
let plugin_state = plugin_state.read().unwrap();
let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let oip = unsafe { &mut *oip };
let output_info = OutputInfo::from_raw(oip);
match plugin.output(output_info) {
Ok(()) => true,
Err(e) => {
tracing::error!("Error during func_output: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
extern "C" fn func_output_unwind<T: OutputSingleton>(
oip: *mut aviutl2_sys::output2::OUTPUT_INFO,
) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_output::<T>(oip)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_output: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
extern "C" fn func_config<T: OutputSingleton>(
hwnd: aviutl2_sys::output2::HWND,
dll_hinst: aviutl2_sys::output2::HINSTANCE,
) -> bool {
let plugin_state = T::__get_singleton_state();
let plugin_state = plugin_state.read().unwrap();
let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let mut handle =
raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
handle.hinstance = Some(NonZeroIsize::new(dll_hinst as isize).unwrap());
match plugin.config(handle) {
Ok(()) => true,
Err(e) => {
tracing::error!("Error during func_config: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
extern "C" fn func_config_unwind<T: OutputSingleton>(
hwnd: aviutl2_sys::output2::HWND,
dll_hinst: aviutl2_sys::output2::HINSTANCE,
) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_config::<T>(hwnd, dll_hinst)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_config: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
extern "C" fn func_get_config_text<T: OutputSingleton>() -> *const u16 {
let plugin_state = T::__get_singleton_state();
let plugin_state = plugin_state.read().unwrap();
let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let text = plugin.config_text();
match text {
Ok(text) => plugin_state.leak_manager.leak_as_wide_string(&text),
Err(e) => {
tracing::error!("Error during func_get_config_text: {}", e);
plugin_state
.leak_manager
.leak_as_wide_string(format!("エラー:{}", e).as_str())
}
}
}
extern "C" fn func_get_config_text_unwind<T: OutputSingleton>() -> *const u16 {
match crate::utils::catch_unwind_with_panic_info(|| func_get_config_text::<T>()) {
Ok(text) => text,
Err(panic_info) => {
tracing::error!("Panic occurred during func_get_config_text: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
std::ptr::null()
}
}
}
extern "C" fn func_load_project_config<T: OutputSingleton>(
project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
) -> bool {
let plugin_state = T::__get_singleton_state();
let plugin_state = plugin_state.read().unwrap();
let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let mut project = unsafe { crate::generic::ProjectFile::from_raw(project) };
match plugin.load_project_config(&mut project) {
Ok(()) => true,
Err(e) => {
tracing::error!("Error during func_load_project_config: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
extern "C" fn func_save_project_config<T: OutputSingleton>(
project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
) -> bool {
let plugin_state = T::__get_singleton_state();
let plugin_state = plugin_state.read().unwrap();
let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
plugin_state.leak_manager.free_leaked_memory();
let plugin = &plugin_state.instance;
let mut project = unsafe { crate::generic::ProjectFile::from_raw(project) };
match plugin.save_project_config(&mut project) {
Ok(()) => true,
Err(e) => {
tracing::error!("Error during func_save_project_config: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
extern "C" fn func_load_project_config_unwind<T: OutputSingleton>(
project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_load_project_config::<T>(project)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!(
"Panic occurred during func_load_project_config: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
extern "C" fn func_save_project_config_unwind<T: OutputSingleton>(
project: *mut aviutl2_sys::plugin2::PROJECT_FILE,
) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_save_project_config::<T>(project)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!(
"Panic occurred during func_save_project_config: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
pub trait OutputSingleton
where
Self: 'static + Send + Sync + crate::output::OutputPlugin,
{
fn __get_singleton_state()
-> &'static std::sync::RwLock<Option<crate::output::__bridge::InternalOutputPluginState<Self>>>;
fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R {
let lock = Self::__get_singleton_state();
let guard = lock.read().unwrap();
let state = guard.as_ref().expect("Plugin not initialized");
f(&state.instance)
}
fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R {
let lock = Self::__get_singleton_state();
let mut guard = lock.write().unwrap();
let state = guard.as_mut().expect("Plugin not initialized");
f(&mut state.instance)
}
}
#[macro_export]
macro_rules! register_output_plugin {
($struct:ident, $($key:ident = $value:expr),* $(,)?) => {
$crate::__internal_module! {
#[unsafe(no_mangle)]
unsafe extern "C" fn RequiredVersion() -> u32 {
$crate::MINIMUM_AVIUTL2_VERSION.into()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeLogger(logger: *mut $crate::sys::logger2::LOG_HANDLE) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::logger::__initialize_logger_unwind(logger)
} else {
$crate::logger::__initialize_logger(logger)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeConfig(
config: *mut $crate::sys::config2::CONFIG_HANDLE
) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::config::__initialize_config_handle_unwind(config)
} else {
$crate::config::__initialize_config_handle(config)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeCache(
cache: *mut $crate::sys::cache2::CACHE_HANDLE
) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::cache::__initialize_cache_unwind(cache)
} else {
$crate::cache::__initialize_cache(cache)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializePlugin(version: u32) -> bool {
unsafe {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::output::__bridge::initialize_plugin_c_unwind::<$struct>(version)
} else {
$crate::output::__bridge::initialize_plugin_c::<$struct>(version)
}
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn UninitializePlugin() {
unsafe {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::output::__bridge::uninitialize_plugin_c_unwind::<$struct>()
} else {
$crate::output::__bridge::uninitialize_plugin::<$struct>()
}
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn GetOutputPluginTable()
-> *mut aviutl2::sys::output2::OUTPUT_PLUGIN_TABLE {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
unsafe { $crate::output::__bridge::create_table_unwind::<$struct>() }
} else {
unsafe { $crate::output::__bridge::create_table::<$struct>() }
}
}
}
}
};
($struct:ident, $($key:ident),* $(,)?) => {
$crate::register_output_plugin!($struct, $( $key = true ),* );
};
($struct:ident) => {
$crate::register_output_plugin!($struct, );
};
}