Added Opus decoder

master
hheik 2024-03-12 11:02:20 +02:00
parent c900e9b96e
commit 7771929002
4 changed files with 241 additions and 64 deletions

View File

@ -11,6 +11,8 @@ bincode = "1.3.3"
crc32fast = "1.3.2"
crossterm = "0.27.0"
interprocess = "1.2.1"
opus = "0.3.0"
ratatui = "0.23.0"
rodio = {version = "0.17.3", features = [ "symphonia-all" ], default-features = false }
serde = "1.0.196"
symphonia = "0.5.4"

View File

@ -4,50 +4,72 @@
// - Mod
// - XM
use std::io::{Read, Seek};
use std::{
fs::File,
io::{BufReader, Read, Seek},
};
use symphonia::core::{
codecs::CODEC_TYPE_OPUS,
formats::{FormatOptions, FormatReader, Track},
io::{MediaSource, MediaSourceStream},
meta::MetadataOptions,
probe::Hint,
};
mod opus;
use crate::server::decoder::opus::OpusDecoder;
pub struct Decoder;
impl Decoder {
pub fn new<R>(mut data: R) -> Result<Box<dyn DecoderImpl<R, Item = i16>>, ()>
pub fn rodio<R>(data: R) -> Result<Box<dyn DecoderImpl<Item = i16>>, ()>
where
R: Read + Seek + Send + Sync + 'static,
R: MediaSource + Read + Seek + Send + 'static,
{
let start_position = data.stream_position().unwrap();
match opus::OpusDecoder::new(&mut data) {
Ok(source) => return Ok(Box::new(source)),
Err(err) => {
eprintln!("Custom decode error: {err:?}");
}
};
data.seek(std::io::SeekFrom::Start(start_position)).unwrap();
match rodio::Decoder::new(data) {
Ok(source) => return Ok(Box::new(source)),
Err(err) => {
eprintln!("Rodio decode error: {err:?}");
Ok(source) => Ok(Box::new(source)),
_ => Err(()),
}
}
pub fn custom<R>(data: R) -> Result<Box<dyn DecoderImpl<Item = i16>>, ()>
where
R: MediaSource + Read + Seek + Send + 'static,
{
let reader = match demux(data) {
Ok(track) => track,
_ => return Err(()),
};
let tracks: Vec<_> = reader
.tracks()
.iter()
.cloned()
.map(|track| (track.codec_params.codec, track.id))
.collect();
for (codec, track_id) in tracks {
match codec {
CODEC_TYPE_OPUS => match OpusDecoder::new(Demuxer::new(reader, track_id)) {
Ok(source) => return Ok(Box::new(source)),
_ => return Err(()),
},
_ => {}
}
}
Err(())
}
}
pub trait DecoderImpl<R>: rodio::Source + Send + Sync + 'static
pub trait DecoderImpl: rodio::Source + Send + 'static
where
R: Read + Seek,
<Self as Iterator>::Item: rodio::Sample,
{
}
impl<R> rodio::Source for Box<dyn DecoderImpl<R, Item = i16>>
where
R: Read + Seek,
{
impl rodio::Source for Box<dyn DecoderImpl<Item = i16>> {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.as_ref().current_frame_len()
@ -69,4 +91,78 @@ where
}
}
impl<R> DecoderImpl<R> for rodio::Decoder<R> where R: Read + Seek + Send + Sync + 'static {}
impl<R> DecoderImpl for rodio::Decoder<R> where R: Read + Seek + Send + 'static {}
pub struct SourceWrapper {
pub reader: BufReader<File>,
is_seekable: bool,
byte_len: Option<u64>,
}
impl SourceWrapper {
pub fn from_file(file: File) -> Self {
let is_seekable = file.is_seekable();
let byte_len = file.byte_len();
Self {
reader: BufReader::new(file),
is_seekable,
byte_len,
}
}
}
impl Read for SourceWrapper {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
self.reader.read(buf)
}
}
impl Seek for SourceWrapper {
fn seek(&mut self, pos: std::io::SeekFrom) -> Result<u64, std::io::Error> {
self.reader.seek(pos)
}
}
impl MediaSource for SourceWrapper {
fn is_seekable(&self) -> bool {
self.is_seekable
}
fn byte_len(&self) -> Option<u64> {
self.byte_len
}
}
pub struct Demuxer {
reader: Box<dyn FormatReader>,
track_id: u32,
}
impl Demuxer {
pub fn new(reader: Box<dyn FormatReader>, track_id: u32) -> Self {
Self { reader, track_id }
}
pub fn get_track(&self) -> Option<&Track> {
self.reader
.tracks()
.iter()
.find(|track| track.id == self.track_id)
}
}
fn demux<R>(data: R) -> Result<Box<dyn FormatReader>, ()>
where
R: MediaSource + 'static,
{
let stream = MediaSourceStream::new(Box::new(data), Default::default());
let hint = Hint::new();
let format_opts: FormatOptions = Default::default();
let metadata_opts: MetadataOptions = Default::default();
let probe = symphonia::default::get_probe()
.format(&hint, stream, &format_opts, &metadata_opts)
.map_err(|_| ())?;
let format = probe.format;
Ok(format)
}

View File

@ -1,58 +1,117 @@
use std::{
io::{Read, Seek},
marker::PhantomData,
use std::time::Duration;
use super::{DecoderImpl, Demuxer};
pub struct OpusDecoder {
demuxer: Demuxer,
decoder: opus::Decoder,
channels: u16,
sample_rate: u32,
total_duration: Option<Duration>,
decoded_buffer: Vec<i16>,
decoded_cursor: usize,
decoded_length: usize,
}
impl OpusDecoder {
pub fn new(demuxer: Demuxer) -> Result<Self, ()> {
let track = demuxer.get_track().ok_or(())?;
let channels = track
.codec_params
.channels
.map(|c| c.count() as u16)
.ok_or(())?;
let sample_rate = track.codec_params.sample_rate.ok_or(())?;
let total_duration = match (track.codec_params.time_base, track.codec_params.n_frames) {
(Some(time_base), Some(n_frames)) => {
Some(Duration::from_secs(time_base.calc_time(n_frames).seconds))
}
_ => None,
};
pub struct OpusDecoder<R>
where
R: Read + Seek,
{
_data: PhantomData<R>,
let channel_enum = match channels {
1 => opus::Channels::Mono,
2 => opus::Channels::Stereo,
_ => return Err(()),
};
let decoder = match opus::Decoder::new(sample_rate, channel_enum) {
Ok(decoder) => decoder,
Err(err) => {
eprintln!("Opus decoder creation error: {err:?}");
return Err(());
}
};
impl<R> super::DecoderImpl<R> for OpusDecoder<R> where R: Read + Seek + Send + Sync + 'static {}
impl<R> OpusDecoder<R>
where
R: Read + Seek + Send + Sync + 'static,
{
pub fn new(data: &mut R) -> Result<Self, ()> {
Err(())
Ok(OpusDecoder {
demuxer,
decoder,
channels,
sample_rate,
total_duration,
decoded_buffer: vec![0; 10000], // TODO: Calculate better estimate
decoded_cursor: 0,
decoded_length: 0,
})
}
}
impl<R> Iterator for OpusDecoder<R>
where
R: Read + Seek,
{
impl DecoderImpl for OpusDecoder {}
impl Iterator for OpusDecoder {
type Item = i16;
fn next(&mut self) -> Option<Self::Item> {
None
while self.decoded_cursor >= self.decoded_length {
// Fill buffer with the next decoded packet
match self.demuxer.reader.next_packet() {
Ok(packet) => {
if packet.track_id() == self.demuxer.track_id {
self.decoded_cursor = 0;
self.decoded_length = 0;
let result =
self.decoder
.decode(packet.buf(), &mut self.decoded_buffer, false);
match result {
Err(err) => {
eprintln!("Opus packet decoding error: {err:?}");
return None;
}
Ok(size) => {
// opus_decode returns the number of decoded samples per channel,
// so the actual written size is that times the number of channels
self.decoded_length = size * self.channels as usize
}
};
}
}
Err(_) => return None,
};
}
let item = self.decoded_buffer.get(self.decoded_cursor).cloned();
self.decoded_cursor += 1;
item
}
}
impl<R> rodio::Source for OpusDecoder<R>
where
R: Read + Seek,
{
impl rodio::Source for OpusDecoder {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
todo!()
None
}
#[inline]
fn channels(&self) -> u16 {
todo!()
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
todo!()
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<std::time::Duration> {
todo!()
self.total_duration
}
}

View File

@ -1,13 +1,15 @@
use std::{
fs::File,
io::BufReader,
path::PathBuf,
sync::{Arc, Mutex},
};
use rodio::{OutputStream, Sink};
use super::{decoder::Decoder, Server};
use super::{
decoder::{Decoder, DecoderImpl, SourceWrapper},
Server,
};
// HACK: hard-coded path to track
static TRACK: &str = "";
@ -46,15 +48,33 @@ pub fn playback_manager(server: Arc<Mutex<Server>>) {
for command in server.lock().unwrap().playback.command_queue.drain(..) {
match command {
PlaybackCommand::Play(track) => {
let file = BufReader::new(File::open(&track).unwrap());
let source = match Decoder::new(file) {
Ok(source) => source,
Err(_) => continue,
let mut source: Option<Box<dyn DecoderImpl<Item = i16>>> = None;
{
let file = File::open(&track).unwrap();
if let Ok(decoder) = Decoder::rodio(SourceWrapper::from_file(file)) {
println!("rodio:\n\tsample_rate: {:?}\n\ttotal_duration: {:?}\n\tchannels: {:?}\n\tcurrent_frame_len: {:?}", decoder.sample_rate(), decoder.total_duration(), decoder.channels(), decoder.current_frame_len());
source = Some(decoder);
};
}
if source.is_none() {
let file = File::open(&track).unwrap();
if let Ok(decoder) = Decoder::custom(SourceWrapper::from_file(file)) {
println!("custom:\n\tsample_rate: {:?}\n\ttotal_duration: {:?}\n\tchannels: {:?}\n\tcurrent_frame_len: {:?}", decoder.sample_rate(), decoder.total_duration(), decoder.channels(), decoder.current_frame_len());
source = Some(decoder);
}
}
match source {
Some(source) => {
sink.clear();
sink.append(source);
sink.play();
}
None => println!("No handler found for '{track:?}'"),
}
}
PlaybackCommand::TogglePause => {
if !sink.empty() {
if sink.is_paused() {