librespot/audio/src/passthrough_decoder.rs

205 lines
6.1 KiB
Rust
Raw Normal View History

2021-01-07 06:42:38 +00:00
// Passthrough decoder for librespot
use super::{AudioDecoder, AudioError, AudioPacket};
use ogg::{OggReadError, Packet, PacketReader, PacketWriteEndInfo, PacketWriter};
use std::fmt;
use std::io::{Read, Seek};
use std::time::{SystemTime, UNIX_EPOCH};
2021-02-27 22:59:53 +00:00
fn get_header<T>(code: u8, rdr: &mut PacketReader<T>) -> Result<Box<[u8]>, PassthroughError>
2021-01-07 06:42:38 +00:00
where
T: Read + Seek,
{
let pck: Packet = rdr.read_packet_expected()?;
let pkt_type = pck.data[0];
debug!("Vorbis header type{}", &pkt_type);
if pkt_type != code {
return Err(PassthroughError(OggReadError::InvalidData));
}
2021-02-27 22:59:53 +00:00
return Ok(pck.data.into_boxed_slice());
2021-01-07 06:42:38 +00:00
}
pub struct PassthroughDecoder<R: Read + Seek> {
rdr: PacketReader<R>,
wtr: PacketWriter<Vec<u8>>,
2021-02-27 22:59:53 +00:00
eos: bool,
bos: bool,
ofsgp_page: u64,
2021-01-07 06:42:38 +00:00
stream_serial: u32,
2021-02-27 22:59:53 +00:00
ident: Box<[u8]>,
comment: Box<[u8]>,
setup: Box<[u8]>,
2021-01-07 06:42:38 +00:00
}
pub struct PassthroughError(ogg::OggReadError);
impl<R: Read + Seek> PassthroughDecoder<R> {
/// Constructs a new Decoder from a given implementation of `Read + Seek`.
pub fn new(rdr: R) -> Result<Self, PassthroughError> {
let mut rdr = PacketReader::new(rdr);
2021-02-27 22:59:53 +00:00
let stream_serial = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u32;
2021-01-07 06:42:38 +00:00
info!("Starting passthrough track with serial {}", stream_serial);
2021-02-27 22:59:53 +00:00
// search for ident, comment, setup
let ident = get_header(1, &mut rdr)?;
let comment = get_header(3, &mut rdr)?;
let setup = get_header(5, &mut rdr)?;
// remove un-needed packets
rdr.delete_unread_packets();
2021-01-07 06:42:38 +00:00
return Ok(PassthroughDecoder {
rdr,
2021-02-27 22:59:53 +00:00
wtr: PacketWriter::new(Vec::new()),
ofsgp_page: 0,
2021-01-07 06:42:38 +00:00
stream_serial,
2021-02-27 22:59:53 +00:00
ident,
comment,
setup,
eos: false,
bos: false,
2021-01-07 06:42:38 +00:00
});
}
}
impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
info!("Seeking to {}", ms);
2021-02-27 22:59:53 +00:00
// add an eos to previous stream if missing
if self.bos == true && self.eos == false {
match self.rdr.read_packet() {
Ok(Some(pck)) => {
let absgp_page = pck.absgp_page() - self.ofsgp_page;
self.wtr
.write_packet(
pck.data.into_boxed_slice(),
self.stream_serial,
PacketWriteEndInfo::EndStream,
absgp_page,
)
.unwrap();
}
_ => warn! {"Cannot write EoS after seeking"},
};
}
self.eos = false;
self.bos = false;
self.ofsgp_page = 0;
self.stream_serial += 1;
2021-01-07 06:42:38 +00:00
// hard-coded to 44.1 kHz
match self.rdr.seek_absgp(None, (ms * 44100 / 1000) as u64) {
2021-02-27 22:59:53 +00:00
Ok(_) => {
// need to set some offset for next_page()
let pck = self.rdr.read_packet().unwrap().unwrap();
self.ofsgp_page = pck.absgp_page();
debug!("Seek to offset page {}", self.ofsgp_page);
return Ok(());
}
2021-01-07 06:42:38 +00:00
Err(err) => return Err(AudioError::PassthroughError(err.into())),
}
}
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
2021-02-27 22:59:53 +00:00
// write headers if we are (re)starting
if self.bos == false {
self.wtr
.write_packet(
self.ident.clone(),
self.stream_serial,
PacketWriteEndInfo::EndPage,
0,
)
.unwrap();
self.wtr
.write_packet(
self.comment.clone(),
self.stream_serial,
PacketWriteEndInfo::NormalPacket,
0,
)
.unwrap();
self.wtr
.write_packet(
self.setup.clone(),
self.stream_serial,
PacketWriteEndInfo::EndPage,
0,
)
.unwrap();
self.bos = true;
debug!("Wrote Ogg headers");
}
2021-01-07 06:42:38 +00:00
loop {
let pck = match self.rdr.read_packet() {
Ok(Some(pck)) => pck,
Ok(None) | Err(OggReadError::NoCapturePatternFound) => {
info!("end of streaming");
return Ok(None);
}
Err(err) => return Err(AudioError::PassthroughError(err.into())),
};
let pckgp_page = pck.absgp_page();
2021-02-27 22:59:53 +00:00
// skip till we have audio and a calculable granule position
if pckgp_page == 0 || pckgp_page == self.ofsgp_page {
continue;
2021-01-07 06:42:38 +00:00
}
// set packet type
let inf = if pck.last_in_stream() {
2021-02-27 22:59:53 +00:00
self.eos = true;
2021-01-07 06:42:38 +00:00
PacketWriteEndInfo::EndStream
} else if pck.last_in_page() {
PacketWriteEndInfo::EndPage
} else {
PacketWriteEndInfo::NormalPacket
};
self.wtr
.write_packet(
pck.data.into_boxed_slice(),
self.stream_serial,
inf,
2021-02-27 22:59:53 +00:00
pckgp_page - self.ofsgp_page,
2021-01-07 06:42:38 +00:00
)
.unwrap();
let data = self.wtr.inner_mut();
if data.len() > 0 {
let result = AudioPacket::OggData(std::mem::take(data));
return Ok(Some(result));
}
}
}
}
impl fmt::Debug for PassthroughError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl From<ogg::OggReadError> for PassthroughError {
fn from(err: OggReadError) -> PassthroughError {
PassthroughError(err)
}
}
impl fmt::Display for PassthroughError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}