use mousiki::decoder::{Decoder, DecoderError};
use mousiki::oggreader::{OggRead, OggReader, OggReaderError, ReadError};
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
use std::process;
const OPUS_TAGS_SIGNATURE: &[u8] = b"OpusTags";
const PCM_BYTES_PER_FRAME: usize = 1920;
struct FileStream {
file: File,
}
impl FileStream {
fn new(file: File) -> Self {
Self { file }
}
}
impl OggRead for FileStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
use std::io::Read;
loop {
match self.file.read(buf) {
Ok(n) => return Ok(n),
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => return Ok(0),
Err(_) => return Err(ReadError::Other),
}
}
}
}
fn main() {
if let Err(err) = run() {
report_error(err);
process::exit(1);
}
}
fn run() -> Result<(), ExampleError> {
let mut args = env::args_os();
let _program = args.next();
let input = args.next().ok_or(ExampleError::Usage)?;
let output = args.next().ok_or(ExampleError::Usage)?;
if args.next().is_some() {
return Err(ExampleError::Usage);
}
let input_path = Path::new(&input);
let output_path = Path::new(&output);
let input_file =
File::open(input_path).map_err(|err| ExampleError::Io("open input", err.kind()))?;
let (mut ogg_reader, _) =
OggReader::new_with(FileStream::new(input_file)).map_err(ExampleError::Ogg)?;
let mut output_file =
File::create(output_path).map_err(|err| ExampleError::Io("create output", err.kind()))?;
let mut decoder = Decoder::new();
let mut pcm = [0u8; PCM_BYTES_PER_FRAME];
loop {
let (segments, _) = match ogg_reader.parse_next_page() {
Ok(result) => result,
Err(OggReaderError::Read(ReadError::UnexpectedEof)) => break,
Err(err) => return Err(ExampleError::Ogg(err)),
};
if let Some(first) = segments.get(0)
&& first.starts_with(OPUS_TAGS_SIGNATURE)
{
continue;
}
for segment in segments.into_iter() {
if segment.is_empty() {
continue;
}
decoder
.decode(segment, &mut pcm)
.map_err(ExampleError::Decoder)?;
output_file
.write_all(&pcm)
.map_err(|err| ExampleError::Io("write output", err.kind()))?;
}
}
Ok(())
}
fn report_error(err: ExampleError) {
match err {
ExampleError::Usage => {
eprintln!("Usage: decode <in-file> <out-file>");
}
ExampleError::Io(context, kind) => {
eprintln!("IO error ({context}): {kind:?}");
}
ExampleError::Ogg(err) => {
eprintln!("ogg reader error: {err}");
}
ExampleError::Decoder(err) => {
eprintln!("decoder error: {err}");
}
}
}
enum ExampleError {
Usage,
Io(&'static str, io::ErrorKind),
Ogg(OggReaderError),
Decoder(DecoderError),
}
impl From<OggReaderError> for ExampleError {
fn from(value: OggReaderError) -> Self {
Self::Ogg(value)
}
}
impl From<DecoderError> for ExampleError {
fn from(value: DecoderError) -> Self {
Self::Decoder(value)
}
}