mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Reintroduce offset for passthrough decoder
This commit is contained in:
parent
84e3fe5558
commit
096269c1d0
6 changed files with 81 additions and 49 deletions
|
@ -16,6 +16,27 @@ impl Deref for AudioFiles {
|
|||
}
|
||||
}
|
||||
|
||||
impl AudioFiles {
|
||||
pub fn is_ogg_vorbis(format: AudioFileFormat) -> bool {
|
||||
matches!(
|
||||
format,
|
||||
AudioFileFormat::OGG_VORBIS_320
|
||||
| AudioFileFormat::OGG_VORBIS_160
|
||||
| AudioFileFormat::OGG_VORBIS_96
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_mp3(format: AudioFileFormat) -> bool {
|
||||
matches!(
|
||||
format,
|
||||
AudioFileFormat::MP3_320
|
||||
| AudioFileFormat::MP3_256
|
||||
| AudioFileFormat::MP3_160
|
||||
| AudioFileFormat::MP3_96
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[AudioFileMessage]> for AudioFiles {
|
||||
fn from(files: &[AudioFileMessage]) -> Self {
|
||||
let audio_files = files
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod file;
|
||||
pub mod item;
|
||||
|
||||
pub use file::AudioFileFormat;
|
||||
pub use file::{AudioFileFormat, AudioFiles};
|
||||
pub use item::AudioItem;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::metadata::audio::AudioFileFormat;
|
||||
|
||||
mod passthrough_decoder;
|
||||
pub use passthrough_decoder::PassthroughDecoder;
|
||||
|
||||
|
@ -59,31 +57,6 @@ impl AudioPacket {
|
|||
pub trait AudioDecoder {
|
||||
fn seek(&mut self, absgp: u64) -> Result<u64, DecoderError>;
|
||||
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>>;
|
||||
|
||||
fn is_ogg_vorbis(format: AudioFileFormat) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
matches!(
|
||||
format,
|
||||
AudioFileFormat::OGG_VORBIS_320
|
||||
| AudioFileFormat::OGG_VORBIS_160
|
||||
| AudioFileFormat::OGG_VORBIS_96
|
||||
)
|
||||
}
|
||||
|
||||
fn is_mp3(format: AudioFileFormat) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
matches!(
|
||||
format,
|
||||
AudioFileFormat::MP3_320
|
||||
| AudioFileFormat::MP3_256
|
||||
| AudioFileFormat::MP3_160
|
||||
| AudioFileFormat::MP3_96
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<symphonia::core::errors::Error> for DecoderError {
|
||||
|
|
|
@ -9,7 +9,7 @@ use ogg::{OggReadError, Packet, PacketReader, PacketWriteEndInfo, PacketWriter};
|
|||
|
||||
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
||||
|
||||
use crate::metadata::audio::AudioFileFormat;
|
||||
use crate::metadata::audio::{AudioFileFormat, AudioFiles};
|
||||
|
||||
fn get_header<T>(code: u8, rdr: &mut PacketReader<T>) -> DecoderResult<Box<[u8]>>
|
||||
where
|
||||
|
@ -44,7 +44,7 @@ pub struct PassthroughDecoder<R: Read + Seek> {
|
|||
impl<R: Read + Seek> PassthroughDecoder<R> {
|
||||
/// Constructs a new Decoder from a given implementation of `Read + Seek`.
|
||||
pub fn new(rdr: R, format: AudioFileFormat) -> DecoderResult<Self> {
|
||||
if !Self::is_ogg_vorbis(format) {
|
||||
if !AudioFiles::is_ogg_vorbis(format) {
|
||||
return Err(DecoderError::PassthroughDecoder(format!(
|
||||
"Passthrough decoder is not implemented for format {:?}",
|
||||
format
|
||||
|
|
|
@ -12,7 +12,10 @@ use symphonia::core::{
|
|||
|
||||
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
||||
|
||||
use crate::{metadata::audio::AudioFileFormat, player::NormalisationData};
|
||||
use crate::{
|
||||
metadata::audio::{AudioFileFormat, AudioFiles},
|
||||
player::NormalisationData,
|
||||
};
|
||||
|
||||
pub struct SymphoniaDecoder {
|
||||
track_id: u32,
|
||||
|
@ -33,10 +36,10 @@ impl SymphoniaDecoder {
|
|||
|
||||
// Not necessary, but speeds up loading.
|
||||
let mut hint = Hint::new();
|
||||
if Self::is_ogg_vorbis(format) {
|
||||
if AudioFiles::is_ogg_vorbis(format) {
|
||||
hint.with_extension("ogg");
|
||||
hint.mime_type("audio/ogg");
|
||||
} else if Self::is_mp3(format) {
|
||||
} else if AudioFiles::is_mp3(format) {
|
||||
hint.with_extension("mp3");
|
||||
hint.mime_type("audio/mp3");
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
convert::Converter,
|
||||
core::{util::SeqGenerator, Error, Session, SpotifyId},
|
||||
decoder::{AudioDecoder, AudioPacket, PassthroughDecoder, SymphoniaDecoder},
|
||||
metadata::audio::{AudioFileFormat, AudioItem},
|
||||
metadata::audio::{AudioFileFormat, AudioFiles, AudioItem},
|
||||
mixer::AudioFilter,
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,10 @@ use crate::{MS_PER_PAGE, NUM_CHANNELS, PAGES_PER_MS, SAMPLES_PER_SECOND};
|
|||
const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000;
|
||||
pub const DB_VOLTAGE_RATIO: f64 = 20.0;
|
||||
|
||||
// Spotify inserts a custom Ogg packet at the start with custom metadata values, that you would
|
||||
// otherwise expect in Vorbis comments. This packet isn't well-formed and players may balk at it.
|
||||
const SPOTIFY_OGG_HEADER_END: u64 = 0xa7;
|
||||
|
||||
pub type PlayerResult = Result<(), Error>;
|
||||
|
||||
pub struct Player {
|
||||
|
@ -907,19 +911,27 @@ impl PlayerTrackLoader {
|
|||
None
|
||||
}
|
||||
};
|
||||
let decrypted_file = AudioDecrypt::new(key, encrypted_file);
|
||||
let mut audio_file =
|
||||
Subfile::new(decrypted_file, stream_loader_controller.len() as u64);
|
||||
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
|
||||
|
||||
let is_ogg_vorbis = AudioFiles::is_ogg_vorbis(format);
|
||||
let (offset, mut normalisation_data) = if is_ogg_vorbis {
|
||||
// Spotify stores normalisation data in a custom Ogg packet instead of Vorbis comments.
|
||||
let normalisation_data =
|
||||
NormalisationData::parse_from_ogg(&mut decrypted_file).ok();
|
||||
(SPOTIFY_OGG_HEADER_END, normalisation_data)
|
||||
} else {
|
||||
(0, None)
|
||||
};
|
||||
|
||||
let audio_file = Subfile::new(
|
||||
decrypted_file,
|
||||
offset,
|
||||
stream_loader_controller.len() as u64,
|
||||
);
|
||||
|
||||
let mut normalisation_data = None;
|
||||
let result = if self.config.passthrough {
|
||||
PassthroughDecoder::new(audio_file, format).map(|x| Box::new(x) as Decoder)
|
||||
} else {
|
||||
// Spotify stores normalisation data in a custom Ogg packet instead of Vorbis comments.
|
||||
if SymphoniaDecoder::is_ogg_vorbis(format) {
|
||||
normalisation_data = NormalisationData::parse_from_ogg(&mut audio_file).ok();
|
||||
}
|
||||
|
||||
SymphoniaDecoder::new(audio_file, format).map(|mut decoder| {
|
||||
// For formats other that Vorbis, we'll try getting normalisation data from
|
||||
// ReplayGain metadata fields, if present.
|
||||
|
@ -2206,21 +2218,30 @@ impl fmt::Debug for PlayerState {
|
|||
|
||||
struct Subfile<T: Read + Seek> {
|
||||
stream: T,
|
||||
offset: u64,
|
||||
length: u64,
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> Subfile<T> {
|
||||
pub fn new(mut stream: T, length: u64) -> Subfile<T> {
|
||||
match stream.seek(SeekFrom::Start(0)) {
|
||||
pub fn new(mut stream: T, offset: u64, length: u64) -> Subfile<T> {
|
||||
let target = SeekFrom::Start(offset);
|
||||
match stream.seek(target) {
|
||||
Ok(pos) => {
|
||||
if pos != 0 {
|
||||
error!("Subfile::new seeking to 0 but position is now {:?}", pos);
|
||||
if pos != offset {
|
||||
error!(
|
||||
"Subfile::new seeking to {:?} but position is now {:?}",
|
||||
target, pos
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Subfile new Error: {}", e),
|
||||
}
|
||||
|
||||
Subfile { stream, length }
|
||||
Subfile {
|
||||
stream,
|
||||
offset,
|
||||
length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2232,7 +2253,21 @@ impl<T: Read + Seek> Read for Subfile<T> {
|
|||
|
||||
impl<T: Read + Seek> Seek for Subfile<T> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.stream.seek(pos)
|
||||
let pos = match pos {
|
||||
SeekFrom::Start(offset) => SeekFrom::Start(offset + self.offset),
|
||||
x => x,
|
||||
};
|
||||
|
||||
let newpos = self.stream.seek(pos)?;
|
||||
|
||||
if newpos >= self.offset {
|
||||
Ok(newpos - self.offset)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"newpos < self.offset",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue