132 lines
4.1 KiB
Rust
132 lines
4.1 KiB
Rust
|
use crate::{media::TrackId, Error, FatalError};
|
||
|
use flow::{ok, return_error, Flow};
|
||
|
use gstreamer::{format::ClockTime, prelude::*, MessageView, StateChangeError};
|
||
|
use std::{
|
||
|
path::PathBuf,
|
||
|
sync::mpsc::{channel, Receiver, Sender},
|
||
|
thread::{self, JoinHandle},
|
||
|
time::Duration,
|
||
|
};
|
||
|
use urlencoding::encode;
|
||
|
|
||
|
pub enum PlaybackControl {
|
||
|
PlayTrack(TrackId),
|
||
|
Stop,
|
||
|
Exit,
|
||
|
}
|
||
|
|
||
|
pub enum PlaybackStatus {
|
||
|
Stopped,
|
||
|
}
|
||
|
|
||
|
pub struct Playback {
|
||
|
handle: JoinHandle<Flow<(), FatalError, Error>>,
|
||
|
control_tx: Sender<PlaybackControl>,
|
||
|
}
|
||
|
|
||
|
impl Playback {
|
||
|
pub fn new() -> Playback {
|
||
|
let (control_tx, control_rx) = channel::<PlaybackControl>();
|
||
|
|
||
|
let handle = thread::spawn(move || {
|
||
|
let mut pipeline = None;
|
||
|
loop {
|
||
|
match control_rx.recv().unwrap() {
|
||
|
PlaybackControl::PlayTrack(id) => match play_track(id) {
|
||
|
Flow::Ok(pipeline_) => pipeline = Some(pipeline_),
|
||
|
Flow::Fatal(err) => panic!("fatal error: {:?}", err),
|
||
|
Flow::Err(err) => panic!("playback error: {:?}", err),
|
||
|
},
|
||
|
PlaybackControl::Stop => {
|
||
|
if let Some(ref pipeline) = pipeline {
|
||
|
return_error!(Flow::from(
|
||
|
pipeline
|
||
|
.set_state(gstreamer::State::Paused)
|
||
|
.map_err(|_| Error::CannotStop)
|
||
|
));
|
||
|
}
|
||
|
}
|
||
|
PlaybackControl::Exit => return ok(()),
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Self { handle, control_tx }
|
||
|
}
|
||
|
|
||
|
pub fn play_track(&self, id: TrackId) {
|
||
|
self.control_tx
|
||
|
.send(PlaybackControl::PlayTrack(id))
|
||
|
.unwrap();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn play_track(id: TrackId) -> Flow<gstreamer::Element, FatalError, Error> {
|
||
|
let pb = PathBuf::from(id.as_ref());
|
||
|
let path = pb
|
||
|
.iter()
|
||
|
.skip(1)
|
||
|
.map(|component| encode(&component.to_string_lossy()).into_owned())
|
||
|
.collect::<PathBuf>();
|
||
|
let playbin = format!("playbin uri=file:///{}", path.display());
|
||
|
println!("setting up to play {}", playbin);
|
||
|
let pipeline = return_error!(Flow::from(
|
||
|
gstreamer::parse_launch(&playbin).map_err(|err| Error::GlibError(err))
|
||
|
));
|
||
|
println!("ready to play");
|
||
|
return_error!(Flow::from(
|
||
|
pipeline
|
||
|
.set_state(gstreamer::State::Playing)
|
||
|
.map_err(|_| Error::CannotPlay)
|
||
|
));
|
||
|
println!("playing started");
|
||
|
ok(pipeline)
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
fn play_track(id: TrackId) -> Flow<(), FatalError, Error> {
|
||
|
let playbin = format!("playbin uri=file://{}", id.as_ref());
|
||
|
let pipeline = return_error!(Flow::from(
|
||
|
gstreamer::parse_launch(&playbin).map_err(|err| Error::GlibError(err))
|
||
|
));
|
||
|
return_error!(Flow::from(
|
||
|
pipeline
|
||
|
.set_state(gstreamer::State::Playing)
|
||
|
.map_err(|_| Error::CannotPlay)
|
||
|
));
|
||
|
|
||
|
let message_handler = {
|
||
|
let pipeline = pipeline.clone();
|
||
|
thread::spawn(move || {
|
||
|
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),
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
};
|
||
|
|
||
|
let query_handler = {
|
||
|
let pipeline = pipeline.clone();
|
||
|
thread::spawn(move || loop {
|
||
|
let position: Option<ClockTime> = pipeline.query_position();
|
||
|
let duration: Option<ClockTime> = pipeline.query_duration();
|
||
|
println!("Position {:?} {:?}", position, duration);
|
||
|
thread::sleep(Duration::from_millis(100));
|
||
|
})
|
||
|
};
|
||
|
|
||
|
ok(())
|
||
|
}
|
||
|
*/
|