Skip to main content

ogg/
ogg.rs

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	/// * The middle packets
13	Continuation = 0,
14
15	/// * The begin of a stream
16	BeginOfStream = 2,
17
18	/// * The last packet of a stream
19	EndOfStream = 4,
20}
21
22/// * An ogg packet as a stream container
23#[derive(Clone)]
24pub struct OggPacket {
25	/// Ogg Version must be zero
26	pub version: u8,
27
28	/// * The first packet should be `OggPacketType::BeginOfStream`
29	/// * The last packet should be `OggPacketType::EndOfStream`
30	/// * The others should be `OggPacketType::Continuation`
31	pub packet_type: OggPacketType,
32
33	/// * For vorbis, this field indicates when you had decoded from the first packet to this packet,
34	///   and you had finished decoding this packet, how many of the audio frames you should get.
35	pub granule_position: u64,
36
37	/// * The identifier for the streams. Every Ogg packet belonging to a stream should have the same `stream_id`.
38	pub stream_id: u32,
39
40	/// * The index of the packet, beginning from zero.
41	pub packet_index: u32,
42
43	/// * The checksum of the packet.
44	pub checksum: u32,
45
46	/// * A table indicates each segment's size, the max is 255. And the size of the table also couldn't exceed 255.
47	pub segment_table: Vec<u8>,
48
49	/// * The data encapsulated in the Ogg Stream
50	pub data: Vec<u8>,
51}
52
53impl OggPacket {
54	/// Create a new Ogg packet
55	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	/// Write some data to the packet, returns the actual written bytes.
69	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	/// Clear all data inside the packet
97	pub fn clear(&mut self) {
98		self.segment_table = Vec::new();
99		self.data = Vec::new();
100	}
101
102	/// Read all of the data as segments from the packet
103	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	/// Get inner data size
115	pub fn get_inner_data_size(&self) -> usize {
116		self.segment_table.iter().map(|&s|s as usize).sum()
117	}
118
119	/// Read all of the data as a flattened `Vec<u8>`
120	pub fn get_inner_data(&self) -> Vec<u8> {
121		self.get_segments().into_iter().flatten().collect()
122	}
123
124	/// Read all of the data as a flattened `Vec<u8>` and consume self
125	pub fn into_inner(self) -> Vec<u8> {
126		self.get_inner_data()
127	}
128
129	/// Calculate the checksum
130	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	/// Set the checksum for the Ogg packet
169	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	/// Serialize the packet to bytes. Only in the bytes form can calculate the checksum.
176	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	/// Retrieve the packet length in bytes
194	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	/// Deserialize the packet
215	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	/// Deserialize to multiple packets
261	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
309/// * An ogg packet reader
310pub struct OggStreamReader<R>
311where
312	R: Read + Debug {
313	/// * The reader
314	pub reader: R,
315
316	/// * The unique stream ID, after read out the first packet, this field is set.
317	pub stream_id: u32,
318
319	/// * If an EOS is encountered, this field is set to true
320	e_o_s: bool,
321
322	/// * If encountered EOF, this field is set to true
323	e_o_f: bool,
324
325	/// * The cached bytes for next read
326	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 => { // Not enough bytes for an Ogg packet
387					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
424/// * An ogg packets writer sink
425pub struct OggStreamWriter<W>
426where
427	W: Write + Debug {
428	/// * The writer, when a packet is full or you want to seal the packet, the packet is flushed in the writer
429	pub writer: W,
430
431	/// * The unique stream ID for a whole stream. Programs use the stream ID to identify which packet is for which stream.
432	pub stream_id: u32,
433
434	/// * The packet index.
435	pub packet_index: u32,
436
437	/// * The current packet, ready to be written.
438	pub cur_packet: OggPacket,
439
440	/// * The granule position is for the programmers to reference it for some purpose.
441	pub granule_position: u64,
442
443	/// * The `OggStreamWriter<W>` implements `Write`, when the `cur_packet` is full, the `on_seal()` closure will be called for updating the granule position.
444	/// * And then the packet will be flushed into the writer.
445	pub on_seal: Box<dyn FnMut(usize) -> u64>,
446
447	/// * How many bytes were written into this stream.
448	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	/// * Set the granule position. This field of data is not used by the Ogg stream.
467	/// * The granule position is for the inner things to reference it for some purpose.
468	pub fn set_granule_position(&mut self, position: u64) {
469		self.granule_position = position
470	}
471
472	/// * Get the granule position you had set before
473	pub fn get_granule_position(&self) -> u64 {
474		self.granule_position
475	}
476
477	/// * Mark the current packet as EOS
478	pub fn mark_cur_packet_as_end_of_stream(&mut self) {
479		self.cur_packet.packet_type = OggPacketType::EndOfStream;
480	}
481
482	/// * Get how many bytes written in this stream
483	pub fn get_bytes_written(&self) -> u64 {
484		self.bytes_written
485	}
486
487	/// * Set a callback for the `Write` trait when it seals the packet, the callback helps with updating the granule position
488	pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
489		self.on_seal = on_seal;
490	}
491
492	/// * Reset the stream state, discard the packet, reinit the packet to a BOS
493	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	/// * Save the current packet and write it to the sink, then create a new packet for writing.
501	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}