Reintroduce offset for passthrough decoder

This commit is contained in:
Roderick van Domburg 2022-01-03 22:20:29 +01:00
parent 84e3fe5558
commit 096269c1d0
No known key found for this signature in database
GPG key ID: FE2585E713F9F30A
6 changed files with 81 additions and 49 deletions

View file

@ -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

View file

@ -1,5 +1,5 @@
pub mod file;
pub mod item;
pub use file::AudioFileFormat;
pub use file::{AudioFileFormat, AudioFiles};
pub use item::AudioItem;

View file

@ -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 {

View file

@ -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

View file

@ -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");
}

View file

@ -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",
))
}
}
}