use std::num::NonZeroIsize;
use crate::{
common::{AnyResult, LeakManager, format_file_filters, load_wide_string},
input::{
AudioFormat, AudioInputInfo, AudioReturner, ImageReturner, InputInfo, InputPixelFormat,
InputPlugin, InputPluginTable, VideoInputInfo,
},
};
impl InputPixelFormat {
fn bytes_count_per_pixel(&self) -> usize {
match self {
InputPixelFormat::Bgr => 3, InputPixelFormat::Bgra => 4, InputPixelFormat::Yuy2 => 2, InputPixelFormat::Pa64 => 8, InputPixelFormat::Hf64 => 8, InputPixelFormat::Yc48 => 6, }
}
}
impl AudioFormat {
fn bytes_per_sample(&self) -> usize {
match self {
AudioFormat::IeeeFloat32 => 4, AudioFormat::Pcm16 => 2, }
}
}
fn audio_sample_count(written: usize, block_align: usize) -> i32 {
assert_ne!(block_align, 0, "Audio block alignment must not be zero");
assert_eq!(
written % block_align,
0,
"Audio data size must be aligned to the sample frame size"
);
i32::try_from(written / block_align).expect("Audio sample count overflow")
}
impl VideoInputInfo {
fn into_raw(self) -> aviutl2_sys::input2::BITMAPINFOHEADER {
let bi_compression = match self.format {
InputPixelFormat::Bgr | InputPixelFormat::Bgra => aviutl2_sys::common::BI_RGB,
InputPixelFormat::Yuy2 => aviutl2_sys::common::BI_YUY2,
InputPixelFormat::Pa64 => aviutl2_sys::common::BI_PA64,
InputPixelFormat::Hf64 => aviutl2_sys::common::BI_HF64,
InputPixelFormat::Yc48 => aviutl2_sys::common::BI_YC48,
};
aviutl2_sys::input2::BITMAPINFOHEADER {
biSize: std::mem::size_of::<aviutl2_sys::input2::BITMAPINFOHEADER>() as u32,
biWidth: self.width as i32,
biHeight: self.height as i32,
biPlanes: 1,
biBitCount: (self.format.bytes_count_per_pixel() * 8) as u16, biCompression: bi_compression,
biSizeImage: (self.width * self.height * self.format.bytes_count_per_pixel() as u32),
biXPelsPerMeter: 0, biYPelsPerMeter: 0, biClrUsed: 0, biClrImportant: 0, }
}
}
impl AudioInputInfo {
fn into_raw(self) -> aviutl2_sys::input2::WAVEFORMATEX {
let format = match self.format {
AudioFormat::IeeeFloat32 => aviutl2_sys::common::WAVE_FORMAT_IEEE_FLOAT,
AudioFormat::Pcm16 => aviutl2_sys::common::WAVE_FORMAT_PCM,
};
let bytes_per_sample = self.format.bytes_per_sample();
aviutl2_sys::input2::WAVEFORMATEX {
wFormatTag: format as u16,
nChannels: self.channels,
nSamplesPerSec: self.sample_rate,
nAvgBytesPerSec: (self.sample_rate
* (self.channels as u32)
* (bytes_per_sample as u32)),
nBlockAlign: (self.channels * bytes_per_sample as u16),
wBitsPerSample: u16::try_from(bytes_per_sample * 8usize)
.expect("Invalid bits per sample"),
cbSize: 0, }
}
}
#[doc(hidden)]
pub struct InternalInputPluginState<T: Send + Sync + InputPlugin> {
plugin_info: InputPluginTable,
global_leak_manager: LeakManager,
leak_manager: LeakManager,
instance: T,
}
impl<T: Send + Sync + InputPlugin> InternalInputPluginState<T> {
pub fn new(instance: T) -> Self {
let plugin_info = instance.plugin_info();
Self {
plugin_info,
global_leak_manager: LeakManager::new(),
leak_manager: LeakManager::new(),
instance,
}
}
}
struct InternalInputHandle<T: Send + Sync> {
input_info: Option<InputInfo>,
num_tracks: std::sync::Mutex<Option<AnyResult<(u32, u32)>>>,
current_video_track: std::sync::OnceLock<u32>,
current_audio_track: std::sync::OnceLock<u32>,
handle: T,
}
pub unsafe fn initialize_plugin_c<T: InputSingleton>(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: InputSingleton>(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: InputSingleton>(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 = InternalInputPluginState::new(internal);
*plugin_state.write().unwrap() = Some(plugin);
Ok(())
}
pub unsafe fn uninitialize_plugin<T: InputSingleton>() {
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: InputSingleton>() {
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: InputSingleton>(
unwind: bool,
) -> *mut aviutl2_sys::input2::INPUT_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");
let plugin_info = &plugin_state.plugin_info;
let file_filter = format_file_filters(&plugin_info.file_filters);
let name = plugin_info.name.clone();
let information = plugin_info.information.clone();
let mut flag = plugin_info.input_type.to_bits();
if plugin_info.concurrent {
flag |= aviutl2_sys::input2::INPUT_PLUGIN_TABLE::FLAG_CONCURRENT;
}
flag |= aviutl2_sys::input2::INPUT_PLUGIN_TABLE::FLAG_MULTI_TRACK;
let func_open = if unwind {
func_open_unwind::<T>
} else {
func_open::<T>
};
let func_close = if unwind {
func_close_unwind::<T>
} else {
func_close::<T>
};
let func_info_get = if unwind {
func_info_get_unwind::<T>
} else {
func_info_get::<T>
};
let func_read_video = if unwind {
func_read_video_unwind::<T>
} else {
func_read_video::<T>
};
let func_read_audio = if unwind {
func_read_audio_unwind::<T>
} else {
func_read_audio::<T>
};
let func_config = if unwind {
func_config_unwind::<T>
} else {
func_config::<T>
};
let func_set_track = if unwind {
func_set_track_unwind::<T>
} else {
func_set_track::<T>
};
let func_time_to_frame = if unwind {
func_time_to_frame_unwind::<T>
} else {
func_time_to_frame::<T>
};
let table = aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
flag,
name: plugin_state.global_leak_manager.leak_as_wide_string(&name),
filefilter: plugin_state
.global_leak_manager
.leak_as_wide_string(&file_filter),
information: plugin_state
.global_leak_manager
.leak_as_wide_string(&information),
func_open: Some(func_open),
func_close: Some(func_close),
func_info_get: Some(func_info_get),
func_read_video: Some(func_read_video),
func_read_audio: Some(func_read_audio),
func_config: plugin_info.can_config.then_some(func_config),
func_set_track: Some(func_set_track),
func_time_to_frame: Some(func_time_to_frame),
};
let table = Box::new(table);
Box::leak(table)
}
pub unsafe fn create_table<T: InputSingleton>() -> *mut aviutl2_sys::input2::INPUT_PLUGIN_TABLE {
create_table_impl::<T>(false)
}
pub unsafe fn create_table_unwind<T: InputSingleton>()
-> *mut aviutl2_sys::input2::INPUT_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_open<T: InputSingleton>(
file: aviutl2_sys::common::LPCWSTR,
) -> aviutl2_sys::input2::INPUT_HANDLE {
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 path = unsafe { load_wide_string(file) };
tracing::info!("func_open called with path: {}", path);
let plugin = &plugin_state.instance;
match plugin.open(std::path::PathBuf::from(path)) {
Ok(handle) => {
let boxed_handle: Box<InternalInputHandle<T::InputHandle>> =
Box::new(InternalInputHandle {
input_info: None,
num_tracks: std::sync::Mutex::new(None),
current_video_track: std::sync::OnceLock::new(),
current_audio_track: std::sync::OnceLock::new(),
handle,
});
Box::into_raw(boxed_handle) as aviutl2_sys::input2::INPUT_HANDLE
}
Err(e) => {
tracing::error!("Error during func_open: {}", e);
std::ptr::null_mut()
}
}
}
extern "C" fn func_open_unwind<T: InputSingleton>(
file: aviutl2_sys::common::LPCWSTR,
) -> aviutl2_sys::input2::INPUT_HANDLE {
match crate::utils::catch_unwind_with_panic_info(|| func_open::<T>(file)) {
Ok(handle) => handle,
Err(panic_info) => {
tracing::error!("Panic occurred during func_open: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
std::ptr::null_mut()
}
}
}
extern "C" fn func_close<T: InputSingleton>(ih: aviutl2_sys::input2::INPUT_HANDLE) -> 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 handle = unsafe { Box::from_raw(ih as *mut InternalInputHandle<T::InputHandle>) };
let plugin = &plugin_state.instance;
match plugin.close(handle.handle) {
Ok(()) => true,
Err(e) => {
tracing::error!("Error during func_close: {}", e);
false
}
}
}
extern "C" fn func_close_unwind<T: InputSingleton>(ih: aviutl2_sys::input2::INPUT_HANDLE) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_close::<T>(ih)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_close: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
extern "C" fn func_info_get<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
iip: *mut aviutl2_sys::input2::INPUT_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 handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
let video_track = {
*handle
.current_video_track
.get()
.expect("unreachable: func_set_track should have been called before func_info_get")
};
let audio_track = {
*handle
.current_audio_track
.get()
.expect("unreachable: func_set_track should have been called before func_info_get")
};
let plugin = &plugin_state.instance;
match T::get_input_info(plugin, &mut handle.handle, video_track, audio_track) {
Ok(info) => {
handle.input_info = Some(info.clone());
if let Some(video_info) = info.video {
let fps = video_info.fps;
let num_frames = video_info.num_frames;
let manual_frame_index = video_info.manual_frame_index;
let width = video_info.width;
let height = video_info.height;
let image_format = video_info.into_raw();
unsafe {
(*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_VIDEO;
if manual_frame_index {
(*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_TIME_TO_FRAME;
}
(*iip).rate = *fps.numer();
(*iip).scale = *fps.denom();
(*iip).n = num_frames as _;
(*iip).format = plugin_state.leak_manager.leak(image_format);
(*iip).format_size = (4 * width * height) as i32; (*iip).audio_n = 0;
(*iip).audio_format = std::ptr::null_mut();
(*iip).audio_format_size = 0;
}
}
if let Some(audio_info) = info.audio {
let num_samples = audio_info.num_samples;
let audio_format = audio_info.into_raw();
let audio_format_size = std::mem::size_of_val(&audio_format) as i32;
unsafe {
(*iip).flag |= aviutl2_sys::input2::INPUT_INFO::FLAG_AUDIO;
(*iip).audio_n = num_samples as _;
(*iip).audio_format = plugin_state.leak_manager.leak(audio_format);
(*iip).audio_format_size = audio_format_size;
}
}
true
}
Err(e) => {
tracing::error!("Error during func_info_get: {}", e);
false
}
}
}
extern "C" fn func_info_get_unwind<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
iip: *mut aviutl2_sys::input2::INPUT_INFO,
) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| func_info_get::<T>(ih, iip)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_info_get: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
extern "C" fn func_read_video<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
frame: i32,
buf: *mut std::ffi::c_void,
) -> i32 {
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 handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
let plugin = &plugin_state.instance;
let frame = frame as u32;
let output_size = {
let video_format = handle
.input_info
.as_ref()
.expect("Unreachable: Input info not set")
.video
.as_ref()
.expect("Unreachable: Video format not set");
(video_format.width as usize)
.checked_mul(video_format.height as usize)
.and_then(|size| size.checked_mul(video_format.format.bytes_count_per_pixel()))
.expect("Video output buffer size overflow")
};
let mut returner = unsafe { ImageReturner::new(buf as *mut u8, output_size) };
let read_result = if plugin_state.plugin_info.concurrent {
T::read_video(plugin, &handle.handle, frame, &mut returner)
} else {
T::read_video_mut(plugin, &mut handle.handle, frame, &mut returner)
};
match read_result {
Ok(()) => {
#[cfg(debug_assertions)]
{
assert_eq!(
returner.written, output_size,
"Image data size does not match expected size"
);
}
returner.written as i32
}
Err(e) => {
tracing::error!("Error during func_read_video: {}", e);
0
}
}
}
extern "C" fn func_read_video_unwind<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
frame: i32,
buf: *mut std::ffi::c_void,
) -> i32 {
match crate::utils::catch_unwind_with_panic_info(|| func_read_video::<T>(ih, frame, buf)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_read_video: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
0
}
}
}
extern "C" fn func_read_audio<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
start: i32,
length: i32,
buf: *mut std::ffi::c_void,
) -> i32 {
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 handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
let plugin = &plugin_state.instance;
let (output_size, block_align) = {
let audio_format = handle
.input_info
.as_ref()
.expect("Unreachable: Input info not set")
.audio
.as_ref()
.expect("Unreachable: Audio format not set");
let block_align = (audio_format.channels as usize)
.checked_mul(audio_format.format.bytes_per_sample())
.expect("Audio block alignment overflow");
assert_ne!(block_align, 0, "Audio block alignment must not be zero");
let output_size = usize::try_from(length)
.expect("Audio read length must not be negative")
.checked_mul(block_align)
.expect("Audio output buffer size overflow");
(output_size, block_align)
};
let mut returner = unsafe { AudioReturner::new(buf as *mut u8, output_size) };
let read_result = if plugin_state.plugin_info.concurrent {
T::read_audio(plugin, &handle.handle, start, length, &mut returner)
} else {
T::read_audio_mut(plugin, &mut handle.handle, start, length, &mut returner)
};
match read_result {
Ok(()) => audio_sample_count(returner.written, block_align),
Err(e) => {
tracing::error!("Error during func_read_audio: {}", e);
0
}
}
}
extern "C" fn func_read_audio_unwind<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
start: i32,
length: i32,
buf: *mut std::ffi::c_void,
) -> i32 {
match crate::utils::catch_unwind_with_panic_info(|| {
func_read_audio::<T>(ih, start, length, buf)
}) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_read_audio: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
0
}
}
}
extern "C" fn func_config<T: InputSingleton>(
hwnd: aviutl2_sys::input2::HWND,
dll_hinst: aviutl2_sys::input2::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 mut handle =
raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
handle.hinstance = Some(NonZeroIsize::new(dll_hinst as isize).unwrap());
let plugin = &plugin_state.instance;
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: InputSingleton>(
hwnd: aviutl2_sys::input2::HWND,
dll_hinst: aviutl2_sys::input2::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_set_track<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
track_type: i32,
track: i32,
) -> i32 {
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 handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
let plugin = &plugin_state.instance;
if track == -1 {
if handle.num_tracks.lock().unwrap().is_none() {
let num_tracks = plugin.get_track_count(&mut handle.handle).map_err(|e| {
tracing::error!("Failed to get track count: {}", e);
e
});
if matches!(num_tracks, Ok((0, _))) {
handle
.current_video_track
.set(0)
.expect("unreachable: func_set_track should only be called once per handle");
}
if matches!(num_tracks, Ok((_, 0))) {
handle
.current_audio_track
.set(0)
.expect("unreachable: func_set_track should only be called once per handle");
}
*handle.num_tracks.lock().unwrap() = Some(num_tracks);
}
match &*handle.num_tracks.lock().unwrap() {
Some(Ok((video_tracks, audio_tracks))) => {
if track_type == aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_VIDEO {
*video_tracks as i32
} else if track_type == aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_AUDIO {
*audio_tracks as i32
} else {
tracing::error!("Invalid track type: {}", track_type);
-1 }
}
Some(Err(e)) => {
tracing::error!("Error occurred while getting track count: {}", e);
-1 }
None => {
unreachable!("Track count should have been initialized before this point");
}
}
} else {
match track_type {
aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_VIDEO => {
let new_track = plugin
.can_set_video_track(&mut handle.handle, track as u32)
.map_or_else(
|e| {
tracing::debug!("Failed to set video track: {}", e);
-1
},
|t| t as i32,
);
handle
.current_video_track
.set(new_track as u32)
.expect("unreachable: func_set_track should only be called once per handle");
new_track
}
aviutl2_sys::input2::INPUT_PLUGIN_TABLE::TRACK_TYPE_AUDIO => {
let new_track = plugin
.can_set_audio_track(&mut handle.handle, track as u32)
.map_or_else(
|e| {
tracing::debug!("Failed to set audio track: {}", e);
-1
},
|t| t as i32,
);
handle
.current_audio_track
.set(new_track as u32)
.expect("unreachable: func_set_track should only be called once per handle");
new_track
}
_ => -1, }
}
}
extern "C" fn func_set_track_unwind<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
track_type: i32,
track: i32,
) -> i32 {
match crate::utils::catch_unwind_with_panic_info(|| func_set_track::<T>(ih, track_type, track))
{
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_set_track: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
-1
}
}
}
extern "C" fn func_time_to_frame<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
time: f64,
) -> i32 {
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 handle = unsafe { &mut *(ih as *mut InternalInputHandle<T::InputHandle>) };
let video_track = {
*handle
.current_video_track
.get()
.expect("unreachable: func_set_track should have been called before func_time_to_frame")
};
let plugin = &plugin_state.instance;
match T::time_to_frame(plugin, &mut handle.handle, video_track, time) {
Ok(frame) => frame as i32,
Err(e) => {
tracing::error!("Error during func_time_to_frame: {}", e);
0
}
}
}
extern "C" fn func_time_to_frame_unwind<T: InputSingleton>(
ih: aviutl2_sys::input2::INPUT_HANDLE,
time: f64,
) -> i32 {
match crate::utils::catch_unwind_with_panic_info(|| func_time_to_frame::<T>(ih, time)) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!("Panic occurred during func_time_to_frame: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
0
}
}
}
pub trait InputSingleton
where
Self: 'static + Send + Sync + crate::input::InputPlugin,
{
fn __get_singleton_state()
-> &'static std::sync::RwLock<Option<crate::input::__bridge::InternalInputPluginState<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_input_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::input::__bridge::initialize_plugin_c_unwind::<$struct>(version)
} else {
$crate::input::__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::input::__bridge::uninitialize_plugin_c_unwind::<$struct>()
} else {
$crate::input::__bridge::uninitialize_plugin::<$struct>()
}
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn GetInputPluginTable()
-> *mut aviutl2::sys::input2::INPUT_PLUGIN_TABLE {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
unsafe { $crate::input::__bridge::create_table_unwind::<$struct>() }
} else {
unsafe { $crate::input::__bridge::create_table::<$struct>() }
}
}
}
}
};
($struct:ident, $($key:ident),* $(,)?) => {
$crate::register_input_plugin!($struct, $( $key = true ),* );
};
($struct:ident) => {
$crate::register_input_plugin!($struct, );
};
}
#[cfg(test)]
mod tests {
use super::audio_sample_count;
#[test]
fn audio_sample_count_converts_bytes_to_sample_frames() {
assert_eq!(audio_sample_count(8192, 8), 1024);
}
#[test]
fn audio_sample_count_accepts_partial_reads() {
assert_eq!(audio_sample_count(400, 8), 50);
}
#[test]
#[should_panic(expected = "Audio data size must be aligned to the sample frame size")]
fn audio_sample_count_rejects_incomplete_sample_frames() {
audio_sample_count(7, 8);
}
}