1#![forbid(unsafe_code)]
5
6use crate::crc::crc32_ogg;
7use crate::error::Error;
8
9pub const MAX_SINGLE_PAGE_PAYLOAD: usize = 65_024;
12
13const CRC_FIELD_OFFSET: usize = 4 + 1 + 1 + 8 + 4 + 4;
16
17#[derive(Debug, Clone)]
25pub struct Muxer {
26 serial: u32,
27 sequence: u32,
28 bos_written: bool,
29}
30
31impl Muxer {
32 #[must_use]
34 pub const fn new(serial: u32) -> Self {
35 Self {
36 serial,
37 sequence: 0,
38 bos_written: false,
39 }
40 }
41
42 pub fn push_packet(
46 &mut self,
47 packet: &[u8],
48 granule_position: i64,
49 eos: bool,
50 out: &mut Vec<u8>,
51 ) -> Result<(), Error> {
52 if packet.len() > MAX_SINGLE_PAGE_PAYLOAD {
53 return Err(Error::PacketTooLargeForSinglePage(packet.len()));
54 }
55 let bos = !self.bos_written;
56 self.bos_written = true;
57 let segments = lacing_values_for(packet.len());
58
59 let page_start = out.len();
60 out.extend_from_slice(b"OggS");
61 out.push(0); let flags = (u8::from(bos) << 1) | (u8::from(eos) << 2);
63 out.push(flags);
64 out.extend_from_slice(&granule_position.to_le_bytes());
65 out.extend_from_slice(&self.serial.to_le_bytes());
66 out.extend_from_slice(&self.sequence.to_le_bytes());
67 out.extend_from_slice(&0u32.to_le_bytes()); out.push(u8::try_from(segments.len()).unwrap_or(u8::MAX));
69 out.extend_from_slice(&segments);
70 out.extend_from_slice(packet);
71
72 let crc = crc32_ogg(&out[page_start..]);
73 let crc_start = page_start + CRC_FIELD_OFFSET;
74 out[crc_start..crc_start + 4].copy_from_slice(&crc.to_le_bytes());
75
76 self.sequence += 1;
77 Ok(())
78 }
79}
80
81fn lacing_values_for(len: usize) -> Vec<u8> {
82 let mut segments = Vec::new();
83 let mut remaining = len;
84 while remaining >= 255 {
85 segments.push(255);
86 remaining -= 255;
87 }
88 segments.push(u8::try_from(remaining).unwrap_or(u8::MAX));
89 segments
90}
91
92#[cfg(test)]
93#[path = "mux_tests.rs"]
94mod tests;