mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Seek in milliseconds and report the actual new position
This commit is contained in:
parent
f3a66d4b99
commit
01448ccbe8
4 changed files with 108 additions and 107 deletions
|
@ -55,8 +55,8 @@ impl AudioPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AudioDecoder {
|
pub trait AudioDecoder {
|
||||||
fn seek(&mut self, absgp: u64) -> Result<u64, DecoderError>;
|
fn seek(&mut self, position_ms: u32) -> Result<u32, DecoderError>;
|
||||||
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>>;
|
fn next_packet(&mut self) -> DecoderResult<Option<(u32, AudioPacket)>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<symphonia::core::errors::Error> for DecoderError {
|
impl From<symphonia::core::errors::Error> for DecoderError {
|
||||||
|
|
|
@ -9,7 +9,10 @@ use ogg::{OggReadError, Packet, PacketReader, PacketWriteEndInfo, PacketWriter};
|
||||||
|
|
||||||
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
||||||
|
|
||||||
use crate::metadata::audio::{AudioFileFormat, AudioFiles};
|
use crate::{
|
||||||
|
metadata::audio::{AudioFileFormat, AudioFiles},
|
||||||
|
MS_PER_PAGE, PAGES_PER_MS,
|
||||||
|
};
|
||||||
|
|
||||||
fn get_header<T>(code: u8, rdr: &mut PacketReader<T>) -> DecoderResult<Box<[u8]>>
|
fn get_header<T>(code: u8, rdr: &mut PacketReader<T>) -> DecoderResult<Box<[u8]>>
|
||||||
where
|
where
|
||||||
|
@ -23,7 +26,7 @@ where
|
||||||
debug!("Vorbis header type {}", &pkt_type);
|
debug!("Vorbis header type {}", &pkt_type);
|
||||||
|
|
||||||
if pkt_type != code {
|
if pkt_type != code {
|
||||||
return Err(DecoderError::PassthroughDecoder("Invalid Data".to_string()));
|
return Err(DecoderError::PassthroughDecoder("Invalid Data".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pck.data.into_boxed_slice())
|
Ok(pck.data.into_boxed_slice())
|
||||||
|
@ -79,10 +82,16 @@ impl<R: Read + Seek> PassthroughDecoder<R> {
|
||||||
bos: false,
|
bos: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn position_pcm_to_ms(position_pcm: u64) -> u32 {
|
||||||
|
(position_pcm as f64 * MS_PER_PAGE) as u32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
|
impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
|
||||||
fn seek(&mut self, absgp: u64) -> Result<u64, DecoderError> {
|
fn seek(&mut self, position_ms: u32) -> Result<u32, DecoderError> {
|
||||||
|
let absgp = (position_ms as f64 * PAGES_PER_MS) as u64;
|
||||||
|
|
||||||
// add an eos to previous stream if missing
|
// add an eos to previous stream if missing
|
||||||
if self.bos && !self.eos {
|
if self.bos && !self.eos {
|
||||||
match self.rdr.read_packet() {
|
match self.rdr.read_packet() {
|
||||||
|
@ -118,18 +127,17 @@ impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
|
||||||
let new_page = pck.absgp_page();
|
let new_page = pck.absgp_page();
|
||||||
self.ofsgp_page = new_page;
|
self.ofsgp_page = new_page;
|
||||||
debug!("Seek to offset page {}", new_page);
|
debug!("Seek to offset page {}", new_page);
|
||||||
Ok(new_page)
|
let new_position_ms = Self::position_pcm_to_ms(new_page);
|
||||||
|
Ok(new_position_ms)
|
||||||
}
|
}
|
||||||
None => Err(DecoderError::PassthroughDecoder(
|
None => Err(DecoderError::PassthroughDecoder("Packet is None".into())),
|
||||||
"Packet is None".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(DecoderError::PassthroughDecoder(e.to_string())),
|
Err(e) => Err(DecoderError::PassthroughDecoder(e.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>> {
|
fn next_packet(&mut self) -> DecoderResult<Option<(u32, AudioPacket)>> {
|
||||||
// write headers if we are (re)starting
|
// write headers if we are (re)starting
|
||||||
if !self.bos {
|
if !self.bos {
|
||||||
self.wtr
|
self.wtr
|
||||||
|
@ -199,8 +207,9 @@ impl<R: Read + Seek> AudioDecoder for PassthroughDecoder<R> {
|
||||||
let data = self.wtr.inner_mut();
|
let data = self.wtr.inner_mut();
|
||||||
|
|
||||||
if !data.is_empty() {
|
if !data.is_empty() {
|
||||||
|
let position_ms = Self::position_pcm_to_ms(pckgp_page);
|
||||||
let ogg_data = AudioPacket::Raw(std::mem::take(data));
|
let ogg_data = AudioPacket::Raw(std::mem::take(data));
|
||||||
return Ok(Some(ogg_data));
|
return Ok(Some((position_ms, ogg_data)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use symphonia::core::{
|
||||||
io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions},
|
io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions},
|
||||||
meta::{MetadataOptions, StandardTagKey, Value},
|
meta::{MetadataOptions, StandardTagKey, Value},
|
||||||
probe::Hint,
|
probe::Hint,
|
||||||
|
units::Time,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
||||||
|
@ -15,10 +16,10 @@ use super::{AudioDecoder, AudioPacket, DecoderError, DecoderResult};
|
||||||
use crate::{
|
use crate::{
|
||||||
metadata::audio::{AudioFileFormat, AudioFiles},
|
metadata::audio::{AudioFileFormat, AudioFiles},
|
||||||
player::NormalisationData,
|
player::NormalisationData,
|
||||||
|
PAGES_PER_MS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SymphoniaDecoder {
|
pub struct SymphoniaDecoder {
|
||||||
track_id: u32,
|
|
||||||
decoder: Box<dyn Decoder>,
|
decoder: Box<dyn Decoder>,
|
||||||
format: Box<dyn FormatReader>,
|
format: Box<dyn FormatReader>,
|
||||||
sample_buffer: SampleBuffer<f64>,
|
sample_buffer: SampleBuffer<f64>,
|
||||||
|
@ -85,7 +86,6 @@ impl SymphoniaDecoder {
|
||||||
let sample_buffer = SampleBuffer::new(max_frames, SignalSpec { rate, channels });
|
let sample_buffer = SampleBuffer::new(max_frames, SignalSpec { rate, channels });
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
track_id: track.id,
|
|
||||||
decoder,
|
decoder,
|
||||||
format,
|
format,
|
||||||
sample_buffer,
|
sample_buffer,
|
||||||
|
@ -127,22 +127,40 @@ impl SymphoniaDecoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ts_to_ms(&self, ts: u64) -> u32 {
|
||||||
|
let time_base = self.decoder.codec_params().time_base;
|
||||||
|
let seeked_to_ms = match time_base {
|
||||||
|
Some(time_base) => {
|
||||||
|
let time = time_base.calc_time(ts);
|
||||||
|
(time.seconds as f64 + time.frac) * 1000.
|
||||||
|
}
|
||||||
|
// Fallback in the unexpected case that the format has no base time set.
|
||||||
|
None => (ts as f64 * PAGES_PER_MS),
|
||||||
|
};
|
||||||
|
seeked_to_ms as u32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioDecoder for SymphoniaDecoder {
|
impl AudioDecoder for SymphoniaDecoder {
|
||||||
// TODO : change to position ms
|
fn seek(&mut self, position_ms: u32) -> Result<u32, DecoderError> {
|
||||||
fn seek(&mut self, absgp: u64) -> Result<u64, DecoderError> {
|
let seconds = position_ms as u64 / 1000;
|
||||||
let seeked_to = self.format.seek(
|
let frac = (position_ms as f64 % 1000.) / 1000.;
|
||||||
|
let time = Time::new(seconds, frac);
|
||||||
|
|
||||||
|
// `track_id: None` implies the default track ID (of the container, not of Spotify).
|
||||||
|
let seeked_to_ts = self.format.seek(
|
||||||
SeekMode::Accurate,
|
SeekMode::Accurate,
|
||||||
SeekTo::TimeStamp {
|
SeekTo::Time {
|
||||||
ts: absgp, // TODO : move to Duration
|
time,
|
||||||
track_id: self.track_id,
|
track_id: None,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(seeked_to.actual_ts)
|
|
||||||
|
Ok(self.ts_to_ms(seeked_to_ts.actual_ts))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_packet(&mut self) -> DecoderResult<Option<AudioPacket>> {
|
fn next_packet(&mut self) -> DecoderResult<Option<(u32, AudioPacket)>> {
|
||||||
let packet = match self.format.next_packet() {
|
let packet = match self.format.next_packet() {
|
||||||
Ok(packet) => packet,
|
Ok(packet) => packet,
|
||||||
Err(Error::IoError(err)) => {
|
Err(Error::IoError(err)) => {
|
||||||
|
@ -159,11 +177,10 @@ impl AudioDecoder for SymphoniaDecoder {
|
||||||
|
|
||||||
match self.decoder.decode(&packet) {
|
match self.decoder.decode(&packet) {
|
||||||
Ok(audio_buf) => {
|
Ok(audio_buf) => {
|
||||||
// TODO : track current playback position
|
|
||||||
self.sample_buffer.copy_interleaved_ref(audio_buf);
|
self.sample_buffer.copy_interleaved_ref(audio_buf);
|
||||||
Ok(Some(AudioPacket::Samples(
|
let position_ms = self.ts_to_ms(packet.pts());
|
||||||
self.sample_buffer.samples().to_vec(),
|
let samples = AudioPacket::Samples(self.sample_buffer.samples().to_vec());
|
||||||
)))
|
Ok(Some((position_ms, samples)))
|
||||||
}
|
}
|
||||||
Err(Error::ResetRequired) => {
|
Err(Error::ResetRequired) => {
|
||||||
// This may happen after a seek.
|
// This may happen after a seek.
|
||||||
|
|
|
@ -34,7 +34,7 @@ use crate::{
|
||||||
mixer::AudioFilter,
|
mixer::AudioFilter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{MS_PER_PAGE, NUM_CHANNELS, PAGES_PER_MS, SAMPLES_PER_SECOND};
|
use crate::SAMPLES_PER_SECOND;
|
||||||
|
|
||||||
const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000;
|
const PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS: u32 = 30000;
|
||||||
pub const DB_VOLTAGE_RATIO: f64 = 20.0;
|
pub const DB_VOLTAGE_RATIO: f64 = 20.0;
|
||||||
|
@ -489,7 +489,7 @@ struct PlayerLoadedTrackData {
|
||||||
stream_loader_controller: StreamLoaderController,
|
stream_loader_controller: StreamLoaderController,
|
||||||
bytes_per_second: usize,
|
bytes_per_second: usize,
|
||||||
duration_ms: u32,
|
duration_ms: u32,
|
||||||
stream_position_pcm: u64,
|
stream_position_ms: u32,
|
||||||
is_explicit: bool,
|
is_explicit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,7 +524,7 @@ enum PlayerState {
|
||||||
stream_loader_controller: StreamLoaderController,
|
stream_loader_controller: StreamLoaderController,
|
||||||
bytes_per_second: usize,
|
bytes_per_second: usize,
|
||||||
duration_ms: u32,
|
duration_ms: u32,
|
||||||
stream_position_pcm: u64,
|
stream_position_ms: u32,
|
||||||
suggested_to_preload_next_track: bool,
|
suggested_to_preload_next_track: bool,
|
||||||
is_explicit: bool,
|
is_explicit: bool,
|
||||||
},
|
},
|
||||||
|
@ -537,7 +537,7 @@ enum PlayerState {
|
||||||
stream_loader_controller: StreamLoaderController,
|
stream_loader_controller: StreamLoaderController,
|
||||||
bytes_per_second: usize,
|
bytes_per_second: usize,
|
||||||
duration_ms: u32,
|
duration_ms: u32,
|
||||||
stream_position_pcm: u64,
|
stream_position_ms: u32,
|
||||||
reported_nominal_start_time: Option<Instant>,
|
reported_nominal_start_time: Option<Instant>,
|
||||||
suggested_to_preload_next_track: bool,
|
suggested_to_preload_next_track: bool,
|
||||||
is_explicit: bool,
|
is_explicit: bool,
|
||||||
|
@ -622,7 +622,7 @@ impl PlayerState {
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
normalisation_data,
|
normalisation_data,
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
@ -635,7 +635,7 @@ impl PlayerState {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -663,7 +663,7 @@ impl PlayerState {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
suggested_to_preload_next_track,
|
suggested_to_preload_next_track,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
} => {
|
} => {
|
||||||
|
@ -676,7 +676,7 @@ impl PlayerState {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
reported_nominal_start_time: None,
|
reported_nominal_start_time: None,
|
||||||
suggested_to_preload_next_track,
|
suggested_to_preload_next_track,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
|
@ -705,7 +705,7 @@ impl PlayerState {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
reported_nominal_start_time: _,
|
reported_nominal_start_time: _,
|
||||||
suggested_to_preload_next_track,
|
suggested_to_preload_next_track,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
|
@ -719,7 +719,7 @@ impl PlayerState {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
suggested_to_preload_next_track,
|
suggested_to_preload_next_track,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
};
|
};
|
||||||
|
@ -981,13 +981,12 @@ impl PlayerTrackLoader {
|
||||||
// the cursor may have been moved by parsing normalisation data. This may not
|
// the cursor may have been moved by parsing normalisation data. This may not
|
||||||
// matter for playback (but won't hurt either), but may be useful for the
|
// matter for playback (but won't hurt either), but may be useful for the
|
||||||
// passthrough decoder.
|
// passthrough decoder.
|
||||||
let position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
|
let stream_position_ms = match decoder.seek(position_ms) {
|
||||||
let stream_position_pcm = match decoder.seek(position_pcm) {
|
Ok(_) => position_ms,
|
||||||
Ok(_) => position_pcm,
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
"PlayerTrackLoader::load_track error seeking to PCM page {}: {}",
|
"PlayerTrackLoader::load_track error seeking to {}: {}",
|
||||||
position_pcm, e
|
position_ms, e
|
||||||
);
|
);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1004,7 @@ impl PlayerTrackLoader {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1118,23 +1117,18 @@ impl Future for PlayerInternal {
|
||||||
play_request_id,
|
play_request_id,
|
||||||
ref mut decoder,
|
ref mut decoder,
|
||||||
normalisation_factor,
|
normalisation_factor,
|
||||||
ref mut stream_position_pcm,
|
ref mut stream_position_ms,
|
||||||
ref mut reported_nominal_start_time,
|
ref mut reported_nominal_start_time,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
..
|
..
|
||||||
} = self.state
|
} = self.state
|
||||||
{
|
{
|
||||||
match decoder.next_packet() {
|
match decoder.next_packet() {
|
||||||
Ok(packet) => {
|
Ok(result) => {
|
||||||
|
if let Some((new_stream_position_ms, ref packet)) = result {
|
||||||
if !passthrough {
|
if !passthrough {
|
||||||
if let Some(ref packet) = packet {
|
|
||||||
match packet.samples() {
|
match packet.samples() {
|
||||||
Ok(samples) => {
|
Ok(_) => {
|
||||||
*stream_position_pcm +=
|
|
||||||
(samples.len() / NUM_CHANNELS as usize) as u64;
|
|
||||||
let stream_position_millis =
|
|
||||||
Self::position_pcm_to_ms(*stream_position_pcm);
|
|
||||||
|
|
||||||
let notify_about_position =
|
let notify_about_position =
|
||||||
match *reported_nominal_start_time {
|
match *reported_nominal_start_time {
|
||||||
None => true,
|
None => true,
|
||||||
|
@ -1144,7 +1138,7 @@ impl Future for PlayerInternal {
|
||||||
- reported_nominal_start_time)
|
- reported_nominal_start_time)
|
||||||
.as_millis()
|
.as_millis()
|
||||||
as i64
|
as i64
|
||||||
- stream_position_millis as i64;
|
- new_stream_position_ms as i64;
|
||||||
lag > Duration::from_secs(1).as_millis()
|
lag > Duration::from_secs(1).as_millis()
|
||||||
as i64
|
as i64
|
||||||
}
|
}
|
||||||
|
@ -1153,13 +1147,13 @@ impl Future for PlayerInternal {
|
||||||
*reported_nominal_start_time = Some(
|
*reported_nominal_start_time = Some(
|
||||||
Instant::now()
|
Instant::now()
|
||||||
- Duration::from_millis(
|
- Duration::from_millis(
|
||||||
stream_position_millis as u64,
|
new_stream_position_ms as u64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self.send_event(PlayerEvent::Playing {
|
self.send_event(PlayerEvent::Playing {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
position_ms: stream_position_millis as u32,
|
position_ms: new_stream_position_ms as u32,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1172,13 +1166,13 @@ impl Future for PlayerInternal {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// position, even if irrelevant, must be set so that seek() is called
|
// position, even if irrelevant, must be set so that seek() is called
|
||||||
*stream_position_pcm = duration_ms.into();
|
*stream_position_ms = new_stream_position_ms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handle_packet(packet, normalisation_factor);
|
self.handle_packet(result, normalisation_factor);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Skipping to next track, unable to get next packet for track <{:?}>: {:?}", track_id, e);
|
error!("Skipping to next track, unable to get next packet for track <{:?}>: {:?}", track_id, e);
|
||||||
|
@ -1198,7 +1192,7 @@ impl Future for PlayerInternal {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
ref mut stream_loader_controller,
|
ref mut stream_loader_controller,
|
||||||
ref mut suggested_to_preload_next_track,
|
ref mut suggested_to_preload_next_track,
|
||||||
..
|
..
|
||||||
|
@ -1207,14 +1201,14 @@ impl Future for PlayerInternal {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
ref mut stream_loader_controller,
|
ref mut stream_loader_controller,
|
||||||
ref mut suggested_to_preload_next_track,
|
ref mut suggested_to_preload_next_track,
|
||||||
..
|
..
|
||||||
} = self.state
|
} = self.state
|
||||||
{
|
{
|
||||||
if (!*suggested_to_preload_next_track)
|
if (!*suggested_to_preload_next_track)
|
||||||
&& ((duration_ms as i64 - Self::position_pcm_to_ms(stream_position_pcm) as i64)
|
&& ((duration_ms as i64 - stream_position_ms as i64)
|
||||||
< PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS as i64)
|
< PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS as i64)
|
||||||
&& stream_loader_controller.range_to_end_available()
|
&& stream_loader_controller.range_to_end_available()
|
||||||
{
|
{
|
||||||
|
@ -1238,14 +1232,6 @@ impl Future for PlayerInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerInternal {
|
impl PlayerInternal {
|
||||||
fn position_pcm_to_ms(position_pcm: u64) -> u32 {
|
|
||||||
(position_pcm as f64 * MS_PER_PAGE) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn position_ms_to_pcm(position_ms: u32) -> u64 {
|
|
||||||
(position_ms as f64 * PAGES_PER_MS) as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure_sink_running(&mut self) {
|
fn ensure_sink_running(&mut self) {
|
||||||
if self.sink_status != SinkStatus::Running {
|
if self.sink_status != SinkStatus::Running {
|
||||||
trace!("== Starting sink ==");
|
trace!("== Starting sink ==");
|
||||||
|
@ -1336,18 +1322,16 @@ impl PlayerInternal {
|
||||||
if let PlayerState::Paused {
|
if let PlayerState::Paused {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
..
|
..
|
||||||
} = self.state
|
} = self.state
|
||||||
{
|
{
|
||||||
self.state.paused_to_playing();
|
self.state.paused_to_playing();
|
||||||
|
|
||||||
let position_ms = Self::position_pcm_to_ms(stream_position_pcm);
|
|
||||||
self.send_event(PlayerEvent::Playing {
|
self.send_event(PlayerEvent::Playing {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
position_ms,
|
position_ms: stream_position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
});
|
});
|
||||||
self.ensure_sink_running();
|
self.ensure_sink_running();
|
||||||
|
@ -1360,7 +1344,7 @@ impl PlayerInternal {
|
||||||
if let PlayerState::Playing {
|
if let PlayerState::Playing {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
..
|
..
|
||||||
} = self.state
|
} = self.state
|
||||||
|
@ -1368,11 +1352,10 @@ impl PlayerInternal {
|
||||||
self.state.playing_to_paused();
|
self.state.playing_to_paused();
|
||||||
|
|
||||||
self.ensure_sink_stopped(false);
|
self.ensure_sink_stopped(false);
|
||||||
let position_ms = Self::position_pcm_to_ms(stream_position_pcm);
|
|
||||||
self.send_event(PlayerEvent::Paused {
|
self.send_event(PlayerEvent::Paused {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
position_ms,
|
position_ms: stream_position_ms,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -1380,9 +1363,9 @@ impl PlayerInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_packet(&mut self, packet: Option<AudioPacket>, normalisation_factor: f64) {
|
fn handle_packet(&mut self, packet: Option<(u32, AudioPacket)>, normalisation_factor: f64) {
|
||||||
match packet {
|
match packet {
|
||||||
Some(mut packet) => {
|
Some((_, mut packet)) => {
|
||||||
if !packet.is_empty() {
|
if !packet.is_empty() {
|
||||||
if let AudioPacket::Samples(ref mut data) = packet {
|
if let AudioPacket::Samples(ref mut data) = packet {
|
||||||
if self.config.normalisation
|
if self.config.normalisation
|
||||||
|
@ -1537,7 +1520,7 @@ impl PlayerInternal {
|
||||||
loaded_track: PlayerLoadedTrackData,
|
loaded_track: PlayerLoadedTrackData,
|
||||||
start_playback: bool,
|
start_playback: bool,
|
||||||
) {
|
) {
|
||||||
let position_ms = Self::position_pcm_to_ms(loaded_track.stream_position_pcm);
|
let position_ms = loaded_track.stream_position_ms;
|
||||||
|
|
||||||
let mut config = self.config.clone();
|
let mut config = self.config.clone();
|
||||||
if config.normalisation_type == NormalisationType::Auto {
|
if config.normalisation_type == NormalisationType::Auto {
|
||||||
|
@ -1569,7 +1552,7 @@ impl PlayerInternal {
|
||||||
stream_loader_controller: loaded_track.stream_loader_controller,
|
stream_loader_controller: loaded_track.stream_loader_controller,
|
||||||
duration_ms: loaded_track.duration_ms,
|
duration_ms: loaded_track.duration_ms,
|
||||||
bytes_per_second: loaded_track.bytes_per_second,
|
bytes_per_second: loaded_track.bytes_per_second,
|
||||||
stream_position_pcm: loaded_track.stream_position_pcm,
|
stream_position_ms: loaded_track.stream_position_ms,
|
||||||
reported_nominal_start_time: Some(
|
reported_nominal_start_time: Some(
|
||||||
Instant::now() - Duration::from_millis(position_ms as u64),
|
Instant::now() - Duration::from_millis(position_ms as u64),
|
||||||
),
|
),
|
||||||
|
@ -1588,7 +1571,7 @@ impl PlayerInternal {
|
||||||
stream_loader_controller: loaded_track.stream_loader_controller,
|
stream_loader_controller: loaded_track.stream_loader_controller,
|
||||||
duration_ms: loaded_track.duration_ms,
|
duration_ms: loaded_track.duration_ms,
|
||||||
bytes_per_second: loaded_track.bytes_per_second,
|
bytes_per_second: loaded_track.bytes_per_second,
|
||||||
stream_position_pcm: loaded_track.stream_position_pcm,
|
stream_position_ms: loaded_track.stream_position_ms,
|
||||||
suggested_to_preload_next_track: false,
|
suggested_to_preload_next_track: false,
|
||||||
is_explicit: loaded_track.is_explicit,
|
is_explicit: loaded_track.is_explicit,
|
||||||
};
|
};
|
||||||
|
@ -1666,15 +1649,13 @@ impl PlayerInternal {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let position_pcm = Self::position_ms_to_pcm(position_ms);
|
if position_ms != loaded_track.stream_position_ms {
|
||||||
|
|
||||||
if position_pcm != loaded_track.stream_position_pcm {
|
|
||||||
loaded_track
|
loaded_track
|
||||||
.stream_loader_controller
|
.stream_loader_controller
|
||||||
.set_random_access_mode();
|
.set_random_access_mode();
|
||||||
// This may be blocking.
|
// This may be blocking.
|
||||||
match loaded_track.decoder.seek(position_pcm) {
|
match loaded_track.decoder.seek(position_ms) {
|
||||||
Ok(_) => loaded_track.stream_position_pcm = position_pcm,
|
Ok(_) => loaded_track.stream_position_ms = position_ms,
|
||||||
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
||||||
}
|
}
|
||||||
loaded_track.stream_loader_controller.set_stream_mode();
|
loaded_track.stream_loader_controller.set_stream_mode();
|
||||||
|
@ -1692,14 +1673,14 @@ impl PlayerInternal {
|
||||||
// Check if we are already playing the track. If so, just do a seek and update our info.
|
// Check if we are already playing the track. If so, just do a seek and update our info.
|
||||||
if let PlayerState::Playing {
|
if let PlayerState::Playing {
|
||||||
track_id: current_track_id,
|
track_id: current_track_id,
|
||||||
ref mut stream_position_pcm,
|
ref mut stream_position_ms,
|
||||||
ref mut decoder,
|
ref mut decoder,
|
||||||
ref mut stream_loader_controller,
|
ref mut stream_loader_controller,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| PlayerState::Paused {
|
| PlayerState::Paused {
|
||||||
track_id: current_track_id,
|
track_id: current_track_id,
|
||||||
ref mut stream_position_pcm,
|
ref mut stream_position_ms,
|
||||||
ref mut decoder,
|
ref mut decoder,
|
||||||
ref mut stream_loader_controller,
|
ref mut stream_loader_controller,
|
||||||
..
|
..
|
||||||
|
@ -1707,13 +1688,11 @@ impl PlayerInternal {
|
||||||
{
|
{
|
||||||
if current_track_id == track_id {
|
if current_track_id == track_id {
|
||||||
// we can use the current decoder. Ensure it's at the correct position.
|
// we can use the current decoder. Ensure it's at the correct position.
|
||||||
let position_pcm = Self::position_ms_to_pcm(position_ms);
|
if position_ms != *stream_position_ms {
|
||||||
|
|
||||||
if position_pcm != *stream_position_pcm {
|
|
||||||
stream_loader_controller.set_random_access_mode();
|
stream_loader_controller.set_random_access_mode();
|
||||||
// This may be blocking.
|
// This may be blocking.
|
||||||
match decoder.seek(position_pcm) {
|
match decoder.seek(position_ms) {
|
||||||
Ok(_) => *stream_position_pcm = position_pcm,
|
Ok(_) => *stream_position_ms = position_ms,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("PlayerInternal::handle_command_load error seeking: {}", e)
|
error!("PlayerInternal::handle_command_load error seeking: {}", e)
|
||||||
}
|
}
|
||||||
|
@ -1726,7 +1705,7 @@ impl PlayerInternal {
|
||||||
let old_state = mem::replace(&mut self.state, PlayerState::Invalid);
|
let old_state = mem::replace(&mut self.state, PlayerState::Invalid);
|
||||||
|
|
||||||
if let PlayerState::Playing {
|
if let PlayerState::Playing {
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
decoder,
|
decoder,
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
|
@ -1736,7 +1715,7 @@ impl PlayerInternal {
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| PlayerState::Paused {
|
| PlayerState::Paused {
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
decoder,
|
decoder,
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
|
@ -1752,7 +1731,7 @@ impl PlayerInternal {
|
||||||
stream_loader_controller,
|
stream_loader_controller,
|
||||||
bytes_per_second,
|
bytes_per_second,
|
||||||
duration_ms,
|
duration_ms,
|
||||||
stream_position_pcm,
|
stream_position_ms,
|
||||||
is_explicit,
|
is_explicit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1785,15 +1764,13 @@ impl PlayerInternal {
|
||||||
mut loaded_track,
|
mut loaded_track,
|
||||||
} = preload
|
} = preload
|
||||||
{
|
{
|
||||||
let position_pcm = Self::position_ms_to_pcm(position_ms);
|
if position_ms != loaded_track.stream_position_ms {
|
||||||
|
|
||||||
if position_pcm != loaded_track.stream_position_pcm {
|
|
||||||
loaded_track
|
loaded_track
|
||||||
.stream_loader_controller
|
.stream_loader_controller
|
||||||
.set_random_access_mode();
|
.set_random_access_mode();
|
||||||
// This may be blocking
|
// This may be blocking
|
||||||
match loaded_track.decoder.seek(position_pcm) {
|
match loaded_track.decoder.seek(position_ms) {
|
||||||
Ok(_) => loaded_track.stream_position_pcm = position_pcm,
|
Ok(_) => loaded_track.stream_position_ms = position_ms,
|
||||||
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
||||||
}
|
}
|
||||||
loaded_track.stream_loader_controller.set_stream_mode();
|
loaded_track.stream_loader_controller.set_stream_mode();
|
||||||
|
@ -1908,20 +1885,18 @@ impl PlayerInternal {
|
||||||
stream_loader_controller.set_random_access_mode();
|
stream_loader_controller.set_random_access_mode();
|
||||||
}
|
}
|
||||||
if let Some(decoder) = self.state.decoder() {
|
if let Some(decoder) = self.state.decoder() {
|
||||||
let position_pcm = Self::position_ms_to_pcm(position_ms);
|
match decoder.seek(position_ms) {
|
||||||
|
|
||||||
match decoder.seek(position_pcm) {
|
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if let PlayerState::Playing {
|
if let PlayerState::Playing {
|
||||||
ref mut stream_position_pcm,
|
ref mut stream_position_ms,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| PlayerState::Paused {
|
| PlayerState::Paused {
|
||||||
ref mut stream_position_pcm,
|
ref mut stream_position_ms,
|
||||||
..
|
..
|
||||||
} = self.state
|
} = self.state
|
||||||
{
|
{
|
||||||
*stream_position_pcm = position_pcm;
|
*stream_position_ms = position_ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => error!("PlayerInternal::handle_command_seek error: {}", e),
|
Err(e) => error!("PlayerInternal::handle_command_seek error: {}", e),
|
||||||
|
|
Loading…
Reference in a new issue