Skip to main content

falcon_ffmpeg/
audio_file.rs

1//! A file's sound alone, decoded on demand: falcon's shared audio decoder
2//! (`falcon_library::audio`) falls back to it for files its pure-Rust
3//! decoders can't read, such as a video's soundtrack or WMA.
4
5use std::path::Path;
6
7use crate::decode::{Decoder, Input, OUT_CHANNELS, OUT_RATE, Packet, Resampler};
8use crate::{Error, Result, ffi};
9
10/// The best sound track of a file as interleaved stereo `f32` at 48 kHz.
11pub struct AudioFile {
12    input: Input,
13    decoder: Decoder,
14    resampler: Resampler,
15    packet: Packet,
16    buf: Vec<f32>,
17    pos: usize,
18    /// After a seek: drop sound before this time.
19    skip_to: Option<f64>,
20    drained: bool,
21    duration: f64,
22    /// Media time of the next sample handed out.
23    position: f64,
24}
25
26impl AudioFile {
27    pub const CHANNELS: u16 = OUT_CHANNELS as u16;
28    pub const RATE: u32 = OUT_RATE;
29
30    pub fn open(path: &Path) -> Result<AudioFile> {
31        let input = Input::open(path)?;
32        let (index, codec) = input.best(ffi::AVMEDIA_TYPE_AUDIO).ok_or_else(|| Error("no sound FFmpeg can decode".into()))?;
33        let decoder = Decoder::open(&input, index, codec)?;
34        let duration = input.duration(Some(index));
35        let (resampler, packet) = (Resampler::new(input.api()), Packet::new(input.api())?);
36        Ok(AudioFile { input, decoder, resampler, packet, buf: Vec::new(), pos: 0, skip_to: None, drained: false, duration, position: 0.0 })
37    }
38
39    /// Length in seconds (0 when unknown).
40    pub fn duration(&self) -> f64 {
41        self.duration
42    }
43
44    /// Where the next sample is, in seconds.
45    pub fn position(&self) -> f64 {
46        self.position
47    }
48
49    /// The next sample, or `None` at the end.
50    pub fn next_sample(&mut self) -> Option<f32> {
51        while self.pos >= self.buf.len() {
52            if !self.fill() {
53                return None;
54            }
55        }
56        let s = self.buf[self.pos];
57        self.pos += 1;
58        if self.pos.is_multiple_of(OUT_CHANNELS) {
59            self.position += 1.0 / f64::from(OUT_RATE);
60        }
61        Some(s)
62    }
63
64    /// The rest of the decoded block, or the next one: interleaved stereo,
65    /// never empty; `None` at the end.
66    pub fn next_block(&mut self) -> Option<&[f32]> {
67        if self.pos >= self.buf.len() && !self.fill() {
68            return None;
69        }
70        let block = &self.buf[self.pos..];
71        self.position += (block.len() / OUT_CHANNELS) as f64 / f64::from(OUT_RATE);
72        self.pos = self.buf.len();
73        Some(block)
74    }
75
76    /// Moves to `secs`: the packet before it, decoded forward to it.
77    pub fn seek(&mut self, secs: f64) -> Result<()> {
78        self.input.seek(secs)?;
79        self.decoder.flush();
80        self.resampler.reset();
81        self.buf.clear();
82        self.pos = 0;
83        self.skip_to = Some(secs.max(0.0));
84        self.drained = false;
85        self.position = secs.max(0.0);
86        Ok(())
87    }
88
89    /// Decodes until there are samples in `buf`; false at the end.
90    fn fill(&mut self) -> bool {
91        self.buf.clear();
92        self.pos = 0;
93        let index = self.decoder.info.index;
94        while self.buf.is_empty() {
95            if self.drained {
96                return false;
97            }
98            self.packet.unref();
99            match self.input.read(&mut self.packet) {
100                Ok(true) if self.packet.stream_index() != index => continue,
101                Ok(true) => {
102                    let _ = self.decoder.send(Some(&self.packet));
103                }
104                _ => {
105                    let _ = self.decoder.send(None);
106                    self.drained = true;
107                }
108            }
109            while self.decoder.receive() {
110                let f = self.decoder.frame();
111                let pts = self.decoder.pts(f);
112                let Ok(mut samples) = self.resampler.convert(f, 1.0) else { continue };
113                if let (Some(target), Some(t)) = (self.skip_to, pts) {
114                    if t < target {
115                        let skip = (((target - t) * f64::from(OUT_RATE)) as usize * OUT_CHANNELS).min(samples.len());
116                        samples.drain(..skip);
117                    }
118                    if !samples.is_empty() {
119                        self.skip_to = None;
120                    }
121                }
122                self.buf.extend_from_slice(&samples);
123            }
124        }
125        true
126    }
127}