Remove busy waiting in SpircManager.

This commit is contained in:
Paul Lietar 2015-07-09 22:04:19 +01:00
parent 1264394838
commit 94503e351b
3 changed files with 109 additions and 76 deletions

View file

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

View file

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

View file

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