Gapless playback works

This commit is contained in:
G2-Games 2023-12-05 12:59:52 -06:00
parent d87927a7db
commit f392e6a0af

View file

@ -1,13 +1,12 @@
// Crate things // Crate things
//use crate::music_controller::config::Config; //use crate::music_controller::config::Config;
use crate::music_storage::music_db::URI; use crate::music_storage::music_db::URI;
use std::error::Error;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{self, Receiver, Sender};
use crossbeam_channel::bounded; use crossbeam_channel::bounded;
use std::error::Error;
use std::sync::{Arc, RwLock};
// GStreamer things // GStreamer things
use glib::{FlagsClass, MainContext}; use glib::FlagsClass;
use gst::{ClockTime, Element}; use gst::{ClockTime, Element};
use gstreamer as gst; use gstreamer as gst;
use gstreamer::prelude::*; use gstreamer::prelude::*;
@ -34,7 +33,6 @@ pub struct Player {
start: Arc<RwLock<Option<Duration>>>, start: Arc<RwLock<Option<Duration>>>,
end: Arc<RwLock<Option<Duration>>>, end: Arc<RwLock<Option<Duration>>>,
pub position: Arc<RwLock<Option<Duration>>>, pub position: Arc<RwLock<Option<Duration>>>,
gapless: bool,
} }
impl Default for Player { impl Default for Player {
@ -48,7 +46,9 @@ impl Player {
// Initialize GStreamer // Initialize GStreamer
gst::init().unwrap(); gst::init().unwrap();
let playbin_arc = Arc::new(RwLock::new(gst::ElementFactory::make("playbin3").build().unwrap())); let playbin_arc = Arc::new(RwLock::new(
gst::ElementFactory::make("playbin3").build().unwrap(),
));
let playbin = playbin_arc.clone(); let playbin = playbin_arc.clone();
@ -66,7 +66,12 @@ impl Player {
.build() .build()
.unwrap(); .unwrap();
playbin.write().unwrap().set_property_from_value("flags", &flags); playbin
.write()
.unwrap()
.set_property_from_value("flags", &flags);
playbin.write().unwrap().set_property("instant-uri", true);
let position = Arc::new(RwLock::new(None)); let position = Arc::new(RwLock::new(None));
let start = Arc::new(RwLock::new(None)); let start = Arc::new(RwLock::new(None));
@ -88,7 +93,7 @@ impl Player {
&& start_update.read().unwrap().is_some() && start_update.read().unwrap().is_some()
&& end_update.read().unwrap().is_some() && end_update.read().unwrap().is_some()
{ {
let atf = end_update.read().unwrap().unwrap() - Duration::milliseconds(100); let atf = end_update.read().unwrap().unwrap() - Duration::milliseconds(250);
if pos_temp.unwrap() >= end_update.read().unwrap().unwrap() { if pos_temp.unwrap() >= end_update.read().unwrap().unwrap() {
message_tx.try_send(PlayerCmd::Eos).unwrap(); message_tx.try_send(PlayerCmd::Eos).unwrap();
playbin_arc playbin_arc
@ -99,10 +104,7 @@ impl Player {
*start_update.write().unwrap() = None; *start_update.write().unwrap() = None;
*end_update.write().unwrap() = None; *end_update.write().unwrap() = None;
} else if pos_temp.unwrap() >= atf { } else if pos_temp.unwrap() >= atf {
match message_tx.try_send(PlayerCmd::AboutToFinish) { let _ = message_tx.try_send(PlayerCmd::AboutToFinish);
Ok(_) => (),
Err(_) => (),
}
} }
// This has to be done AFTER the current time in the file // This has to be done AFTER the current time in the file
@ -118,13 +120,6 @@ impl Player {
} }
}); });
/*
playbin.read().unwrap().connect("about-to-finish", false, move |_| {
//message_tx.send(PlayerCmd::AboutToFinish).unwrap();
None
});
*/
let source = None; let source = None;
Self { Self {
source, source,
@ -132,7 +127,6 @@ impl Player {
message_rx, message_rx,
paused: false, paused: false,
volume: 1.0, volume: 1.0,
gapless: false,
start, start,
end, end,
position, position,
@ -144,23 +138,22 @@ impl Player {
} }
pub fn enqueue_next(&mut self, next_track: &URI) { pub fn enqueue_next(&mut self, next_track: &URI) {
self.ready().unwrap();
self.set_source(next_track); self.set_source(next_track);
self.play().unwrap();
} }
/// Set the playback URI /// Set the playback URI
fn set_source(&mut self, source: &URI) { fn set_source(&mut self, source: &URI) {
let uri = self.playbin.read().unwrap().property_value("current-uri");
self.source = Some(source.clone()); self.source = Some(source.clone());
match source { match source {
URI::Cue {start, end, ..} => { URI::Cue { start, end, .. } => {
self.playbin.write().unwrap().set_property("uri", source.as_uri()); self.playbin.write().unwrap().set_property("uri", source.as_uri());
// Set the start and end positions of the CUE file // Set the start and end positions of the CUE file
*self.start.write().unwrap() = Some(Duration::from_std(*start).unwrap()); *self.start.write().unwrap() = Some(Duration::from_std(*start).unwrap());
*self.end.write().unwrap() = Some(Duration::from_std(*end).unwrap()); *self.end.write().unwrap() = Some(Duration::from_std(*end).unwrap());
self.pause().unwrap(); self.play().unwrap();
// Wait for it to be ready, and then move to the proper position // Wait for it to be ready, and then move to the proper position
let now = std::time::Instant::now(); let now = std::time::Instant::now();
@ -171,23 +164,21 @@ impl Player {
std::thread::sleep(std::time::Duration::from_millis(1)); std::thread::sleep(std::time::Duration::from_millis(1));
} }
panic!("Couldn't seek to beginning of cue track in reasonable time (>20ms)"); panic!("Couldn't seek to beginning of cue track in reasonable time (>20ms)");
}, }
_ => { _ => {
self.playbin.write().unwrap().set_property("uri", source.as_uri()); self.playbin.write().unwrap().set_property("uri", source.as_uri());
self.pause().unwrap(); self.play().unwrap();
while self.playbin.read().unwrap().query_duration::<ClockTime>().is_none() { while uri.get::<&str>().unwrap_or("") == self.property("current-uri").get::<&str>().unwrap_or("")
std::thread::sleep(std::time::Duration::from_millis(1)); || self.raw_duration().is_none()
}; {
std::thread::sleep(std::time::Duration::from_millis(10));
}
*self.start.write().unwrap() = Some(Duration::seconds(0)); *self.start.write().unwrap() = Some(Duration::seconds(0));
*self.end.write().unwrap() = self.playbin *self.end.write().unwrap() = self.raw_duration();
.read() }
.unwrap()
.query_duration::<ClockTime>()
.map(|pos| Duration::nanoseconds(pos.nseconds() as i64));
},
} }
} }
@ -209,10 +200,7 @@ impl Player {
} }
fn set_state(&mut self, state: gst::State) -> Result<(), gst::StateChangeError> { fn set_state(&mut self, state: gst::State) -> Result<(), gst::StateChangeError> {
self.playbin self.playbin.write().unwrap().set_state(state)?;
.write()
.unwrap()
.set_state(state)?;
Ok(()) Ok(())
} }
@ -253,14 +241,18 @@ impl Player {
if self.end.read().unwrap().is_some() && self.start.read().unwrap().is_some() { if self.end.read().unwrap().is_some() && self.start.read().unwrap().is_some() {
Some(self.end.read().unwrap().unwrap() - self.start.read().unwrap().unwrap()) Some(self.end.read().unwrap().unwrap() - self.start.read().unwrap().unwrap())
} else { } else {
self.playbin self.raw_duration()
.read()
.unwrap()
.query_duration::<ClockTime>()
.map(|pos| Duration::nanoseconds(pos.nseconds() as i64))
} }
} }
pub fn raw_duration(&self) -> Option<Duration> {
self.playbin
.read()
.unwrap()
.query_duration::<ClockTime>()
.map(|pos| Duration::nanoseconds(pos.nseconds() as i64))
}
/// Seek relative to the current position /// Seek relative to the current position
pub fn seek_by(&mut self, seek_amount: Duration) -> Result<(), Box<dyn Error>> { pub fn seek_by(&mut self, seek_amount: Duration) -> Result<(), Box<dyn Error>> {
let time_pos = match *self.position.read().unwrap() { let time_pos = match *self.position.read().unwrap() {
@ -275,7 +267,19 @@ impl Player {
/// Seek absolutely /// Seek absolutely
pub fn seek_to(&mut self, target_pos: Duration) -> Result<(), Box<dyn Error>> { pub fn seek_to(&mut self, target_pos: Duration) -> Result<(), Box<dyn Error>> {
let seek_pos_clock = ClockTime::from_useconds(target_pos.num_microseconds().unwrap() as u64); if self.start.read().unwrap().is_none() {
return Err("Failed to seek: No START time".into());
}
if self.end.read().unwrap().is_none() {
return Err("Failed to seek: No END time".into());
}
let clamped_target = target_pos.clamp(self.start.read().unwrap().unwrap(), self.end.read().unwrap().unwrap());
let seek_pos_clock =
ClockTime::from_useconds(clamped_target.num_microseconds().unwrap() as u64);
self.set_gstreamer_volume(0.0); self.set_gstreamer_volume(0.0);
self.playbin self.playbin
.write() .write()
@ -288,6 +292,10 @@ impl Player {
pub fn state(&mut self) -> gst::State { pub fn state(&mut self) -> gst::State {
self.playbin.read().unwrap().current_state() self.playbin.read().unwrap().current_state()
} }
pub fn property(&self, property: &str) -> glib::Value {
self.playbin.read().unwrap().property_value(property)
}
} }
impl Drop for Player { impl Drop for Player {