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 !self.e_o_s {
378					if packet.packet_type == OggPacketType::EndOfStream {
379						self.e_o_s = true;
380					}
381					self.cached_bytes = self.cached_bytes[packet_length..].to_vec();
382					Ok(Some(packet))
383				} else {
384					Ok(None)
385				}
386			}
387			Err(e) => match e.kind() {
388				io::ErrorKind::UnexpectedEof => { // Not enough bytes for an Ogg packet
389					if self.e_o_s {
390						Ok(None)
391					} else {
392						let to_read = max(packet_length, Self::READ_SIZE);
393						let read = self.safe_read(to_read)?;
394						self.cached_bytes.extend(&read);
395						if read.len() < to_read {
396							if self.e_o_f == false {
397								self.e_o_f = true;
398								self.get_packet()
399							} else {
400								if read.len() == 0 {
401									Ok(None)
402								} else {
403									Err(e)
404								}
405							}
406						} else {
407							self.get_packet()
408						}
409					}
410				}
411				_ => Err(e)
412			}
413		}
414	}
415
416	pub fn is_eos(&self) -> bool {
417		self.e_o_s
418	}
419
420	pub fn is_eof(&self) -> bool {
421		self.e_o_f
422	}
423}
424
425
426/// * An ogg packets writer sink
427pub struct OggStreamWriter<W>
428where
429	W: Write + Debug {
430	/// * The writer, when a packet is full or you want to seal the packet, the packet is flushed in the writer
431	pub writer: W,
432
433	/// * The unique stream ID for a whole stream. Programs use the stream ID to identify which packet is for which stream.
434	pub stream_id: u32,
435
436	/// * The packet index.
437	pub packet_index: u32,
438
439	/// * The current packet, ready to be written.
440	pub cur_packet: OggPacket,
441
442	/// * The granule position is for the programmers to reference it for some purpose.
443	pub granule_position: u64,
444
445	/// * The `OggStreamWriter<W>` implements `Write`, when the `cur_packet` is full, the `on_seal()` closure will be called for updating the granule position.
446	/// * And then the packet will be flushed into the writer.
447	pub on_seal: Box<dyn FnMut(usize) -> u64>,
448
449	/// * How many bytes were written into this stream.
450	pub bytes_written: u64,
451}
452
453impl<W> OggStreamWriter<W>
454where
455	W: Write + Debug {
456	pub fn new(writer: W, stream_id: u32) -> Self {
457		Self {
458			writer,
459			stream_id,
460			packet_index : 0,
461			cur_packet: OggPacket::new(stream_id, OggPacketType::BeginOfStream, 0),
462			granule_position: 0,
463			bytes_written: 0,
464			on_seal: Box::new(|i|i as u64),
465		}
466	}
467
468	/// * Set the granule position. This field of data is not used by the Ogg stream.
469	/// * The granule position is for the inner things to reference it for some purpose.
470	pub fn set_granule_position(&mut self, position: u64) {
471		self.granule_position = position
472	}
473
474	/// * Get the granule position you had set before
475	pub fn get_granule_position(&self) -> u64 {
476		self.granule_position
477	}
478
479	/// * Mark the current packet as EOS
480	pub fn mark_cur_packet_as_end_of_stream(&mut self) {
481		self.cur_packet.packet_type = OggPacketType::EndOfStream;
482	}
483
484	/// * Get how many bytes written in this stream
485	pub fn get_bytes_written(&self) -> u64 {
486		self.bytes_written
487	}
488
489	/// * Set a callback for the `Write` trait when it seals the packet, the callback helps with updating the granule position
490	pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
491		self.on_seal = on_seal;
492	}
493
494	/// * Reset the stream state, discard the packet, reinit the packet to a BOS
495	pub fn reset(&mut self) {
496		self.packet_index = 0;
497		self.cur_packet = OggPacket::new(self.stream_id, OggPacketType::BeginOfStream, 0);
498		self.granule_position = 0;
499		self.bytes_written = 0;
500	}
501
502	/// * Save the current packet and write it to the sink, then create a new packet for writing.
503	pub fn seal_packet(&mut self, granule_position: u64, is_end_of_stream: bool) -> io::Result<()> {
504		self.packet_index += 1;
505		self.granule_position = granule_position;
506		self.cur_packet.granule_position = self.granule_position;
507		let packed = if is_end_of_stream {
508			self.cur_packet.packet_type = OggPacketType::EndOfStream;
509			mem::take(&mut self.cur_packet).into_bytes()
510		} else {
511			mem::replace(&mut self.cur_packet, OggPacket::new(self.stream_id, OggPacketType::Continuation, self.packet_index)).into_bytes()
512		};
513		self.writer.write_all(&packed)?;
514		Ok(())
515	}
516}
517
518impl<W> Write for OggStreamWriter<W>
519where
520	W: Write + Debug {
521	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
522		self.bytes_written = buf.len() as u64;
523		let mut buf = buf;
524		let mut written_total = 0usize;
525		while !buf.is_empty() {
526			let written = self.cur_packet.write(buf);
527			buf = &buf[written..];
528			written_total += written;
529			if !buf.is_empty() {
530				self.granule_position = (self.on_seal)(self.cur_packet.get_inner_data_size());
531				self.seal_packet(self.granule_position, false)?;
532			}
533		}
534		Ok(written_total)
535	}
536
537	fn flush(&mut self) -> io::Result<()> {
538		self.writer.flush()
539	}
540}
541
542impl<W> Debug for OggStreamWriter<W>
543where
544	W: Write + Debug {
545	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
546		f.debug_struct(&format!("OggStreamWriter<{}>", std::any::type_name::<W>()))
547		.field("writer", &self.writer)
548		.field("stream_id", &format_args!("0x{:08x}", self.stream_id))
549		.field("packet_index", &self.packet_index)
550		.field("cur_packet", &self.cur_packet)
551		.field("granule_position", &self.granule_position)
552		.field("on_seal", &format_args!("<closure>"))
553		.field("bytes_written", &self.bytes_written)
554		.finish()
555	}
556}
557
558impl<W> Drop for OggStreamWriter<W>
559where
560	W: Write + Debug {
561	fn drop(&mut self) {
562		self.seal_packet(self.granule_position, true).unwrap();
563	}
564}
565
566#[test]
567fn test_ogg() {
568	use std::{
569		fs::File,
570		io::BufReader,
571	};
572	let mut oggreader = OggStreamReader::new(BufReader::new(File::open("test.ogg").unwrap()));
573	loop {
574		let packet = oggreader.get_packet().unwrap();
575		if let Some(packet) = packet {
576			dbg!(packet);
577		} else {
578			break;
579		}
580	}
581}