use std::borrow::Cow;
use crate::common::{FileFilter, Rational32, Yc48, f16};
use zerocopy::IntoBytes;
#[derive(Debug, Clone)]
pub struct InputPluginTable {
pub name: String,
pub information: String,
pub input_type: InputType,
pub concurrent: bool,
pub file_filters: Vec<FileFilter>,
pub can_config: bool,
}
#[derive(Debug, Clone)]
pub struct VideoInputInfo {
pub fps: Rational32,
pub num_frames: u32,
pub manual_frame_index: bool,
pub width: u32,
pub height: u32,
pub format: InputPixelFormat,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InputPixelFormat {
Bgr,
Bgra,
Yuy2,
Pa64,
Yc48,
Hf64,
}
#[derive(Debug, Clone)]
pub struct AudioInputInfo {
pub sample_rate: u32,
pub num_samples: u32,
pub channels: u16,
pub format: AudioFormat,
}
#[derive(Debug, Clone)]
pub enum AudioFormat {
Pcm16,
IeeeFloat32,
}
#[derive(Debug, Clone)]
pub struct InputInfo {
pub video: Option<VideoInputInfo>,
pub audio: Option<AudioInputInfo>,
}
#[derive(Debug, Clone)]
pub enum InputType {
Video,
Audio,
Both,
}
impl InputType {
pub(crate) fn to_bits(&self) -> i32 {
match self {
InputType::Video => 1,
InputType::Audio => 2,
InputType::Both => 3,
}
}
}
#[derive(Debug, Clone)]
pub struct ImageBuffer(pub Vec<u8>);
impl std::ops::Deref for ImageBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait IntoImage {
fn into_image(self) -> crate::input::ImageBuffer;
}
impl<T: AsImage> IntoImage for T {
fn into_image(self) -> ImageBuffer {
ImageBuffer(self.as_image().into_owned())
}
}
pub trait AsImage {
fn as_image(&'_ self) -> Cow<'_, [u8]>;
}
impl AsImage for ImageBuffer {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(&self.0)
}
}
impl AsImage for Vec<u8> {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(self)
}
}
impl AsImage for &[u8] {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(self)
}
}
impl AsImage for Cow<'_, [u8]> {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
match self {
Cow::Borrowed(b) => Cow::Borrowed(b),
Cow::Owned(b) => Cow::Borrowed(b),
}
}
}
duplicate::duplicate! {
[
Name Trait method;
[ImageReturner] [AsImage] [as_image];
[AudioReturner] [AsAudio] [as_audio];
]
pub struct Name {
ptr: *mut u8,
capacity: usize,
pub(crate) written: usize,
}
impl Name {
pub(crate) unsafe fn new(ptr: *mut u8, capacity: usize) -> Self {
Self {
ptr,
capacity,
written: 0,
}
}
fn assert_writable(&self, len: usize) {
let remaining = self.capacity - self.written;
assert!(
len <= remaining,
"Output buffer overflow: attempted to write {len} bytes with {remaining} bytes remaining"
);
}
pub fn write(&mut self, data: &impl Trait) {
let image = data.method();
self.assert_writable(image.len());
unsafe {
std::ptr::copy_nonoverlapping(image.as_ptr(), self.ptr.add(self.written), image.len());
}
self.written += image.len();
}
pub fn write_with<E>(
&mut self,
len: usize,
writer: impl FnOnce(&mut [u8]) -> Result<(), E>,
) -> Result<(), E> {
self.assert_writable(len);
let buffer = unsafe {
std::slice::from_raw_parts_mut(self.ptr.add(self.written), len)
};
writer(buffer)?;
self.written += len;
Ok(())
}
}
}
#[cfg(test)]
mod returner_tests {
use super::ImageReturner;
#[test]
fn write_with_writes_directly_and_updates_length() {
let mut output = [0u8; 4];
let mut returner = unsafe { ImageReturner::new(output.as_mut_ptr(), output.len()) };
returner
.write_with(output.len(), |destination| {
destination.copy_from_slice(&[1, 2, 3, 4]);
Ok::<(), ()>(())
})
.unwrap();
assert_eq!(returner.written, output.len());
assert_eq!(output, [1, 2, 3, 4]);
}
#[test]
#[should_panic(expected = "Output buffer overflow")]
fn write_rejects_data_larger_than_remaining_capacity() {
let mut output = [0u8; 4];
let mut returner = unsafe { ImageReturner::new(output.as_mut_ptr(), output.len()) };
returner.write(&vec![0u8; output.len() + 1]);
}
#[test]
#[should_panic(expected = "Output buffer overflow")]
fn write_with_rejects_length_larger_than_remaining_capacity() {
let mut output = [0u8; 4];
let mut returner = unsafe { ImageReturner::new(output.as_mut_ptr(), output.len()) };
returner
.write_with(output.len() + 1, |_| Ok::<(), ()>(()))
.unwrap();
}
}
#[duplicate::duplicate_item(
T;
[Vec<u16>];
[Vec<i16>];
[Vec<f16>];
[Vec<Yc48>];
)]
impl AsImage for T {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(self.as_bytes())
}
}
#[cfg(feature = "image")]
impl AsImage for image::RgbImage {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
let row_bytes = self.width() as usize * 3;
let stride = crate::utils::bgr_stride(self.width() as usize);
let height = self.height() as usize;
let mut data = vec![0; stride * height];
for y in 0..height {
let source_start = (height - 1 - y) * row_bytes;
let row = &mut data[y * stride..y * stride + row_bytes];
row.copy_from_slice(&self.as_raw()[source_start..source_start + row_bytes]);
crate::utils::rgb_to_bgr_bytes(row);
}
Cow::Owned(data)
}
}
#[cfg(feature = "image")]
impl AsImage for image::RgbaImage {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
let mut data = self.as_raw().to_owned();
crate::utils::bgra_to_rgba_bytes(&mut data);
crate::utils::flip_vertical(&mut data, self.width() as usize * 4, self.height() as usize);
Cow::Owned(data)
}
}
#[cfg(feature = "image")]
impl AsImage for image::ImageBuffer<image::Rgb<u16>, Vec<u16>> {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
let data = self.as_raw();
Cow::Owned(data.as_bytes().to_vec())
}
}
#[cfg(feature = "image")]
impl AsImage for image::ImageBuffer<image::Rgba<u16>, Vec<u16>> {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
let data = self.as_raw();
Cow::Owned(data.as_bytes().to_vec())
}
}
macro_rules! as_image_impl_for_tuple {
($type:ty, $($name:ident),+) => {
impl AsImage for Vec<$type> {
fn as_image(&'_ self) -> Cow<'_, [u8]> {
let mut img_data = Vec::with_capacity(self.len() * std::mem::size_of::<$type>());
for ($($name,)+) in self {
$(img_data.extend_from_slice(&$name.to_le_bytes());)+
}
Cow::Owned(img_data)
}
}
};
}
as_image_impl_for_tuple!((u8, u8, u8), r, g, b);
as_image_impl_for_tuple!((u8, u8, u8, u8), r, g, b, a);
as_image_impl_for_tuple!((u16, u16, u16, u16), r, g, b, a);
as_image_impl_for_tuple!((f16, f16, f16, f16), r, g, b, a);
as_image_impl_for_tuple!((i16, i16, i16), y, cb, cr);
#[derive(Debug, Clone)]
pub struct AudioBuffer(pub Vec<u8>);
impl std::ops::Deref for AudioBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait IntoAudio {
fn into_audio(self) -> crate::input::AudioBuffer;
}
impl<T: AsAudio> IntoAudio for T {
fn into_audio(self) -> AudioBuffer {
AudioBuffer(self.as_audio().into_owned())
}
}
pub trait AsAudio {
fn as_audio(&'_ self) -> Cow<'_, [u8]>;
}
impl AsAudio for AudioBuffer {
fn as_audio(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(&self.0)
}
}
impl AsAudio for Vec<u8> {
fn as_audio(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(self)
}
}
#[duplicate::duplicate_item(
T;
[Vec<u16>];
[Vec<f32>];
)]
impl AsAudio for T {
fn as_audio(&'_ self) -> Cow<'_, [u8]> {
Cow::Borrowed(self.as_bytes())
}
}
macro_rules! into_audio_impl_for_tuple {
($type:ty, $($name:ident),+) => {
impl AsAudio for Vec<$type> {
fn as_audio(&'_ self) -> Cow<'_, [u8]> {
let mut audio_data = Vec::with_capacity(self.len() * std::mem::size_of::<$type>());
for ($($name,)+) in self {
$(audio_data.extend_from_slice(&$name.to_le_bytes());)+
}
Cow::Owned(audio_data)
}
}
};
}
into_audio_impl_for_tuple!((u16, u16), l, r);
into_audio_impl_for_tuple!((f32, f32), l, r);
pub trait InputPlugin: Send + Sync + Sized {
type InputHandle: std::any::Any + Send + Sync;
fn new(info: crate::common::AviUtl2Info) -> crate::common::AnyResult<Self>;
fn plugin_info(&self) -> crate::input::InputPluginTable;
fn open(&self, file: std::path::PathBuf) -> crate::common::AnyResult<Self::InputHandle>;
fn close(&self, handle: Self::InputHandle) -> crate::common::AnyResult<()>;
fn get_track_count(
&self,
handle: &mut Self::InputHandle,
) -> crate::common::AnyResult<(u32, u32)> {
let info = self.get_input_info(handle, 0, 0)?;
let video_tracks = info.video.as_ref().map_or(0, |_| 1);
let audio_tracks = info.audio.as_ref().map_or(0, |_| 1);
Ok((video_tracks, audio_tracks))
}
fn get_input_info(
&self,
handle: &mut Self::InputHandle,
video_track: u32,
audio_track: u32,
) -> crate::common::AnyResult<crate::input::InputInfo>;
fn read_video(
&self,
handle: &Self::InputHandle,
frame: u32,
returner: &mut crate::input::ImageReturner,
) -> crate::common::AnyResult<()> {
let _ = (handle, frame, returner);
Result::<(), anyhow::Error>::Err(anyhow::anyhow!(
"read_video is not implemented for this plugin"
))
}
fn read_video_mut(
&self,
handle: &mut Self::InputHandle,
frame: u32,
returner: &mut crate::input::ImageReturner,
) -> crate::common::AnyResult<()> {
self.read_video(handle, frame, returner)
}
fn can_set_video_track(
&self,
handle: &mut Self::InputHandle,
track: u32,
) -> crate::common::AnyResult<u32> {
let _ = handle;
Ok(track)
}
fn time_to_frame(
&self,
handle: &mut Self::InputHandle,
track: u32,
time: f64,
) -> crate::common::AnyResult<u32> {
const RESOLUTION: i32 = 1000; let info = self.get_input_info(handle, track, 0)?;
if let Some(video_info) = &info.video {
Ok(
(video_info.fps * Rational32::new((time * RESOLUTION as f64) as i32, RESOLUTION))
.to_integer() as u32,
)
} else {
Err(anyhow::anyhow!("No video information available"))
}
}
fn read_audio(
&self,
handle: &Self::InputHandle,
start: i32,
length: i32,
returner: &mut crate::input::AudioReturner,
) -> crate::common::AnyResult<()> {
let _ = (handle, start, length, returner);
Result::<(), anyhow::Error>::Err(anyhow::anyhow!(
"read_audio is not implemented for this plugin"
))
}
fn read_audio_mut(
&self,
handle: &mut Self::InputHandle,
start: i32,
length: i32,
returner: &mut crate::input::AudioReturner,
) -> crate::common::AnyResult<()> {
self.read_audio(handle, start, length, returner)
}
fn can_set_audio_track(
&self,
handle: &mut Self::InputHandle,
track: u32,
) -> crate::common::AnyResult<u32> {
let _ = handle;
Ok(track)
}
fn config(&self, hwnd: crate::common::Win32WindowHandle) -> crate::common::AnyResult<()> {
let _ = hwnd;
Ok(())
}
fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R
where
Self: crate::input::__bridge::InputSingleton,
{
<Self as crate::input::__bridge::InputSingleton>::with_instance(f)
}
fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R
where
Self: crate::input::__bridge::InputSingleton,
{
<Self as crate::input::__bridge::InputSingleton>::with_instance_mut(f)
}
}