Skip to main content

aviutl2/
cache.rs

1//! AviUtl2のキャッシュ関連機能へのインターフェースを提供します。
2//!
3//! # Panics
4//!
5//! キャッシュハンドルが初期化されていない場合panicします。
6
7use std::{ffi::c_void, path::Path, sync::Arc};
8
9use num_rational::Rational32;
10use parking_lot::lock_api::RawRwLock;
11
12use crate::{common::CWString, filter::RgbaPixel};
13
14struct InternalCacheHandle(*mut aviutl2_sys::cache2::CACHE_HANDLE);
15unsafe impl Send for InternalCacheHandle {}
16
17static CACHE_HANDLE: std::sync::OnceLock<std::sync::Mutex<InternalCacheHandle>> =
18    std::sync::OnceLock::new();
19
20static CACHE_LOCKS: std::sync::LazyLock<dashmap::DashMap<usize, Arc<parking_lot::RawRwLock>>> =
21    std::sync::LazyLock::new(dashmap::DashMap::new);
22
23#[doc(hidden)]
24pub struct GlobalCacheHandle;
25
26/// CacheHandleをキャッシュの識別子として使用するための構造体。
27pub const GLOBAL_CACHE_HANDLE: GlobalCacheHandle = GlobalCacheHandle;
28
29impl AsCacheIdentifier for GlobalCacheHandle {
30    fn as_cache_identifier(&self) -> *mut c_void {
31        with_cache_handle(|handle| handle as *mut c_void).expect("Cache handle is not initialized")
32    }
33}
34
35#[doc(hidden)]
36pub fn __initialize_cache(handle: *mut aviutl2_sys::cache2::CACHE_HANDLE) {
37    CACHE_HANDLE
38        .set(std::sync::Mutex::new(InternalCacheHandle(handle)))
39        .ok()
40        .expect("Cache handle is already initialized");
41}
42
43#[doc(hidden)]
44pub fn __initialize_cache_unwind(handle: *mut aviutl2_sys::cache2::CACHE_HANDLE) {
45    if let Err(panic_info) = crate::__catch_unwind_with_panic_info(|| __initialize_cache(handle)) {
46        tracing::error!("Panic occurred during InitializeCache: {}", panic_info);
47        let _ = crate::logger::write_error_log(&panic_info);
48    }
49}
50
51/// キャッシュ操作のエラー。
52#[derive(Debug, thiserror::Error)]
53pub enum CacheError {
54    #[error("api call failed")]
55    ApiCallFailed,
56    #[error("input utf-16 string contains null byte")]
57    InputCwstrContainsNull(#[from] crate::common::NullByteError),
58    #[error("value is out of range")]
59    ValueOutOfRange,
60    #[error("invalid audio channel")]
61    InvalidAudioChannel,
62}
63
64/// キャッシュの識別子を提供するためのトレイト。
65pub trait AsCacheIdentifier {
66    fn as_cache_identifier(&self) -> *mut c_void;
67}
68
69impl AsCacheIdentifier for *mut c_void {
70    fn as_cache_identifier(&self) -> *mut c_void {
71        *self
72    }
73}
74
75impl AsCacheIdentifier for *const c_void {
76    fn as_cache_identifier(&self) -> *mut c_void {
77        *self as *mut c_void
78    }
79}
80
81impl AsCacheIdentifier for usize {
82    fn as_cache_identifier(&self) -> *mut c_void {
83        *self as *mut c_void
84    }
85}
86
87enum CacheAccessMode {
88    Read,
89    Write,
90}
91
92struct CacheAccessGuard {
93    lock: Arc<parking_lot::RawRwLock>,
94    mode: CacheAccessMode,
95}
96
97impl CacheAccessGuard {
98    fn read(key: usize) -> Self {
99        let lock = CACHE_LOCKS
100            .entry(key)
101            .or_insert_with(|| Arc::new(parking_lot::RawRwLock::INIT))
102            .clone();
103        lock.lock_shared();
104        Self {
105            lock,
106            mode: CacheAccessMode::Read,
107        }
108    }
109
110    fn write(key: usize) -> Self {
111        let lock = CACHE_LOCKS
112            .entry(key)
113            .or_insert_with(|| Arc::new(parking_lot::RawRwLock::INIT))
114            .clone();
115        lock.lock_exclusive();
116        Self {
117            lock,
118            mode: CacheAccessMode::Write,
119        }
120    }
121}
122
123impl Drop for CacheAccessGuard {
124    fn drop(&mut self) {
125        unsafe {
126            match self.mode {
127                CacheAccessMode::Read => self.lock.unlock_shared(),
128                CacheAccessMode::Write => self.lock.unlock_exclusive(),
129            }
130        }
131    }
132}
133
134/// 画像キャッシュデータの読み取りガード。
135pub struct CacheImageReadGuard {
136    _access: CacheAccessGuard,
137    raw: aviutl2_sys::cache2::CACHE_IMAGE,
138    len: usize,
139}
140
141/// 画像キャッシュデータの書き込みガード。
142pub struct CacheImageWriteGuard {
143    _access: CacheAccessGuard,
144    raw: aviutl2_sys::cache2::CACHE_IMAGE,
145    len: usize,
146}
147
148/// 音声キャッシュデータの読み取りガード。
149pub struct CacheAudioReadGuard {
150    _access: CacheAccessGuard,
151    raw: aviutl2_sys::cache2::CACHE_AUDIO,
152    len: usize,
153}
154
155/// 音声キャッシュデータの書き込みガード。
156pub struct CacheAudioWriteGuard {
157    _access: CacheAccessGuard,
158    raw: aviutl2_sys::cache2::CACHE_AUDIO,
159    len: usize,
160}
161
162/// メディアファイルの画像キャッシュデータの読み取りガード。
163pub struct CacheFileImageReadGuard {
164    _access: CacheAccessGuard,
165    raw: aviutl2_sys::cache2::CACHE_FILE_IMAGE,
166    byte_len: usize,
167}
168
169unsafe impl Send for CacheImageReadGuard {}
170unsafe impl Send for CacheImageWriteGuard {}
171unsafe impl Send for CacheAudioReadGuard {}
172unsafe impl Send for CacheAudioWriteGuard {}
173unsafe impl Send for CacheFileImageReadGuard {}
174
175/// メディアファイルの画像キャッシュのピクセルフォーマット。
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177pub enum CacheImagePixelFormat {
178    /// DXGI_FORMAT_R8G8B8A8_UNORM。
179    Rgba,
180    /// DXGI_FORMAT_B8G8R8A8_UNORM。
181    Bgra,
182    /// DXGI_FORMAT_B8G8R8X8_UNORM。
183    Bgr,
184    /// DXGI_FORMAT_R16G16B16A16_UNORM。
185    Pa64,
186    /// DXGI_FORMAT_R16G16B16A16_FLOAT。
187    Hf64,
188    /// DXGI_FORMAT_YUY2。
189    Yuy2,
190    /// DXGI_FORMAT_R16G16B16A16_SNORM。
191    Yc48,
192}
193
194impl From<aviutl2_sys::filter2::INPUT_PIXEL_FORMAT> for CacheImagePixelFormat {
195    fn from(value: aviutl2_sys::filter2::INPUT_PIXEL_FORMAT) -> Self {
196        match value {
197            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::RGBA => Self::Rgba,
198            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::BGRA => Self::Bgra,
199            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::BGR => Self::Bgr,
200            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::PA64 => Self::Pa64,
201            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::HF64 => Self::Hf64,
202            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::YUY2 => Self::Yuy2,
203            aviutl2_sys::filter2::INPUT_PIXEL_FORMAT::YC48 => Self::Yc48,
204        }
205    }
206}
207
208/// メディアファイルのビデオ情報。
209#[derive(Debug, Clone, Copy, PartialEq)]
210pub struct VideoFileInfo {
211    /// 総時間。
212    pub total_time: f64,
213    /// 総フレーム数。
214    pub frame_num: usize,
215    /// トラック数。
216    pub track_num: usize,
217    /// 解像度。
218    pub width: usize,
219    /// 解像度。
220    pub height: usize,
221    /// フレームレート。
222    pub fps: Rational32,
223}
224
225/// メディアファイルのオーディオ情報。
226#[derive(Debug, Clone, Copy, PartialEq)]
227pub struct AudioFileInfo {
228    /// 総時間。
229    pub total_time: f64,
230    /// 総サンプル数。
231    pub sample_num: usize,
232    /// トラック数。
233    pub track_num: usize,
234    /// サンプリングレート。
235    pub rate: i32,
236    /// チャンネル数。
237    pub channel: usize,
238}
239
240impl CacheImageReadGuard {
241    /// 画像の幅。
242    pub fn width(&self) -> usize {
243        self.raw.width as usize
244    }
245
246    /// 画像の高さ。
247    pub fn height(&self) -> usize {
248        self.raw.height as usize
249    }
250
251    /// RGBAピクセル列として取得する。
252    pub fn as_slice(&self) -> &[RgbaPixel] {
253        unsafe { std::slice::from_raw_parts(self.raw.buffer as *const RgbaPixel, self.len) }
254    }
255
256    /// RGBAピクセル列へのポインタを取得する。
257    pub fn as_ptr(&self) -> *const RgbaPixel {
258        self.raw.buffer as *const RgbaPixel
259    }
260
261    /// `u8`スライスとして取得する。
262    pub fn as_u8_slice(&self) -> &[u8] {
263        unsafe {
264            std::slice::from_raw_parts(
265                self.raw.buffer as *const u8,
266                self.len * std::mem::size_of::<RgbaPixel>(),
267            )
268        }
269    }
270
271    /// `u8`スライスへのポインタを取得する。
272    pub fn as_u8_ptr(&self) -> *const u8 {
273        self.raw.buffer as *const u8
274    }
275}
276
277impl CacheImageWriteGuard {
278    /// 画像の幅。
279    pub fn width(&self) -> usize {
280        self.raw.width as usize
281    }
282
283    /// 画像の高さ。
284    pub fn height(&self) -> usize {
285        self.raw.height as usize
286    }
287
288    /// RGBAピクセル列として取得する。
289    pub fn as_slice(&self) -> &[RgbaPixel] {
290        unsafe { std::slice::from_raw_parts(self.raw.buffer as *const RgbaPixel, self.len) }
291    }
292
293    /// RGBAピクセル列として可変取得する。
294    pub fn as_slice_mut(&mut self) -> &mut [RgbaPixel] {
295        unsafe { std::slice::from_raw_parts_mut(self.raw.buffer as *mut RgbaPixel, self.len) }
296    }
297
298    /// RGBAピクセル列への可変ポインタを取得する。
299    pub fn as_mut_ptr(&mut self) -> *mut RgbaPixel {
300        self.raw.buffer as *mut RgbaPixel
301    }
302
303    /// `u8`スライスとして取得する。
304    pub fn as_u8_slice(&self) -> &[u8] {
305        unsafe {
306            std::slice::from_raw_parts(
307                self.raw.buffer as *const u8,
308                self.len * std::mem::size_of::<RgbaPixel>(),
309            )
310        }
311    }
312
313    /// `u8`スライスとして可変取得する。
314    pub fn as_u8_slice_mut(&mut self) -> &mut [u8] {
315        unsafe {
316            std::slice::from_raw_parts_mut(
317                self.raw.buffer as *mut u8,
318                self.len * std::mem::size_of::<RgbaPixel>(),
319            )
320        }
321    }
322
323    /// RGBAピクセル列への可変ポインタを取得する。
324    pub fn as_mut_u8_ptr(&mut self) -> *mut u8 {
325        self.raw.buffer as *mut u8
326    }
327}
328
329impl CacheAudioReadGuard {
330    /// 音声キャッシュのサンプル数。
331    pub fn sample_num(&self) -> usize {
332        self.len
333    }
334
335    /// 音声キャッシュのチャンネル数。
336    pub fn channel_num(&self) -> usize {
337        self.raw.channel_num as usize
338    }
339
340    /// 指定チャンネルのサンプル列を取得する。
341    pub fn channel(&self, channel: crate::filter::AudioChannel) -> Option<&[f32]> {
342        let buffer = self.channel_ptr(channel)?;
343        Some(unsafe { std::slice::from_raw_parts(buffer, self.len) })
344    }
345
346    fn channel_ptr(&self, channel: crate::filter::AudioChannel) -> Option<*const f32> {
347        match i32::from(channel) {
348            0 if !self.raw.buffer0.is_null() => Some(self.raw.buffer0),
349            1 if self.raw.channel_num >= 2 && !self.raw.buffer1.is_null() => Some(self.raw.buffer1),
350            _ => None,
351        }
352    }
353}
354
355impl CacheAudioWriteGuard {
356    /// 音声キャッシュのサンプル数。
357    pub fn sample_num(&self) -> usize {
358        self.len
359    }
360
361    /// 音声キャッシュのチャンネル数。
362    pub fn channel_num(&self) -> usize {
363        self.raw.channel_num as usize
364    }
365
366    /// 指定チャンネルのサンプル列を取得する。
367    pub fn channel(&self, channel: crate::filter::AudioChannel) -> Option<&[f32]> {
368        let buffer = self.channel_ptr(channel)?;
369        Some(unsafe { std::slice::from_raw_parts(buffer, self.len) })
370    }
371
372    /// 指定チャンネルのサンプル列を可変取得する。
373    pub fn channel_mut(&mut self, channel: crate::filter::AudioChannel) -> Option<&mut [f32]> {
374        let buffer = self.channel_ptr_mut(channel)?;
375        Some(unsafe { std::slice::from_raw_parts_mut(buffer, self.len) })
376    }
377
378    fn channel_ptr(&self, channel: crate::filter::AudioChannel) -> Option<*const f32> {
379        match i32::from(channel) {
380            0 if !self.raw.buffer0.is_null() => Some(self.raw.buffer0),
381            1 if self.raw.channel_num >= 2 && !self.raw.buffer1.is_null() => Some(self.raw.buffer1),
382            _ => None,
383        }
384    }
385
386    fn channel_ptr_mut(&mut self, channel: crate::filter::AudioChannel) -> Option<*mut f32> {
387        match i32::from(channel) {
388            0 if !self.raw.buffer0.is_null() => Some(self.raw.buffer0),
389            1 if self.raw.channel_num >= 2 && !self.raw.buffer1.is_null() => Some(self.raw.buffer1),
390            _ => None,
391        }
392    }
393}
394
395impl CacheFileImageReadGuard {
396    /// 画像の幅。
397    pub fn width(&self) -> usize {
398        self.raw.width as usize
399    }
400
401    /// 画像の高さ。
402    pub fn height(&self) -> usize {
403        self.raw.height as usize
404    }
405
406    /// 画像データの横1ラインのバイト数。
407    pub fn pitch(&self) -> usize {
408        self.raw.pitch as usize
409    }
410
411    /// ピクセルフォーマット。
412    pub fn format(&self) -> CacheImagePixelFormat {
413        self.raw.format.into()
414    }
415
416    /// 画像データを `u8` スライスとして取得する。
417    pub fn as_u8_slice(&self) -> &[u8] {
418        unsafe { std::slice::from_raw_parts(self.raw.buffer as *const u8, self.byte_len) }
419    }
420
421    /// 画像データへのポインタを取得する。
422    pub fn as_ptr(&self) -> *const c_void {
423        self.raw.buffer
424    }
425}
426
427/// 画像キャッシュデータを取得する。
428pub fn get_image_cache(
429    identifier: &impl AsCacheIdentifier,
430    name: &str,
431) -> Result<Option<CacheImageReadGuard>, CacheError> {
432    let wide_name = CWString::new(name)?;
433    let identifier = identifier.as_cache_identifier();
434    let raw = with_cache_handle(|handle| unsafe {
435        ((*handle).get_image_cache)(identifier, wide_name.as_ptr())
436    })?;
437    if raw.buffer.is_null() {
438        Ok(None)
439    } else {
440        Ok(Some(image_read_guard(raw)?))
441    }
442}
443
444/// 画像キャッシュデータを取得し、可変取得する。
445pub fn get_image_cache_mut(
446    identifier: &impl AsCacheIdentifier,
447    name: &str,
448) -> Result<Option<CacheImageWriteGuard>, CacheError> {
449    let wide_name = CWString::new(name)?;
450    let identifier = identifier.as_cache_identifier();
451    let raw = with_cache_handle(|handle| unsafe {
452        ((*handle).get_image_cache)(identifier, wide_name.as_ptr())
453    })?;
454    if raw.buffer.is_null() {
455        Ok(None)
456    } else {
457        Ok(Some(image_write_guard(raw)?))
458    }
459}
460
461/// 画像キャッシュデータを作成し、可変取得する。
462pub fn create_image_cache(
463    identifier: &impl AsCacheIdentifier,
464    name: &str,
465    width: usize,
466    height: usize,
467) -> Result<CacheImageWriteGuard, CacheError> {
468    let wide_name = CWString::new(name)?;
469    let width = i32::try_from(width).map_err(|_| CacheError::ValueOutOfRange)?;
470    let height = i32::try_from(height).map_err(|_| CacheError::ValueOutOfRange)?;
471    let identifier = identifier.as_cache_identifier();
472    let raw = with_cache_handle(|handle| unsafe {
473        ((*handle).create_image_cache)(identifier, wide_name.as_ptr(), width, height)
474    })?;
475    image_write_guard(raw)
476}
477
478/// 音声キャッシュデータを取得する。
479pub fn get_audio_cache(
480    identifier: &impl AsCacheIdentifier,
481    name: &str,
482) -> Result<Option<CacheAudioReadGuard>, CacheError> {
483    let wide_name = CWString::new(name)?;
484    let identifier = identifier.as_cache_identifier();
485    let raw = with_cache_handle(|handle| unsafe {
486        ((*handle).get_audio_cache)(identifier, wide_name.as_ptr())
487    })?;
488    if raw.buffer0.is_null() {
489        Ok(None)
490    } else {
491        Ok(Some(audio_read_guard(raw)?))
492    }
493}
494
495/// 音声キャッシュデータを取得し、可変取得する。
496pub fn get_audio_cache_mut(
497    identifier: &impl AsCacheIdentifier,
498    name: &str,
499) -> Result<Option<CacheAudioWriteGuard>, CacheError> {
500    let wide_name = CWString::new(name)?;
501    let identifier = identifier.as_cache_identifier();
502    let raw = with_cache_handle(|handle| unsafe {
503        ((*handle).get_audio_cache)(identifier, wide_name.as_ptr())
504    })?;
505    if raw.buffer0.is_null() {
506        Ok(None)
507    } else {
508        Ok(Some(audio_write_guard(raw)?))
509    }
510}
511
512/// 音声キャッシュデータを作成し、可変取得する。
513pub fn create_audio_cache(
514    identifier: &impl AsCacheIdentifier,
515    name: &str,
516    sample_num: usize,
517    channel_num: usize,
518) -> Result<CacheAudioWriteGuard, CacheError> {
519    if !(1..=2).contains(&channel_num) {
520        return Err(CacheError::InvalidAudioChannel);
521    }
522    let wide_name = CWString::new(name)?;
523    let sample_num = i32::try_from(sample_num).map_err(|_| CacheError::ValueOutOfRange)?;
524    let channel_num = i32::try_from(channel_num).map_err(|_| CacheError::ValueOutOfRange)?;
525    let identifier = identifier.as_cache_identifier();
526    let raw = with_cache_handle(|handle| unsafe {
527        ((*handle).create_audio_cache)(identifier, wide_name.as_ptr(), sample_num, channel_num)
528    })?;
529    audio_write_guard(raw)
530}
531
532/// 画像キャッシュデータをクリアする。
533pub fn clear_image_cache(
534    identifier: &impl AsCacheIdentifier,
535    name: &str,
536) -> Result<(), CacheError> {
537    let wide_name = CWString::new(name)?;
538    let identifier = identifier.as_cache_identifier();
539    with_cache_handle(|handle| unsafe {
540        ((*handle).clear_image_cache)(identifier, wide_name.as_ptr())
541    })?;
542    Ok(())
543}
544
545/// 音声キャッシュデータをクリアする。
546pub fn clear_audio_cache(
547    identifier: &impl AsCacheIdentifier,
548    name: &str,
549) -> Result<(), CacheError> {
550    let wide_name = CWString::new(name)?;
551    let identifier = identifier.as_cache_identifier();
552    with_cache_handle(|handle| unsafe {
553        ((*handle).clear_audio_cache)(identifier, wide_name.as_ptr())
554    })?;
555    Ok(())
556}
557
558/// メディアファイルのビデオ情報を取得する。
559pub fn get_video_file_info(path: impl AsRef<Path>) -> Result<Option<VideoFileInfo>, CacheError> {
560    let file = CWString::new(&path.as_ref().to_string_lossy())?;
561    let mut raw = std::mem::MaybeUninit::<aviutl2_sys::cache2::VIDEO_INFO>::uninit();
562    let success = with_cache_handle(|handle| unsafe {
563        ((*handle).get_video_file_info)(
564            file.as_ptr(),
565            raw.as_mut_ptr(),
566            std::mem::size_of::<aviutl2_sys::cache2::VIDEO_INFO>() as i32,
567        )
568    })?;
569    if !success {
570        return Ok(None);
571    }
572    let raw = unsafe { raw.assume_init() };
573    Ok(Some(VideoFileInfo {
574        total_time: raw.total_time,
575        frame_num: usize::try_from(raw.frame_num).map_err(|_| CacheError::ValueOutOfRange)?,
576        track_num: usize::try_from(raw.track_num).map_err(|_| CacheError::ValueOutOfRange)?,
577        width: usize::try_from(raw.width).map_err(|_| CacheError::ValueOutOfRange)?,
578        height: usize::try_from(raw.height).map_err(|_| CacheError::ValueOutOfRange)?,
579        fps: Rational32::new(raw.rate, raw.scale),
580    }))
581}
582
583/// メディアファイルのオーディオ情報を取得する。
584pub fn get_audio_file_info(path: impl AsRef<Path>) -> Result<Option<AudioFileInfo>, CacheError> {
585    let file = CWString::new(&path.as_ref().to_string_lossy())?;
586    let mut raw = std::mem::MaybeUninit::<aviutl2_sys::cache2::AUDIO_INFO>::uninit();
587    let success = with_cache_handle(|handle| unsafe {
588        ((*handle).get_audio_file_info)(
589            file.as_ptr(),
590            raw.as_mut_ptr(),
591            std::mem::size_of::<aviutl2_sys::cache2::AUDIO_INFO>() as i32,
592        )
593    })?;
594    if !success {
595        return Ok(None);
596    }
597    let raw = unsafe { raw.assume_init() };
598    Ok(Some(AudioFileInfo {
599        total_time: raw.total_time,
600        sample_num: usize::try_from(raw.sample_num).map_err(|_| CacheError::ValueOutOfRange)?,
601        track_num: usize::try_from(raw.track_num).map_err(|_| CacheError::ValueOutOfRange)?,
602        rate: raw.rate,
603        channel: usize::try_from(raw.channel).map_err(|_| CacheError::ValueOutOfRange)?,
604    }))
605}
606
607/// 画像ファイルから画像データをキャッシュ経由で取得する。
608pub fn get_image_file_cache(
609    path: impl AsRef<Path>,
610) -> Result<Option<CacheFileImageReadGuard>, CacheError> {
611    let file = CWString::new(&path.as_ref().to_string_lossy())?;
612    let raw =
613        with_cache_handle(|handle| unsafe { ((*handle).get_image_file_cache)(file.as_ptr()) })?;
614    if raw.buffer.is_null() {
615        Ok(None)
616    } else {
617        Ok(Some(file_image_read_guard(raw)?))
618    }
619}
620
621/// メディアファイルから指定フレームの画像データをキャッシュ経由で取得する。
622pub fn get_video_file_cache(
623    path: impl AsRef<Path>,
624    track: usize,
625    frame: usize,
626) -> Result<Option<CacheFileImageReadGuard>, CacheError> {
627    let file = CWString::new(&path.as_ref().to_string_lossy())?;
628    let track = i32::try_from(track).map_err(|_| CacheError::ValueOutOfRange)?;
629    let frame = i32::try_from(frame).map_err(|_| CacheError::ValueOutOfRange)?;
630    let raw = with_cache_handle(|handle| unsafe {
631        ((*handle).get_video_file_cache)(file.as_ptr(), track, frame)
632    })?;
633    if raw.buffer.is_null() {
634        Ok(None)
635    } else {
636        Ok(Some(file_image_read_guard(raw)?))
637    }
638}
639
640/// メディアファイルから指定時間の画像データをキャッシュ経由で取得する。
641pub fn get_video_file_cache_by_time(
642    path: impl AsRef<Path>,
643    track: usize,
644    time: f64,
645) -> Result<Option<CacheFileImageReadGuard>, CacheError> {
646    let file = CWString::new(&path.as_ref().to_string_lossy())?;
647    let track = i32::try_from(track).map_err(|_| CacheError::ValueOutOfRange)?;
648    let raw = with_cache_handle(|handle| unsafe {
649        ((*handle).get_video_file_cache_by_time)(file.as_ptr(), track, time)
650    })?;
651    if raw.buffer.is_null() {
652        Ok(None)
653    } else {
654        Ok(Some(file_image_read_guard(raw)?))
655    }
656}
657
658/// メディアファイルから音声データをキャッシュ経由で取得する。
659///
660/// # Panics
661///
662/// `buffer0` と `buffer1` の長さが異なる場合にpanicします。
663///
664/// # Returns
665///
666/// 戻り値は実際に取得したサンプル数です。
667pub fn get_audio_file_data(
668    path: impl AsRef<Path>,
669    track: usize,
670    sample_index: usize,
671    buffer0: &mut [f32],
672    buffer1: &mut [f32],
673) -> Result<usize, CacheError> {
674    assert_eq!(
675        buffer0.len(),
676        buffer1.len(),
677        "Audio buffers must have the same length"
678    );
679    let file = CWString::new(&path.as_ref().to_string_lossy())?;
680    let track = i32::try_from(track).map_err(|_| CacheError::ValueOutOfRange)?;
681    let sample_index = i64::try_from(sample_index).map_err(|_| CacheError::ValueOutOfRange)?;
682    let sample_num = i32::try_from(buffer0.len()).map_err(|_| CacheError::ValueOutOfRange)?;
683    let read = with_cache_handle(|handle| unsafe {
684        ((*handle).get_audio_file_data)(
685            file.as_ptr(),
686            track,
687            sample_index,
688            sample_num,
689            buffer0.as_mut_ptr(),
690            buffer1.as_mut_ptr(),
691        )
692    })?;
693    usize::try_from(read).map_err(|_| CacheError::ValueOutOfRange)
694}
695
696fn image_read_guard(
697    raw: aviutl2_sys::cache2::CACHE_IMAGE,
698) -> Result<CacheImageReadGuard, CacheError> {
699    let len = image_len(&raw)?;
700    let access = CacheAccessGuard::read(raw.buffer as usize);
701    Ok(CacheImageReadGuard {
702        _access: access,
703        raw,
704        len,
705    })
706}
707
708fn image_write_guard(
709    raw: aviutl2_sys::cache2::CACHE_IMAGE,
710) -> Result<CacheImageWriteGuard, CacheError> {
711    if raw.buffer.is_null() {
712        return Err(CacheError::ApiCallFailed);
713    }
714    let len = image_len(&raw)?;
715    let access = CacheAccessGuard::write(raw.buffer as usize);
716    Ok(CacheImageWriteGuard {
717        _access: access,
718        raw,
719        len,
720    })
721}
722
723fn audio_read_guard(
724    raw: aviutl2_sys::cache2::CACHE_AUDIO,
725) -> Result<CacheAudioReadGuard, CacheError> {
726    let len = audio_len(&raw)?;
727    let access = CacheAccessGuard::read(raw.buffer0 as usize);
728    Ok(CacheAudioReadGuard {
729        _access: access,
730        raw,
731        len,
732    })
733}
734
735fn audio_write_guard(
736    raw: aviutl2_sys::cache2::CACHE_AUDIO,
737) -> Result<CacheAudioWriteGuard, CacheError> {
738    if raw.buffer0.is_null() {
739        return Err(CacheError::ApiCallFailed);
740    }
741    let len = audio_len(&raw)?;
742    let access = CacheAccessGuard::write(raw.buffer0 as usize);
743    Ok(CacheAudioWriteGuard {
744        _access: access,
745        raw,
746        len,
747    })
748}
749
750fn file_image_read_guard(
751    raw: aviutl2_sys::cache2::CACHE_FILE_IMAGE,
752) -> Result<CacheFileImageReadGuard, CacheError> {
753    let byte_len = file_image_byte_len(&raw)?;
754    let access = CacheAccessGuard::read(raw.buffer as usize);
755    Ok(CacheFileImageReadGuard {
756        _access: access,
757        raw,
758        byte_len,
759    })
760}
761
762fn image_len(raw: &aviutl2_sys::cache2::CACHE_IMAGE) -> Result<usize, CacheError> {
763    let width = usize::try_from(raw.width).map_err(|_| CacheError::ValueOutOfRange)?;
764    let height = usize::try_from(raw.height).map_err(|_| CacheError::ValueOutOfRange)?;
765    width.checked_mul(height).ok_or(CacheError::ValueOutOfRange)
766}
767
768fn file_image_byte_len(raw: &aviutl2_sys::cache2::CACHE_FILE_IMAGE) -> Result<usize, CacheError> {
769    let pitch = usize::try_from(raw.pitch).map_err(|_| CacheError::ValueOutOfRange)?;
770    let height = usize::try_from(raw.height).map_err(|_| CacheError::ValueOutOfRange)?;
771    pitch.checked_mul(height).ok_or(CacheError::ValueOutOfRange)
772}
773
774fn audio_len(raw: &aviutl2_sys::cache2::CACHE_AUDIO) -> Result<usize, CacheError> {
775    usize::try_from(raw.sample_num).map_err(|_| CacheError::ValueOutOfRange)
776}
777
778fn with_cache_handle<T>(
779    f: impl FnOnce(*mut aviutl2_sys::cache2::CACHE_HANDLE) -> T,
780) -> Result<T, CacheError> {
781    let handle = CACHE_HANDLE.get().expect("Cache handle is not initialized");
782    let handle = handle.lock().unwrap();
783    if handle.0.is_null() {
784        panic!("Cache handle raw pointer is null");
785    }
786    Ok(f(handle.0))
787}