mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Fix error hitting play when loading
Further changes: - Improve some debug and trace messages - Default to streaming download strategy - Synchronize mixer volume on loading play - Use default normalisation values when the file position isn't exactly what we need it to be - Update track position only when the decoder reports a successful seek
This commit is contained in:
parent
0f78fc277e
commit
332f9f04b1
3 changed files with 116 additions and 65 deletions
|
@ -341,6 +341,8 @@ impl AudioFile {
|
||||||
|
|
||||||
let session_ = session.clone();
|
let session_ = session.clone();
|
||||||
session.spawn(complete_rx.map_ok(move |mut file| {
|
session.spawn(complete_rx.map_ok(move |mut file| {
|
||||||
|
debug!("Downloading file {} complete", file_id);
|
||||||
|
|
||||||
if let Some(cache) = session_.cache() {
|
if let Some(cache) = session_.cache() {
|
||||||
if let Some(cache_id) = cache.file(file_id) {
|
if let Some(cache_id) = cache.file(file_id) {
|
||||||
if let Err(e) = cache.save_file(file_id, &mut file) {
|
if let Err(e) = cache.save_file(file_id, &mut file) {
|
||||||
|
@ -349,8 +351,6 @@ impl AudioFile {
|
||||||
debug!("File {} cached to {:?}", file_id, cache_id);
|
debug!("File {} cached to {:?}", file_id, cache_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Downloading file {} complete", file_id);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -399,10 +399,12 @@ impl AudioFileStreaming {
|
||||||
INITIAL_DOWNLOAD_SIZE
|
INITIAL_DOWNLOAD_SIZE
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Streaming {}", file_id);
|
|
||||||
|
|
||||||
let cdn_url = CdnUrl::new(file_id).resolve_audio(&session).await?;
|
let cdn_url = CdnUrl::new(file_id).resolve_audio(&session).await?;
|
||||||
|
|
||||||
|
if let Ok(url) = cdn_url.try_get_url() {
|
||||||
|
trace!("Streaming from {}", url);
|
||||||
|
}
|
||||||
|
|
||||||
let mut streamer = session
|
let mut streamer = session
|
||||||
.spclient()
|
.spclient()
|
||||||
.stream_from_cdn(&cdn_url, 0, download_size)?;
|
.stream_from_cdn(&cdn_url, 0, download_size)?;
|
||||||
|
@ -438,7 +440,7 @@ impl AudioFileStreaming {
|
||||||
requested: RangeSet::new(),
|
requested: RangeSet::new(),
|
||||||
downloaded: RangeSet::new(),
|
downloaded: RangeSet::new(),
|
||||||
}),
|
}),
|
||||||
download_strategy: Mutex::new(DownloadStrategy::RandomAccess()), // start with random access mode until someone tells us otherwise
|
download_strategy: Mutex::new(DownloadStrategy::Streaming()),
|
||||||
number_of_open_requests: AtomicUsize::new(0),
|
number_of_open_requests: AtomicUsize::new(0),
|
||||||
ping_time_ms: AtomicUsize::new(0),
|
ping_time_ms: AtomicUsize::new(0),
|
||||||
read_position: AtomicUsize::new(0),
|
read_position: AtomicUsize::new(0),
|
||||||
|
|
|
@ -626,7 +626,11 @@ impl SpircTask {
|
||||||
if Some(play_request_id) == self.play_request_id {
|
if Some(play_request_id) == self.play_request_id {
|
||||||
match event {
|
match event {
|
||||||
PlayerEvent::EndOfTrack { .. } => self.handle_end_of_track(),
|
PlayerEvent::EndOfTrack { .. } => self.handle_end_of_track(),
|
||||||
PlayerEvent::Loading { .. } => self.notify(None, false),
|
PlayerEvent::Loading { .. } => {
|
||||||
|
trace!("==> kPlayStatusLoading");
|
||||||
|
self.state.set_status(PlayStatus::kPlayStatusLoading);
|
||||||
|
self.notify(None, false)
|
||||||
|
}
|
||||||
PlayerEvent::Playing { position_ms, .. } => {
|
PlayerEvent::Playing { position_ms, .. } => {
|
||||||
trace!("==> kPlayStatusPlay");
|
trace!("==> kPlayStatusPlay");
|
||||||
let new_nominal_start_time = self.now_ms() - position_ms as i64;
|
let new_nominal_start_time = self.now_ms() - position_ms as i64;
|
||||||
|
@ -687,7 +691,9 @@ impl SpircTask {
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PlayerEvent::Stopped { .. } => match self.play_status {
|
PlayerEvent::Stopped { .. } => {
|
||||||
|
trace!("==> kPlayStatusStop");
|
||||||
|
match self.play_status {
|
||||||
SpircPlayStatus::Stopped => Ok(()),
|
SpircPlayStatus::Stopped => Ok(()),
|
||||||
_ => {
|
_ => {
|
||||||
warn!("The player has stopped unexpectedly.");
|
warn!("The player has stopped unexpectedly.");
|
||||||
|
@ -695,7 +701,8 @@ impl SpircTask {
|
||||||
self.play_status = SpircPlayStatus::Stopped;
|
self.play_status = SpircPlayStatus::Stopped;
|
||||||
self.notify(None, true)
|
self.notify(None, true)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
PlayerEvent::TimeToPreloadNextTrack { .. } => {
|
PlayerEvent::TimeToPreloadNextTrack { .. } => {
|
||||||
self.handle_preload_next_track();
|
self.handle_preload_next_track();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -923,12 +930,6 @@ impl SpircTask {
|
||||||
position_ms,
|
position_ms,
|
||||||
preloading_of_next_track_triggered,
|
preloading_of_next_track_triggered,
|
||||||
} => {
|
} => {
|
||||||
// TODO - also apply this to the arm below
|
|
||||||
// Synchronize the volume from the mixer. This is useful on
|
|
||||||
// systems that can switch sources from and back to librespot.
|
|
||||||
let current_volume = self.mixer.volume();
|
|
||||||
self.set_volume(current_volume);
|
|
||||||
|
|
||||||
self.player.play();
|
self.player.play();
|
||||||
self.state.set_status(PlayStatus::kPlayStatusPlay);
|
self.state.set_status(PlayStatus::kPlayStatusPlay);
|
||||||
self.update_state_position(position_ms);
|
self.update_state_position(position_ms);
|
||||||
|
@ -938,13 +939,16 @@ impl SpircTask {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
SpircPlayStatus::LoadingPause { position_ms } => {
|
SpircPlayStatus::LoadingPause { position_ms } => {
|
||||||
// TODO - fix "Player::play called from invalid state" when hitting play
|
|
||||||
// on initial start-up, when starting halfway a track
|
|
||||||
self.player.play();
|
self.player.play();
|
||||||
self.play_status = SpircPlayStatus::LoadingPlay { position_ms };
|
self.play_status = SpircPlayStatus::LoadingPlay { position_ms };
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synchronize the volume from the mixer. This is useful on
|
||||||
|
// systems that can switch sources from and back to librespot.
|
||||||
|
let current_volume = self.mixer.volume();
|
||||||
|
self.set_volume(current_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_play_pause(&mut self) {
|
fn handle_play_pause(&mut self) {
|
||||||
|
@ -1252,11 +1256,11 @@ impl SpircTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
|
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
|
||||||
debug!("State: {:?}", frame.get_state());
|
trace!("State: {:#?}", frame.get_state());
|
||||||
let index = frame.get_state().get_playing_track_index();
|
let index = frame.get_state().get_playing_track_index();
|
||||||
let context_uri = frame.get_state().get_context_uri().to_owned();
|
let context_uri = frame.get_state().get_context_uri().to_owned();
|
||||||
let tracks = frame.get_state().get_track();
|
let tracks = frame.get_state().get_track();
|
||||||
debug!("Frame has {:?} tracks", tracks.len());
|
trace!("Frame has {:?} tracks", tracks.len());
|
||||||
if context_uri.starts_with("spotify:station:")
|
if context_uri.starts_with("spotify:station:")
|
||||||
|| context_uri.starts_with("spotify:dailymix:")
|
|| context_uri.starts_with("spotify:dailymix:")
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::max,
|
cmp::max,
|
||||||
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
io::{self, Read, Seek, SeekFrom},
|
io::{self, Read, Seek, SeekFrom},
|
||||||
mem,
|
mem,
|
||||||
|
@ -234,7 +235,16 @@ impl Default for NormalisationData {
|
||||||
impl NormalisationData {
|
impl NormalisationData {
|
||||||
fn parse_from_file<T: Read + Seek>(mut file: T) -> io::Result<NormalisationData> {
|
fn parse_from_file<T: Read + Seek>(mut file: T) -> io::Result<NormalisationData> {
|
||||||
const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144;
|
const SPOTIFY_NORMALIZATION_HEADER_START_OFFSET: u64 = 144;
|
||||||
file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET))?;
|
|
||||||
|
let newpos = file.seek(SeekFrom::Start(SPOTIFY_NORMALIZATION_HEADER_START_OFFSET))?;
|
||||||
|
if newpos != SPOTIFY_NORMALIZATION_HEADER_START_OFFSET {
|
||||||
|
error!(
|
||||||
|
"NormalisationData::parse_from_file seeking to {} but position is now {}",
|
||||||
|
SPOTIFY_NORMALIZATION_HEADER_START_OFFSET, newpos
|
||||||
|
);
|
||||||
|
error!("Falling back to default (non-track and non-album) normalisation data.");
|
||||||
|
return Ok(NormalisationData::default());
|
||||||
|
}
|
||||||
|
|
||||||
let track_gain_db = file.read_f32::<LittleEndian>()?;
|
let track_gain_db = file.read_f32::<LittleEndian>()?;
|
||||||
let track_peak = file.read_f32::<LittleEndian>()?;
|
let track_peak = file.read_f32::<LittleEndian>()?;
|
||||||
|
@ -527,7 +537,7 @@ impl PlayerState {
|
||||||
Stopped | EndOfTrack { .. } | Paused { .. } | Loading { .. } => false,
|
Stopped | EndOfTrack { .. } | Paused { .. } | Loading { .. } => false,
|
||||||
Playing { .. } => true,
|
Playing { .. } => true,
|
||||||
Invalid => {
|
Invalid => {
|
||||||
error!("PlayerState is_playing: invalid state");
|
error!("PlayerState::is_playing in invalid state");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -555,7 +565,7 @@ impl PlayerState {
|
||||||
ref mut decoder, ..
|
ref mut decoder, ..
|
||||||
} => Some(decoder),
|
} => Some(decoder),
|
||||||
Invalid => {
|
Invalid => {
|
||||||
error!("PlayerState decoder: invalid state");
|
error!("PlayerState::decoder in invalid state");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,7 +584,7 @@ impl PlayerState {
|
||||||
..
|
..
|
||||||
} => Some(stream_loader_controller),
|
} => Some(stream_loader_controller),
|
||||||
Invalid => {
|
Invalid => {
|
||||||
error!("PlayerState stream_loader_controller: invalid state");
|
error!("PlayerState::stream_loader_controller in invalid state");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -582,7 +592,8 @@ impl PlayerState {
|
||||||
|
|
||||||
fn playing_to_end_of_track(&mut self) {
|
fn playing_to_end_of_track(&mut self) {
|
||||||
use self::PlayerState::*;
|
use self::PlayerState::*;
|
||||||
match mem::replace(self, Invalid) {
|
let new_state = mem::replace(self, Invalid);
|
||||||
|
match new_state {
|
||||||
Playing {
|
Playing {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
|
@ -608,7 +619,10 @@ impl PlayerState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Called playing_to_end_of_track in non-playing state.");
|
error!(
|
||||||
|
"Called playing_to_end_of_track in non-playing state: {:?}",
|
||||||
|
new_state
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -616,7 +630,8 @@ impl PlayerState {
|
||||||
|
|
||||||
fn paused_to_playing(&mut self) {
|
fn paused_to_playing(&mut self) {
|
||||||
use self::PlayerState::*;
|
use self::PlayerState::*;
|
||||||
match ::std::mem::replace(self, Invalid) {
|
let new_state = mem::replace(self, Invalid);
|
||||||
|
match new_state {
|
||||||
Paused {
|
Paused {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
|
@ -644,7 +659,10 @@ impl PlayerState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("PlayerState paused_to_playing: invalid state");
|
error!(
|
||||||
|
"PlayerState::paused_to_playing in invalid state: {:?}",
|
||||||
|
new_state
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -652,7 +670,8 @@ impl PlayerState {
|
||||||
|
|
||||||
fn playing_to_paused(&mut self) {
|
fn playing_to_paused(&mut self) {
|
||||||
use self::PlayerState::*;
|
use self::PlayerState::*;
|
||||||
match ::std::mem::replace(self, Invalid) {
|
let new_state = mem::replace(self, Invalid);
|
||||||
|
match new_state {
|
||||||
Playing {
|
Playing {
|
||||||
track_id,
|
track_id,
|
||||||
play_request_id,
|
play_request_id,
|
||||||
|
@ -680,7 +699,10 @@ impl PlayerState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("PlayerState playing_to_paused: invalid state");
|
error!(
|
||||||
|
"PlayerState::playing_to_paused in invalid state: {:?}",
|
||||||
|
new_state
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,15 +922,18 @@ impl PlayerTrackLoader {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut stream_position_pcm = 0;
|
||||||
let position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
|
let position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
|
||||||
|
|
||||||
if position_pcm != 0 {
|
if position_pcm > 0 {
|
||||||
if let Err(e) = decoder.seek(position_pcm) {
|
stream_loader_controller.set_random_access_mode();
|
||||||
error!("PlayerTrackLoader load_track: {}", e);
|
match decoder.seek(position_pcm) {
|
||||||
|
Ok(_) => stream_position_pcm = position_pcm,
|
||||||
|
Err(e) => error!("PlayerTrackLoader::load_track error seeking: {}", e),
|
||||||
}
|
}
|
||||||
stream_loader_controller.set_stream_mode();
|
stream_loader_controller.set_stream_mode();
|
||||||
}
|
};
|
||||||
let stream_position_pcm = position_pcm;
|
|
||||||
info!("<{}> ({} ms) loaded", audio.name, audio.duration);
|
info!("<{}> ({} ms) loaded", audio.name, audio.duration);
|
||||||
|
|
||||||
return Some(PlayerLoadedTrackData {
|
return Some(PlayerLoadedTrackData {
|
||||||
|
@ -1237,7 +1262,7 @@ impl PlayerInternal {
|
||||||
}
|
}
|
||||||
PlayerState::Stopped => (),
|
PlayerState::Stopped => (),
|
||||||
PlayerState::Invalid => {
|
PlayerState::Invalid => {
|
||||||
error!("PlayerInternal handle_player_stop: invalid state");
|
error!("PlayerInternal::handle_player_stop in invalid state");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1263,7 +1288,7 @@ impl PlayerInternal {
|
||||||
});
|
});
|
||||||
self.ensure_sink_running();
|
self.ensure_sink_running();
|
||||||
} else {
|
} else {
|
||||||
error!("Player::play called from invalid state");
|
error!("Player::play called from invalid state: {:?}", self.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,7 +1312,7 @@ impl PlayerInternal {
|
||||||
duration_ms,
|
duration_ms,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
error!("Player::pause called from invalid state");
|
error!("Player::pause called from invalid state: {:?}", self.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1548,7 +1573,10 @@ impl PlayerInternal {
|
||||||
position_ms,
|
position_ms,
|
||||||
}),
|
}),
|
||||||
PlayerState::Invalid { .. } => {
|
PlayerState::Invalid { .. } => {
|
||||||
error!("PlayerInternal handle_command_load: invalid state");
|
error!(
|
||||||
|
"Player::handle_command_load called from invalid state: {:?}",
|
||||||
|
self.state
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1578,12 +1606,12 @@ impl PlayerInternal {
|
||||||
loaded_track
|
loaded_track
|
||||||
.stream_loader_controller
|
.stream_loader_controller
|
||||||
.set_random_access_mode();
|
.set_random_access_mode();
|
||||||
if let Err(e) = loaded_track.decoder.seek(position_pcm) {
|
|
||||||
// This may be blocking.
|
// This may be blocking.
|
||||||
error!("PlayerInternal handle_command_load: {}", e);
|
match loaded_track.decoder.seek(position_pcm) {
|
||||||
|
Ok(_) => loaded_track.stream_position_pcm = position_pcm,
|
||||||
|
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
||||||
}
|
}
|
||||||
loaded_track.stream_loader_controller.set_stream_mode();
|
loaded_track.stream_loader_controller.set_stream_mode();
|
||||||
loaded_track.stream_position_pcm = position_pcm;
|
|
||||||
}
|
}
|
||||||
self.preload = PlayerPreload::None;
|
self.preload = PlayerPreload::None;
|
||||||
self.start_playback(track_id, play_request_id, loaded_track, play);
|
self.start_playback(track_id, play_request_id, loaded_track, play);
|
||||||
|
@ -1617,12 +1645,14 @@ impl PlayerInternal {
|
||||||
|
|
||||||
if position_pcm != *stream_position_pcm {
|
if position_pcm != *stream_position_pcm {
|
||||||
stream_loader_controller.set_random_access_mode();
|
stream_loader_controller.set_random_access_mode();
|
||||||
if let Err(e) = decoder.seek(position_pcm) {
|
|
||||||
// This may be blocking.
|
// This may be blocking.
|
||||||
error!("PlayerInternal handle_command_load: {}", e);
|
match decoder.seek(position_pcm) {
|
||||||
|
Ok(_) => *stream_position_pcm = position_pcm,
|
||||||
|
Err(e) => {
|
||||||
|
error!("PlayerInternal::handle_command_load error seeking: {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stream_loader_controller.set_stream_mode();
|
stream_loader_controller.set_stream_mode();
|
||||||
*stream_position_pcm = position_pcm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the info from the current state into a PlayerLoadedTrackData so we can use
|
// Move the info from the current state into a PlayerLoadedTrackData so we can use
|
||||||
|
@ -1692,9 +1722,10 @@ impl PlayerInternal {
|
||||||
loaded_track
|
loaded_track
|
||||||
.stream_loader_controller
|
.stream_loader_controller
|
||||||
.set_random_access_mode();
|
.set_random_access_mode();
|
||||||
if let Err(e) = loaded_track.decoder.seek(position_pcm) {
|
|
||||||
// This may be blocking
|
// This may be blocking
|
||||||
error!("PlayerInternal handle_command_load: {}", e);
|
match loaded_track.decoder.seek(position_pcm) {
|
||||||
|
Ok(_) => loaded_track.stream_position_pcm = position_pcm,
|
||||||
|
Err(e) => error!("PlayerInternal handle_command_load: {}", e),
|
||||||
}
|
}
|
||||||
loaded_track.stream_loader_controller.set_stream_mode();
|
loaded_track.stream_loader_controller.set_stream_mode();
|
||||||
}
|
}
|
||||||
|
@ -1824,10 +1855,10 @@ impl PlayerInternal {
|
||||||
*stream_position_pcm = position_pcm;
|
*stream_position_pcm = position_pcm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => error!("PlayerInternal handle_command_seek: {}", e),
|
Err(e) => error!("PlayerInternal::handle_command_seek error: {}", e),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Player::seek called from invalid state");
|
error!("Player::seek called from invalid state: {:?}", self.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're playing, ensure, that we have enough data leaded to avoid a buffer underrun.
|
// If we're playing, ensure, that we have enough data leaded to avoid a buffer underrun.
|
||||||
|
@ -1988,8 +2019,8 @@ impl Drop for PlayerInternal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Debug for PlayerCommand {
|
impl fmt::Debug for PlayerCommand {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
PlayerCommand::Load {
|
PlayerCommand::Load {
|
||||||
track_id,
|
track_id,
|
||||||
|
@ -2024,8 +2055,8 @@ impl ::std::fmt::Debug for PlayerCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Debug for PlayerState {
|
impl fmt::Debug for PlayerState {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use PlayerState::*;
|
use PlayerState::*;
|
||||||
match *self {
|
match *self {
|
||||||
Stopped => f.debug_struct("Stopped").finish(),
|
Stopped => f.debug_struct("Stopped").finish(),
|
||||||
|
@ -2076,9 +2107,19 @@ struct Subfile<T: Read + Seek> {
|
||||||
|
|
||||||
impl<T: Read + Seek> Subfile<T> {
|
impl<T: Read + Seek> Subfile<T> {
|
||||||
pub fn new(mut stream: T, offset: u64) -> Subfile<T> {
|
pub fn new(mut stream: T, offset: u64) -> Subfile<T> {
|
||||||
if let Err(e) = stream.seek(SeekFrom::Start(offset)) {
|
let target = SeekFrom::Start(offset);
|
||||||
error!("Subfile new Error: {}", e);
|
match stream.seek(target) {
|
||||||
|
Ok(pos) => {
|
||||||
|
if pos != offset {
|
||||||
|
error!(
|
||||||
|
"Subfile::new seeking to {:?} but position is now {:?}",
|
||||||
|
target, pos
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Err(e) => error!("Subfile new Error: {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
Subfile { stream, offset }
|
Subfile { stream, offset }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2097,10 +2138,14 @@ impl<T: Read + Seek> Seek for Subfile<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let newpos = self.stream.seek(pos)?;
|
let newpos = self.stream.seek(pos)?;
|
||||||
if newpos > self.offset {
|
|
||||||
|
if newpos >= self.offset {
|
||||||
Ok(newpos - self.offset)
|
Ok(newpos - self.offset)
|
||||||
} else {
|
} else {
|
||||||
Ok(0)
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"newpos < self.offset",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue