Skip to main content

pito_codecs/media/front/
sound.rs

1use crate::mp4::{CodecConfig, PcmConfig};
2
3use super::demux::Demux;
4use crate::media::{Error, Result};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7enum Kind {
8    U8,
9    S8,
10    S16,
11    S24,
12    S32,
13    F32,
14    F64,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct Pcm {
19    kind: Kind,
20    big_endian: bool,
21    channels: u16,
22    rate: u32,
23}
24
25impl Pcm {
26    pub fn new(
27        bits: u16,
28        float: bool,
29        signed: bool,
30        big_endian: bool,
31        channels: u64,
32        rate: f64,
33    ) -> Result<Pcm> {
34        let kind = match (float, bits) {
35            (false, 8) if !signed => Kind::U8,
36            (false, 8) => Kind::S8,
37            (false, 16) => Kind::S16,
38            (false, 24) => Kind::S24,
39            (false, 32) => Kind::S32,
40            (true, 32) => Kind::F32,
41            (true, 64) => Kind::F64,
42            _ => {
43                let form = match (float, signed) {
44                    (true, _) => "float",
45                    (false, true) => "signed",
46                    (false, false) => "unsigned",
47                };
48                return Err(Error::Unsupported(format!("{form} {bits}-bit PCM")));
49            }
50        };
51        if !(1..=64).contains(&channels) {
52            return Err(Error::Invalid(format!("PCM with {channels} channels")));
53        }
54        if !rate.is_finite() || !(1000.0..=768_000.0).contains(&rate) {
55            return Err(Error::Invalid(format!("PCM at {rate} Hz")));
56        }
57        Ok(Pcm {
58            kind,
59            big_endian,
60            channels: channels as u16,
61            rate: rate.round() as u32,
62        })
63    }
64
65    pub fn from_mp4(p: &PcmConfig) -> Result<Pcm> {
66        Pcm::new(
67            p.bits,
68            p.float,
69            p.signed,
70            p.big_endian,
71            p.channels as u64,
72            p.sample_rate,
73        )
74    }
75
76    fn width(&self) -> usize {
77        match self.kind {
78            Kind::U8 | Kind::S8 => 1,
79            Kind::S16 => 2,
80            Kind::S24 => 3,
81            Kind::S32 | Kind::F32 => 4,
82            Kind::F64 => 8,
83        }
84    }
85
86    pub fn decode(&self, data: &[u8], out: &mut Vec<f32>) -> Result<()> {
87        let w = self.width();
88        let frame = w * self.channels as usize;
89        if !data.len().is_multiple_of(frame) {
90            return Err(Error::Invalid(format!(
91                "a PCM packet of {} bytes, not whole {frame}-byte frames",
92                data.len()
93            )));
94        }
95        out.clear();
96        out.reserve(data.len() / w);
97        let be = self.big_endian;
98        match self.kind {
99            Kind::U8 => out.extend(data.iter().map(|&b| (b as f32 - 128.0) / 128.0)),
100            Kind::S8 => out.extend(data.iter().map(|&b| b as i8 as f32 / 128.0)),
101            Kind::S16 => out.extend(data.chunks_exact(2).map(|c| {
102                let v = [c[0], c[1]];
103                let s = if be {
104                    i16::from_be_bytes(v)
105                } else {
106                    i16::from_le_bytes(v)
107                };
108                s as f32 / 32768.0
109            })),
110            Kind::S24 => out.extend(data.chunks_exact(3).map(|c| {
111                let v = if be {
112                    [c[0], c[1], c[2], 0]
113                } else {
114                    [c[2], c[1], c[0], 0]
115                };
116                (i32::from_be_bytes(v) >> 8) as f32 / 8_388_608.0
117            })),
118            Kind::S32 => out.extend(data.chunks_exact(4).map(|c| {
119                let v = [c[0], c[1], c[2], c[3]];
120                let s = if be {
121                    i32::from_be_bytes(v)
122                } else {
123                    i32::from_le_bytes(v)
124                };
125                s as f32 / 2_147_483_648.0
126            })),
127            Kind::F32 => out.extend(data.chunks_exact(4).map(|c| {
128                let v = [c[0], c[1], c[2], c[3]];
129                if be {
130                    f32::from_be_bytes(v)
131                } else {
132                    f32::from_le_bytes(v)
133                }
134            })),
135            Kind::F64 => out.extend(data.chunks_exact(8).map(|c| {
136                let v = [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]];
137                (if be {
138                    f64::from_be_bytes(v)
139                } else {
140                    f64::from_le_bytes(v)
141                }) as f32
142            })),
143        }
144        Ok(())
145    }
146}
147
148pub enum Sound {
149    Aac(Box<crate::aac::Decoder>),
150    #[cfg(feature = "ac3")]
151    Ac3(Box<crate::ac3::Decoder>),
152    #[cfg(feature = "opus")]
153    Opus(Box<crate::opus::Decoder>),
154    #[cfg(feature = "mp3")]
155    Mp3(Box<crate::mp3::Decoder>),
156    Pcm(Pcm),
157}
158
159fn unsupported(name: &str) -> Error {
160    Error::Unsupported(format!(
161        "our audio decoders read AAC, AC-3, E-AC-3, Opus, MP3 and PCM, not {}",
162        if name.is_empty() {
163            "this track's codec"
164        } else {
165            name
166        }
167    ))
168}
169
170fn mkv_pcm(t: &crate::mkv::Track) -> Result<Pcm> {
171    let a = t.audio.clone().unwrap_or_default();
172    let (float, big_endian) = match t.codec_id.as_str() {
173        "A_PCM/INT/LIT" => (false, false),
174        "A_PCM/INT/BIG" => (false, true),
175        "A_PCM/FLOAT/IEEE" => (true, false),
176        id => return Err(unsupported(id)),
177    };
178    let bits = a.bit_depth.unwrap_or(if float { 32 } else { 16 });
179    let bits = u16::try_from(bits).map_err(|_| Error::Invalid(format!("{bits}-bit PCM")))?;
180    Pcm::new(
181        bits,
182        float,
183        bits != 8,
184        big_endian,
185        a.channels,
186        a.sampling_frequency,
187    )
188}
189
190#[cfg(feature = "opus")]
191fn dops(e: &crate::mp4::SampleEntry) -> Option<&[u8]> {
192    let extra = match e.audio.as_ref().map_or(0, |a| a.qt_version) {
193        1 => 16,
194        2 => 36,
195        _ => 0,
196    };
197    let mut rest = e.raw.get(28 + extra..)?;
198    while rest.len() >= 8 {
199        let size = u32::from_be_bytes(rest.get(..4)?.try_into().ok()?) as usize;
200        if size < 8 || size > rest.len() {
201            return None;
202        }
203        if rest.get(4..8)? == b"dOps" {
204            return rest.get(8..size);
205        }
206        rest = rest.get(size..)?;
207    }
208    None
209}
210
211impl Sound {
212    #[cfg(feature = "mp3")]
213    fn mp3(rate: f64, channels: u64) -> Sound {
214        let rate = if (1.0..=768_000.0).contains(&rate) {
215            rate.round() as u32
216        } else {
217            0
218        };
219        Sound::Mp3(Box::new(crate::mp3::Decoder::with_format(
220            rate,
221            channels.min(2) as u16,
222        )))
223    }
224
225    pub fn open(d: &Demux, track: usize) -> Result<Sound> {
226        match d {
227            Demux::Mp4(r) => {
228                let t = r
229                    .tracks
230                    .get(track)
231                    .ok_or_else(|| Error::Invalid(format!("no track {track}")))?;
232                let Some(e) = t.entry() else {
233                    return Err(unsupported(""));
234                };
235                match &e.codec {
236                    CodecConfig::Aac(_) => {
237                        let asc = t.extradata().ok_or_else(|| {
238                            Error::Invalid("AAC track without a config".to_string())
239                        })?;
240                        Ok(Sound::Aac(Box::new(crate::aac::Decoder::new(&asc)?)))
241                    }
242                    CodecConfig::Pcm(p) => Ok(Sound::Pcm(Pcm::from_mp4(p)?)),
243                    #[cfg(feature = "ac3")]
244                    CodecConfig::Other(f) if &f.0 == b"ac-3" || &f.0 == b"ec-3" => {
245                        Ok(Sound::Ac3(Box::new(crate::ac3::Decoder::new(
246                            t.sample_rate(),
247                            t.channels() as u16,
248                        ))))
249                    }
250                    #[cfg(feature = "mp3")]
251                    CodecConfig::Mp3 => {
252                        let a = e.audio.clone().unwrap_or_default();
253                        Ok(Sound::mp3(a.sample_rate, a.channels as u64))
254                    }
255                    #[cfg(feature = "opus")]
256                    CodecConfig::Other(f) if &f.0 == b"Opus" => {
257                        let head = dops(e).ok_or_else(|| {
258                            Error::Invalid("Opus track without a dOps box".to_string())
259                        })?;
260                        let head = crate::opus::OpusHead::from_dops(head)?;
261                        Ok(Sound::Opus(Box::new(crate::opus::Decoder::from_head(
262                            head, 48000,
263                        )?)))
264                    }
265                    other => Err(unsupported(&other.name())),
266                }
267            }
268            Demux::Mkv(r) => {
269                let t = r
270                    .tracks
271                    .get(track)
272                    .ok_or_else(|| Error::Invalid(format!("no track {track}")))?;
273                match t.codec_name() {
274                    "aac" => {
275                        let asc = t.extradata().ok_or_else(|| {
276                            Error::Invalid("AAC track without a config".to_string())
277                        })?;
278                        Ok(Sound::Aac(Box::new(crate::aac::Decoder::new(&asc)?)))
279                    }
280                    #[cfg(feature = "opus")]
281                    "opus" => Ok(Sound::Opus(Box::new(crate::opus::Decoder::new(
282                        &t.codec_private,
283                    )?))),
284                    "pcm" => Ok(Sound::Pcm(mkv_pcm(t)?)),
285                    #[cfg(feature = "ac3")]
286                    "ac3" | "eac3" => Ok(Sound::Ac3(Box::new(crate::ac3::Decoder::new(
287                        t.sample_rate(),
288                        t.channels() as u16,
289                    )))),
290                    #[cfg(feature = "mp3")]
291                    "mp3" | "mp2" | "mp1" => {
292                        let a = t.audio.clone().unwrap_or_default();
293                        Ok(Sound::mp3(a.sampling_frequency, a.channels))
294                    }
295                    name => Err(unsupported(name)),
296                }
297            }
298        }
299    }
300
301    pub fn rate(&self) -> u32 {
302        match self {
303            Sound::Aac(d) => d.rate(),
304            #[cfg(feature = "ac3")]
305            Sound::Ac3(d) => d.rate(),
306            #[cfg(feature = "opus")]
307            Sound::Opus(d) => d.sample_rate(),
308            #[cfg(feature = "mp3")]
309            Sound::Mp3(d) => d.rate(),
310            Sound::Pcm(p) => p.rate,
311        }
312    }
313
314    pub fn channels(&self) -> u16 {
315        match self {
316            Sound::Aac(d) => d.channels(),
317            #[cfg(feature = "ac3")]
318            Sound::Ac3(d) => d.channels(),
319            #[cfg(feature = "opus")]
320            Sound::Opus(d) => d.channels() as u16,
321            #[cfg(feature = "mp3")]
322            Sound::Mp3(d) => d.channels(),
323            Sound::Pcm(p) => p.channels,
324        }
325    }
326
327    pub fn pcm_frames(&self, bytes: u32) -> Option<u64> {
328        match self {
329            Sound::Pcm(p) => Some(bytes as u64 / (p.width() * p.channels as usize).max(1) as u64),
330            Sound::Aac(_) => None,
331            #[cfg(feature = "ac3")]
332            Sound::Ac3(_) => None,
333            #[cfg(feature = "opus")]
334            Sound::Opus(_) => None,
335            #[cfg(feature = "mp3")]
336            Sound::Mp3(_) => None,
337        }
338    }
339
340    pub fn pre_roll(&self) -> f64 {
341        match self {
342            #[cfg(feature = "opus")]
343            Sound::Opus(_) => 0.08,
344            #[cfg(feature = "ac3")]
345            Sound::Ac3(d) => 1536.0 / d.rate().max(1) as f64,
346            #[cfg(feature = "mp3")]
347            Sound::Mp3(_) => 0.2,
348            Sound::Aac(_) | Sound::Pcm(_) => 0.0,
349        }
350    }
351
352    pub fn decode(&mut self, data: &[u8], out: &mut Vec<f32>) -> Result<u16> {
353        match self {
354            Sound::Aac(d) => {
355                let b = d.decode_frame(data)?;
356                *out = b.samples;
357                Ok(b.channels)
358            }
359            #[cfg(feature = "opus")]
360            Sound::Opus(d) => {
361                out.clear();
362                d.decode_into(Some(data), out)?;
363                Ok(d.channels() as u16)
364            }
365            #[cfg(feature = "ac3")]
366            Sound::Ac3(d) => {
367                d.decode_into(data, out)?;
368                Ok(d.channels())
369            }
370            #[cfg(feature = "mp3")]
371            Sound::Mp3(d) => {
372                d.decode_packet(data, out)?;
373                Ok(d.channels())
374            }
375            Sound::Pcm(p) => {
376                p.decode(data, out)?;
377                Ok(p.channels)
378            }
379        }
380    }
381}