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#[derive(Debug, Clone)]
14pub struct OggPacket {
15 pub version: u8,
17
18 pub packet_type: OggPacketType,
22
23 pub granule_position: u64,
26
27 pub stream_id: u32,
29
30 pub packet_index: u32,
32
33 pub checksum: u32,
35
36 pub segment_table: Vec<u8>,
38
39 pub data: Vec<u8>,
41}
42
43impl OggPacket {
44 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 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 pub fn clear(&mut self) {
88 self.segment_table = Vec::new();
89 self.data = Vec::new();
90 }
91
92 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 pub fn get_inner_data_size(&self) -> usize {
106 self.segment_table.iter().map(|&s|s as usize).sum()
107 }
108
109 pub fn get_inner_data(&self) -> Vec<u8> {
111 self.get_segments().into_iter().flatten().collect()
112 }
113
114 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 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 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 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 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 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
276pub struct OggStreamWriter<W>
278where
279 W: Write + Debug {
280 pub writer: W,
282
283 pub stream_id: u32,
285
286 pub packet_index: u32,
288
289 pub cur_packet: OggPacket,
291
292 pub granule_position: u64,
294
295 pub on_seal: Box<dyn FnMut(usize) -> u64>,
298
299 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 pub fn set_granule_position(&mut self, position: u64) {
321 self.granule_position = position
322 }
323
324 pub fn get_granule_position(&self) -> u64 {
326 self.granule_position
327 }
328
329 pub fn mark_cur_packet_as_end_of_stream(&mut self) {
331 self.cur_packet.packet_type = OggPacketType::EndOfStream;
332 }
333
334 pub fn get_bytes_written(&self) -> u64 {
336 self.bytes_written
337 }
338
339 pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
341 self.on_seal = on_seal;
342 }
343
344 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 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