mirror of
https://github.com/librespot-org/librespot.git
synced 2025-10-06 03:50:06 +02:00
Merge branch 'dev' into tokio_migration
This commit is contained in:
commit
678d1777fd
32 changed files with 674 additions and 360 deletions
|
@ -2,12 +2,12 @@ extern crate lewton;
|
|||
|
||||
use self::lewton::inside_ogg::OggStreamReader;
|
||||
|
||||
use super::{AudioDecoder, AudioError, AudioPacket};
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub struct VorbisDecoder<R: Read + Seek>(OggStreamReader<R>);
|
||||
pub struct VorbisPacket(Vec<i16>);
|
||||
pub struct VorbisError(lewton::VorbisError);
|
||||
|
||||
impl<R> VorbisDecoder<R>
|
||||
|
@ -17,41 +17,38 @@ where
|
|||
pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
|
||||
Ok(VorbisDecoder(OggStreamReader::new(input)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
|
||||
impl<R> AudioDecoder for VorbisDecoder<R>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
|
||||
let absgp = ms * 44100 / 1000;
|
||||
self.0.seek_absgp_pg(absgp as u64)?;
|
||||
Ok(())
|
||||
match self.0.seek_absgp_pg(absgp as u64) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(err) => return Err(AudioError::VorbisError(err.into())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_packet(&mut self) -> Result<Option<VorbisPacket>, VorbisError> {
|
||||
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
|
||||
use self::lewton::audio::AudioReadError::AudioIsHeader;
|
||||
use self::lewton::OggReadError::NoCapturePatternFound;
|
||||
use self::lewton::VorbisError::BadAudio;
|
||||
use self::lewton::VorbisError::OggError;
|
||||
loop {
|
||||
match self.0.read_dec_packet_itl() {
|
||||
Ok(Some(packet)) => return Ok(Some(VorbisPacket(packet))),
|
||||
Ok(Some(packet)) => return Ok(Some(AudioPacket::Samples(packet))),
|
||||
Ok(None) => return Ok(None),
|
||||
|
||||
Err(BadAudio(AudioIsHeader)) => (),
|
||||
Err(OggError(NoCapturePatternFound)) => (),
|
||||
Err(err) => return Err(err.into()),
|
||||
Err(err) => return Err(AudioError::VorbisError(err.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VorbisPacket {
|
||||
pub fn data(&self) -> &[i16] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> &mut [i16] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lewton::VorbisError> for VorbisError {
|
||||
fn from(err: lewton::VorbisError) -> VorbisError {
|
||||
VorbisError(err)
|
||||
|
|
|
@ -23,6 +23,7 @@ mod fetch;
|
|||
mod lewton_decoder;
|
||||
#[cfg(any(feature = "with-tremor", feature = "with-vorbis"))]
|
||||
mod libvorbis_decoder;
|
||||
mod passthrough_decoder;
|
||||
|
||||
mod range_set;
|
||||
|
||||
|
@ -32,8 +33,70 @@ pub use fetch::{
|
|||
READ_AHEAD_BEFORE_PLAYBACK_ROUNDTRIPS, READ_AHEAD_BEFORE_PLAYBACK_SECONDS,
|
||||
READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
pub enum AudioPacket {
|
||||
Samples(Vec<i16>),
|
||||
OggData(Vec<u8>),
|
||||
}
|
||||
|
||||
impl AudioPacket {
|
||||
pub fn samples(&self) -> &[i16] {
|
||||
match self {
|
||||
AudioPacket::Samples(s) => s,
|
||||
AudioPacket::OggData(_) => panic!("can't return OggData on samples"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn oggdata(&self) -> &[u8] {
|
||||
match self {
|
||||
AudioPacket::Samples(_) => panic!("can't return samples on OggData"),
|
||||
AudioPacket::OggData(d) => d,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
AudioPacket::Samples(s) => s.is_empty(),
|
||||
AudioPacket::OggData(d) => d.is_empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))]
|
||||
pub use crate::lewton_decoder::{VorbisDecoder, VorbisError, VorbisPacket};
|
||||
pub use crate::lewton_decoder::{VorbisDecoder, VorbisError};
|
||||
#[cfg(any(feature = "with-tremor", feature = "with-vorbis"))]
|
||||
pub use libvorbis_decoder::{VorbisDecoder, VorbisError, VorbisPacket};
|
||||
pub use libvorbis_decoder::{VorbisDecoder, VorbisError};
|
||||
pub use passthrough_decoder::{PassthroughDecoder, PassthroughError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AudioError {
|
||||
PassthroughError(PassthroughError),
|
||||
VorbisError(VorbisError),
|
||||
}
|
||||
|
||||
impl fmt::Display for AudioError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
AudioError::PassthroughError(err) => write!(f, "PassthroughError({})", err),
|
||||
AudioError::VorbisError(err) => write!(f, "VorbisError({})", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VorbisError> for AudioError {
|
||||
fn from(err: VorbisError) -> AudioError {
|
||||
AudioError::VorbisError(VorbisError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PassthroughError> for AudioError {
|
||||
fn from(err: PassthroughError) -> AudioError {
|
||||
AudioError::PassthroughError(PassthroughError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AudioDecoder {
|
||||
fn seek(&mut self, ms: i64) -> Result<(), AudioError>;
|
||||
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError>;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ extern crate librespot_tremor as vorbis;
|
|||
#[cfg(not(feature = "with-tremor"))]
|
||||
extern crate vorbis;
|
||||
|
||||
use super::{AudioDecoder, AudioError, AudioPacket};
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
|
||||
pub struct VorbisPacket(vorbis::Packet);
|
||||
pub struct VorbisError(vorbis::VorbisError);
|
||||
|
||||
impl<R> VorbisDecoder<R>
|
||||
|
@ -18,23 +18,28 @@ where
|
|||
pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
|
||||
Ok(VorbisDecoder(vorbis::Decoder::new(input)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> AudioDecoder for VorbisDecoder<R>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
#[cfg(not(feature = "with-tremor"))]
|
||||
pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
|
||||
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
|
||||
self.0.time_seek(ms as f64 / 1000f64)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-tremor")]
|
||||
pub fn seek(&mut self, ms: i64) -> Result<(), VorbisError> {
|
||||
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
|
||||
self.0.time_seek(ms)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn next_packet(&mut self) -> Result<Option<VorbisPacket>, VorbisError> {
|
||||
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
|
||||
loop {
|
||||
match self.0.packets().next() {
|
||||
Some(Ok(packet)) => return Ok(Some(VorbisPacket(packet))),
|
||||
Some(Ok(packet)) => return Ok(Some(AudioPacket::Samples(packet.data))),
|
||||
None => return Ok(None),
|
||||
|
||||
Some(Err(vorbis::VorbisError::Hole)) => (),
|
||||
|
@ -44,16 +49,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl VorbisPacket {
|
||||
pub fn data(&self) -> &[i16] {
|
||||
&self.0.data
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> &mut [i16] {
|
||||
&mut self.0.data
|
||||
}
|
||||
}
|
||||
|
||||
impl From<vorbis::VorbisError> for VorbisError {
|
||||
fn from(err: vorbis::VorbisError) -> VorbisError {
|
||||
VorbisError(err)
|
||||
|
@ -77,3 +72,9 @@ impl error::Error for VorbisError {
|
|||
error::Error::source(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<vorbis::VorbisError> for AudioError {
|
||||
fn from(err: vorbis::VorbisError) -> AudioError {
|
||||
AudioError::VorbisError(VorbisError(err))
|
||||
}
|
||||
}
|
||||
|
|
191
audio/src/passthrough_decoder.rs
Normal file
191
audio/src/passthrough_decoder.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
// 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};
|
||||
|
||||
fn write_headers<T: Read + Seek>(
|
||||
rdr: &mut PacketReader<T>,
|
||||
wtr: &mut PacketWriter<Vec<u8>>,
|
||||
) -> Result<u32, PassthroughError> {
|
||||
let mut stream_serial: u32 = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u32;
|
||||
|
||||
// search for ident, comment, setup
|
||||
get_header(1, rdr, wtr, &mut stream_serial, PacketWriteEndInfo::EndPage)?;
|
||||
get_header(
|
||||
3,
|
||||
rdr,
|
||||
wtr,
|
||||
&mut stream_serial,
|
||||
PacketWriteEndInfo::NormalPacket,
|
||||
)?;
|
||||
get_header(5, rdr, wtr, &mut stream_serial, PacketWriteEndInfo::EndPage)?;
|
||||
|
||||
// remove un-needed packets
|
||||
rdr.delete_unread_packets();
|
||||
return Ok(stream_serial);
|
||||
}
|
||||
|
||||
fn get_header<T>(
|
||||
code: u8,
|
||||
rdr: &mut PacketReader<T>,
|
||||
wtr: &mut PacketWriter<Vec<u8>>,
|
||||
stream_serial: &mut u32,
|
||||
info: PacketWriteEndInfo,
|
||||
) -> Result<u32, PassthroughError>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
let pck: Packet = rdr.read_packet_expected()?;
|
||||
|
||||
// set a unique serial number
|
||||
if pck.stream_serial() != 0 {
|
||||
*stream_serial = pck.stream_serial();
|
||||
}
|
||||
|
||||
let pkt_type = pck.data[0];
|
||||
debug!("Vorbis header type{}", &pkt_type);
|
||||
|
||||
// all headers are mandatory
|
||||
if pkt_type != code {
|
||||
return Err(PassthroughError(OggReadError::InvalidData));
|
||||
}
|
||||
|
||||
// headers keep original granule number
|
||||
let absgp_page = pck.absgp_page();
|
||||
wtr.write_packet(
|
||||
pck.data.into_boxed_slice(),
|
||||
*stream_serial,
|
||||
info,
|
||||
absgp_page,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
return Ok(*stream_serial);
|
||||
}
|
||||
|
||||
pub struct PassthroughDecoder<R: Read + Seek> {
|
||||
rdr: PacketReader<R>,
|
||||
wtr: PacketWriter<Vec<u8>>,
|
||||
lastgp_page: Option<u64>,
|
||||
absgp_page: u64,
|
||||
stream_serial: u32,
|
||||
}
|
||||
|
||||
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);
|
||||
let mut wtr = PacketWriter::new(Vec::new());
|
||||
|
||||
let stream_serial = write_headers(&mut rdr, &mut wtr)?;
|
||||
info!("Starting passthrough track with serial {}", stream_serial);
|
||||
|
||||
return Ok(PassthroughDecoder {
|
||||
rdr,
|
||||
wtr,
|
||||
lastgp_page: Some(0),
|
||||
absgp_page: 0,
|
||||
stream_serial,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
|
||||
fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
|
||||
info!("Seeking to {}", ms);
|
||||
self.lastgp_page = match ms {
|
||||
0 => Some(0),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// hard-coded to 44.1 kHz
|
||||
match self.rdr.seek_absgp(None, (ms * 44100 / 1000) as u64) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(err) => return Err(AudioError::PassthroughError(err.into())),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
|
||||
let mut skip = self.lastgp_page.is_none();
|
||||
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();
|
||||
let lastgp_page = self.lastgp_page.get_or_insert(pckgp_page);
|
||||
|
||||
// consume packets till next page to get a granule reference
|
||||
if skip {
|
||||
if *lastgp_page == pckgp_page {
|
||||
debug!("skipping packet");
|
||||
continue;
|
||||
}
|
||||
skip = false;
|
||||
info!("skipped at {}", pckgp_page);
|
||||
}
|
||||
|
||||
// now we can calculate absolute granule
|
||||
self.absgp_page += pckgp_page - *lastgp_page;
|
||||
self.lastgp_page = Some(pckgp_page);
|
||||
|
||||
// set packet type
|
||||
let inf = if pck.last_in_stream() {
|
||||
self.lastgp_page = Some(0);
|
||||
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,
|
||||
self.absgp_page,
|
||||
)
|
||||
.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)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue