aviutl2\output/
binding.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicUsize, Ordering},
4};
5
6use crate::{
7    common::{AnyResult, AviUtl2Info, FileFilter, Rational32, Win32WindowHandle, load_wide_string},
8    output::video_frame::FromRawVideoFrame,
9};
10use aviutl2_sys::output2::OUTPUT_INFO;
11
12/// 出力プラグインの情報を表す構造体。
13#[derive(Debug, Clone)]
14pub struct OutputPluginTable {
15    /// プラグインの名前。
16    pub name: String,
17    /// プラグインの情報。
18    /// 「プラグイン情報」ダイアログで表示されます。
19    pub information: String,
20    /// 出力の種類(動画、音声、両方)。
21    pub output_type: OutputType,
22
23    /// 出力ダイアログで使われるファイルフィルタ。
24    pub file_filters: Vec<FileFilter>,
25
26    /// 設定ダイアログがあるかどうか。
27    pub can_config: bool,
28}
29
30/// 出力の種類を表す列挙型。
31#[derive(Debug, Clone)]
32pub enum OutputType {
33    /// 動画のみの出力。
34    Video,
35    /// 音声のみの出力。
36    Audio,
37    /// 動画と音声の両方を出力。
38    Both,
39}
40
41impl OutputType {
42    pub(crate) fn to_bits(&self) -> i32 {
43        match self {
44            OutputType::Video => 1,
45            OutputType::Audio => 2,
46            OutputType::Both => 3,
47        }
48    }
49}
50
51/// 出力情報を表す構造体。
52#[derive(Debug, Clone)]
53pub struct OutputInfo {
54    /// 動画出力情報。動画出力がない場合は`None`。
55    pub video: Option<VideoOutputInfo>,
56    /// 音声出力情報。音声出力がない場合は`None`。
57    pub audio: Option<AudioOutputInfo>,
58    /// 出力先のファイルパス。
59    pub path: std::path::PathBuf,
60
61    pub(crate) internal: *mut OUTPUT_INFO,
62    pub(crate) last_frame_id: Arc<AtomicUsize>,
63}
64
65unsafe impl Send for OutputInfo {}
66unsafe impl Sync for OutputInfo {}
67
68/// 動画の出力情報を表す構造体。
69#[derive(Debug, Clone)]
70pub struct VideoOutputInfo {
71    /// 動画の幅(ピクセル単位)。
72    pub width: u32,
73    /// 動画の高さ(ピクセル単位)。
74    pub height: u32,
75    /// 動画のフレームレート(分数形式)。
76    pub fps: Rational32,
77    /// 動画のフレーム数。
78    pub num_frames: u32,
79}
80
81/// 音声の出力情報を表す構造体。
82#[derive(Debug, Clone)]
83pub struct AudioOutputInfo {
84    /// 音声のサンプルレート(Hz単位)。
85    pub sample_rate: u32,
86    /// 音声のサンプル数。
87    pub num_samples: u32,
88    /// 音声のチャンネル数。
89    pub num_channels: u32,
90}
91
92/// 出力プラグインのトレイト。
93/// このトレイトを実装し、[`crate::register_output_plugin!`] マクロを使用してプラグインを登録します。
94pub trait OutputPlugin: Send + Sync + Sized {
95    /// プラグインを初期化する。
96    fn new(info: AviUtl2Info) -> AnyResult<Self>;
97
98    /// プラグインの情報を返す。
99    fn plugin_info(&self) -> OutputPluginTable;
100
101    /// 出力を開始する。
102    fn output(&self, info: OutputInfo) -> AnyResult<()>;
103
104    /// 出力設定のダイアログを表示する。
105    fn config(&self, _hwnd: Win32WindowHandle) -> AnyResult<()> {
106        Ok(())
107    }
108
109    /// 出力設定のテキスト情報を返す。
110    /// 出力ダイアログの下の設定ボタンの隣に表示されます。
111    fn config_text(&self) -> AnyResult<String> {
112        Ok(String::new())
113    }
114
115    /// シングルトンインスタンスを参照するためのヘルパーメソッド。
116    ///
117    /// # Panics
118    ///
119    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
120    fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R
121    where
122        Self: crate::output::__bridge::OutputSingleton,
123    {
124        <Self as crate::output::__bridge::OutputSingleton>::with_instance(f)
125    }
126
127    /// シングルトンインスタンスを可変参照するためのヘルパーメソッド。
128    ///
129    /// # Panics
130    ///
131    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
132    fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R
133    where
134        Self: crate::output::__bridge::OutputSingleton,
135    {
136        <Self as crate::output::__bridge::OutputSingleton>::with_instance_mut(f)
137    }
138}
139
140/// 音声サンプルを表すトレイト。
141/// aviutl2-rsでは、このトレイトを実装した型で音声サンプルのフォーマットを指定します。
142pub trait FromRawAudioSamples: Sized + Send + Sync + Copy {
143    /// 音声サンプルのフォーマットを表す定数。
144    const FORMAT: u32;
145
146    /// 音声サンプルの情報を生のポインタから取得する。
147    ///
148    /// # Safety
149    /// func_get_audioの戻り値のポインタのみが許容される。
150    unsafe fn from_raw(length: i32, num_channels: u32, audio_data_ptr: *const u8) -> Vec<Self>;
151}
152
153impl OutputInfo {
154    pub(crate) fn from_raw(oip: *mut aviutl2_sys::output2::OUTPUT_INFO) -> Self {
155        let raw = unsafe { &*oip };
156
157        Self {
158            video: if raw.flag & aviutl2_sys::output2::OUTPUT_INFO::FLAG_VIDEO != 0 {
159                Some(VideoOutputInfo {
160                    width: raw.w as u32,
161                    height: raw.h as u32,
162                    fps: Rational32::new(raw.rate, raw.scale),
163                    num_frames: raw.n as u32,
164                })
165            } else {
166                None
167            },
168            audio: if raw.flag & aviutl2_sys::output2::OUTPUT_INFO::FLAG_AUDIO != 0 {
169                Some(AudioOutputInfo {
170                    sample_rate: raw.audio_rate as u32,
171                    num_samples: raw.audio_n as u32,
172                    num_channels: raw.audio_ch as u32,
173                })
174            } else {
175                None
176            },
177
178            path: std::path::PathBuf::from(unsafe { load_wide_string(raw.savefile) }),
179
180            internal: oip,
181            last_frame_id: Arc::new(AtomicUsize::new(0)),
182        }
183    }
184
185    /// 動画のフレームを取得する。
186    pub fn get_video_frame<F: FromRawVideoFrame>(&self, frame: i32) -> Option<F> {
187        if let Some(video) = &self.video {
188            if F::check(video).is_err() {
189                return None;
190            }
191            if frame < 0 || frame >= video.num_frames as i32 {
192                return None;
193            }
194            unsafe { self.get_video_frame_unchecked::<F>(frame) }
195        } else {
196            None
197        }
198    }
199
200    /// 動画のフレームを取得する。
201    /// [`Self::get_video_frame`]と違い、[`FromRawVideoFrame::check`]や境界のチェックを行いません。
202    ///
203    /// # Safety
204    /// 以下は未定義動作です:
205    /// - [`FromRawVideoFrame::check`]がfalseの場合
206    /// - `frame`が動画のフレーム数の範囲外の場合
207    pub unsafe fn get_video_frame_unchecked<F: FromRawVideoFrame>(&self, frame: i32) -> Option<F> {
208        let frame_ptr = unsafe { self.internal.as_mut().and_then(|oip| oip.func_get_video) }?;
209        let frame_data_ptr = frame_ptr(frame, F::FORMAT) as *mut u8;
210        let video = self.video.as_ref()?;
211        let current_frame_id = self.last_frame_id.fetch_add(1, Ordering::SeqCst) + 1;
212        let frame = unsafe {
213            F::from_raw(
214                video,
215                frame_data_ptr,
216                Arc::clone(&self.last_frame_id),
217                current_frame_id,
218            )
219        };
220        Some(frame)
221    }
222
223    /// 動画のフレームをイテレータとして取得する。
224    pub fn get_video_frames_iter<F: FromRawVideoFrame>(&self) -> VideoFramesIterator<'_, F> {
225        VideoFramesIterator::new(self)
226    }
227
228    /// 指定した区間の音声サンプルとチャンネル数を取得する。
229    pub fn get_audio_samples<F: FromRawAudioSamples>(
230        &self,
231        start: i32,
232        length: i32,
233    ) -> Option<(Vec<F>, u32)> {
234        let audio = self.audio.as_ref()?;
235        let audio_ptr = unsafe { self.internal.as_mut().and_then(|oip| oip.func_get_audio) }?;
236        let mut readed = 0;
237        let audio_data_ptr = audio_ptr(start, length, &mut readed, F::FORMAT) as *mut u8;
238
239        let samples = unsafe { F::from_raw(length, audio.num_channels, audio_data_ptr) };
240
241        Some((samples, audio.num_channels))
242    }
243
244    /// 指定した区間の音声サンプルをモノラル形式で取得する。
245    /// `num_channels`が1の場合はそのまま、2の場合は左チャンネルのサンプルを返します。
246    pub fn get_mono_audio_samples<F: FromRawAudioSamples>(
247        &self,
248        start: i32,
249        length: i32,
250    ) -> Option<Vec<F>> {
251        let (samples, num_channels) = self.get_audio_samples(start, length)?;
252        if num_channels == 1 {
253            Some(samples)
254        } else {
255            Some(
256                samples
257                    .chunks(num_channels as usize)
258                    .map(|chunk| chunk[0])
259                    .collect(),
260            )
261        }
262    }
263
264    /// モノラルの音声サンプルをイテレータとして取得する。
265    ///
266    /// # Arguments
267    /// - `length`: 一回のイテレーションで取得するサンプル数。
268    pub fn get_mono_audio_samples_iter<F: FromRawAudioSamples>(
269        &'_ self,
270        length: i32,
271    ) -> MonoAudioSamplesIterator<'_, F> {
272        MonoAudioSamplesIterator::new(self, length)
273    }
274
275    /// 指定した区間の音声サンプルをステレオ形式で取得する。
276    /// `num_channels`が2の場合はそのまま、1の場合はチャンネルを複製してステレオ形式に変換します。
277    pub fn get_stereo_audio_samples<F: FromRawAudioSamples>(
278        &self,
279        start: i32,
280        length: i32,
281    ) -> Option<Vec<(F, F)>> {
282        let (samples, num_channels) = self.get_audio_samples(start, length)?;
283        if num_channels == 2 {
284            Some(
285                samples
286                    .chunks(num_channels as usize)
287                    .map(|chunk| (chunk[0], chunk[1]))
288                    .collect(),
289            )
290        } else {
291            None
292        }
293    }
294
295    /// ステレオの音声サンプルをイテレータとして取得する。
296    ///
297    /// # Arguments
298    /// - `length`: 一回のイテレーションで取得するサンプル数。
299    pub fn get_stereo_audio_samples_iter<F: FromRawAudioSamples>(
300        &'_ self,
301        length: i32,
302    ) -> StereoAudioSamplesIterator<'_, F> {
303        StereoAudioSamplesIterator::new(self, length)
304    }
305
306    /// 出力が中断されたかどうかを確認する。
307    pub fn is_aborted(&self) -> bool {
308        let is_abort_func = unsafe { self.internal.as_mut().and_then(|oip| oip.func_is_abort) };
309        is_abort_func.is_none_or(|f| f())
310    }
311
312    /// 出力の進行状況を更新する。
313    pub fn update_display(&self, current_frame: i32, total_frames: i32) {
314        if let Some(func) = unsafe {
315            self.internal
316                .as_mut()
317                .and_then(|oip| oip.func_rest_time_disp)
318        } {
319            func(current_frame, total_frames);
320        }
321    }
322
323    /// データ取得のバッファ数(フレーム数)を設定する。
324    /// バッファ数の半分のデータを先読みリクエストするようになります。
325    pub fn set_buffer_size(&self, video_size: i32, audio_size: i32) {
326        if let Some(func) = unsafe {
327            self.internal
328                .as_mut()
329                .and_then(|oip| oip.func_set_buffer_size)
330        } {
331            func(video_size, audio_size);
332        }
333    }
334}
335
336impl Drop for OutputInfo {
337    fn drop(&mut self) {
338        self.last_frame_id.store(usize::MAX, Ordering::SeqCst);
339    }
340}
341
342/// 動画フレームのイテレータ。
343///
344/// # See Also
345/// [`OutputInfo::get_video_frames_iter`]
346#[derive(Debug, Clone)]
347pub struct VideoFramesIterator<'a, F: FromRawVideoFrame> {
348    output_info: &'a OutputInfo,
349    current_frame: i32,
350    total_frames: i32,
351    last_updated_time: std::time::Instant,
352    check_result: bool,
353    _marker: std::marker::PhantomData<F>,
354}
355
356impl<'a, F: FromRawVideoFrame> VideoFramesIterator<'a, F> {
357    pub(crate) fn new(output_info: &'a OutputInfo) -> Self {
358        let total_frames = output_info
359            .video
360            .as_ref()
361            .map_or(0, |v| v.num_frames as i32);
362        Self {
363            output_info,
364            current_frame: 0,
365            total_frames,
366            last_updated_time: std::time::Instant::now(),
367            check_result: output_info
368                .video
369                .as_ref()
370                .is_some_and(|v| F::check(v).is_ok()),
371            _marker: std::marker::PhantomData,
372        }
373    }
374}
375
376impl<'a, F: FromRawVideoFrame> Iterator for VideoFramesIterator<'a, F> {
377    type Item = (i32, F);
378
379    fn next(&mut self) -> Option<Self::Item> {
380        if !self.check_result {
381            return None;
382        }
383        if self.current_frame >= self.total_frames {
384            return None;
385        }
386
387        if self.output_info.is_aborted() {
388            return None;
389        }
390
391        let frame = unsafe {
392            self.output_info
393                .get_video_frame_unchecked(self.current_frame)
394        };
395        if let Some(frame_data) = frame {
396            let current_frame = self.current_frame;
397            self.current_frame += 1;
398            if self.last_updated_time.elapsed().as_secs_f32() > 0.1 {
399                self.output_info
400                    .update_display(current_frame, self.total_frames);
401                self.last_updated_time = std::time::Instant::now();
402            }
403            Some((current_frame, frame_data))
404        } else {
405            None
406        }
407    }
408}
409
410duplicate::duplicate! {
411    [
412        Name                         method                     IterType Doc                                    Also;
413        [MonoAudioSamplesIterator]   [get_mono_audio_samples]   [F]      ["モノラル音声サンプルのイテレータ。"] ["[`OutputInfo::get_mono_audio_samples_iter`]"];
414        [StereoAudioSamplesIterator] [get_stereo_audio_samples] [(F, F)] ["ステレオ音声サンプルのイテレータ。"] ["[`OutputInfo::get_stereo_audio_samples_iter`]"];
415    ]
416
417    #[doc = Doc]
418    ///
419    /// # See Also
420    #[doc = Also]
421    #[derive(Debug, Clone)]
422    pub struct Name<'a, F: FromRawAudioSamples> {
423        output_info: &'a OutputInfo,
424        length: i32,
425        total_length: i32,
426        readed: i32,
427        _marker: std::marker::PhantomData<F>,
428    }
429
430    impl<'a, F: FromRawAudioSamples> Name<'a, F> {
431        pub(crate) fn new(output_info: &'a OutputInfo, length: i32) -> Self {
432            Self {
433                output_info,
434                length,
435                total_length: output_info.audio.as_ref().map_or(0, |a| a.num_samples as i32),
436                readed: 0,
437                _marker: std::marker::PhantomData,
438            }
439        }
440    }
441
442    impl<'a, F: FromRawAudioSamples> Iterator for Name<'a, F> {
443        type Item = (usize, Vec<IterType>);
444
445        fn next(&mut self) -> Option<Self::Item> {
446            if self.readed >= self.total_length {
447                return None;
448            }
449            if self.output_info.is_aborted() {
450                return None;
451            }
452
453            let length_to_read = self.length.min(self.total_length - self.readed);
454            let samples = self.output_info.method(self.readed, length_to_read);
455            if let Some(samples) = samples {
456                let start_frame = self.readed;
457                self.readed += samples.len() as i32;
458                Some((start_frame as usize, samples))
459            } else {
460                None
461            }
462        }
463    }
464}