Skip to main content

pixelflux/recorder/
ogg.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! Reader for the Ogg Opus stream an audio sink serves: pages are taken as they arrive
8//! and split into packets, a packet continued across pages reassembled. The first packet
9//! is the `OpusHead` the audio track is declared from; every later packet comes with the
10//! page's granule position, the 48 kHz sample count at the end of the last packet the
11//! page completes, which is the recorder's audio clock.
12
13use std::collections::VecDeque;
14use std::io::{self, Read};
15
16/// What `OpusHead` declares; `mapping` holds the stream count, coupled count, and channel
17/// table for channel mapping family 1 and is empty for family 0.
18pub struct OpusHead {
19    pub channels: u8,
20    pub pre_skip: u16,
21    pub input_sample_rate: u32,
22    pub output_gain: i16,
23    pub mapping_family: u8,
24    pub mapping: Vec<u8>,
25}
26
27pub fn parse_opus_head(packet: &[u8]) -> Option<OpusHead> {
28    if packet.len() < 19 || &packet[..8] != b"OpusHead" {
29        return None;
30    }
31    let mapping_family = packet[18];
32    let channels = packet[9];
33    let mapping = if mapping_family == 0 {
34        Vec::new()
35    } else {
36        packet.get(19..21 + channels as usize)?.to_vec()
37    };
38    Some(OpusHead {
39        channels,
40        pre_skip: u16::from_le_bytes([packet[10], packet[11]]),
41        input_sample_rate: u32::from_le_bytes(packet[12..16].try_into().ok()?),
42        output_gain: i16::from_le_bytes([packet[16], packet[17]]),
43        mapping_family,
44        mapping,
45    })
46}
47
48/// Splits an Ogg stream into packets.
49pub struct PageReader<R: Read> {
50    inner: R,
51    /// A packet the previous page did not complete.
52    carry: Vec<u8>,
53    pending: VecDeque<(Vec<u8>, Option<u64>)>,
54}
55
56impl<R: Read> PageReader<R> {
57    pub fn new(inner: R) -> Self {
58        Self {
59            inner,
60            carry: Vec::new(),
61            pending: VecDeque::new(),
62        }
63    }
64
65    pub fn inner_mut(&mut self) -> &mut R {
66        &mut self.inner
67    }
68
69    /// The next packet with the granule of the page it completes, `None` on the page's
70    /// earlier packets; `Ok(None)` once the stream has ended.
71    pub fn next_packet(&mut self) -> io::Result<Option<(Vec<u8>, Option<u64>)>> {
72        while self.pending.is_empty() {
73            if !self.read_page()? {
74                return Ok(None);
75            }
76        }
77        Ok(self.pending.pop_front())
78    }
79
80    fn read_exact_or_end(&mut self, buf: &mut [u8]) -> io::Result<bool> {
81        let mut filled = 0;
82        while filled < buf.len() {
83            match self.inner.read(&mut buf[filled..]) {
84                Ok(0) => return Ok(false),
85                Ok(n) => filled += n,
86                Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
87                Err(e) => return Err(e),
88            }
89        }
90        Ok(true)
91    }
92
93    fn read_page(&mut self) -> io::Result<bool> {
94        let mut header = [0u8; 27];
95        if !self.read_exact_or_end(&mut header)? {
96            return Ok(false);
97        }
98        if &header[..4] != b"OggS" {
99            return Err(io::Error::new(
100                io::ErrorKind::InvalidData,
101                "not an Ogg page",
102            ));
103        }
104        let granule = u64::from_le_bytes(header[6..14].try_into().unwrap());
105        let mut lacing = vec![0u8; header[26] as usize];
106        if !self.read_exact_or_end(&mut lacing)? {
107            return Ok(false);
108        }
109        let mut body = vec![0u8; lacing.iter().map(|&l| l as usize).sum()];
110        if !self.read_exact_or_end(&mut body)? {
111            return Ok(false);
112        }
113        let mut at = 0;
114        let mut completed: Vec<Vec<u8>> = Vec::new();
115        for &lace in &lacing {
116            self.carry.extend_from_slice(&body[at..at + lace as usize]);
117            at += lace as usize;
118            if lace < 255 {
119                completed.push(std::mem::take(&mut self.carry));
120            }
121        }
122        let last = completed.len().saturating_sub(1);
123        for (i, packet) in completed.into_iter().enumerate() {
124            self.pending
125                .push_back((packet, (i == last).then_some(granule)));
126        }
127        Ok(true)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    fn page(granule: u64, packets: &[&[u8]], continued_tail: bool) -> Vec<u8> {
136        let mut lacing = Vec::new();
137        let mut body = Vec::new();
138        for (i, p) in packets.iter().enumerate() {
139            lacing.extend(std::iter::repeat_n(255u8, p.len() / 255));
140            if !(continued_tail && i == packets.len() - 1) {
141                lacing.push((p.len() % 255) as u8);
142            }
143            body.extend_from_slice(p);
144        }
145        let mut out = b"OggS\0\0".to_vec();
146        out.extend_from_slice(&granule.to_le_bytes());
147        out.extend_from_slice(&[0u8; 12]);
148        out.push(lacing.len() as u8);
149        out.extend_from_slice(&lacing);
150        out.extend_from_slice(&body);
151        out
152    }
153
154    /// Packets come out in order, a page's granule on its last packet only, a packet cut
155    /// across two pages whole, and a head parses into its fields.
156    #[test]
157    fn packets_and_granules() {
158        let mut head = b"OpusHead".to_vec();
159        head.extend_from_slice(&[1, 2, 0x38, 0x01, 0x80, 0xbb, 0, 0, 0, 0, 0]);
160        let long = vec![7u8; 510];
161        let mut stream = page(0, &[&head], false);
162        stream.extend(page(1920, &[b"aa", b"bbb"], false));
163        stream.extend(page(2880, &[&long], true));
164        stream.extend(page(3840, &[&[8u8; 0][..]], false));
165        let mut reader = PageReader::new(&stream[..]);
166        let (first, g0) = reader.next_packet().unwrap().unwrap();
167        let parsed = parse_opus_head(&first).expect("head");
168        assert_eq!(
169            (
170                parsed.channels,
171                parsed.pre_skip,
172                parsed.input_sample_rate,
173                parsed.mapping_family
174            ),
175            (2, 312, 48000, 0)
176        );
177        assert_eq!(g0, Some(0));
178        assert_eq!(
179            reader.next_packet().unwrap().unwrap(),
180            (b"aa".to_vec(), None)
181        );
182        assert_eq!(
183            reader.next_packet().unwrap().unwrap(),
184            (b"bbb".to_vec(), Some(1920))
185        );
186        assert_eq!(reader.next_packet().unwrap().unwrap(), (long, Some(3840)));
187        assert!(reader.next_packet().unwrap().is_none());
188    }
189}