use std::{io::Write, mem::MaybeUninit, os::raw::c_int, slice};
use ogg_next_sys::{
ogg_packet, ogg_page, ogg_stream_clear, ogg_stream_flush, ogg_stream_init, ogg_stream_packetin,
ogg_stream_pageout, ogg_stream_pageout_fill, ogg_stream_state
};
use crate::VorbisError;
pub struct OggStream {
ogg_stream: ogg_stream_state
}
impl OggStream {
pub fn new(stream_serial: i32) -> Result<Self, VorbisError> {
let mut ogg_stream = MaybeUninit::uninit();
unsafe {
libogg_return_value_to_result!(ogg_stream_init(
ogg_stream.as_mut_ptr(),
stream_serial
))?;
Ok(Self {
ogg_stream: ogg_stream.assume_init()
})
}
}
pub fn write_pending_pages<W: Write>(
&mut self,
mut sink: W,
minimum_page_data_size: Option<u16>
) -> Result<(), VorbisError> {
let mut ogg_page = MaybeUninit::uninit();
loop {
unsafe {
let no_pending_page = if let Some(minimum_page_data_size) = minimum_page_data_size {
ogg_stream_pageout_fill(
&mut self.ogg_stream,
ogg_page.as_mut_ptr(),
minimum_page_data_size as c_int
)
} else {
ogg_stream_pageout(&mut self.ogg_stream, ogg_page.as_mut_ptr())
} == 0;
if no_pending_page {
break;
}
OggPage {
ogg_page: ogg_page.assume_init()
}
.write(&mut sink)?;
}
}
Ok(())
}
pub fn flush<W: Write>(&mut self, mut sink: W) -> Result<(), VorbisError> {
let mut ogg_page = MaybeUninit::uninit();
unsafe {
while ogg_stream_flush(&mut self.ogg_stream, ogg_page.as_mut_ptr()) != 0 {
OggPage {
ogg_page: ogg_page.assume_init()
}
.write(&mut sink)?;
}
}
Ok(())
}
}
impl Drop for OggStream {
fn drop(&mut self) {
unsafe { ogg_stream_clear(&mut self.ogg_stream) };
}
}
pub struct OggPacket {
ogg_packet: ogg_packet
}
impl OggPacket {
pub fn new(ogg_packet: ogg_packet) -> Self {
OggPacket { ogg_packet }
}
pub fn submit(&mut self, ogg_stream: &mut OggStream) -> Result<(), VorbisError> {
unsafe {
libogg_return_value_to_result!(ogg_stream_packetin(
&mut ogg_stream.ogg_stream,
&mut self.ogg_packet
))?;
}
Ok(())
}
}
pub struct OggPage {
ogg_page: ogg_page
}
impl OggPage {
fn write<W: Write>(&self, mut sink: W) -> Result<(), VorbisError> {
unsafe {
sink.write_all(slice::from_raw_parts(
self.ogg_page.header,
self.ogg_page.header_len as usize
))?;
sink.write_all(slice::from_raw_parts(
self.ogg_page.body,
self.ogg_page.body_len as usize
))?;
}
Ok(())
}
}