#![forbid(unsafe_code)]
use crate::crc::crc32_ogg;
use crate::error::Error;
pub const MAX_SINGLE_PAGE_PAYLOAD: usize = 65_024;
const CRC_FIELD_OFFSET: usize = 4 + 1 + 1 + 8 + 4 + 4;
#[derive(Debug, Clone)]
pub struct Muxer {
serial: u32,
sequence: u32,
bos_written: bool,
}
impl Muxer {
#[must_use]
pub const fn new(serial: u32) -> Self {
Self {
serial,
sequence: 0,
bos_written: false,
}
}
pub fn push_packet(
&mut self,
packet: &[u8],
granule_position: i64,
eos: bool,
out: &mut Vec<u8>,
) -> Result<(), Error> {
if packet.len() > MAX_SINGLE_PAGE_PAYLOAD {
return Err(Error::PacketTooLargeForSinglePage(packet.len()));
}
let bos = !self.bos_written;
self.bos_written = true;
let segments = lacing_values_for(packet.len());
let page_start = out.len();
out.extend_from_slice(b"OggS");
out.push(0); let flags = (u8::from(bos) << 1) | (u8::from(eos) << 2);
out.push(flags);
out.extend_from_slice(&granule_position.to_le_bytes());
out.extend_from_slice(&self.serial.to_le_bytes());
out.extend_from_slice(&self.sequence.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes()); out.push(u8::try_from(segments.len()).unwrap_or(u8::MAX));
out.extend_from_slice(&segments);
out.extend_from_slice(packet);
let crc = crc32_ogg(&out[page_start..]);
let crc_start = page_start + CRC_FIELD_OFFSET;
out[crc_start..crc_start + 4].copy_from_slice(&crc.to_le_bytes());
self.sequence += 1;
Ok(())
}
}
fn lacing_values_for(len: usize) -> Vec<u8> {
let mut segments = Vec::new();
let mut remaining = len;
while remaining >= 255 {
segments.push(255);
remaining -= 255;
}
segments.push(u8::try_from(remaining).unwrap_or(u8::MAX));
segments
}
#[cfg(test)]
#[path = "mux_tests.rs"]
mod tests;