use gstreamer::{prelude::*, ClockTime, MessageType, MessageView}; use std::sync::{Arc, RwLock}; pub struct AudioControl { bus: gstreamer::Bus, pipeline: gstreamer::Pipeline, mixer: gstreamer::Element, audio_sink: gstreamer::Element, bus_monitor: std::thread::JoinHandle<()>, playing: Arc<RwLock<bool>>, } impl Default for AudioControl { fn default() -> Self { let pipeline = gstreamer::Pipeline::new(); let bus = pipeline.bus().unwrap(); let mixer = gstreamer::ElementFactory::find("audiomixer") .unwrap() .load() .unwrap() .create() .build() .unwrap(); pipeline.add(&mixer).unwrap(); let audio_sink = gstreamer::ElementFactory::find("pulsesink") .unwrap() .load() .unwrap() .create() .build() .unwrap(); pipeline.add(&audio_sink).unwrap(); mixer.link(&audio_sink).unwrap(); let playing = Arc::new(RwLock::new(false)); let bus_monitor = std::thread::spawn({ let pipeline_object = pipeline.clone().upcast::<gstreamer::Object>(); let playing = playing.clone(); let bus = bus.clone(); move || loop { if let Some(msg) = bus.timed_pop_filtered( ClockTime::NONE, &[ MessageType::Error, MessageType::Eos, MessageType::StateChanged, ], ) { match msg.view() { MessageView::StateChanged(st) => { if msg.src() == Some(&pipeline_object) { *playing.write().unwrap() = st.current() == gstreamer::State::Playing; } } MessageView::Error(err) => { println!("error: {:?}", err); } MessageView::Eos(_) => { println!("EOS"); } _ => { unreachable!(); } } } } }); Self { bus, pipeline, mixer, audio_sink, bus_monitor, playing, } } } impl AudioControl { pub fn playing(&self) -> bool { *self.playing.read().unwrap() } pub fn play(&self) { let mut playing = self.playing.write().unwrap(); if !*playing { // self.pipeline.set_state(gstreamer::State::Playing).unwrap(); *playing = true; } } pub fn stop(&self) { let mut playing = self.playing.write().unwrap(); if *playing { // self.pipeline.set_state(gstreamer::State::Paused).unwrap(); *playing = false; } } pub fn add_track(&mut self, path: String) { let source = gstreamer::ElementFactory::find("filesrc") .unwrap() .load() .unwrap() .create() .property("location", path) .build() .unwrap(); self.pipeline.add(&source).unwrap(); let decoder = gstreamer::ElementFactory::find("decodebin") .unwrap() .load() .unwrap() .create() .build() .unwrap(); self.pipeline.add(&decoder).unwrap(); source.link(&decoder).unwrap(); let volume = gstreamer::ElementFactory::find("volume") .unwrap() .load() .unwrap() .create() .property("mute", false) .property("volume", 0.75) .build() .unwrap(); self.pipeline.add(&volume).unwrap(); volume.link(&self.mixer).unwrap(); decoder.connect_pad_added(move |_, pad| { let next_pad = volume.static_pad("sink").unwrap(); pad.link(&next_pad).unwrap(); }); } pub fn remove_track(&mut self, path: String) { /* Need to run EOS through to a probe on the trailing end of the volume element */ } }