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,
9
10 BeginOfStream = 2,
12
13 EndOfStream = 4,
15}
16
17#[derive(Debug, Clone)]
19pub struct OggPacket {
20 pub version: u8,
22
23 pub packet_type: OggPacketType,
27
28 pub granule_position: u64,
31
32 pub stream_id: u32,
34
35 pub packet_index: u32,
37
38 pub checksum: u32,
40
41 pub segment_table: Vec<u8>,
43
44 pub data: Vec<u8>,
46}
47
48impl OggPacket {
49 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 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 pub fn clear(&mut self) {
93 self.segment_table = Vec::new();
94 self.data = Vec::new();
95 }
96
97 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 pub fn get_inner_data_size(&self) -> usize {
111 self.segment_table.iter().map(|&s|s as usize).sum()
112 }
113
114 pub fn get_inner_data(&self) -> Vec<u8> {
116 self.get_segments().into_iter().flatten().collect()
117 }
118
119 pub fn crc(mut crc: u32, data: &[u8]) -> u32 {
121 type CrcTableType = [u32; 256];
122 fn ogg_generate_crc_table() -> CrcTableType {
123 use std::mem::MaybeUninit;
124 #[allow(invalid_value)]
125 #[allow(clippy::uninit_assumed_init)]
126 let mut crc_lookup: CrcTableType = unsafe{MaybeUninit::uninit().assume_init()};
127 (0..256).for_each(|i|{
128 let mut r: u32 = i << 24;
129 for _ in 0..8 {
130 r = (r << 1) ^ (-(((r >> 31) & 1) as i32) as u32 & 0x04c11db7);
131 }
132 crc_lookup[i as usize] = r;
133 });
134 crc_lookup
135 }
136
137 use std::sync::OnceLock;
138 static OGG_CRC_TABLE: OnceLock<CrcTableType> = OnceLock::<CrcTableType>::new();
139 let crc_lookup = OGG_CRC_TABLE.get_or_init(ogg_generate_crc_table);
140
141 for b in data {
142 crc = (crc << 8) ^ crc_lookup[(*b as u32 ^ (crc >> 24)) as usize];
143 }
144
145 crc
146 }
147
148 pub fn get_checksum(ogg_packet: &[u8]) -> io::Result<u32> {
149 if ogg_packet.len() < 27 {
150 Err(io::Error::new(ErrorKind::InvalidData, format!("The given packet is too small: {} < 27", ogg_packet.len())))
151 } else {
152 let mut field_cleared = ogg_packet.to_vec();
153 field_cleared[22..26].copy_from_slice(&[0u8; 4]);
154 Ok(Self::crc(0, &field_cleared))
155 }
156 }
157
158 pub fn fill_checksum_field(ogg_packet: &mut [u8]) -> io::Result<()> {
160 let checksum = Self::get_checksum(ogg_packet)?;
161 ogg_packet[22..26].copy_from_slice(&checksum.to_le_bytes());
162 Ok(())
163 }
164
165 pub fn into_bytes(self) -> Vec<u8> {
167 let mut ret: Vec<u8> = [
168 b"OggS" as &[u8],
169 &[self.version],
170 &[self.packet_type as u8],
171 &self.granule_position.to_le_bytes() as &[u8],
172 &self.stream_id.to_le_bytes() as &[u8],
173 &self.packet_index.to_le_bytes() as &[u8],
174 &0u32.to_le_bytes() as &[u8],
175 &[self.segment_table.len() as u8],
176 &self.segment_table,
177 &self.data,
178 ].into_iter().flatten().copied().collect();
179 Self::fill_checksum_field(&mut ret).unwrap();
180 ret
181 }
182
183 pub fn get_length(ogg_packet: &[u8]) -> io::Result<usize> {
185 if ogg_packet.len() < 27 {
186 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < 27", ogg_packet.len())))
187 } else if ogg_packet[0..4] != *b"OggS" {
188 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
189 } else if ogg_packet[4] != 0 {
190 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
191 } else {
192 match ogg_packet[5] {
193 0 | 2 | 4 => (),
194 o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
195 }
196 let num_segments = ogg_packet[26] as usize;
197 let data_start = 27 + num_segments;
198 let segment_table = &ogg_packet[27..data_start];
199 let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
200 Ok(data_start + data_length)
201 }
202 }
203
204 pub fn from_bytes(ogg_packet: &[u8], packet_length: &mut usize) -> io::Result<Self> {
206 if ogg_packet.len() < 27 {
207 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < 27", ogg_packet.len())))
208 } else if ogg_packet[0..4] != *b"OggS" {
209 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: expected `OggS`, got `{}`", String::from_utf8_lossy(&ogg_packet[0..4]))))
210 } else if ogg_packet[4] != 0 {
211 Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `version` = {} (should be zero)", ogg_packet[4])))
212 } else {
213 let packet_type = match ogg_packet[5] {
214 0 => OggPacketType::Continuation,
215 2 => OggPacketType::BeginOfStream,
216 4 => OggPacketType::EndOfStream,
217 o => return Err(io::Error::new(ErrorKind::InvalidData, format!("While parsing Ogg packet: invalid `packet_type` = {o} (should be 0, 2, 4)"))),
218 };
219 let num_segments = ogg_packet[26] as usize;
220 let data_start = 27 + num_segments;
221 let segment_table = &ogg_packet[27..data_start];
222 let data_length: usize = segment_table.iter().map(|&s|s as usize).sum();
223 *packet_length = data_start + data_length;
224 if ogg_packet.len() < *packet_length {
225 Err(io::Error::new(ErrorKind::UnexpectedEof, format!("The given ogg page size is too small: {} < {packet_length}", ogg_packet.len())))
226 } else {
227 let ret = Self{
228 version: 0,
229 packet_type,
230 granule_position: u64::from_le_bytes(ogg_packet[6..14].try_into().unwrap()),
231 stream_id: u32::from_le_bytes(ogg_packet[14..18].try_into().unwrap()),
232 packet_index: u32::from_le_bytes(ogg_packet[18..22].try_into().unwrap()),
233 checksum: u32::from_le_bytes(ogg_packet[22..26].try_into().unwrap()),
234 segment_table: segment_table.to_vec(),
235 data: ogg_packet[data_start..*packet_length].to_vec(),
236 };
237 let checksum = Self::get_checksum(&ogg_packet[..*packet_length])?;
238 if ret.checksum != checksum {
239 Err(io::Error::new(ErrorKind::InvalidData, format!("Ogg packet checksum not match: should be 0x{:x}, got 0x{:x}", checksum, ret.checksum)))
240 } else {
241 Ok(ret)
242 }
243 }
244 }
245 }
246
247 pub fn from_cursor(cursor: &mut Cursor<Vec<u8>>) -> Vec<OggPacket> {
249 let mut data: &[u8] = cursor.get_ref();
250 let mut packet_length = 0usize;
251 let mut bytes_read = 0usize;
252 let mut ret = Vec::<OggPacket>::new();
253 while let Ok(packet) = Self::from_bytes(data, &mut packet_length) {
254 bytes_read += packet_length;
255 ret.push(packet);
256 data = &data[packet_length..];
257 if data.is_empty() {
258 break;
259 }
260 }
261 cursor.set_position(bytes_read as u64);
262 ret
263 }
264}
265
266impl Default for OggPacket {
267 fn default() -> Self {
268 Self {
269 version: 0,
270 packet_type: OggPacketType::BeginOfStream,
271 granule_position: 0,
272 stream_id: 0,
273 packet_index: 0,
274 checksum: 0,
275 segment_table: Vec::new(),
276 data: Vec::new(),
277 }
278 }
279}
280
281pub struct OggStreamWriter<W>
283where
284 W: Write + Debug {
285 pub writer: W,
287
288 pub stream_id: u32,
290
291 pub packet_index: u32,
293
294 pub cur_packet: OggPacket,
296
297 pub granule_position: u64,
299
300 pub on_seal: Box<dyn FnMut(usize) -> u64>,
303
304 pub bytes_written: u64,
306}
307
308impl<W> OggStreamWriter<W>
309where
310 W: Write + Debug {
311 pub fn new(writer: W, stream_id: u32) -> Self {
312 Self {
313 writer,
314 stream_id,
315 packet_index : 0,
316 cur_packet: OggPacket::new(stream_id, OggPacketType::BeginOfStream, 0),
317 granule_position: 0,
318 bytes_written: 0,
319 on_seal: Box::new(|i|i as u64),
320 }
321 }
322
323 pub fn set_granule_position(&mut self, position: u64) {
326 self.granule_position = position
327 }
328
329 pub fn get_granule_position(&self) -> u64 {
331 self.granule_position
332 }
333
334 pub fn mark_cur_packet_as_end_of_stream(&mut self) {
336 self.cur_packet.packet_type = OggPacketType::EndOfStream;
337 }
338
339 pub fn get_bytes_written(&self) -> u64 {
341 self.bytes_written
342 }
343
344 pub fn set_on_seal_callback(&mut self, on_seal: Box<dyn FnMut(usize) -> u64>) {
346 self.on_seal = on_seal;
347 }
348
349 pub fn reset(&mut self) {
351 self.packet_index = 0;
352 self.cur_packet = OggPacket::new(self.stream_id, OggPacketType::BeginOfStream, 0);
353 self.granule_position = 0;
354 self.bytes_written = 0;
355 }
356
357 pub fn seal_packet(&mut self, granule_position: u64, is_end_of_stream: bool) -> io::Result<()> {
359 self.packet_index += 1;
360 self.granule_position = granule_position;
361 self.cur_packet.granule_position = self.granule_position;
362 let packed = if is_end_of_stream {
363 self.cur_packet.packet_type = OggPacketType::EndOfStream;
364 mem::take(&mut self.cur_packet).into_bytes()
365 } else {
366 mem::replace(&mut self.cur_packet, OggPacket::new(self.stream_id, OggPacketType::Continuation, self.packet_index)).into_bytes()
367 };
368 self.writer.write_all(&packed)?;
369 Ok(())
370 }
371}
372
373impl<W> Write for OggStreamWriter<W>
374where
375 W: Write + Debug {
376 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
377 self.bytes_written = buf.len() as u64;
378 let mut buf = buf;
379 let mut written_total = 0usize;
380 while !buf.is_empty() {
381 let written = self.cur_packet.write(buf);
382 buf = &buf[written..];
383 written_total += written;
384 if !buf.is_empty() {
385 self.granule_position = (self.on_seal)(self.cur_packet.get_inner_data_size());
386 self.seal_packet(self.granule_position, false)?;
387 }
388 }
389 Ok(written_total)
390 }
391
392 fn flush(&mut self) -> io::Result<()> {
393 self.writer.flush()
394 }
395}
396
397impl<W> Debug for OggStreamWriter<W>
398where
399 W: Write + Debug {
400 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
401 f.debug_struct(&format!("OggStreamWriter<{}>", std::any::type_name::<W>()))
402 .field("writer", &self.writer)
403 .field("stream_id", &format_args!("0x{:08x}", self.stream_id))
404 .field("packet_index", &self.packet_index)
405 .field("cur_packet", &self.cur_packet)
406 .field("granule_position", &self.granule_position)
407 .field("on_seal", &format_args!("<closure>"))
408 .field("bytes_written", &self.bytes_written)
409 .finish()
410 }
411}
412
413impl<W> Drop for OggStreamWriter<W>
414where
415 W: Write + Debug {
416 fn drop(&mut self) {
417 self.seal_packet(self.granule_position, true).unwrap();
418 }
419}
420