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