mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
player: Add seek_at command for precise seeking
This commit is contained in:
parent
8c0a37357d
commit
f50fca02ae
1 changed files with 38 additions and 8 deletions
|
@ -10,7 +10,7 @@ use audio_backend::Sink;
|
||||||
use metadata::{FileFormat, Track, TrackRef};
|
use metadata::{FileFormat, Track, TrackRef};
|
||||||
use session::{Bitrate, Session};
|
use session::{Bitrate, Session};
|
||||||
use util::{self, SpotifyId, Subfile};
|
use util::{self, SpotifyId, Subfile};
|
||||||
use spirc::PlayStatus;
|
pub use spirc::PlayStatus;
|
||||||
|
|
||||||
#[cfg(not(feature = "with-tremor"))]
|
#[cfg(not(feature = "with-tremor"))]
|
||||||
fn vorbis_time_seek_ms<R>(decoder: &mut vorbis::Decoder<R>, ms: i64) -> Result<(), vorbis::VorbisError> where R: Read + Seek {
|
fn vorbis_time_seek_ms<R>(decoder: &mut vorbis::Decoder<R>, ms: i64) -> Result<(), vorbis::VorbisError> where R: Read + Seek {
|
||||||
|
@ -34,6 +34,7 @@ fn vorbis_time_tell_ms<R>(decoder: &mut vorbis::Decoder<R>) -> Result<i64, vorbi
|
||||||
|
|
||||||
pub type PlayerObserver = Box<Fn(&PlayerState) + Send>;
|
pub type PlayerObserver = Box<Fn(&PlayerState) + Send>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
state: Arc<Mutex<PlayerState>>,
|
state: Arc<Mutex<PlayerState>>,
|
||||||
observers: Arc<Mutex<Vec<PlayerObserver>>>,
|
observers: Arc<Mutex<Vec<PlayerObserver>>>,
|
||||||
|
@ -43,13 +44,14 @@ pub struct Player {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PlayerState {
|
pub struct PlayerState {
|
||||||
status: PlayStatus,
|
pub status: PlayStatus,
|
||||||
position_ms: u32,
|
pub position_ms: u32,
|
||||||
position_measured_at: i64,
|
pub position_measured_at: i64,
|
||||||
update_time: i64,
|
pub update_time: i64,
|
||||||
volume: u16,
|
pub volume: u16,
|
||||||
|
pub track: Option<SpotifyId>,
|
||||||
|
|
||||||
end_of_track: bool,
|
pub end_of_track: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PlayerInternal {
|
struct PlayerInternal {
|
||||||
|
@ -68,6 +70,7 @@ enum PlayerCommand {
|
||||||
Volume(u16),
|
Volume(u16),
|
||||||
Stop,
|
Stop,
|
||||||
Seek(u32),
|
Seek(u32),
|
||||||
|
SeekAt(u32, i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
|
@ -81,6 +84,7 @@ impl Player {
|
||||||
position_measured_at: 0,
|
position_measured_at: 0,
|
||||||
update_time: util::now_ms(),
|
update_time: util::now_ms(),
|
||||||
volume: 0xFFFF,
|
volume: 0xFFFF,
|
||||||
|
track: None,
|
||||||
end_of_track: false,
|
end_of_track: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -126,6 +130,10 @@ impl Player {
|
||||||
self.command(PlayerCommand::Seek(position_ms));
|
self.command(PlayerCommand::Seek(position_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn seek_at(&self, position_ms: u32, measured_at: i64) {
|
||||||
|
self.command(PlayerCommand::SeekAt(position_ms, measured_at));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> PlayerState {
|
pub fn state(&self) -> PlayerState {
|
||||||
self.state.lock().unwrap().clone()
|
self.state.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
@ -176,6 +184,7 @@ impl PlayerInternal {
|
||||||
state.status = PlayStatus::kPlayStatusPause;
|
state.status = PlayStatus::kPlayStatusPause;
|
||||||
state.position_ms = position;
|
state.position_ms = position;
|
||||||
state.position_measured_at = util::now_ms();
|
state.position_measured_at = util::now_ms();
|
||||||
|
state.track = Some(track_id);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
drop(decoder);
|
drop(decoder);
|
||||||
|
@ -235,6 +244,20 @@ impl PlayerInternal {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Some(PlayerCommand::SeekAt(position, measured_at)) => {
|
||||||
|
let position = (util::now_ms() - measured_at + position as i64) as u32;
|
||||||
|
|
||||||
|
let before = util::now_ms();
|
||||||
|
vorbis_time_seek_ms(decoder.as_mut().unwrap(), position as i64).unwrap();
|
||||||
|
self.update(|state| {
|
||||||
|
state.position_ms = vorbis_time_tell_ms(decoder.as_mut().unwrap()).unwrap() as u32;
|
||||||
|
state.position_measured_at = util::now_ms();
|
||||||
|
|
||||||
|
println!("SEEK: {} {} {}", before, util::now_ms(), util::now_ms() - before);
|
||||||
|
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
Some(PlayerCommand::Play) => {
|
Some(PlayerCommand::Play) => {
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
state.status = PlayStatus::kPlayStatusPlay;
|
state.status = PlayStatus::kPlayStatusPlay;
|
||||||
|
@ -249,7 +272,7 @@ impl PlayerInternal {
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
state.status = PlayStatus::kPlayStatusPause;
|
state.status = PlayStatus::kPlayStatusPause;
|
||||||
state.update_time = util::now_ms();
|
state.update_time = util::now_ms();
|
||||||
state.position_ms = vorbis_time_tell_ms(decoder.as_mut().unwrap()).unwrap() as u32;
|
state.position_ms = decoder.as_mut().map(|d| vorbis_time_tell_ms(d).unwrap()).unwrap_or(0) as u32;
|
||||||
state.position_measured_at = util::now_ms();
|
state.position_measured_at = util::now_ms();
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
@ -286,6 +309,13 @@ impl PlayerInternal {
|
||||||
let buffer = apply_volume(self.state.lock().unwrap().volume,
|
let buffer = apply_volume(self.state.lock().unwrap().volume,
|
||||||
&packet.data);
|
&packet.data);
|
||||||
sink.write(&buffer).unwrap();
|
sink.write(&buffer).unwrap();
|
||||||
|
|
||||||
|
self.update(|state| {
|
||||||
|
state.position_ms = vorbis_time_tell_ms(decoder.as_mut().unwrap()).unwrap() as u32;
|
||||||
|
state.position_measured_at = util::now_ms();
|
||||||
|
|
||||||
|
false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Some(Err(vorbis::VorbisError::Hole)) => (),
|
Some(Err(vorbis::VorbisError::Hole)) => (),
|
||||||
Some(Err(e)) => panic!("Vorbis error {:?}", e),
|
Some(Err(e)) => panic!("Vorbis error {:?}", e),
|
||||||
|
|
Loading…
Reference in a new issue