use crate::{media::TrackId, Error, FatalError}; use flow::{ok, return_error, Flow}; use gstreamer::{format::ClockTime, prelude::*, MessageView, StateChangeError}; use std::{ path::PathBuf, thread::{self, JoinHandle}, time::Duration, }; use tokio::sync::mpsc::{channel, Receiver, Sender}; use urlencoding::encode; pub enum PlaybackControl { PlayTrack(TrackId), Stop, Exit, } pub enum PlaybackStatus { Stopped, } pub struct Playback { events_handle: tokio::task::JoinHandle<()>, pipeline: gstreamer::Element, } impl Playback { pub fn new(id: TrackId) -> Result { let pb = PathBuf::from(id.as_ref()); let path = pb .iter() .skip(1) .map(|component| encode(&component.to_string_lossy()).into_owned()) .collect::(); let pipeline = gstreamer::parse_launch(&format!("playbin uri=file:///{}", path.display())) .map_err(|err| Error::GlibError(err))?; pipeline .set_state(gstreamer::State::Playing) .map_err(|err| Error::CannotPlay(err.to_string()))?; let events_handle = tokio::task::spawn_blocking({ let pipeline = pipeline.clone(); move || pipeline_events(pipeline) }); Ok(Self { events_handle, pipeline, }) } pub fn stop(&self) -> Result<(), Error> { self.events_handle.abort(); self.pipeline .set_state(gstreamer::State::Paused) .map_err(|_| Error::CannotStop)?; Ok(()) } pub fn position(&self) -> (Option, Option) { let position = self.pipeline.query_position(); let duration = self.pipeline.query_duration(); (position, duration) } } impl Drop for Playback { fn drop(&mut self) { self.stop(); } } fn pipeline_events(pipeline: gstreamer::Element) { let bus = pipeline.bus().unwrap(); for msg in bus.iter_timed(gstreamer::ClockTime::NONE) { match msg.view() { MessageView::Eos(_) => (), MessageView::Error(err) => { println!( "Error from {:?}: {} ({:?})", err.src().map(|s| s.path_string()), err.error(), err.debug() ); } msg => println!("{:?}", msg), } } }