diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 758cc5fa..89036025 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -546,14 +546,10 @@ impl SpircTask { PlayerEvent::EndOfTrack { .. } => self.handle_end_of_track(), PlayerEvent::Loading { .. } => (), PlayerEvent::Playing { position_ms, .. } => { - let new_nominal_start_time = - self.now_ms() - position_ms as i64; + let new_nominal_start_time = self.now_ms() - position_ms as i64; match self.play_status { SpircPlayStatus::Playing { nominal_start_time } => { - if (nominal_start_time - new_nominal_start_time) - .abs() - > 100 - { + if (nominal_start_time - new_nominal_start_time).abs() > 100 { self.update_state_position(position_ms); self.notify(None); self.play_status = SpircPlayStatus::Playing { @@ -611,14 +607,17 @@ impl SpircTask { self.play_status = SpircPlayStatus::Stopped; } }, - PlayerEvent::TimeToPreloadNextTrack {..} => match self.play_status { - SpircPlayStatus::Paused {..}|SpircPlayStatus::Playing {..}| SpircPlayStatus::LoadingPause{..}|SpircPlayStatus::LoadingPlay {..} => { + PlayerEvent::TimeToPreloadNextTrack { .. } => match self.play_status { + SpircPlayStatus::Paused { .. } + | SpircPlayStatus::Playing { .. } + | SpircPlayStatus::LoadingPause { .. } + | SpircPlayStatus::LoadingPlay { .. } => { if let Some(track_id) = self.preview_next_track() { self.player.preload(track_id); } } SpircPlayStatus::Stopped => (), - } + }, _ => (), } } diff --git a/examples/play.rs b/examples/play.rs index acefe570..4ba4c5b5 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -7,53 +7,8 @@ use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; use librespot::playback::config::PlayerConfig; -use futures::stream::Stream; -use futures::{Async, Future, Poll}; use librespot::playback::audio_backend; -use librespot::playback::player::{Player, PlayerEvent, PlayerEventChannel}; - -pub struct SingleTrackPlayer { - play_request_id: u64, - event_channel: PlayerEventChannel, -} - -impl SingleTrackPlayer { - pub fn new(ref mut player: Player, track_id: SpotifyId) -> SingleTrackPlayer { - let event_channel = player.get_player_event_channel(); - let play_request_id = player.load(track_id, true, 0); - SingleTrackPlayer { - play_request_id, - event_channel, - } - } -} - -impl Future for SingleTrackPlayer { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - loop { - match self.event_channel.poll().unwrap() { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(None) => return Ok(Async::Ready(())), - Async::Ready(Some(event)) => match event { - PlayerEvent::EndOfTrack { - play_request_id, .. - } - | PlayerEvent::Stopped { - play_request_id, .. - } => { - if play_request_id == self.play_request_id { - return Ok(Async::Ready(())); - } - } - _ => (), - }, - } - } - } -} +use librespot::playback::player::Player; fn main() { let mut core = Core::new().unwrap(); @@ -79,12 +34,14 @@ fn main() { .run(Session::connect(session_config, credentials, None, handle)) .unwrap(); - let (player, _) = Player::new(player_config, session.clone(), None, move || { + let (mut player, _) = Player::new(player_config, session.clone(), None, move || { (backend)(None) }); + player.load(track, true, 0); + println!("Playing..."); - core.run(SingleTrackPlayer::new(player, track)).unwrap(); + core.run(player.get_end_of_track_future()).unwrap(); println!("Done"); } diff --git a/playback/src/player.rs b/playback/src/player.rs index 0d2c1efb..d7df74ff 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -247,10 +247,8 @@ impl Player { play_request_id } - pub fn preload(&mut self, track_id: SpotifyId) { - self.command(PlayerCommand::Preload { - track_id, - }); + pub fn preload(&self, track_id: SpotifyId) { + self.command(PlayerCommand::Preload { track_id }); } pub fn play(&self) { @@ -275,6 +273,19 @@ impl Player { event_receiver } + pub fn get_end_of_track_future(&self) -> Box> { + let result = self + .get_player_event_channel() + .filter(|event| match event { + PlayerEvent::EndOfTrack { .. } | PlayerEvent::Stopped { .. } => true, + _ => false, + }) + .into_future() + .map_err(|_| ()) + .map(|_| ()); + Box::new(result) + } + pub fn emit_volume_set_event(&self, volume: u16) { self.command(PlayerCommand::EmitVolumeSetEvent(volume)); } @@ -299,7 +310,7 @@ struct PlayerLoadedTrackData { stream_loader_controller: StreamLoaderController, bytes_per_second: usize, duration_ms: u32, - stream_position: u64, + stream_position_pcm: u64, } enum PlayerPreload { @@ -332,7 +343,7 @@ enum PlayerState { stream_loader_controller: StreamLoaderController, bytes_per_second: usize, duration_ms: u32, - stream_position: u64, + stream_position_pcm: u64, suggested_to_preload_next_track: bool, }, Playing { @@ -343,7 +354,7 @@ enum PlayerState { stream_loader_controller: StreamLoaderController, bytes_per_second: usize, duration_ms: u32, - stream_position: u64, + stream_position_pcm: u64, reported_nominal_start_time: Option, suggested_to_preload_next_track: bool, }, @@ -430,7 +441,7 @@ impl PlayerState { stream_loader_controller, duration_ms, bytes_per_second, - stream_position, + stream_position_pcm, suggested_to_preload_next_track, } => { *self = Playing { @@ -441,7 +452,7 @@ impl PlayerState { stream_loader_controller, duration_ms, bytes_per_second, - stream_position, + stream_position_pcm, reported_nominal_start_time: None, suggested_to_preload_next_track, }; @@ -461,7 +472,7 @@ impl PlayerState { stream_loader_controller, duration_ms, bytes_per_second, - stream_position, + stream_position_pcm, reported_nominal_start_time: _, suggested_to_preload_next_track, } => { @@ -473,7 +484,7 @@ impl PlayerState { stream_loader_controller, duration_ms, bytes_per_second, - stream_position, + stream_position_pcm, suggested_to_preload_next_track, }; } @@ -526,7 +537,7 @@ impl PlayerTrackLoader { } } - fn load_track(&self, spotify_id: SpotifyId, position: u64) -> Option { + fn load_track(&self, spotify_id: SpotifyId, position_ms: u32) -> Option { let audio = match AudioItem::get_audio_item(&self.session, spotify_id).wait() { Ok(audio) => audio, Err(_) => { @@ -580,7 +591,7 @@ impl PlayerTrackLoader { }; let bytes_per_second = self.stream_data_rate(*format); - let play_from_beginning = position == 0; + let play_from_beginning = position_ms == 0; let key = self.session.audio_key().request(spotify_id, file_id); let encrypted_file = AudioFile::open( @@ -632,14 +643,14 @@ impl PlayerTrackLoader { let mut decoder = VorbisDecoder::new(audio_file).unwrap(); - if position != 0 { - match decoder.seek(position as i64) { + if position_ms != 0 { + match decoder.seek(position_ms as i64) { Ok(_) => (), Err(err) => error!("Vorbis error: {:?}", err), } stream_loader_controller.set_stream_mode(); } - let stream_position = position * 441 / 10; + let stream_position_pcm = PlayerInternal::position_ms_to_pcm(position_ms); info!("<{}> ({} ms) loaded", audio.name, audio.duration); Some(PlayerLoadedTrackData { decoder, @@ -647,7 +658,7 @@ impl PlayerTrackLoader { stream_loader_controller, bytes_per_second, duration_ms, - stream_position, + stream_position_pcm, }) } } @@ -736,7 +747,7 @@ impl Future for PlayerInternal { play_request_id, ref mut decoder, normalisation_factor, - ref mut stream_position, + ref mut stream_position_pcm, ref mut reported_nominal_start_time, duration_ms, .. @@ -746,17 +757,18 @@ impl Future for PlayerInternal { let packet = decoder.next_packet().expect("Vorbis error"); if let Some(ref packet) = packet { - *stream_position = *stream_position + (packet.data().len() / 2) as u64; - let stream_position_seconds = *stream_position / 44100; - if stream_position_seconds != last_printed_stream_position_for_debug { + *stream_position_pcm = + *stream_position_pcm + (packet.data().len() / 2) as u64; + let stream_position_millis = Self::position_pcm_to_ms(*stream_position_pcm); + + if stream_position_millis / 1000 != last_printed_stream_position_for_debug { trace!( "Stream position: {} ({} seconds)", - *stream_position, - stream_position_seconds + *stream_position_pcm, + stream_position_millis / 1000 ); - last_printed_stream_position_for_debug = stream_position_seconds; + last_printed_stream_position_for_debug = stream_position_millis / 1000; } - let stream_position_millis = *stream_position * 10 / 441; let notify_about_position = match *reported_nominal_start_time { None => true, @@ -800,7 +812,7 @@ impl Future for PlayerInternal { track_id, play_request_id, duration_ms, - stream_position, + stream_position_pcm, ref mut stream_loader_controller, ref mut suggested_to_preload_next_track, .. @@ -809,13 +821,13 @@ impl Future for PlayerInternal { track_id, play_request_id, duration_ms, - stream_position, + stream_position_pcm, ref mut stream_loader_controller, ref mut suggested_to_preload_next_track, .. } = self.state { - let stream_position_millis = stream_position * 10 / 441; + let stream_position_millis = Self::position_pcm_to_ms(stream_position_pcm); if (!*suggested_to_preload_next_track) && ((duration_ms as i64 - stream_position_millis as i64) < PRELOAD_NEXT_TRACK_BEFORE_END_DURATION_MS as i64) @@ -841,6 +853,14 @@ impl Future for PlayerInternal { } impl PlayerInternal { + fn position_pcm_to_ms(position_pcm: u64) -> u32 { + (position_pcm * 10 / 441) as u32 + } + + fn position_ms_to_pcm(position_ms: u32) -> u64 { + position_ms as u64 * 441 / 10 + } + fn start_sink(&mut self) { match self.sink.start() { Ok(()) => self.sink_running = true, @@ -939,7 +959,7 @@ impl PlayerInternal { loaded_track: PlayerLoadedTrackData, start_playback: bool, ) { - let position_ms = (loaded_track.stream_position * 10 / 441) as u32; + let position_ms = Self::position_pcm_to_ms(loaded_track.stream_position_pcm); match self.state { PlayerState::Playing { @@ -984,7 +1004,7 @@ impl PlayerInternal { stream_loader_controller: loaded_track.stream_loader_controller, duration_ms: loaded_track.duration_ms, bytes_per_second: loaded_track.bytes_per_second, - stream_position: loaded_track.stream_position, + stream_position_pcm: loaded_track.stream_position_pcm, reported_nominal_start_time: Some( Instant::now() - Duration::from_millis(position_ms as u64), ), @@ -999,7 +1019,7 @@ impl PlayerInternal { stream_loader_controller: loaded_track.stream_loader_controller, duration_ms: loaded_track.duration_ms, bytes_per_second: loaded_track.bytes_per_second, - stream_position: loaded_track.stream_position, + stream_position_pcm: loaded_track.stream_position_pcm, suggested_to_preload_next_track: false, }; @@ -1102,8 +1122,7 @@ impl PlayerInternal { self.preload = PlayerPreload::None; - let loader = loader - .or_else(|| Some(self.load_track_threaded(track_id, position_ms as u64))); + let loader = loader.or_else(|| Some(self.load_track(track_id, position_ms))); let loader = loader.unwrap(); self.state = PlayerState::Loading { @@ -1130,28 +1149,28 @@ impl PlayerInternal { } } if let PlayerPreload::None = self.preload { - let loader = self.load_track_threaded(track_id, 0); + let loader = self.load_track(track_id, 0); self.preload = PlayerPreload::Loading { track_id, loader } } } - PlayerCommand::Seek(position) => { + PlayerCommand::Seek(position_ms) => { if let Some(stream_loader_controller) = self.state.stream_loader_controller() { stream_loader_controller.set_random_access_mode(); } if let Some(decoder) = self.state.decoder() { - match decoder.seek(position as i64) { + match decoder.seek(position_ms as i64) { Ok(_) => { if let PlayerState::Playing { - ref mut stream_position, + ref mut stream_position_pcm, .. } | PlayerState::Paused { - ref mut stream_position, + ref mut stream_position_pcm, .. } = self.state { - *stream_position = position as u64 * 441 / 10; + *stream_position_pcm = Self::position_ms_to_pcm(position_ms); } } Err(err) => error!("Vorbis error: {:?}", err), @@ -1176,11 +1195,11 @@ impl PlayerInternal { } = self.state { *reported_nominal_start_time = - Some(Instant::now() - Duration::from_millis(position as u64)); + Some(Instant::now() - Duration::from_millis(position_ms as u64)); self.send_event(PlayerEvent::Playing { track_id, play_request_id, - position_ms: position, + position_ms: position_ms, duration_ms, }); } @@ -1194,7 +1213,7 @@ impl PlayerInternal { self.send_event(PlayerEvent::Paused { track_id, play_request_id, - position_ms: position, + position_ms: position_ms, duration_ms, }); } @@ -1204,13 +1223,13 @@ impl PlayerInternal { if let PlayerState::Paused { track_id, play_request_id, - stream_position, + stream_position_pcm, .. } = self.state { self.state.paused_to_playing(); - let position_ms = (stream_position * 10 / 441) as u32; + let position_ms = Self::position_pcm_to_ms(stream_position_pcm); self.send_event(PlayerEvent::Started { track_id, play_request_id, @@ -1226,7 +1245,7 @@ impl PlayerInternal { if let PlayerState::Playing { track_id, play_request_id, - stream_position, + stream_position_pcm, duration_ms, .. } = self.state @@ -1234,7 +1253,7 @@ impl PlayerInternal { self.state.playing_to_paused(); self.stop_sink_if_running(); - let position_ms = (stream_position * 10 / 441) as u32; + let position_ms = Self::position_pcm_to_ms(stream_position_pcm); self.send_event(PlayerEvent::Paused { track_id, play_request_id, @@ -1268,48 +1287,10 @@ impl PlayerInternal { } } - // fn find_available_alternative<'a>(&self, audio: &'a AudioItem) -> Option> { - // if audio.available { - // Some(Cow::Borrowed(audio)) - // } else { - // if let Some(alternatives) = &audio.alternatives { - // let alternatives = alternatives - // .iter() - // .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)); - // let alternatives = future::join_all(alternatives).wait().unwrap(); - // alternatives - // .into_iter() - // .find(|alt| alt.available) - // .map(Cow::Owned) - // } else { - // None - // } - // } - // } - - // fn stream_data_rate(&self, format: FileFormat) -> usize { - // match format { - // FileFormat::OGG_VORBIS_96 => 12 * 1024, - // FileFormat::OGG_VORBIS_160 => 20 * 1024, - // FileFormat::OGG_VORBIS_320 => 40 * 1024, - // FileFormat::MP3_256 => 32 * 1024, - // FileFormat::MP3_320 => 40 * 1024, - // FileFormat::MP3_160 => 20 * 1024, - // FileFormat::MP3_96 => 12 * 1024, - // FileFormat::MP3_160_ENC => 20 * 1024, - // FileFormat::MP4_128_DUAL => 16 * 1024, - // FileFormat::OTHER3 => 40 * 1024, // better some high guess than nothing - // FileFormat::AAC_160 => 20 * 1024, - // FileFormat::AAC_320 => 40 * 1024, - // FileFormat::MP4_128 => 16 * 1024, - // FileFormat::OTHER5 => 40 * 1024, // better some high guess than nothing - // } - // } - - fn load_track_threaded( + fn load_track( &self, spotify_id: SpotifyId, - position: u64, + position_ms: u32, ) -> Box> { // This method creates a future that returns the loaded stream and associated info. // Ideally all work should be done using asynchronous code. However, seek() on the @@ -1326,7 +1307,7 @@ impl PlayerInternal { std::thread::spawn(move || { loader - .load_track(spotify_id, position) + .load_track(spotify_id, position_ms) .and_then(move |data| { let _ = result_tx.send(data); Some(()) @@ -1336,130 +1317,6 @@ impl PlayerInternal { Box::new(result_rx.map_err(|_| ())) } - // fn load_track( - // &self, - // spotify_id: SpotifyId, - // position: u64, - // ) -> Option<(Decoder, f32, StreamLoaderController, usize, u64)> { - // let audio = match AudioItem::get_audio_item(&self.session, spotify_id).wait() { - // Ok(audio) => audio, - // Err(_) => { - // error!("Unable to load audio item."); - // return None; - // } - // }; - // - // info!("Loading <{}> with Spotify URI <{}>", audio.name, audio.uri); - // - // let audio = match self.find_available_alternative(&audio) { - // Some(audio) => audio, - // None => { - // warn!("<{}> is not available", audio.uri); - // return None; - // } - // }; - // // (Most) podcasts seem to support only 96 bit Vorbis, so fall back to it - // let formats = match self.config.bitrate { - // Bitrate::Bitrate96 => [ - // FileFormat::OGG_VORBIS_96, - // FileFormat::OGG_VORBIS_160, - // FileFormat::OGG_VORBIS_320, - // ], - // Bitrate::Bitrate160 => [ - // FileFormat::OGG_VORBIS_160, - // FileFormat::OGG_VORBIS_96, - // FileFormat::OGG_VORBIS_320, - // ], - // Bitrate::Bitrate320 => [ - // FileFormat::OGG_VORBIS_320, - // FileFormat::OGG_VORBIS_160, - // FileFormat::OGG_VORBIS_96, - // ], - // }; - // let format = formats - // .iter() - // .find(|format| audio.files.contains_key(format)) - // .unwrap(); - // - // let file_id = match audio.files.get(&format) { - // Some(&file_id) => file_id, - // None => { - // warn!("<{}> in not available in format {:?}", audio.name, format); - // return None; - // } - // }; - // - // let bytes_per_second = self.stream_data_rate(*format); - // let play_from_beginning = position == 0; - // - // let key = self.session.audio_key().request(spotify_id, file_id); - // let encrypted_file = AudioFile::open( - // &self.session, - // file_id, - // bytes_per_second, - // play_from_beginning, - // ); - // - // let encrypted_file = match encrypted_file.wait() { - // Ok(encrypted_file) => encrypted_file, - // Err(_) => { - // error!("Unable to load encrypted file."); - // return None; - // } - // }; - // - // let mut stream_loader_controller = encrypted_file.get_stream_loader_controller(); - // - // if play_from_beginning { - // // No need to seek -> we stream from the beginning - // stream_loader_controller.set_stream_mode(); - // } else { - // // we need to seek -> we set stream mode after the initial seek. - // stream_loader_controller.set_random_access_mode(); - // } - // - // let key = match key.wait() { - // Ok(key) => key, - // Err(_) => { - // error!("Unable to load decryption key"); - // return None; - // } - // }; - // - // let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); - // - // let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { - // Ok(normalisation_data) => { - // NormalisationData::get_factor(&self.config, normalisation_data) - // } - // Err(_) => { - // warn!("Unable to extract normalisation data, using default value."); - // 1.0 as f32 - // } - // }; - // - // let audio_file = Subfile::new(decrypted_file, 0xa7); - // - // let mut decoder = VorbisDecoder::new(audio_file).unwrap(); - // - // if position != 0 { - // match decoder.seek(position as i64) { - // Ok(_) => (), - // Err(err) => error!("Vorbis error: {:?}", err), - // } - // stream_loader_controller.set_stream_mode(); - // } - // let stream_position = position * 441 / 10; - // info!("<{}> loaded", audio.name); - // Some(( - // decoder, - // normalisation_factor, - // stream_loader_controller, - // bytes_per_second, - // stream_position, - // )) - // } - fn preload_data_before_playback(&mut self) { if let PlayerState::Playing { bytes_per_second,