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