ogg/
ogg.rs

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