1use std::io::Write;
2use std::ops::Range;
3use std::sync::OnceLock;
4
5use crate::media::{AudioBuffer, Error, Result};
6use crate::opus::header::OpusHead;
7use crate::opus::multistream::Decoder;
8use crate::opus::packet::packet_samples_48k;
9
10const MAX_PAGE_BODY: usize = 4096;
11const MAX_PAGE_PACKETS: usize = 50;
12
13fn crc_table() -> &'static [u32; 256] {
14 static TABLE: OnceLock<[u32; 256]> = OnceLock::new();
15 TABLE.get_or_init(|| {
16 let mut t = [0u32; 256];
17 for (i, e) in t.iter_mut().enumerate() {
18 let mut r = (i as u32) << 24;
19 for _ in 0..8 {
20 r = if r & 0x8000_0000 != 0 {
21 (r << 1) ^ 0x04C1_1DB7
22 } else {
23 r << 1
24 };
25 }
26 *e = r;
27 }
28 t
29 })
30}
31
32pub fn crc32(data: &[u8]) -> u32 {
33 let t = crc_table();
34 data.iter()
35 .fold(0u32, |c, &b| (c << 8) ^ t[((c >> 24) as u8 ^ b) as usize])
36}
37
38pub fn sniff(head: &[u8]) -> bool {
39 head.len() >= 27 && &head[..4] == b"OggS" && head[4] == 0
40}
41
42pub struct OggOpus {
43 pub head: OpusHead,
44 pub tags: Vec<u8>,
45 pub serial: u32,
46 data: Vec<u8>,
47 pub packets: Vec<Range<usize>>,
48 pub granules: Vec<Option<i64>>,
49 pub warnings: Vec<String>,
50}
51
52impl OggOpus {
53 pub fn parse(src: &[u8]) -> Result<OggOpus> {
54 let mut pos = 0usize;
55 let mut serial = None;
56 let mut headers: Vec<Vec<u8>> = Vec::new();
57 let mut data = Vec::new();
58 let mut packets = Vec::new();
59 let mut granules = Vec::new();
60 let mut warnings = Vec::new();
61 let mut partial: Option<usize> = None;
62 let mut hdr_buf = Vec::new();
63 let mut found = false;
64 while pos + 27 <= src.len() {
65 if !sniff(&src[pos..]) {
66 match src[pos + 1..].windows(4).position(|w| w == b"OggS") {
67 Some(i) => {
68 if found {
69 warnings.push(format!("lost sync at byte {pos}"));
70 }
71 pos += 1 + i;
72 continue;
73 }
74 None => break,
75 }
76 }
77 let nseg = src[pos + 26] as usize;
78 let lacing_end = pos + 27 + nseg;
79 if lacing_end > src.len() {
80 warnings.push(format!("truncated page at byte {pos}"));
81 break;
82 }
83 let lacing = &src[pos + 27..lacing_end];
84 let body_len: usize = lacing.iter().map(|&b| b as usize).sum();
85 let page_end = lacing_end + body_len;
86 if page_end > src.len() {
87 warnings.push(format!("truncated page at byte {pos}"));
88 break;
89 }
90 let page = &src[pos..page_end];
91 let stored = u32::from_le_bytes([page[22], page[23], page[24], page[25]]);
92 let t = crc_table();
93 let mut crc = 0u32;
94 for (i, &b) in page.iter().enumerate() {
95 let b = if (22..26).contains(&i) { 0 } else { b };
96 crc = (crc << 8) ^ t[((crc >> 24) as u8 ^ b) as usize];
97 }
98 if crc != stored {
99 warnings.push(format!("bad CRC on page at byte {pos}"));
100 pos += 1;
101 continue;
102 }
103 found = true;
104 let flags = page[5];
105 let granule = i64::from_le_bytes([
106 page[6], page[7], page[8], page[9], page[10], page[11], page[12], page[13],
107 ]);
108 let page_serial = u32::from_le_bytes([page[14], page[15], page[16], page[17]]);
109 pos = page_end;
110 match serial {
111 None if flags & 0x02 != 0 && src[lacing_end..page_end].starts_with(b"OpusHead") => {
112 serial = Some(page_serial)
113 }
114 Some(s) if s == page_serial => {}
115 _ => continue,
116 }
117 if flags & 0x01 == 0 && partial.is_some() {
118 warnings.push(format!("incomplete packet before page at byte {pos}"));
119 partial = None;
120 hdr_buf.clear();
121 }
122 let mut skip = flags & 0x01 != 0 && partial.is_none() && headers.len() == 2;
123 let mut off = lacing_end;
124 let mut completed_any = false;
125 for &l in lacing {
126 let seg = &src[off..off + l as usize];
127 off += l as usize;
128 if skip {
129 if l < 255 {
130 skip = false;
131 }
132 continue;
133 }
134 if headers.len() < 2 {
135 hdr_buf.extend_from_slice(seg);
136 partial = Some(0);
137 if l < 255 {
138 headers.push(std::mem::take(&mut hdr_buf));
139 partial = None;
140 }
141 continue;
142 }
143 let start = *partial.get_or_insert(data.len());
144 data.extend_from_slice(seg);
145 if l < 255 {
146 packets.push(start..data.len());
147 granules.push(None);
148 partial = None;
149 completed_any = true;
150 }
151 }
152 if completed_any
153 && granule != -1
154 && let Some(g) = granules.last_mut()
155 {
156 *g = Some(granule);
157 }
158 }
159 if !found {
160 return Err(Error::Invalid("not an Ogg file".into()));
161 }
162 let mut headers = headers.into_iter();
163 let head_bytes = headers
164 .next()
165 .ok_or_else(|| Error::Invalid("no Opus stream in the Ogg file".into()))?;
166 let head = OpusHead::parse(&head_bytes)?;
167 let tags = headers.next().unwrap_or_default();
168 Ok(OggOpus {
169 head,
170 tags,
171 serial: serial.unwrap_or(0),
172 data,
173 packets,
174 granules,
175 warnings,
176 })
177 }
178
179 pub fn packet(&self, i: usize) -> &[u8] {
180 &self.data[self.packets[i].clone()]
181 }
182
183 pub fn end_granule(&self) -> Option<i64> {
184 self.granules.iter().rev().find_map(|g| *g)
185 }
186
187 pub fn samples(&self) -> u64 {
188 let coded: u64 = (0..self.packets.len())
189 .map(|i| packet_samples_48k(self.packet(i)).unwrap_or(0) as u64)
190 .sum();
191 let end = self.end_granule().map_or(coded, |g| g.max(0) as u64);
192 end.min(coded).saturating_sub(self.head.pre_skip as u64)
193 }
194
195 pub fn decode(&self) -> Result<AudioBuffer> {
196 let mut dec = Decoder::from_head(self.head.clone(), 48000)?;
197 let channels = dec.channels();
198 let total = self.samples() as usize;
199 let skip = self.head.pre_skip as usize;
200 let mut out = AudioBuffer::new(48000, channels as u16);
201 let most = self.packets.len().saturating_mul(5760);
202 let frames = total.saturating_add(skip).saturating_add(5760).min(most);
203 out.samples.reserve(frames.saturating_mul(channels));
204 for i in 0..self.packets.len() {
205 dec.decode_into(Some(self.packet(i)), &mut out.samples)?;
206 }
207 let have = out.samples.len() / channels.max(1);
208 out.samples.drain(..skip.min(have) * channels);
209 out.samples.truncate(total.saturating_mul(channels));
210 Ok(out)
211 }
212}
213
214pub fn decode(src: &[u8]) -> Result<AudioBuffer> {
215 OggOpus::parse(src)?.decode()
216}
217
218pub struct Writer<W: Write> {
219 w: W,
220 serial: u32,
221 sequence: u32,
222 body: Vec<u8>,
223 lacing: Vec<u8>,
224 packets: usize,
225 granule: i64,
226 page: Vec<u8>,
227}
228
229impl<W: Write> Writer<W> {
230 pub fn new(w: W, serial: u32, head: &OpusHead, vendor: &str) -> Result<Self> {
231 let mut s = Writer {
232 w,
233 serial,
234 sequence: 0,
235 body: Vec::new(),
236 lacing: Vec::new(),
237 packets: 0,
238 granule: 0,
239 page: Vec::new(),
240 };
241 s.add(&head.to_bytes());
242 s.flush_page(0x02, 0)?;
243 let mut tags = b"OpusTags".to_vec();
244 tags.extend((vendor.len() as u32).to_le_bytes());
245 tags.extend(vendor.as_bytes());
246 tags.extend(0u32.to_le_bytes());
247 s.add(&tags);
248 s.flush_page(0, 0)?;
249 Ok(s)
250 }
251
252 fn add(&mut self, packet: &[u8]) {
253 let mut n = packet.len();
254 while n >= 255 {
255 self.lacing.push(255);
256 n -= 255;
257 }
258 self.lacing.push(n as u8);
259 self.body.extend_from_slice(packet);
260 self.packets += 1;
261 }
262
263 pub fn push(&mut self, packet: &[u8], granule: i64) -> Result<()> {
264 if self.lacing.len() + packet.len() / 255 + 1 > 255 {
265 self.flush_page(0, self.granule)?;
266 }
267 self.add(packet);
268 self.granule = granule;
269 if self.body.len() >= MAX_PAGE_BODY || self.packets >= MAX_PAGE_PACKETS {
270 self.flush_page(0, granule)?;
271 }
272 Ok(())
273 }
274
275 fn flush_page(&mut self, flags: u8, granule: i64) -> Result<()> {
276 let mut segs = &self.lacing[..];
277 let mut body = &self.body[..];
278 let mut continued = false;
279 loop {
280 let n = segs.len().min(255);
281 let (now, rest) = segs.split_at(n);
282 let len: usize = now.iter().map(|&b| b as usize).sum();
283 let last = rest.is_empty();
284 let page_granule = if last && now.last().is_none_or(|&l| l < 255) {
285 granule
286 } else {
287 -1
288 };
289 let page = &mut self.page;
290 page.clear();
291 page.extend_from_slice(b"OggS\0");
292 page.push(flags | if continued { 0x01 } else { 0 });
293 page.extend(page_granule.to_le_bytes());
294 page.extend(self.serial.to_le_bytes());
295 page.extend(self.sequence.to_le_bytes());
296 page.extend([0u8; 4]);
297 page.push(n as u8);
298 page.extend_from_slice(now);
299 page.extend_from_slice(&body[..len]);
300 let crc = crc32(page);
301 page[22..26].copy_from_slice(&crc.to_le_bytes());
302 self.w.write_all(page)?;
303 self.sequence += 1;
304 body = &body[len..];
305 segs = rest;
306 continued = now.last().is_some_and(|&l| l == 255);
307 if last {
308 break;
309 }
310 }
311 self.lacing.clear();
312 self.body.clear();
313 self.packets = 0;
314 Ok(())
315 }
316
317 pub fn finish(mut self) -> Result<W> {
318 self.flush_page(0x04, self.granule)?;
319 self.w.flush()?;
320 Ok(self.w)
321 }
322}