1#![allow(dead_code)]
2
3use std::{
4 cmp::max,
5 io::{self, Read, Write, Cursor, ErrorKind},
6 mem,
7 fmt::{self, Debug, Formatter}
8};
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum OggPacketType {
12 Continuation = 0,
14
15 BeginOfStream = 2,
17
18 EndOfStream = 4,
20}
21
22#[derive(Clone)]
24pub struct OggPacket {
25 pub version: u8,
27
28 pub packet_type: OggPacketType,
32
33 pub granule_position: u64,
36
37 pub stream_id: u32,
39
40 pub packet_index: u32,
42
43 pub checksum: u32,
45
46 pub segment_table: Vec<u8>,
48
49 pub data: Vec<u8>,
51}
52
53impl OggPacket {
54 pub fn new(stream_id: u32, packet_type: OggPacketType, packet_index: u32) -> Self {
56 Self {
57 version: 0,
58 packet_type,
59 granule_position: 0,
60 stream_id,
61 packet_index,
62 checksum: 0,
63 segment_table: Vec::new(),
64 data: Vec::new(),
65 }
66 }
67
68 pub fn write(&mut self, data: &[u8]) -> usize {
70 let mut written = 0usize;
71 let mut to_write = data.len();
72 if to_write == 0 {
73 return 0;
74 }
75 while self.segment_table.len() < 255 {
76 if to_write >= 255 {
77 let new_pos = written + 255;
78 self.segment_table.push(255);
79 self.data.extend(data[written..new_pos].to_vec());
80 written = new_pos;
81 to_write -= 255;
82 } else {
83 if to_write == 0 {
84 break;
85 }
86 let new_pos = written + to_write;
87 self.segment_table.push(to_write as u8);
88 self.data.extend(data[written..new_pos].to_vec());
89 written = new_pos;
90 break;
91 }
92 }
93 written
94 }
95
96 pub fn clear(&mut self) {
98 self.segment_table = Vec::new();
99 self.data = Vec::new();
100 }
101
102 pub fn get_segments(&self) -> Vec<Vec<u8>> {
104 let mut ret = Vec::<Vec<u8>>::with_capacity(self.segment_table.len());
105 let mut pos = 0usize;
106 self.segment_table.iter().for_each(|&size|{
107 let next_pos = pos + size as usize;
108 ret.push(self.data[pos..next_pos].to_vec());
109 pos = next_pos;
110 });
111 ret
112 }
113
114 pub fn get_inner_data_size(&self) -> usize {
116 self.segment_table.iter().map(|&s|s as usize).sum()
117 }
118
119 pub fn get_inner_data(&self) -> Vec<u8> {
121 self.get_segments().into_iter().flatten().collect()
122 }
123
124 pub fn into_inner(self) -> Vec<u8> {
126 self.get_inner_data()
127 }
128
129 pub fn crc(mut crc: u32, data: &[u8]) -> u32 {
131 type CrcTableType = [u32; 256];
132 fn ogg_generate_crc_table() -> CrcTableType {
133 use std::mem::MaybeUninit;
134 #[allow(invalid_value)]
135 #[allow(clippy::uninit_assumed_init)]
136 let mut crc_lookup: CrcTableType = unsafe{MaybeUninit::uninit().assume_init()};
137 (0..256).for_each(|i|{
138 let mut r: u32 = i << 24;
139 for _ in 0..8 {
140 r = (r << 1) ^ (-(((r >> 31) & 1) as i32) as u32 & 0x04c11db7);
141 }
142 crc_lookup[i as usize] = r;
143 });
144 crc_lookup
145 }
146
147 use std::sync::OnceLock;
148 static OGG_CRC_TABLE: OnceLock<CrcTableType> = OnceLock::<CrcTableType>::new();
149 let crc_lookup = OGG_CRC_TABLE.get_or_init(ogg_generate_crc_table);
150
151 for b in data {
152 crc = (crc << 8) ^ crc_lookup[(*b as u32 ^ (crc >> 24)) as usize];
153 }
154
155 crc
156 }
157
158 pub fn get_checksum(ogg_packet: &[u8]) -> io::Result<u32> {
159 if ogg_packet.len() < 27 {
160 Err(io::Error::new(ErrorKind::InvalidData, format!("The given packet is too small: {} < 27", ogg_packet.len())))
161 } else {
162 let mut field_cleared = ogg_packet.to_vec();
163 field_cleared[22..26].copy_from_slice(&[0u8; 4]);
164 Ok(Self::crc(0, &field_cleared))
165 }
166 }
167
168 pub fn fill_checksum_field(ogg_packet: &mut [u8]) -> io::Result<()> {
170 let checksum = Self::get_checksum(ogg_packet)?;
171 ogg_packet[22..26].copy_from_slice(&checksum.to_le_bytes());
172 Ok(())
173 }
174
175 pub fn into_bytes(self) -> Vec<u8> {
177 let mut ret: Vec<u8> = [
178 b"OggS" as &[u8],
179 &[self.version],
180 &[self.packet_type as u8],
181 &self.granule_position.to_le_bytes() as &[u8],
182 &self.stream_id.to_le_bytes() as &[u8],
183 &self.packet_index.to_le_bytes() as &[u8],
184 &0u32.to_le_bytes() as &[u8],
185 &[self.segment_table.len() as u8],
186 &self.segment_table,
187 &self.data,
188 ].into_iter().flatten().copied().collect();
189 Self::fill_checksum_field(&mut ret).unwrap();
190 ret
191 }
192
193 pub fn get_length(ogg_packet: &[u8]) -> io::Result<usize> {
195 if ogg_packet.len() < 27 {
196 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < 27", ogg_packet.len())))
197 } else if ogg_packet[0..4] != *b"OggS" {
198 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
199 } else if ogg_packet[4] != 0 {
200 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
201 } else {
202 match ogg_packet[5] {
203 0 | 2 | 4 => (),
204 o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
205 }
206 let num_segments = ogg_packet[26] as usize;
207 let data_start = 27 + num_segments;
208 let segment_table = &ogg_packet[27..data_start];
209 let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
210 Ok(data_start + data_length)
211 }
212 }
213
214 pub fn from_bytes(ogg_packet: &[u8], packet_length: &mut usize) -> io::Result<Self> {
216 if ogg_packet.len() < 27 {
217 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given data size is too small: {} < 27", ogg_packet.len())))
218 } else if ogg_packet[0..4] != *b"OggS" {
219 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
220 } else if ogg_packet[4] != 0 {
221 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
222 } else {
223 let packet_type = match ogg_packet[5] {
224 0 => OggPacketType::Continuation,
225 2 => OggPacketType::BeginOfStream,
226 4 => OggPacketType::EndOfStream,
227 o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
228 };
229 let num_segments = ogg_packet[26] as usize;
230 let data_start = 27 + num_segments;
231 if data_start > ogg_packet.len() {
232 return Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given data size is too small: {}", ogg_packet.len())));
233 }
234 let segment_table = &ogg_packet[27..data_start];
235 let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
236 *packet_length = data_start + data_length;
237 if ogg_packet.len() < *packet_length {
238 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given data size is too small: {} < {packet_length}", ogg_packet.len())))
239 } else {
240 let ret = Self{
241 version: 0,
242 packet_type,
243 granule_position: u64::from_le_bytes(ogg_packet[6..14].try_into().unwrap()),
244 stream_id: u32::from_le_bytes(ogg_packet[14..18].try_into().unwrap()),
245 packet_index: u32::from_le_bytes(ogg_packet[18..22].try_into().unwrap()),
246 checksum: u32::from_le_bytes(ogg_packet[22..26].try_into().unwrap()),
247 segment_table: segment_table.to_vec(),
248 data: ogg_packet[data_start..*packet_length].to_vec(),
249 };
250 let checksum = Self::get_checksum(&ogg_packet[..*packet_length])?;
251 if ret.checksum != checksum {
252 Err(io::Error::new(ErrorKind::InvalidData, format!("Ogg packet checksum not match: should be 0x{:x}, got 0x{:x}", checksum, ret.checksum)))
253 } else {
254 Ok(ret)
255 }
256 }
257 }
258 }
259
260 pub fn from_cursor(cursor: &mut Cursor<Vec<u8>>) -> Vec<OggPacket> {
262 let mut data: &[u8] = cursor.get_ref();
263 let mut packet_length = 0usize;
264 let mut bytes_read = 0usize;
265 let mut ret = Vec::<OggPacket>::new();
266 while let Ok(packet) = Self::from_bytes(data, &mut packet_length) {
267 bytes_read += packet_length;
268 ret.push(packet);
269 data = &data[packet_length..];
270 if data.is_empty() {
271 break;
272 }
273 }
274 cursor.set_position(bytes_read as u64);
275 ret
276 }
277}
278
279impl Debug for OggPacket {
280 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
281 f.debug_struct("OggPacket")
282 .field("version", &self.version)
283 .field("packet_type", &self.packet_type)
284 .field("granule_position", &self.granule_position)
285 .field("stream_id", &self.stream_id)
286 .field("packet_index", &self.packet_index)
287 .field("checksum", &format_args!("0x{:08x}", self.checksum))
288 .field("segment_table", &self.segment_table)
289 .field("data", &format_args!("[u8; {}]", self.data.len()))
290 .finish()
291 }
292}
293
294impl Default for OggPacket {
295 fn default() -> Self {
296 Self {
297 version: 0,
298 packet_type: OggPacketType::BeginOfStream,
299 granule_position: 0,
300 stream_id: 0,
301 packet_index: 0,
302 checksum: 0,
303 segment_table: Vec::new(),
304 data: Vec::new(),
305 }
306 }
307}
308
309pub struct OggStreamReader<R>
311where
312 R: Read + Debug {
313 pub reader: R,
315
316 pub stream_id: u32,
318
319 e_o_s: bool,
321
322 e_o_f: bool,
324
325 cached_bytes: Vec<u8>,
327}
328
329impl<R> OggStreamReader<R>
330where
331 R: Read + Debug {
332 const READ_SIZE: usize = 2048;
333
334 pub fn new(reader: R) -> Self {
335 Self {
336 reader,
337 stream_id: 0,
338 e_o_s: false,
339 e_o_f: false,
340 cached_bytes: Vec::new(),
341 }
342 }
343
344 fn safe_read(&mut self, target_len: usize) -> io::Result<Vec<u8>> {
345 let mut buf = vec![0u8; target_len];
346 let mut bytes_read = 0usize;
347 while bytes_read < target_len {
348 let read = match self.reader.read(&mut buf[bytes_read..]) {
349 Ok(0) => break,
350 Ok(size) => size,
351 Err(e) => match e.kind() {
352 io::ErrorKind::Interrupted => {
353 0
354 }
355 io::ErrorKind::UnexpectedEof => {
356 break;
357 }
358 _ => {
359 if bytes_read > 0 {
360 break;
361 } else {
362 return Err(e);
363 }
364 }
365 }
366 };
367 bytes_read += read;
368 }
369 buf.truncate(bytes_read);
370 Ok(buf)
371 }
372
373 pub fn get_packet(&mut self) -> io::Result<Option<OggPacket>> {
374 let mut packet_length = 0usize;
375 match OggPacket::from_bytes(&self.cached_bytes, &mut packet_length) {
376 Ok(packet) => {
377 if packet.packet_type == OggPacketType::EndOfStream {
378 self.e_o_s = true;
379 } else {
380 self.e_o_s = false;
381 }
382 self.cached_bytes = self.cached_bytes[packet_length..].to_vec();
383 Ok(Some(packet))
384 }
385 Err(e) => match e.kind() {
386 io::ErrorKind::UnexpectedEof => { if self.e_o_s {
388 Ok(None)
389 } else {
390 let to_read = max(packet_length, Self::READ_SIZE);
391 let read = self.safe_read(to_read)?;
392 self.cached_bytes.extend(&read);
393 if read.len() < to_read {
394 if self.e_o_f == false {
395 self.e_o_f = true;
396 self.get_packet()
397 } else {
398 if read.len() == 0 {
399 Ok(None)
400 } else {
401 Err(e)
402 }
403 }
404 } else {
405 self.get_packet()
406 }
407 }
408 }
409 _ => Err(e)
410 }
411 }
412 }
413
414 pub fn is_eos(&self) -> bool {
415 self.e_o_s
416 }
417
418 pub fn is_eof(&self) -> bool {
419 self.e_o_f
420 }
421}
422
423
424pub struct OggStreamWriter<W>
426where
427 W: Write + Debug {
428 pub writer: W,
430
431 pub stream_id: u32,
433
434 pub packet_index: u32,
436
437 pub cur_packet: OggPacket,
439
440 pub granule_position: u64,
442
443 pub on_seal: Box<dyn FnMut(usize) -> u64>,
446
447 pub bytes_written: u64,
449}
450
451impl<W> OggStreamWriter<W>
452where
453 W: Write + Debug {
454 pub fn new(writer: W, stream_id: u32) -> Self {
455 Self {
456 writer,
457 stream_id,
458 packet_index : 0,
459 cur_packet: OggPacket::new(stream_id, OggPacketType::BeginOfStream, 0),
460 granule_position: 0,
461 bytes_written: 0,
462 on_seal: Box::new(|i|i as u64),
463 }
464 }
465
466 pub fn set_granule_position(&mut self, position: u64) {
469 self.granule_position = position
470 }
471
472 pub fn get_granule_position(&self) -> u64 {
474 self.granule_position
475 }
476
477 pub fn mark_cur_packet_as_end_of_stream(&mut self) {
479 self.cur_packet.packet_type = OggPacketType::EndOfStream;
480 }
481
482 pub fn get_bytes_written(&self) -> u64 {
484 self.bytes_written
485 }
486
487 pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
489 self.on_seal = on_seal;
490 }
491
492 pub fn reset(&mut self) {
494 self.packet_index = 0;
495 self.cur_packet = OggPacket::new(self.stream_id, OggPacketType::BeginOfStream, 0);
496 self.granule_position = 0;
497 self.bytes_written = 0;
498 }
499
500 pub fn seal_packet(&mut self, granule_position: u64, is_end_of_stream: bool) -> io::Result<()> {
502 self.packet_index += 1;
503 self.granule_position = granule_position;
504 self.cur_packet.granule_position = self.granule_position;
505 let packed = if is_end_of_stream {
506 self.cur_packet.packet_type = OggPacketType::EndOfStream;
507 mem::take(&mut self.cur_packet).into_bytes()
508 } else {
509 mem::replace(&mut self.cur_packet, OggPacket::new(self.stream_id, OggPacketType::Continuation, self.packet_index)).into_bytes()
510 };
511 self.writer.write_all(&packed)?;
512 Ok(())
513 }
514}
515
516impl<W> Write for OggStreamWriter<W>
517where
518 W: Write + Debug {
519 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
520 self.bytes_written = buf.len() as u64;
521 let mut buf = buf;
522 let mut written_total = 0usize;
523 while !buf.is_empty() {
524 let written = self.cur_packet.write(buf);
525 buf = &buf[written..];
526 written_total += written;
527 if !buf.is_empty() {
528 self.granule_position = (self.on_seal)(self.cur_packet.get_inner_data_size());
529 self.seal_packet(self.granule_position, false)?;
530 }
531 }
532 Ok(written_total)
533 }
534
535 fn flush(&mut self) -> io::Result<()> {
536 self.writer.flush()
537 }
538}
539
540impl<W> Debug for OggStreamWriter<W>
541where
542 W: Write + Debug {
543 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
544 f.debug_struct(&format!("OggStreamWriter<{}>", std::any::type_name::<W>()))
545 .field("writer", &self.writer)
546 .field("stream_id", &format_args!("0x{:08x}", self.stream_id))
547 .field("packet_index", &self.packet_index)
548 .field("cur_packet", &self.cur_packet)
549 .field("granule_position", &self.granule_position)
550 .field("on_seal", &format_args!("<closure>"))
551 .field("bytes_written", &self.bytes_written)
552 .finish()
553 }
554}
555
556impl<W> Drop for OggStreamWriter<W>
557where
558 W: Write + Debug {
559 fn drop(&mut self) {
560 self.seal_packet(self.granule_position, true).unwrap();
561 }
562}
563
564#[test]
565fn test_ogg() {
566 use std::{
567 fs::File,
568 io::BufReader,
569 };
570 let mut oggreader = OggStreamReader::new(BufReader::new(File::open("test.ogg").unwrap()));
571 loop {
572 let packet = oggreader.get_packet().unwrap();
573 if let Some(packet) = packet {
574 dbg!(packet);
575 } else {
576 break;
577 }
578 }
579}