Skip to main content

falcon_library/audio/
ffmpeg.rs

1//! The FFmpeg backend: files symphonia and the Opus decoder can't read
2//! (the soundtrack of a Matroska, WebM, AVI or WMV file) decode
3//! through `falcon-ffmpeg` when an LGPL FFmpeg is installed.
4
5use std::path::Path;
6
7use super::{AudioError, AudioSource, Block};
8
9/// A file's sound decoded by FFmpeg, as stereo `f32` at 48 kHz.
10pub struct FfmpegSource(falcon_ffmpeg::AudioFile);
11
12impl FfmpegSource {
13    /// Opens `path` when FFmpeg is installed and can read its sound.
14    pub fn open(path: &Path) -> Result<FfmpegSource, AudioError> {
15        if !falcon_ffmpeg::available() {
16            return Err(AudioError::UnsupportedCodec);
17        }
18        falcon_ffmpeg::AudioFile::open(path).map(FfmpegSource).map_err(|_| AudioError::UnsupportedCodec)
19    }
20}
21
22impl AudioSource for FfmpegSource {
23    fn channels(&self) -> u16 {
24        falcon_ffmpeg::AudioFile::CHANNELS
25    }
26
27    fn sample_rate(&self) -> u32 {
28        falcon_ffmpeg::AudioFile::RATE
29    }
30
31    fn duration(&self) -> Option<f64> {
32        Some(self.0.duration()).filter(|d| *d > 0.0)
33    }
34
35    fn next_block(&mut self) -> Option<Block<'_>> {
36        let samples = self.0.next_block()?;
37        Some(Block { samples, channels: falcon_ffmpeg::AudioFile::CHANNELS, rate: falcon_ffmpeg::AudioFile::RATE })
38    }
39
40    fn seek(&mut self, secs: f64) -> Result<f64, AudioError> {
41        let t = match self.duration() {
42            Some(d) => secs.clamp(0.0, d),
43            None => secs.max(0.0),
44        };
45        self.0.seek(t).map_err(|e| AudioError::Seek(e.0))?;
46        Ok(t)
47    }
48
49    fn position(&self) -> f64 {
50        self.0.position()
51    }
52
53    fn backend(&self) -> &'static str {
54        "ffmpeg"
55    }
56}