#![forbid(unsafe_code)]
use std::collections::VecDeque;
use bytes::Bytes;
use crate::crc::crc32_ogg;
use crate::error::Error;
use crate::types::Packet;
const HEADER_LEN: usize = 27; const CRC_FIELD_OFFSET: usize = 4 + 1 + 1 + 8 + 4 + 4;
#[derive(Debug, Default)]
pub struct Demuxer {
buf: Vec<u8>,
partial: Vec<u8>,
has_partial: bool,
ready: VecDeque<Packet>,
}
impl Demuxer {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn push_bytes(&mut self, data: &[u8]) {
self.buf.extend_from_slice(data);
}
pub fn poll_packet(&mut self) -> Result<Option<Packet>, Error> {
loop {
if let Some(packet) = self.ready.pop_front() {
return Ok(Some(packet));
}
if !self.parse_one_page()? {
return Ok(None);
}
}
}
fn parse_one_page(&mut self) -> Result<bool, Error> {
if self.buf.len() < HEADER_LEN {
return Ok(false);
}
if &self.buf[0..4] != b"OggS" {
return Err(Error::BadCapturePattern);
}
let version = self.buf[4];
if version != 0 {
return Err(Error::UnsupportedVersion(version));
}
let flags = self.buf[5];
let continued = flags & 0x01 != 0;
let bos = flags & 0x02 != 0;
let eos = flags & 0x04 != 0;
let granule_position = i64::from_le_bytes(self.buf[6..14].try_into().unwrap_or_default());
let serial = u32::from_le_bytes(self.buf[14..18].try_into().unwrap_or_default());
let crc_declared = u32::from_le_bytes(self.buf[22..26].try_into().unwrap_or_default());
let page_segments = usize::from(self.buf[26]);
let with_seg_table_len = HEADER_LEN + page_segments;
if self.buf.len() < with_seg_table_len {
return Ok(false);
}
let segment_table = self.buf[HEADER_LEN..with_seg_table_len].to_vec();
let payload_len: usize = segment_table.iter().map(|&s| usize::from(s)).sum();
let total_page_len = with_seg_table_len + payload_len;
if self.buf.len() < total_page_len {
return Ok(false);
}
if continued != self.has_partial {
return Err(Error::ContinuationFlagMismatch { flag: continued });
}
let mut page_for_crc = self.buf[0..total_page_len].to_vec();
page_for_crc[CRC_FIELD_OFFSET..CRC_FIELD_OFFSET + 4].fill(0);
let computed = crc32_ogg(&page_for_crc);
if computed != crc_declared {
return Err(Error::CrcMismatch {
expected: crc_declared,
computed,
});
}
let payload_start = with_seg_table_len;
let mut seg_start = payload_start;
let mut offset = payload_start;
let page_count =
u32::try_from(segment_table.iter().filter(|&&s| s < 255).count()).unwrap_or(u32::MAX);
let mut page_index = 0u32;
for &seg in &segment_table {
offset += usize::from(seg);
if seg < 255 {
let chunk = &self.buf[seg_start..offset];
seg_start = offset;
if self.has_partial {
self.partial.extend_from_slice(chunk);
self.ready.push_back(Packet {
data: Bytes::copy_from_slice(&self.partial),
granule_position,
serial,
bos,
eos,
page_index,
page_count,
});
self.partial.clear();
self.has_partial = false;
} else {
self.ready.push_back(Packet {
data: Bytes::copy_from_slice(chunk),
granule_position,
serial,
bos,
eos,
page_index,
page_count,
});
}
page_index += 1;
}
}
if seg_start < total_page_len {
self.partial
.extend_from_slice(&self.buf[seg_start..total_page_len]);
self.has_partial = true;
}
self.buf.drain(0..total_page_len);
Ok(true)
}
}
#[cfg(test)]
#[path = "demux_tests.rs"]
mod tests;