Input: Allow Restartable sources to be lazy

This change is made with queue users in mind. Since sources
of this kind *know* how to (re)create themselves, they can
avoid being created at all until needed.

This also adds machinery to preload tracks *before* they are
needed, for gapless playback on queues and so on. Queues
make use of the event system to do this.
This commit is contained in:
Kyle Simpson
2020-12-28 17:02:10 +00:00
parent c0d3cb3113
commit 03ae0e7628
10 changed files with 368 additions and 101 deletions

View File

@@ -6,7 +6,7 @@ use crate::{
};
use async_trait::async_trait;
use parking_lot::Mutex;
use std::{collections::VecDeque, ops::Deref, sync::Arc};
use std::{collections::VecDeque, ops::Deref, sync::Arc, time::Duration};
use tracing::{info, warn};
/// A simple queue for several audio sources, designed to
@@ -145,6 +145,23 @@ impl EventHandler for QueueHandler {
}
}
struct SongPreloader {
remote_lock: Arc<Mutex<TrackQueueCore>>,
}
#[async_trait]
impl EventHandler for SongPreloader {
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
let inner = self.remote_lock.lock();
if let Some(track) = inner.tracks.get(1) {
let _ = track.0.make_playable();
}
None
}
}
impl TrackQueue {
/// Create a new, empty, track queue.
pub fn new() -> Self {
@@ -194,6 +211,23 @@ impl TrackQueue {
track.position,
);
// Attempts to start loading the next track before this one ends.
// Idea is to provide as close to gapless playback as possible,
// while minimising memory use.
if let Some(time) = track.source.metadata.duration {
let preload_time = time.checked_sub(Duration::from_secs(5)).unwrap_or_default();
let remote_lock = self.inner.clone();
track
.events
.as_mut()
.expect("Queue inspecting EventStore on new Track: did not exist.")
.add_event(
EventData::new(Event::Delayed(preload_time), SongPreloader { remote_lock }),
track.position,
);
}
inner.tracks.push_back(Queued(track_handle));
}