#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod ogg;
use alloc::boxed::Box;
use core::{convert::TryInto, fmt};
pub struct Demuxer {
stream: ogg::Stream,
header: Option<InternalHeader>,
tags: Option<Box<[u8]>>,
}
impl Demuxer {
pub fn new() -> Self {
Self {
stream: ogg::Stream::new(),
header: None,
tags: None,
}
}
pub fn push(&mut self, data: &[u8]) -> Result<()> {
self.stream.push(data);
'next_page: loop {
let mut page = match self.stream.next() {
None => {
break;
}
Some(x) => x,
};
let page_header = page.header();
if self.header.is_none() {
loop {
let packet = match page.next() {
None => continue 'next_page,
Some(x) => x,
};
const ID_MAGIC: &[u8; 8] = b"OpusHead";
if page_header.bos {
if !packet.starts_with(ID_MAGIC)
|| packet.get(8).ok_or(Error::Malformed)? & 0xF0 != 0
{
continue;
}
self.header = Some(InternalHeader {
serial: page_header.stream_serial,
header: Header {
channels: packet[9],
pre_skip: u16::from_le_bytes(
packet
.get(10..12)
.ok_or(Error::Malformed)?
.try_into()
.unwrap(),
),
output_gain: i16::from_le_bytes(
packet
.get(16..18)
.ok_or(Error::Malformed)?
.try_into()
.unwrap(),
),
},
});
break;
}
}
}
if self.tags.is_none() {
loop {
let packet = match page.next() {
None => continue 'next_page,
Some(x) => x,
};
const COMMENT_MAGIC: &[u8; 8] = b"OpusTags";
if Some(page_header.stream_serial) != self.header.as_ref().map(|x| x.serial) {
continue;
}
if packet.starts_with(COMMENT_MAGIC) {
self.tags = Some(packet.into());
break;
}
}
}
break;
}
Ok(())
}
#[inline]
pub fn header(&self) -> Option<&Header> {
self.header.as_ref().map(|x| &x.header)
}
#[inline]
pub fn tags(&self) -> Option<&[u8]> {
self.tags.as_deref()
}
pub fn next(&mut self) -> Option<&[u8]> {
let header = match (&self.header, &self.tags) {
(&Some(ref h), &Some(_)) => h,
_ => return None,
};
loop {
let page = self.stream.next()?;
if page.header().stream_serial != header.serial {
continue;
}
if let Some(packet) = page.into_next() {
unsafe {
return Some(core::mem::transmute::<&[u8], &[u8]>(packet));
}
}
}
}
}
impl Default for Demuxer {
fn default() -> Self {
Self::new()
}
}
struct InternalHeader {
serial: u32,
header: Header,
}
#[derive(Debug, Copy, Clone)]
pub struct Header {
pub channels: u8,
pub pre_skip: u16,
pub output_gain: i16,
}
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone)]
pub enum Error {
Malformed,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("malformed container")
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}