Skip to main content

aviutl2\output/
binding.rs

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/// 出力プラグインの情報を表す構造体。
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    pub project_config: bool,
31}
32
33/// 出力の種類を表す列挙型。
34#[derive(Debug, Clone)]
35pub enum OutputType {
36    /// 動画のみの出力。
37    Video,
38    /// 音声のみの出力。
39    Audio,
40    /// 動画と音声の両方を出力。
41    Both,
42    /// 静止画出力のみ。
43    /// OUTPUT_INFOが1フレーム出力になり、また出力完了時の通知やサウンド再生が行われなくなります。
44    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/// 出力情報を表す構造体。
59#[derive(Debug, Clone)]
60pub struct OutputInfo {
61    /// 動画出力情報。動画出力がない場合は`None`。
62    pub video: Option<VideoOutputInfo>,
63    /// 音声出力情報。音声出力がない場合は`None`。
64    pub audio: Option<AudioOutputInfo>,
65    /// 出力先のファイルパス。
66    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/// 動画の出力情報を表す構造体。
76#[derive(Debug, Clone)]
77pub struct VideoOutputInfo {
78    /// 動画の幅(ピクセル単位)。
79    pub width: u32,
80    /// 動画の高さ(ピクセル単位)。
81    pub height: u32,
82    /// 動画のフレームレート(分数形式)。
83    pub fps: Rational32,
84    /// 動画のフレーム数。
85    pub num_frames: u32,
86}
87
88/// 音声の出力情報を表す構造体。
89#[derive(Debug, Clone)]
90pub struct AudioOutputInfo {
91    /// 音声のサンプルレート(Hz単位)。
92    pub sample_rate: u32,
93    /// 音声のサンプル数。
94    pub num_samples: u32,
95    /// 音声のチャンネル数。
96    pub num_channels: u32,
97}
98
99/// 出力プラグインのトレイト。
100/// このトレイトを実装し、[`crate::register_output_plugin!`] マクロを使用してプラグインを登録します。
101pub trait OutputPlugin: Send + Sync + Sized {
102    /// プラグインを初期化する。
103    fn new(info: crate::common::AviUtl2Info) -> crate::common::AnyResult<Self>;
104
105    /// プラグインの情報を返す。
106    fn plugin_info(&self) -> crate::output::OutputPluginTable;
107
108    /// 出力を開始する。
109    fn output(&self, info: crate::output::OutputInfo) -> crate::common::AnyResult<()>;
110
111    /// 出力設定のダイアログを表示する。
112    ///
113    /// # Note
114    ///
115    /// [`crate::output::OutputPluginTable::can_config`] が `true` の場合にのみ呼び出されます。
116    fn config(&self, hwnd: crate::common::Win32WindowHandle) -> crate::common::AnyResult<()> {
117        let _ = hwnd;
118        Ok(())
119    }
120
121    /// 出力設定のテキスト情報を返す。
122    /// 出力ダイアログの下の設定ボタンの隣に表示されます。
123    fn config_text(&self) -> crate::common::AnyResult<String> {
124        Ok(String::new())
125    }
126
127    /// プロジェクトファイルから出力設定を読み込む。
128    ///
129    /// # Note
130    ///
131    /// [`crate::output::OutputPluginTable::project_config`] が `true` の場合にのみ呼び出されます。
132    fn load_project_config(
133        &self,
134        project: &mut crate::generic::ProjectFile,
135    ) -> crate::common::AnyResult<()> {
136        let _ = project;
137        Ok(())
138    }
139
140    /// プロジェクトファイルに出力設定を書き込む。
141    ///
142    /// # Note
143    ///
144    /// [`crate::output::OutputPluginTable::project_config`] が `true` の場合にのみ呼び出されます。
145    fn save_project_config(
146        &self,
147        project: &mut crate::generic::ProjectFile,
148    ) -> crate::common::AnyResult<()> {
149        let _ = project;
150        Ok(())
151    }
152
153    /// シングルトンインスタンスを参照するためのヘルパーメソッド。
154    ///
155    /// # Panics
156    ///
157    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
158    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    /// シングルトンインスタンスを可変参照するためのヘルパーメソッド。
166    ///
167    /// # Panics
168    ///
169    /// プラグインが初期化されていない場合や、二重に呼び出された場合にパニックします。
170    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
178/// 音声サンプルを表すトレイト。
179/// aviutl2-rsでは、このトレイトを実装した型で音声サンプルのフォーマットを指定します。
180pub trait FromRawAudioSamples: Sized + Send + Sync + Copy {
181    /// 音声サンプルのフォーマットを表す定数。
182    const FORMAT: u32;
183
184    /// 音声サンプルの情報を生のポインタから取得する。
185    ///
186    /// # Safety
187    /// func_get_audioの戻り値のポインタのみが許容される。
188    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    /// 動画のフレームを取得する。
224    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    /// 動画のフレームを取得する。
239    /// [`Self::get_video_frame`]と違い、[`FromRawVideoFrame::check`]や境界のチェックを行いません。
240    ///
241    /// # Safety
242    /// 以下は未定義動作です:
243    /// - [`FromRawVideoFrame::check`]がfalseの場合
244    /// - `frame`が動画のフレーム数の範囲外の場合
245    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    /// 動画のフレームをイテレータとして取得する。
262    pub fn get_video_frames_iter<F: FromRawVideoFrame>(&self) -> VideoFramesIterator<'_, F> {
263        VideoFramesIterator::new(self)
264    }
265
266    /// 指定した区間の音声サンプルとチャンネル数を取得する。
267    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    /// 指定した区間の音声サンプルをモノラル形式で取得する。
283    /// `num_channels`が1の場合はそのまま、2の場合は左チャンネルのサンプルを返します。
284    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    /// モノラルの音声サンプルをイテレータとして取得する。
303    ///
304    /// # Arguments
305    /// - `length`: 一回のイテレーションで取得するサンプル数。
306    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    /// 指定した区間の音声サンプルをステレオ形式で取得する。
314    /// `num_channels`が2の場合はそのまま、1の場合はチャンネルを複製してステレオ形式に変換します。
315    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    /// ステレオの音声サンプルをイテレータとして取得する。
334    ///
335    /// # Arguments
336    /// - `length`: 一回のイテレーションで取得するサンプル数。
337    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    /// 出力が中断されたかどうかを確認する。
345    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    /// 出力の進行状況を更新する。
351    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    /// データ取得のバッファ数(フレーム数)を設定する。
362    /// バッファ数の半分のデータを先読みリクエストするようになります。
363    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/// 動画フレームのイテレータ。
381///
382/// # See Also
383/// [`OutputInfo::get_video_frames_iter`]
384#[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    ///
457    /// # See Also
458    #[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}