1use std::sync::{
2 Arc,
3 atomic::{AtomicUsize, Ordering},
4};
5
6use crate::{
7 common::{FileFilter, Rational32, load_wide_string},
8 output::video_frame::FromRawVideoFrame,
9};
10use aviutl2_sys::output2::OUTPUT_INFO;
11
12#[derive(Debug, Clone)]
14pub struct OutputPluginTable {
15 pub name: String,
17 pub information: String,
20 pub output_type: OutputType,
22
23 pub file_filters: Vec<FileFilter>,
25
26 pub can_config: bool,
28
29 pub project_config: bool,
31}
32
33#[derive(Debug, Clone)]
35pub enum OutputType {
36 Video,
38 Audio,
40 Both,
42 Image,
45}
46
47impl OutputType {
48 pub(crate) fn to_bits(&self) -> i32 {
49 match self {
50 OutputType::Video => 1,
51 OutputType::Audio => 2,
52 OutputType::Both => 3,
53 OutputType::Image => 4,
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct OutputInfo {
61 pub video: Option<VideoOutputInfo>,
63 pub audio: Option<AudioOutputInfo>,
65 pub path: std::path::PathBuf,
67
68 pub(crate) internal: *mut OUTPUT_INFO,
69 pub(crate) last_frame_id: Arc<AtomicUsize>,
70}
71
72unsafe impl Send for OutputInfo {}
73unsafe impl Sync for OutputInfo {}
74
75#[derive(Debug, Clone)]
77pub struct VideoOutputInfo {
78 pub width: u32,
80 pub height: u32,
82 pub fps: Rational32,
84 pub num_frames: u32,
86}
87
88#[derive(Debug, Clone)]
90pub struct AudioOutputInfo {
91 pub sample_rate: u32,
93 pub num_samples: u32,
95 pub num_channels: u32,
97}
98
99pub trait OutputPlugin: Send + Sync + Sized {
102 fn new(info: crate::common::AviUtl2Info) -> crate::common::AnyResult<Self>;
104
105 fn plugin_info(&self) -> crate::output::OutputPluginTable;
107
108 fn output(&self, info: crate::output::OutputInfo) -> crate::common::AnyResult<()>;
110
111 fn config(&self, hwnd: crate::common::Win32WindowHandle) -> crate::common::AnyResult<()> {
117 let _ = hwnd;
118 Ok(())
119 }
120
121 fn config_text(&self) -> crate::common::AnyResult<String> {
124 Ok(String::new())
125 }
126
127 fn load_project_config(
133 &self,
134 project: &mut crate::generic::ProjectFile,
135 ) -> crate::common::AnyResult<()> {
136 let _ = project;
137 Ok(())
138 }
139
140 fn save_project_config(
146 &self,
147 project: &mut crate::generic::ProjectFile,
148 ) -> crate::common::AnyResult<()> {
149 let _ = project;
150 Ok(())
151 }
152
153 fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R
159 where
160 Self: crate::output::__bridge::OutputSingleton,
161 {
162 <Self as crate::output::__bridge::OutputSingleton>::with_instance(f)
163 }
164
165 fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R
171 where
172 Self: crate::output::__bridge::OutputSingleton,
173 {
174 <Self as crate::output::__bridge::OutputSingleton>::with_instance_mut(f)
175 }
176}
177
178pub trait FromRawAudioSamples: Sized + Send + Sync + Copy {
181 const FORMAT: u32;
183
184 unsafe fn from_raw(length: i32, num_channels: u32, audio_data_ptr: *const u8) -> Vec<Self>;
189}
190
191impl OutputInfo {
192 pub(crate) fn from_raw(oip: *mut aviutl2_sys::output2::OUTPUT_INFO) -> Self {
193 let raw = unsafe { &*oip };
194
195 Self {
196 video: if raw.flag & aviutl2_sys::output2::OUTPUT_INFO::FLAG_VIDEO != 0 {
197 Some(VideoOutputInfo {
198 width: raw.w as u32,
199 height: raw.h as u32,
200 fps: Rational32::new(raw.rate, raw.scale),
201 num_frames: raw.n as u32,
202 })
203 } else {
204 None
205 },
206 audio: if raw.flag & aviutl2_sys::output2::OUTPUT_INFO::FLAG_AUDIO != 0 {
207 Some(AudioOutputInfo {
208 sample_rate: raw.audio_rate as u32,
209 num_samples: raw.audio_n as u32,
210 num_channels: raw.audio_ch as u32,
211 })
212 } else {
213 None
214 },
215
216 path: std::path::PathBuf::from(unsafe { load_wide_string(raw.savefile) }),
217
218 internal: oip,
219 last_frame_id: Arc::new(AtomicUsize::new(0)),
220 }
221 }
222
223 pub fn get_video_frame<F: FromRawVideoFrame>(&self, frame: i32) -> Option<F> {
225 if let Some(video) = &self.video {
226 if F::check(video).is_err() {
227 return None;
228 }
229 if frame < 0 || frame >= video.num_frames as i32 {
230 return None;
231 }
232 unsafe { self.get_video_frame_unchecked::<F>(frame) }
233 } else {
234 None
235 }
236 }
237
238 pub unsafe fn get_video_frame_unchecked<F: FromRawVideoFrame>(&self, frame: i32) -> Option<F> {
246 let frame_ptr = unsafe { self.internal.as_mut().and_then(|oip| oip.func_get_video) }?;
247 let frame_data_ptr = frame_ptr(frame, F::FORMAT) as *mut u8;
248 let video = self.video.as_ref()?;
249 let current_frame_id = self.last_frame_id.fetch_add(1, Ordering::SeqCst) + 1;
250 let frame = unsafe {
251 F::from_raw(
252 video,
253 frame_data_ptr,
254 Arc::clone(&self.last_frame_id),
255 current_frame_id,
256 )
257 };
258 Some(frame)
259 }
260
261 pub fn get_video_frames_iter<F: FromRawVideoFrame>(&self) -> VideoFramesIterator<'_, F> {
263 VideoFramesIterator::new(self)
264 }
265
266 pub fn get_audio_samples<F: FromRawAudioSamples>(
268 &self,
269 start: i32,
270 length: i32,
271 ) -> Option<(Vec<F>, u32)> {
272 let audio = self.audio.as_ref()?;
273 let audio_ptr = unsafe { self.internal.as_mut().and_then(|oip| oip.func_get_audio) }?;
274 let mut readed = 0;
275 let audio_data_ptr = audio_ptr(start, length, &mut readed, F::FORMAT) as *mut u8;
276
277 let samples = unsafe { F::from_raw(length, audio.num_channels, audio_data_ptr) };
278
279 Some((samples, audio.num_channels))
280 }
281
282 pub fn get_mono_audio_samples<F: FromRawAudioSamples>(
285 &self,
286 start: i32,
287 length: i32,
288 ) -> Option<Vec<F>> {
289 let (samples, num_channels) = self.get_audio_samples(start, length)?;
290 if num_channels == 1 {
291 Some(samples)
292 } else {
293 Some(
294 samples
295 .chunks(num_channels as usize)
296 .map(|chunk| chunk[0])
297 .collect(),
298 )
299 }
300 }
301
302 pub fn get_mono_audio_samples_iter<F: FromRawAudioSamples>(
307 &'_ self,
308 length: i32,
309 ) -> MonoAudioSamplesIterator<'_, F> {
310 MonoAudioSamplesIterator::new(self, length)
311 }
312
313 pub fn get_stereo_audio_samples<F: FromRawAudioSamples>(
316 &self,
317 start: i32,
318 length: i32,
319 ) -> Option<Vec<(F, F)>> {
320 let (samples, num_channels) = self.get_audio_samples(start, length)?;
321 if num_channels == 2 {
322 Some(
323 samples
324 .chunks(num_channels as usize)
325 .map(|chunk| (chunk[0], chunk[1]))
326 .collect(),
327 )
328 } else {
329 None
330 }
331 }
332
333 pub fn get_stereo_audio_samples_iter<F: FromRawAudioSamples>(
338 &'_ self,
339 length: i32,
340 ) -> StereoAudioSamplesIterator<'_, F> {
341 StereoAudioSamplesIterator::new(self, length)
342 }
343
344 pub fn is_aborted(&self) -> bool {
346 let is_abort_func = unsafe { self.internal.as_mut().and_then(|oip| oip.func_is_abort) };
347 is_abort_func.is_none_or(|f| f())
348 }
349
350 pub fn update_display(&self, current_frame: i32, total_frames: i32) {
352 if let Some(func) = unsafe {
353 self.internal
354 .as_mut()
355 .and_then(|oip| oip.func_rest_time_disp)
356 } {
357 func(current_frame, total_frames);
358 }
359 }
360
361 pub fn set_buffer_size(&self, video_size: i32, audio_size: i32) {
364 if let Some(func) = unsafe {
365 self.internal
366 .as_mut()
367 .and_then(|oip| oip.func_set_buffer_size)
368 } {
369 func(video_size, audio_size);
370 }
371 }
372}
373
374impl Drop for OutputInfo {
375 fn drop(&mut self) {
376 self.last_frame_id.store(usize::MAX, Ordering::SeqCst);
377 }
378}
379
380#[derive(Debug, Clone)]
385pub struct VideoFramesIterator<'a, F: FromRawVideoFrame> {
386 output_info: &'a OutputInfo,
387 current_frame: i32,
388 total_frames: i32,
389 last_updated_time: std::time::Instant,
390 check_result: bool,
391 _marker: std::marker::PhantomData<F>,
392}
393
394impl<'a, F: FromRawVideoFrame> VideoFramesIterator<'a, F> {
395 pub(crate) fn new(output_info: &'a OutputInfo) -> Self {
396 let total_frames = output_info
397 .video
398 .as_ref()
399 .map_or(0, |v| v.num_frames as i32);
400 Self {
401 output_info,
402 current_frame: 0,
403 total_frames,
404 last_updated_time: std::time::Instant::now(),
405 check_result: output_info
406 .video
407 .as_ref()
408 .is_some_and(|v| F::check(v).is_ok()),
409 _marker: std::marker::PhantomData,
410 }
411 }
412}
413
414impl<'a, F: FromRawVideoFrame> Iterator for VideoFramesIterator<'a, F> {
415 type Item = (i32, F);
416
417 fn next(&mut self) -> Option<Self::Item> {
418 if !self.check_result {
419 return None;
420 }
421 if self.current_frame >= self.total_frames {
422 return None;
423 }
424
425 if self.output_info.is_aborted() {
426 return None;
427 }
428
429 let frame = unsafe {
430 self.output_info
431 .get_video_frame_unchecked(self.current_frame)
432 };
433 if let Some(frame_data) = frame {
434 let current_frame = self.current_frame;
435 self.current_frame += 1;
436 if self.last_updated_time.elapsed().as_secs_f32() > 0.1 {
437 self.output_info
438 .update_display(current_frame, self.total_frames);
439 self.last_updated_time = std::time::Instant::now();
440 }
441 Some((current_frame, frame_data))
442 } else {
443 None
444 }
445 }
446}
447
448duplicate::duplicate! {
449 [
450 Name method IterType Doc Also;
451 [MonoAudioSamplesIterator] [get_mono_audio_samples] [F] ["モノラル音声サンプルのイテレータ。"] ["[`OutputInfo::get_mono_audio_samples_iter`]"];
452 [StereoAudioSamplesIterator] [get_stereo_audio_samples] [(F, F)] ["ステレオ音声サンプルのイテレータ。"] ["[`OutputInfo::get_stereo_audio_samples_iter`]"];
453 ]
454
455 #[doc = Doc]
456 #[doc = Also]
459 #[derive(Debug, Clone)]
460 pub struct Name<'a, F: FromRawAudioSamples> {
461 output_info: &'a OutputInfo,
462 length: i32,
463 total_length: i32,
464 readed: i32,
465 _marker: std::marker::PhantomData<F>,
466 }
467
468 impl<'a, F: FromRawAudioSamples> Name<'a, F> {
469 pub(crate) fn new(output_info: &'a OutputInfo, length: i32) -> Self {
470 Self {
471 output_info,
472 length,
473 total_length: output_info.audio.as_ref().map_or(0, |a| a.num_samples as i32),
474 readed: 0,
475 _marker: std::marker::PhantomData,
476 }
477 }
478 }
479
480 impl<'a, F: FromRawAudioSamples> Iterator for Name<'a, F> {
481 type Item = (usize, Vec<IterType>);
482
483 fn next(&mut self) -> Option<Self::Item> {
484 if self.readed >= self.total_length {
485 return None;
486 }
487 if self.output_info.is_aborted() {
488 return None;
489 }
490
491 let length_to_read = self.length.min(self.total_length - self.readed);
492 let samples = self.output_info.method(self.readed, length_to_read);
493 if let Some(samples) = samples {
494 let start_frame = self.readed;
495 self.readed += samples.len() as i32;
496 Some((start_frame as usize, samples))
497 } else {
498 None
499 }
500 }
501 }
502}