ogg/
ogg.rs

1#![allow(dead_code)]
2
3use std::{io::{self, Read, Write, Cursor, ErrorKind}, mem, fmt::{self, Debug, Formatter}};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum OggPacketType {
7	/// * The middle packets
8	Continuation = 0,
9
10	/// * The begin of a stream
11	BeginOfStream = 2,
12
13	/// * The last packet of a stream
14	EndOfStream = 4,
15}
16
17/// * An ogg packet as a stream container
18#[derive(Debug, Clone)]
19pub struct OggPacket {
20	/// Ogg Version must be zero
21	pub version: u8,
22
23	/// * The first packet should be `OggPacketType::BeginOfStream`
24	/// * The last packet should be `OggPacketType::EndOfStream`
25	/// * The others should be `OggPacketType::Continuation`
26	pub packet_type: OggPacketType,
27
28	/// * For vorbis, this field indicates when you had decoded from the first packet to this packet,
29	///   and you had finished decoding this packet, how many of the audio frames you should get.
30	pub granule_position: u64,
31
32	/// * The identifier for the streams. Every Ogg packet belonging to a stream should have the same `stream_id`.
33	pub stream_id: u32,
34
35	/// * The index of the packet, beginning from zero.
36	pub packet_index: u32,
37
38	/// * The checksum of the packet.
39	pub checksum: u32,
40
41	/// * A table indicates each segment's size, the max is 255. And the size of the table also couldn't exceed 255.
42	pub segment_table: Vec<u8>,
43
44	/// * The data encapsulated in the Ogg Stream
45	pub data: Vec<u8>,
46}
47
48impl OggPacket {
49	/// Create a new Ogg packet
50	pub fn new(stream_id: u32, packet_type: OggPacketType, packet_index: u32) -> Self {
51		Self {
52			version: 0,
53			packet_type,
54			granule_position: 0,
55			stream_id,
56			packet_index,
57			checksum: 0,
58			segment_table: Vec::new(),
59			data: Vec::new(),
60		}
61	}
62
63	/// Write some data to the packet, returns the actual written bytes.
64	pub fn write(&mut self, data: &[u8]) -> usize {
65		let mut written = 0usize;
66		let mut to_write = data.len();
67		if to_write == 0 {
68			return 0;
69		}
70		while self.segment_table.len() < 255 {
71			if to_write >= 255 {
72				let new_pos = written + 255;
73				self.segment_table.push(255);
74				self.data.extend(data[written..new_pos].to_vec());
75				written = new_pos;
76				to_write -= 255;
77			} else {
78				if to_write == 0 {
79					break;
80				}
81				let new_pos = written + to_write;
82				self.segment_table.push(to_write as u8);
83				self.data.extend(data[written..new_pos].to_vec());
84				written = new_pos;
85				break;
86			}
87		}
88		written
89	}
90
91	/// Clear all data inside the packet
92	pub fn clear(&mut self) {
93		self.segment_table = Vec::new();
94		self.data = Vec::new();
95	}
96
97	/// Read all of the data as segments from the packet
98	pub fn get_segments(&self) -> Vec<Vec<u8>> {
99		let mut ret = Vec::<Vec<u8>>::with_capacity(self.segment_table.len());
100		let mut pos = 0usize;
101		self.segment_table.iter().for_each(|&size|{
102			let next_pos = pos + size as usize;
103			ret.push(self.data[pos..next_pos].to_vec());
104			pos = next_pos;
105		});
106		ret
107	}
108
109	/// Get inner data size
110	pub fn get_inner_data_size(&self) -> usize {
111		self.segment_table.iter().map(|&s|s as usize).sum()
112	}
113
114	/// Read all of the data as a flattened `Vec<u8>`
115	pub fn get_inner_data(&self) -> Vec<u8> {
116		self.get_segments().into_iter().flatten().collect()
117	}
118
119	/// Read all of the data as a flattened `Vec<u8>` and consume self
120	pub fn into_inner(self) -> Vec<u8> {
121		self.get_inner_data()
122	}
123
124	/// Calculate the checksum
125	pub fn crc(mut crc: u32, data: &[u8]) -> u32 {
126        type CrcTableType = [u32; 256];
127        fn ogg_generate_crc_table() -> CrcTableType {
128            use std::mem::MaybeUninit;
129            #[allow(invalid_value)]
130            #[allow(clippy::uninit_assumed_init)]
131            let mut crc_lookup: CrcTableType = unsafe{MaybeUninit::uninit().assume_init()};
132            (0..256).for_each(|i|{
133                let mut r: u32 = i << 24;
134                for _ in 0..8 {
135                    r = (r << 1) ^ (-(((r >> 31) & 1) as i32) as u32 & 0x04c11db7);
136                }
137                crc_lookup[i as usize] = r;
138            });
139            crc_lookup
140        }
141
142        use std::sync::OnceLock;
143        static OGG_CRC_TABLE: OnceLock<CrcTableType> = OnceLock::<CrcTableType>::new();
144        let crc_lookup = OGG_CRC_TABLE.get_or_init(ogg_generate_crc_table);
145
146        for b in data {
147            crc = (crc << 8) ^ crc_lookup[(*b as u32 ^ (crc >> 24)) as usize];
148        }
149
150        crc
151	}
152
153	pub fn get_checksum(ogg_packet: &[u8]) -> io::Result<u32> {
154		if ogg_packet.len() < 27 {
155			Err(io::Error::new(ErrorKind::InvalidData, format!("The given packet is too small: {} < 27", ogg_packet.len())))
156		} else {
157			let mut field_cleared = ogg_packet.to_vec();
158			field_cleared[22..26].copy_from_slice(&[0u8; 4]);
159			Ok(Self::crc(0, &field_cleared))
160		}
161	}
162
163	/// Set the checksum for the Ogg packet
164	pub fn fill_checksum_field(ogg_packet: &mut [u8]) -> io::Result<()> {
165		let checksum = Self::get_checksum(ogg_packet)?;
166		ogg_packet[22..26].copy_from_slice(&checksum.to_le_bytes());
167		Ok(())
168	}
169
170	/// Serialize the packet to bytes. Only in the bytes form can calculate the checksum.
171	pub fn into_bytes(self) -> Vec<u8> {
172		let mut ret: Vec<u8> = [
173			b"OggS" as &[u8],
174			&[self.version],
175			&[self.packet_type as u8],
176			&self.granule_position.to_le_bytes() as &[u8],
177			&self.stream_id.to_le_bytes() as &[u8],
178			&self.packet_index.to_le_bytes() as &[u8],
179			&0u32.to_le_bytes() as &[u8],
180			&[self.segment_table.len() as u8],
181			&self.segment_table,
182			&self.data,
183		].into_iter().flatten().copied().collect();
184		Self::fill_checksum_field(&mut ret).unwrap();
185		ret
186	}
187
188	/// Retrieve the packet length in bytes
189	pub fn get_length(ogg_packet: &[u8]) -> io::Result<usize> {
190		if ogg_packet.len() < 27 {
191			Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < 27", ogg_packet.len())))
192		} else if ogg_packet[0..4] != *b"OggS" {
193			Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
194		} else if ogg_packet[4] != 0 {
195			Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
196		} else {
197			match ogg_packet[5] {
198				0 | 2 | 4 => (),
199				o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
200			}
201			let num_segments = ogg_packet[26] as usize;
202			let data_start = 27 + num_segments;
203			let segment_table = &ogg_packet[27..data_start];
204			let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
205			Ok(data_start + data_length)
206		}
207	}
208
209	/// Deserialize the packet
210	pub fn from_bytes(ogg_packet: &[u8], packet_length: &mut usize) -> io::Result<Self> {
211		if ogg_packet.len() < 27 {
212			Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < 27", ogg_packet.len())))
213		} else if ogg_packet[0..4] != *b"OggS" {
214			Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
215		} else if ogg_packet[4] != 0 {
216			Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
217		} else {
218			let packet_type = match ogg_packet[5] {
219				0 => OggPacketType::Continuation,
220				2 => OggPacketType::BeginOfStream,
221				4 => OggPacketType::EndOfStream,
222				o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
223			};
224			let num_segments = ogg_packet[26] as usize;
225			let data_start = 27 + num_segments;
226			let segment_table = &ogg_packet[27..data_start];
227			let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
228			*packet_length = data_start + data_length;
229			if ogg_packet.len() < *packet_length {
230				Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < {packet_length}", ogg_packet.len())))
231			} else {
232				let ret = Self{
233					version: 0,
234					packet_type,
235					granule_position: u64::from_le_bytes(ogg_packet[6..14].try_into().unwrap()),
236					stream_id: u32::from_le_bytes(ogg_packet[14..18].try_into().unwrap()),
237					packet_index: u32::from_le_bytes(ogg_packet[18..22].try_into().unwrap()),
238					checksum: u32::from_le_bytes(ogg_packet[22..26].try_into().unwrap()),
239					segment_table: segment_table.to_vec(),
240					data: ogg_packet[data_start..*packet_length].to_vec(),
241				};
242				let checksum = Self::get_checksum(&ogg_packet[..*packet_length])?;
243				if ret.checksum != checksum {
244					Err(io::Error::new(ErrorKind::InvalidData, format!("Ogg packet checksum not match: should be 0x{:x}, got 0x{:x}", checksum, ret.checksum)))
245				} else {
246					Ok(ret)
247				}
248			}
249		}
250	}
251
252	/// Deserialize to multiple packets
253	pub fn from_cursor(cursor: &mut Cursor<Vec<u8>>) -> Vec<OggPacket> {
254		let mut data: &[u8] = cursor.get_ref();
255		let mut packet_length = 0usize;
256		let mut bytes_read = 0usize;
257		let mut ret = Vec::<OggPacket>::new();
258		while let Ok(packet) = Self::from_bytes(data, &mut packet_length) {
259			bytes_read += packet_length;
260			ret.push(packet);
261			data = &data[packet_length..];
262			if data.is_empty() {
263				break;
264			}
265		}
266		cursor.set_position(bytes_read as u64);
267		ret
268	}
269}
270
271impl Default for OggPacket {
272	fn default() -> Self {
273		Self {
274			version: 0,
275			packet_type: OggPacketType::BeginOfStream,
276			granule_position: 0,
277			stream_id: 0,
278			packet_index: 0,
279			checksum: 0,
280			segment_table: Vec::new(),
281			data: Vec::new(),
282		}
283	}
284}
285
286/// * An ogg packet reader
287pub struct OggStreamReader<R>
288where
289	R: Read + Debug {
290	/// * The reader
291	pub reader: R,
292
293	/// * The unique stream ID, after read out the first packet, this field is set.
294	pub stream_id: u32,
295
296	/// * If an EOS is encountered, this field is set to true
297	e_o_s: bool,
298
299	/// * If encountered EOF, this field is set to true
300	e_o_f: bool,
301
302	/// * The cached bytes for next read
303	cached_bytes: Vec<u8>,
304}
305
306impl<R> OggStreamReader<R>
307where
308	R: Read + Debug {
309	const READ_SIZE: usize = 2048;
310
311	pub fn new(reader: R) -> Self {
312		Self {
313			reader,
314			stream_id: 0,
315			e_o_s: false,
316			e_o_f: false,
317			cached_bytes: Vec::new(),
318		}
319	}
320
321	fn safe_read(&mut self, target_len: usize) -> io::Result<Vec<u8>> {
322		let mut buf = vec![0u8; target_len];
323		let mut bytes_read = 0usize;
324		while bytes_read < target_len {
325			let read = match self.reader.read(&mut buf[bytes_read..]) {
326				Ok(0) => break,
327				Ok(size) => size,
328				Err(e) => match e.kind() {
329					io::ErrorKind::Interrupted => {
330						0
331					}
332					io::ErrorKind::UnexpectedEof => {
333						break;
334					}
335					_ => {
336						if bytes_read > 0 {
337							break;
338						} else {
339							return Err(e);
340						}
341					}
342				}
343			};
344			bytes_read += read;
345		}
346		buf.truncate(bytes_read);
347		Ok(buf)
348	}
349
350	pub fn get_packet(&mut self) -> io::Result<Option<OggPacket>> {
351		let mut packet_length = 0usize;
352		match OggPacket::from_bytes(&self.cached_bytes, &mut packet_length) {
353			Ok(packet) => {
354				if packet.packet_type == OggPacketType::EndOfStream {
355					self.e_o_s = true;
356				}
357				self.cached_bytes = self.cached_bytes[packet_length..].to_vec();
358				Ok(Some(packet))
359			}
360			Err(e) => match e.kind() {
361				io::ErrorKind::UnexpectedEof => { // Not enough bytes for an Ogg packet
362					let read = self.safe_read(Self::READ_SIZE)?;
363					self.cached_bytes.extend(&read);
364					if read.len() < Self::READ_SIZE {
365						if self.e_o_f == false {
366							self.e_o_f = true;
367							self.get_packet()
368						} else {
369							Err(e)
370						}
371					} else {
372						self.get_packet()
373					}
374				}
375				_ => Err(e)
376			}
377		}
378	}
379
380	pub fn is_eos(&self) -> bool {
381		self.e_o_s
382	}
383
384	pub fn is_eof(&self) -> bool {
385		self.e_o_f
386	}
387}
388
389
390/// * An ogg packets writer sink
391pub struct OggStreamWriter<W>
392where
393	W: Write + Debug {
394	/// * The writer, when a packet is full or you want to seal the packet, the packet is flushed in the writer
395	pub writer: W,
396
397	/// * The unique stream ID for a whole stream. Programs use the stream ID to identify which packet is for which stream.
398	pub stream_id: u32,
399
400	/// * The packet index.
401	pub packet_index: u32,
402
403	/// * The current packet, ready to be written.
404	pub cur_packet: OggPacket,
405
406	/// * The granule position is for the programmers to reference it for some purpose.
407	pub granule_position: u64,
408
409	/// * The `OggStreamWriter<W>` implements `Write`, when the `cur_packet` is full, the `on_seal()` closure will be called for updating the granule position.
410	/// * And then the packet will be flushed into the writer.
411	pub on_seal: Box<dyn FnMut(usize) -> u64>,
412
413	/// * How many bytes were written into this stream.
414	pub bytes_written: u64,
415}
416
417impl<W> OggStreamWriter<W>
418where
419	W: Write + Debug {
420	pub fn new(writer: W, stream_id: u32) -> Self {
421		Self {
422			writer,
423			stream_id,
424			packet_index : 0,
425			cur_packet: OggPacket::new(stream_id, OggPacketType::BeginOfStream, 0),
426			granule_position: 0,
427			bytes_written: 0,
428			on_seal: Box::new(|i|i as u64),
429		}
430	}
431
432	/// * Set the granule position. This field of data is not used by the Ogg stream.
433	/// * The granule position is for the inner things to reference it for some purpose.
434	pub fn set_granule_position(&mut self, position: u64) {
435		self.granule_position = position
436	}
437
438	/// * Get the granule position you had set before
439	pub fn get_granule_position(&self) -> u64 {
440		self.granule_position
441	}
442
443	/// * Mark the current packet as EOS
444	pub fn mark_cur_packet_as_end_of_stream(&mut self) {
445		self.cur_packet.packet_type = OggPacketType::EndOfStream;
446	}
447
448	/// * Get how many bytes written in this stream
449	pub fn get_bytes_written(&self) -> u64 {
450		self.bytes_written
451	}
452
453	/// * Set a callback for the `Write` trait when it seals the packet, the callback helps with updating the granule position
454	pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
455		self.on_seal = on_seal;
456	}
457
458	/// * Reset the stream state, discard the packet, reinit the packet to a BOS
459	pub fn reset(&mut self) {
460		self.packet_index = 0;
461		self.cur_packet = OggPacket::new(self.stream_id, OggPacketType::BeginOfStream, 0);
462		self.granule_position = 0;
463		self.bytes_written = 0;
464	}
465
466	/// * Save the current packet and write it to the sink, then create a new packet for writing.
467	pub fn seal_packet(&mut self, granule_position: u64, is_end_of_stream: bool) -> io::Result<()> {
468		self.packet_index += 1;
469		self.granule_position = granule_position;
470		self.cur_packet.granule_position = self.granule_position;
471		let packed = if is_end_of_stream {
472			self.cur_packet.packet_type = OggPacketType::EndOfStream;
473			mem::take(&mut self.cur_packet).into_bytes()
474		} else {
475			mem::replace(&mut self.cur_packet, OggPacket::new(self.stream_id, OggPacketType::Continuation, self.packet_index)).into_bytes()
476		};
477		self.writer.write_all(&packed)?;
478		Ok(())
479	}
480}
481
482impl<W> Write for OggStreamWriter<W>
483where
484	W: Write + Debug {
485	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
486		self.bytes_written = buf.len() as u64;
487		let mut buf = buf;
488		let mut written_total = 0usize;
489		while !buf.is_empty() {
490			let written = self.cur_packet.write(buf);
491			buf = &buf[written..];
492			written_total += written;
493			if !buf.is_empty() {
494				self.granule_position = (self.on_seal)(self.cur_packet.get_inner_data_size());
495				self.seal_packet(self.granule_position, false)?;
496			}
497		}
498		Ok(written_total)
499	}
500
501	fn flush(&mut self) -> io::Result<()> {
502		self.writer.flush()
503	}
504}
505
506impl<W> Debug for OggStreamWriter<W>
507where
508	W: Write + Debug {
509	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
510		f.debug_struct(&format!("OggStreamWriter<{}>", std::any::type_name::<W>()))
511		.field("writer", &self.writer)
512		.field("stream_id", &format_args!("0x{:08x}", self.stream_id))
513		.field("packet_index", &self.packet_index)
514		.field("cur_packet", &self.cur_packet)
515		.field("granule_position", &self.granule_position)
516		.field("on_seal", &format_args!("<closure>"))
517		.field("bytes_written", &self.bytes_written)
518		.finish()
519	}
520}
521
522impl<W> Drop for OggStreamWriter<W>
523where
524	W: Write + Debug {
525	fn drop(&mut self) {
526		self.seal_packet(self.granule_position, true).unwrap();
527	}
528}
529