Skip to content

Commit 6907ecd

Browse files
authored
Merge branch 'dev' into resampling
2 parents a5529be + 054074c commit 6907ecd

File tree

11 files changed

+106
-37
lines changed

11 files changed

+106
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ https://github.com/librespot-org/librespot
3636
- [all] `chrono` replaced with `time` (breaking)
3737
- [all] `time` updated (CVE-2020-26235)
3838
- [all] Improve lock contention and performance (breaking)
39+
- [all] Use a single `player` instance. Eliminates occasional `player` and
40+
`audio backend` restarts, which can cause issues with some playback
41+
configurations.
3942
- [audio] Files are now downloaded over the HTTPS CDN (breaking)
4043
- [audio] Improve file opening and seeking performance (breaking)
4144
- [core] MSRV is now 1.65 (breaking)

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

connect/src/spirc.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
future::Future,
44
pin::Pin,
55
sync::atomic::{AtomicUsize, Ordering},
6+
sync::Arc,
67
time::{SystemTime, UNIX_EPOCH},
78
};
89

@@ -77,8 +78,8 @@ enum SpircPlayStatus {
7778
type BoxedStream<T> = Pin<Box<dyn FusedStream<Item = T> + Send>>;
7879

7980
struct SpircTask {
80-
player: Player,
81-
mixer: Box<dyn Mixer>,
81+
player: Arc<Player>,
82+
mixer: Arc<dyn Mixer>,
8283

8384
sequence: SeqGenerator<u32>,
8485

@@ -272,8 +273,8 @@ impl Spirc {
272273
config: ConnectConfig,
273274
session: Session,
274275
credentials: Credentials,
275-
player: Player,
276-
mixer: Box<dyn Mixer>,
276+
player: Arc<Player>,
277+
mixer: Arc<dyn Mixer>,
277278
) -> Result<(Spirc, impl Future<Output = ()>), Error> {
278279
let spirc_id = SPIRC_COUNTER.fetch_add(1, Ordering::AcqRel);
279280
debug!("new Spirc[{}]", spirc_id);
@@ -663,6 +664,11 @@ impl SpircTask {
663664
}
664665

665666
fn handle_player_event(&mut self, event: PlayerEvent) -> Result<(), Error> {
667+
// update play_request_id
668+
if let PlayerEvent::PlayRequestIdChanged { play_request_id } = event {
669+
self.play_request_id = Some(play_request_id);
670+
return Ok(());
671+
}
666672
// we only process events if the play_request_id matches. If it doesn't, it is
667673
// an event that belongs to a previous track and only arrives now due to a race
668674
// condition. In this case we have updated the state already and don't want to
@@ -1462,7 +1468,7 @@ impl SpircTask {
14621468
Some((track, index)) => {
14631469
self.state.set_playing_track_index(index);
14641470

1465-
self.play_request_id = Some(self.player.load(track, start_playing, position_ms));
1471+
self.player.load(track, start_playing, position_ms);
14661472

14671473
self.update_state_position(position_ms);
14681474
if start_playing {

core/src/authentication.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl From<AuthenticationError> for Error {
2727
}
2828

2929
/// The credentials are used to log into the Spotify API.
30-
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
30+
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3131
pub struct Credentials {
3232
pub username: String,
3333

core/src/session.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,13 @@ impl Session {
176176
self.set_username(&reusable_credentials.username);
177177
if let Some(cache) = self.cache() {
178178
if store_credentials {
179-
cache.save_credentials(&reusable_credentials);
179+
let cred_changed = cache
180+
.credentials()
181+
.map(|c| c != reusable_credentials)
182+
.unwrap_or(true);
183+
if cred_changed {
184+
cache.save_credentials(&reusable_credentials);
185+
}
180186
}
181187
}
182188

examples/play_connect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use librespot_metadata::{Album, Metadata};
1717
use librespot_playback::mixer::{softmixer::SoftMixer, Mixer, MixerConfig};
1818
use librespot_protocol::spirc::TrackRef;
1919
use std::env;
20+
use std::sync::Arc;
2021
use tokio::join;
2122

2223
#[tokio::main]
@@ -55,7 +56,7 @@ async fn main() {
5556
session.clone(),
5657
credentials,
5758
player,
58-
Box::new(SoftMixer::open(MixerConfig::default())),
59+
Arc::new(SoftMixer::open(MixerConfig::default())),
5960
)
6061
.await
6162
.unwrap();

playback/src/audio_backend/jackaudio.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ impl Open for JackSink {
5555
let client_name = client_name.unwrap_or_else(|| "librespot".to_string());
5656
let (client, _status) =
5757
Client::new(&client_name[..], ClientOptions::NO_START_SERVER).unwrap();
58-
let ch_r = client.register_port("out_0", AudioOut::default()).unwrap();
59-
let ch_l = client.register_port("out_1", AudioOut::default()).unwrap();
58+
let ch_r = client.register_port("out_0", AudioOut).unwrap();
59+
let ch_l = client.register_port("out_1", AudioOut).unwrap();
6060
// buffer for samples from librespot (~10ms)
6161
let (tx, rx) = sync_channel::<f32>(NUM_CHANNELS as usize * 1024 * AudioFormat::F32.size());
6262
let jack_data = JackData {

playback/src/mixer/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use std::sync::Arc;
2+
13
use crate::config::VolumeCtrl;
24

35
pub mod mappings;
46
use self::mappings::MappedCtrl;
57

68
pub struct NoOpVolume;
79

8-
pub trait Mixer: Send {
10+
pub trait Mixer: Send + Sync {
911
fn open(config: MixerConfig) -> Self
1012
where
1113
Self: Sized;
@@ -55,10 +57,10 @@ impl Default for MixerConfig {
5557
}
5658
}
5759

58-
pub type MixerFn = fn(MixerConfig) -> Box<dyn Mixer>;
60+
pub type MixerFn = fn(MixerConfig) -> Arc<dyn Mixer>;
5961

60-
fn mk_sink<M: Mixer + 'static>(config: MixerConfig) -> Box<dyn Mixer> {
61-
Box::new(M::open(config))
62+
fn mk_sink<M: Mixer + 'static>(config: MixerConfig) -> Arc<dyn Mixer> {
63+
Arc::new(M::open(config))
6264
}
6365

6466
pub const MIXERS: &[(&str, MixerFn)] = &[

playback/src/player.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ pub type PlayerResult = Result<(), Error>;
5151
pub struct Player {
5252
commands: Option<mpsc::UnboundedSender<PlayerCommand>>,
5353
thread_handle: Option<thread::JoinHandle<()>>,
54-
play_request_id_generator: SeqGenerator<u64>,
5554
}
5655

5756
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
@@ -79,14 +78,14 @@ struct PlayerInternal {
7978
auto_normalise_as_album: bool,
8079

8180
player_id: usize,
81+
play_request_id_generator: SeqGenerator<u64>,
8282
}
8383

8484
pub static PLAYER_COUNTER: AtomicUsize = AtomicUsize::new(0);
8585

8686
enum PlayerCommand {
8787
Load {
8888
track_id: SpotifyId,
89-
play_request_id: u64,
9089
play: bool,
9190
position_ms: u32,
9291
},
@@ -97,6 +96,7 @@ enum PlayerCommand {
9796
Pause,
9897
Stop,
9998
Seek(u32),
99+
SetSession(Session),
100100
AddEventSender(mpsc::UnboundedSender<PlayerEvent>),
101101
SetSinkEventCallback(Option<SinkEventCallback>),
102102
EmitVolumeChangedEvent(u16),
@@ -123,6 +123,10 @@ enum PlayerCommand {
123123

124124
#[derive(Debug, Clone)]
125125
pub enum PlayerEvent {
126+
// Play request id changed
127+
PlayRequestIdChanged {
128+
play_request_id: u64,
129+
},
126130
// Fired when the player is stopped (e.g. by issuing a "stop" command to the player).
127131
Stopped {
128132
play_request_id: u64,
@@ -318,7 +322,7 @@ impl Player {
318322
session: Session,
319323
volume_getter: Box<dyn VolumeGetter>,
320324
sink_builder: F,
321-
) -> Self
325+
) -> Arc<Self>
322326
where
323327
F: FnOnce() -> Box<dyn Sink> + Send + 'static,
324328
{
@@ -349,6 +353,7 @@ impl Player {
349353
auto_normalise_as_album: false,
350354

351355
player_id,
356+
play_request_id_generator: SeqGenerator::new(0),
352357
};
353358

354359
// While PlayerInternal is written as a future, it still contains blocking code.
@@ -382,11 +387,17 @@ impl Player {
382387
}
383388
};
384389

385-
Self {
390+
Arc::new(Self {
386391
commands: Some(cmd_tx),
387392
thread_handle: Some(handle),
388-
play_request_id_generator: SeqGenerator::new(0),
393+
})
394+
}
395+
396+
pub fn is_invalid(&self) -> bool {
397+
if let Some(handle) = self.thread_handle.as_ref() {
398+
return handle.is_finished();
389399
}
400+
true
390401
}
391402

392403
fn command(&self, cmd: PlayerCommand) {
@@ -397,16 +408,12 @@ impl Player {
397408
}
398409
}
399410

400-
pub fn load(&mut self, track_id: SpotifyId, start_playing: bool, position_ms: u32) -> u64 {
401-
let play_request_id = self.play_request_id_generator.get();
411+
pub fn load(&self, track_id: SpotifyId, start_playing: bool, position_ms: u32) {
402412
self.command(PlayerCommand::Load {
403413
track_id,
404-
play_request_id,
405414
play: start_playing,
406415
position_ms,
407416
});
408-
409-
play_request_id
410417
}
411418

412419
pub fn preload(&self, track_id: SpotifyId) {
@@ -429,6 +436,10 @@ impl Player {
429436
self.command(PlayerCommand::Seek(position_ms));
430437
}
431438

439+
pub fn set_session(&self, session: Session) {
440+
self.command(PlayerCommand::SetSession(session));
441+
}
442+
432443
pub fn get_player_event_channel(&self) -> PlayerEventChannel {
433444
let (event_sender, event_receiver) = mpsc::unbounded_channel();
434445
self.command(PlayerCommand::AddEventSender(event_sender));
@@ -1264,10 +1275,6 @@ impl Future for PlayerInternal {
12641275
}
12651276
}
12661277

1267-
if self.session.is_invalid() {
1268-
return Poll::Ready(());
1269-
}
1270-
12711278
if (!self.state.is_playing()) && all_futures_completed_or_not_ready {
12721279
return Poll::Pending;
12731280
}
@@ -1515,10 +1522,15 @@ impl PlayerInternal {
15151522
fn handle_command_load(
15161523
&mut self,
15171524
track_id: SpotifyId,
1518-
play_request_id: u64,
1525+
play_request_id_option: Option<u64>,
15191526
play: bool,
15201527
position_ms: u32,
15211528
) -> PlayerResult {
1529+
let play_request_id =
1530+
play_request_id_option.unwrap_or(self.play_request_id_generator.get());
1531+
1532+
self.send_event(PlayerEvent::PlayRequestIdChanged { play_request_id });
1533+
15221534
if !self.config.gapless {
15231535
self.ensure_sink_stopped(play);
15241536
}
@@ -1771,7 +1783,7 @@ impl PlayerInternal {
17711783
{
17721784
return self.handle_command_load(
17731785
track_id,
1774-
play_request_id,
1786+
Some(play_request_id),
17751787
start_playback,
17761788
position_ms,
17771789
);
@@ -1828,10 +1840,9 @@ impl PlayerInternal {
18281840
match cmd {
18291841
PlayerCommand::Load {
18301842
track_id,
1831-
play_request_id,
18321843
play,
18331844
position_ms,
1834-
} => self.handle_command_load(track_id, play_request_id, play, position_ms)?,
1845+
} => self.handle_command_load(track_id, None, play, position_ms)?,
18351846

18361847
PlayerCommand::Preload { track_id } => self.handle_command_preload(track_id),
18371848

@@ -1843,6 +1854,8 @@ impl PlayerInternal {
18431854

18441855
PlayerCommand::Stop => self.handle_player_stop(),
18451856

1857+
PlayerCommand::SetSession(session) => self.session = session,
1858+
18461859
PlayerCommand::AddEventSender(sender) => self.event_senders.push(sender),
18471860

18481861
PlayerCommand::SetSinkEventCallback(callback) => self.sink_event_callback = callback,
@@ -2057,6 +2070,7 @@ impl fmt::Debug for PlayerCommand {
20572070
PlayerCommand::Pause => f.debug_tuple("Pause").finish(),
20582071
PlayerCommand::Stop => f.debug_tuple("Stop").finish(),
20592072
PlayerCommand::Seek(position) => f.debug_tuple("Seek").field(&position).finish(),
2073+
PlayerCommand::SetSession(_) => f.debug_tuple("SetSession").finish(),
20602074
PlayerCommand::AddEventSender(_) => f.debug_tuple("AddEventSender").finish(),
20612075
PlayerCommand::SetSinkEventCallback(_) => {
20622076
f.debug_tuple("SetSinkEventCallback").finish()

0 commit comments

Comments
 (0)