mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
Remove busy waiting in SpircManager.
This commit is contained in:
parent
1264394838
commit
94503e351b
3 changed files with 109 additions and 76 deletions
|
@ -1,6 +1,6 @@
|
||||||
#![crate_name = "librespot"]
|
#![crate_name = "librespot"]
|
||||||
|
|
||||||
#![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future)]
|
#![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future,mpsc_select)]
|
||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
//#![allow(unused_imports,dead_code)]
|
//#![allow(unused_imports,dead_code)]
|
||||||
|
|
||||||
|
|
113
src/player.rs
113
src/player.rs
|
@ -76,9 +76,7 @@ impl <'s> PlayerInternal<'s> {
|
||||||
portaudio::initialize().unwrap();
|
portaudio::initialize().unwrap();
|
||||||
|
|
||||||
let stream = portaudio::stream::Stream::<i16>::open_default(
|
let stream = portaudio::stream::Stream::<i16>::open_default(
|
||||||
0,
|
0, 2, 44100.0,
|
||||||
2,
|
|
||||||
44100.0,
|
|
||||||
portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
|
portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
|
||||||
None
|
None
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -88,16 +86,15 @@ impl <'s> PlayerInternal<'s> {
|
||||||
loop {
|
loop {
|
||||||
match self.commands.try_recv() {
|
match self.commands.try_recv() {
|
||||||
Ok(PlayerCommand::Load(id, play, position)) => {
|
Ok(PlayerCommand::Load(id, play, position)) => {
|
||||||
println!("Load");
|
self.update(|state| {
|
||||||
let mut h = self.state.0.lock().unwrap();
|
if state.status == PlayStatus::kPlayStatusPlay {
|
||||||
if h.status == PlayStatus::kPlayStatusPlay {
|
|
||||||
stream.stop().unwrap();
|
stream.stop().unwrap();
|
||||||
}
|
}
|
||||||
h.status = PlayStatus::kPlayStatusLoading;
|
state.status = PlayStatus::kPlayStatusLoading;
|
||||||
h.position_ms = position;
|
state.position_ms = position;
|
||||||
h.position_measured_at = util::now_ms();
|
state.position_measured_at = util::now_ms();
|
||||||
h.update_time = util::now_ms();
|
return true;
|
||||||
drop(h);
|
});
|
||||||
|
|
||||||
let track : TrackRef = self.session.metadata(id);
|
let track : TrackRef = self.session.metadata(id);
|
||||||
let file_id = *track.wait().unwrap().files.first().unwrap();
|
let file_id = *track.wait().unwrap().files.first().unwrap();
|
||||||
|
@ -109,48 +106,54 @@ impl <'s> PlayerInternal<'s> {
|
||||||
self.session.audio_file(file_id)), 0xa7)).unwrap());
|
self.session.audio_file(file_id)), 0xa7)).unwrap());
|
||||||
decoder.as_mut().unwrap().time_seek(position as f64 / 1000f64).unwrap();
|
decoder.as_mut().unwrap().time_seek(position as f64 / 1000f64).unwrap();
|
||||||
|
|
||||||
let mut h = self.state.0.lock().unwrap();
|
self.update(|state| {
|
||||||
h.status = if play {
|
state.status = if play {
|
||||||
stream.start().unwrap();
|
stream.start().unwrap();
|
||||||
PlayStatus::kPlayStatusPlay
|
PlayStatus::kPlayStatusPlay
|
||||||
} else {
|
} else {
|
||||||
PlayStatus::kPlayStatusPause
|
PlayStatus::kPlayStatusPause
|
||||||
};
|
};
|
||||||
h.position_ms = position;
|
state.position_ms = position;
|
||||||
h.position_measured_at = util::now_ms();
|
state.position_measured_at = util::now_ms();
|
||||||
h.update_time = util::now_ms();
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
println!("Load Done");
|
println!("Load Done");
|
||||||
}
|
}
|
||||||
Ok(PlayerCommand::Seek(ms)) => {
|
Ok(PlayerCommand::Seek(ms)) => {
|
||||||
let mut h = self.state.0.lock().unwrap();
|
|
||||||
decoder.as_mut().unwrap().time_seek(ms as f64 / 1000f64).unwrap();
|
decoder.as_mut().unwrap().time_seek(ms as f64 / 1000f64).unwrap();
|
||||||
h.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
|
self.update(|state| {
|
||||||
h.position_measured_at = util::now_ms();
|
state.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
|
||||||
h.update_time = util::now_ms();
|
state.position_measured_at = util::now_ms();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
Ok(PlayerCommand::Play) => {
|
Ok(PlayerCommand::Play) => {
|
||||||
println!("Play");
|
self.update(|state| {
|
||||||
let mut h = self.state.0.lock().unwrap();
|
state.status = PlayStatus::kPlayStatusPlay;
|
||||||
h.status = PlayStatus::kPlayStatusPlay;
|
return true;
|
||||||
h.update_time = util::now_ms();
|
});
|
||||||
|
|
||||||
stream.start().unwrap();
|
stream.start().unwrap();
|
||||||
},
|
},
|
||||||
Ok(PlayerCommand::Pause) => {
|
Ok(PlayerCommand::Pause) => {
|
||||||
let mut h = self.state.0.lock().unwrap();
|
self.update(|state| {
|
||||||
h.status = PlayStatus::kPlayStatusPause;
|
state.status = PlayStatus::kPlayStatusPause;
|
||||||
h.update_time = util::now_ms();
|
state.update_time = util::now_ms();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
stream.stop().unwrap();
|
stream.stop().unwrap();
|
||||||
},
|
},
|
||||||
Ok(PlayerCommand::Stop) => {
|
Ok(PlayerCommand::Stop) => {
|
||||||
let mut h = self.state.0.lock().unwrap();
|
self.update(|state| {
|
||||||
if h.status == PlayStatus::kPlayStatusPlay {
|
if state.status == PlayStatus::kPlayStatusPlay {
|
||||||
stream.stop().unwrap();
|
state.status = PlayStatus::kPlayStatusPause;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
h.status = PlayStatus::kPlayStatusPause;
|
stream.stop().unwrap();
|
||||||
h.update_time = util::now_ms();
|
|
||||||
decoder = None;
|
decoder = None;
|
||||||
},
|
},
|
||||||
Err(..) => (),
|
Err(..) => (),
|
||||||
|
@ -170,9 +173,17 @@ impl <'s> PlayerInternal<'s> {
|
||||||
Err(e) => panic!("Vorbis error {:?}", e)
|
Err(e) => panic!("Vorbis error {:?}", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut h = self.state.0.lock().unwrap();
|
self.update(|state| {
|
||||||
h.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
|
let now = util::now_ms();
|
||||||
h.position_measured_at = util::now_ms();
|
|
||||||
|
if now - state.position_measured_at > 5000 {
|
||||||
|
state.position_ms = (decoder.as_mut().unwrap().time_tell().unwrap() * 1000f64) as u32;
|
||||||
|
state.position_measured_at = now;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +191,17 @@ impl <'s> PlayerInternal<'s> {
|
||||||
|
|
||||||
portaudio::terminate().unwrap();
|
portaudio::terminate().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update<F>(&self, f: F)
|
||||||
|
where F: FnOnce(&mut MutexGuard<PlayerState>) -> bool {
|
||||||
|
let mut guard = self.state.0.lock().unwrap();
|
||||||
|
let update = f(&mut guard);
|
||||||
|
if update {
|
||||||
|
guard.update_time = util::now_ms();
|
||||||
|
self.state.1.notify_all();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'s> SpircDelegate for Player<'s> {
|
impl <'s> SpircDelegate for Player<'s> {
|
||||||
|
@ -210,9 +232,24 @@ impl <'s> SpircDelegate for Player<'s> {
|
||||||
self.state.0.lock().unwrap()
|
self.state.0.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
|
fn updates(&self) -> mpsc::Receiver<i64> {
|
||||||
-> MutexGuard<'a, Self::State> {
|
let state = self.state.clone();
|
||||||
self.state.1.wait(guard).unwrap()
|
let (update_tx, update_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut guard = state.0.lock().unwrap();
|
||||||
|
let mut last_update;
|
||||||
|
loop {
|
||||||
|
last_update = guard.update_time;
|
||||||
|
update_tx.send(guard.update_time).unwrap();
|
||||||
|
|
||||||
|
while last_update >= guard.update_time {
|
||||||
|
guard = state.1.wait(guard).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return update_rx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
src/spirc.rs
28
src/spirc.rs
|
@ -1,11 +1,11 @@
|
||||||
use protobuf::{self, Message};
|
use protobuf::{self, Message};
|
||||||
|
use std::sync::{mpsc, MutexGuard};
|
||||||
|
|
||||||
use util;
|
use util;
|
||||||
use session::Session;
|
use session::Session;
|
||||||
use util::SpotifyId;
|
use util::SpotifyId;
|
||||||
use util::version::version_string;
|
use util::version::version_string;
|
||||||
use mercury::{MercuryRequest, MercuryMethod};
|
use mercury::{MercuryRequest, MercuryMethod};
|
||||||
use std::sync::MutexGuard;
|
|
||||||
|
|
||||||
use librespot_protocol as protocol;
|
use librespot_protocol as protocol;
|
||||||
pub use librespot_protocol::spirc::PlayStatus;
|
pub use librespot_protocol::spirc::PlayStatus;
|
||||||
|
@ -47,8 +47,7 @@ pub trait SpircDelegate {
|
||||||
fn stop(&self);
|
fn stop(&self);
|
||||||
|
|
||||||
fn state(&self) -> MutexGuard<Self::State>;
|
fn state(&self) -> MutexGuard<Self::State>;
|
||||||
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
|
fn updates(&self) -> mpsc::Receiver<i64>;
|
||||||
-> MutexGuard<'a, Self::State>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SpircState {
|
pub trait SpircState {
|
||||||
|
@ -89,13 +88,13 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let rx = self.session.mercury_sub(format!("hm://remote/user/{}/v23", self.username));
|
let rx = self.session.mercury_sub(format!("hm://remote/user/{}/v23", self.username));
|
||||||
|
let updates = self.delegate.updates();
|
||||||
self.notify(None);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Ok(pkt) = rx.try_recv() {
|
select! {
|
||||||
|
pkt = rx.recv() => {
|
||||||
let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(
|
let frame = protobuf::parse_from_bytes::<protocol::spirc::Frame>(
|
||||||
pkt.payload.front().unwrap()).unwrap();
|
pkt.unwrap().payload.front().unwrap()).unwrap();
|
||||||
println!("{:?} {} {} {} {}",
|
println!("{:?} {} {} {} {}",
|
||||||
frame.get_typ(),
|
frame.get_typ(),
|
||||||
frame.get_device_state().get_name(),
|
frame.get_device_state().get_name(),
|
||||||
|
@ -107,17 +106,14 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
frame.get_recipient().contains(&self.ident)) {
|
frame.get_recipient().contains(&self.ident)) {
|
||||||
self.handle(frame);
|
self.handle(frame);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
update_time = updates.recv() => {
|
||||||
if {
|
self.state_update_id = update_time.unwrap();
|
||||||
let state = self.delegate.state();
|
|
||||||
state.update_time() > self.state_update_id
|
|
||||||
} {
|
|
||||||
self.state_update_id = util::now_ms();
|
|
||||||
self.notify(None);
|
self.notify(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle(&mut self, frame: protocol::spirc::Frame) {
|
fn handle(&mut self, frame: protocol::spirc::Frame) {
|
||||||
if frame.get_recipient().len() > 0 {
|
if frame.get_recipient().len() > 0 {
|
||||||
|
@ -170,7 +166,7 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
recipient: protobuf::RepeatedField::from_vec(
|
recipient: protobuf::RepeatedField::from_vec(
|
||||||
recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
recipient.map(|r| vec![r.to_string()] ).unwrap_or(vec![])
|
||||||
),
|
),
|
||||||
state_update_id: self.state_update_id
|
state_update_id: self.state_update_id as i64
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.is_active {
|
if self.is_active {
|
||||||
|
@ -219,7 +215,7 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
volume: self.volume as u32,
|
volume: self.volume as u32,
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
error_code: 0,
|
error_code: 0,
|
||||||
became_active_at: if self.is_active { self.became_active_at } else { 0 },
|
became_active_at: if self.is_active { self.became_active_at as i64 } else { 0 },
|
||||||
capabilities => [
|
capabilities => [
|
||||||
@{
|
@{
|
||||||
typ: protocol::spirc::CapabilityType::kCanBePlayer,
|
typ: protocol::spirc::CapabilityType::kCanBePlayer,
|
||||||
|
|
Loading…
Reference in a new issue