webrtc_media/io/ogg_reader/
mod.rs

1#[cfg(test)]
2mod ogg_reader_test;
3
4use std::io::{Cursor, Read};
5
6use byteorder::{LittleEndian, ReadBytesExt};
7use bytes::BytesMut;
8
9use crate::error::{Error, Result};
10use crate::io::ResetFn;
11
12pub const PAGE_HEADER_TYPE_CONTINUATION_OF_STREAM: u8 = 0x00;
13pub const PAGE_HEADER_TYPE_BEGINNING_OF_STREAM: u8 = 0x02;
14pub const PAGE_HEADER_TYPE_END_OF_STREAM: u8 = 0x04;
15pub const DEFAULT_PRE_SKIP: u16 = 3840; // 3840 recommended in the RFC
16pub const PAGE_HEADER_SIGNATURE: &[u8] = b"OggS";
17pub const ID_PAGE_SIGNATURE: &[u8] = b"OpusHead";
18pub const COMMENT_PAGE_SIGNATURE: &[u8] = b"OpusTags";
19pub const PAGE_HEADER_SIZE: usize = 27;
20pub const ID_PAGE_PAYLOAD_SIZE: usize = 19;
21
22/// OggReader is used to read Ogg files and return page payloads
23pub struct OggReader<R: Read> {
24    reader: R,
25    bytes_read: usize,
26    checksum_table: [u32; 256],
27    do_checksum: bool,
28}
29
30/// OggHeader is the metadata from the first two pages
31/// in the file (ID and Comment)
32/// <https://tools.ietf.org/html/rfc7845.html#section-3>
33pub struct OggHeader {
34    pub channel_map: u8,
35    pub channels: u8,
36    pub output_gain: u16,
37    pub pre_skip: u16,
38    pub sample_rate: u32,
39    pub version: u8,
40}
41
42/// OggPageHeader is the metadata for a Page
43/// Pages are the fundamental unit of multiplexing in an Ogg stream
44/// <https://tools.ietf.org/html/rfc7845.html#section-1>
45pub struct OggPageHeader {
46    pub granule_position: u64,
47
48    sig: [u8; 4],
49    version: u8,
50    header_type: u8,
51    serial: u32,
52    index: u32,
53    segments_count: u8,
54}
55
56impl<R: Read> OggReader<R> {
57    /// new returns a new Ogg reader and Ogg header
58    /// with an io.Reader input
59    pub fn new(reader: R, do_checksum: bool) -> Result<(OggReader<R>, OggHeader)> {
60        let mut r = OggReader {
61            reader,
62            bytes_read: 0,
63            checksum_table: generate_checksum_table(),
64            do_checksum,
65        };
66
67        let header = r.read_headers()?;
68
69        Ok((r, header))
70    }
71
72    fn read_headers(&mut self) -> Result<OggHeader> {
73        let (payload, page_header) = self.parse_next_page()?;
74
75        if page_header.sig != PAGE_HEADER_SIGNATURE {
76            return Err(Error::ErrBadIDPageSignature);
77        }
78
79        if page_header.header_type != PAGE_HEADER_TYPE_BEGINNING_OF_STREAM {
80            return Err(Error::ErrBadIDPageType);
81        }
82
83        if payload.len() != ID_PAGE_PAYLOAD_SIZE {
84            return Err(Error::ErrBadIDPageLength);
85        }
86
87        let s = &payload[..8];
88        if s != ID_PAGE_SIGNATURE {
89            return Err(Error::ErrBadIDPagePayloadSignature);
90        }
91
92        let mut reader = Cursor::new(&payload[8..]);
93        let version = reader.read_u8()?; //8
94        let channels = reader.read_u8()?; //9
95        let pre_skip = reader.read_u16::<LittleEndian>()?; //10-11
96        let sample_rate = reader.read_u32::<LittleEndian>()?; //12-15
97        let output_gain = reader.read_u16::<LittleEndian>()?; //16-17
98        let channel_map = reader.read_u8()?; //18
99
100        Ok(OggHeader {
101            channel_map,
102            channels,
103            output_gain,
104            pre_skip,
105            sample_rate,
106            version,
107        })
108    }
109
110    // parse_next_page reads from stream and returns Ogg page payload, header,
111    // and an error if there is incomplete page data.
112    pub fn parse_next_page(&mut self) -> Result<(BytesMut, OggPageHeader)> {
113        let mut h = [0u8; PAGE_HEADER_SIZE];
114        self.reader.read_exact(&mut h)?;
115
116        let mut head_reader = Cursor::new(h);
117        let mut sig = [0u8; 4]; //0-3
118        head_reader.read_exact(&mut sig)?;
119        let version = head_reader.read_u8()?; //4
120        let header_type = head_reader.read_u8()?; //5
121        let granule_position = head_reader.read_u64::<LittleEndian>()?; //6-13
122        let serial = head_reader.read_u32::<LittleEndian>()?; //14-17
123        let index = head_reader.read_u32::<LittleEndian>()?; //18-21
124        let checksum = head_reader.read_u32::<LittleEndian>()?; //22-25
125        let segments_count = head_reader.read_u8()?; //26
126
127        let mut size_buffer = vec![0u8; segments_count as usize];
128        self.reader.read_exact(&mut size_buffer)?;
129
130        let mut payload_size = 0usize;
131        for s in &size_buffer {
132            payload_size += *s as usize;
133        }
134
135        let mut payload = BytesMut::with_capacity(payload_size);
136        payload.resize(payload_size, 0);
137        self.reader.read_exact(&mut payload)?;
138
139        if self.do_checksum {
140            let mut sum = 0;
141
142            for (index, v) in h.iter().enumerate() {
143                // Don't include expected checksum in our generation
144                if index > 21 && index < 26 {
145                    sum = self.update_checksum(0, sum);
146                    continue;
147                }
148                sum = self.update_checksum(*v, sum);
149            }
150
151            for v in &size_buffer {
152                sum = self.update_checksum(*v, sum);
153            }
154            for v in &payload[..] {
155                sum = self.update_checksum(*v, sum);
156            }
157
158            if sum != checksum {
159                return Err(Error::ErrChecksumMismatch);
160            }
161        }
162
163        let page_header = OggPageHeader {
164            granule_position,
165            sig,
166            version,
167            header_type,
168            serial,
169            index,
170            segments_count,
171        };
172
173        Ok((payload, page_header))
174    }
175
176    /// reset_reader resets the internal stream of OggReader. This is useful
177    /// for live streams, where the end of the file might be read without the
178    /// data being finished.
179    pub fn reset_reader(&mut self, mut reset: ResetFn<R>) {
180        self.reader = reset(self.bytes_read);
181    }
182
183    fn update_checksum(&self, v: u8, sum: u32) -> u32 {
184        (sum << 8) ^ self.checksum_table[(((sum >> 24) as u8) ^ v) as usize]
185    }
186}
187
188pub(crate) fn generate_checksum_table() -> [u32; 256] {
189    let mut table = [0u32; 256];
190    const POLY: u32 = 0x04c11db7;
191
192    for (i, t) in table.iter_mut().enumerate() {
193        let mut r = (i as u32) << 24;
194        for _ in 0..8 {
195            if (r & 0x80000000) != 0 {
196                r = (r << 1) ^ POLY;
197            } else {
198                r <<= 1;
199            }
200        }
201        *t = r;
202    }
203    table
204}