mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-04-19 10:02:53 -05:00
Gapless playback works
This commit is contained in:
parent
d87927a7db
commit
f392e6a0af
1 changed files with 53 additions and 45 deletions
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue