From 7467e8d5b208e3c9a7add07191743c66f0033336 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 28 Aug 2024 21:59:39 -0400 Subject: [PATCH] Set up a set of pipelines that mix two different file sources --- gm-dash/server/src/bin/play-track.rs | 257 ++++++++++++--------------- 1 file changed, 114 insertions(+), 143 deletions(-) diff --git a/gm-dash/server/src/bin/play-track.rs b/gm-dash/server/src/bin/play-track.rs index f3f7d9b..c02c576 100644 --- a/gm-dash/server/src/bin/play-track.rs +++ b/gm-dash/server/src/bin/play-track.rs @@ -1,115 +1,75 @@ use std::time::Duration; use gstreamer::{ - prelude::*, Bus, Element, EventType, MessageType, MessageView, Pad, PadProbeData, PadProbeInfo, PadProbeReturn, Pipeline + prelude::*, Bus, Element, EventType, MessageType, MessageView, Pad, PadDirection, PadPresence, + PadProbeData, PadProbeInfo, PadProbeReturn, PadTemplate, Pipeline, }; use pipewire::{context::Context, main_loop::MainLoop}; fn main() { gstreamer::init(); - // let srcfactory = gstreamer::ElementFactory::find("filesrc").unwrap().load().unwrap(); - let source = gstreamer::ElementFactory::find("filesrc") - .unwrap() - .load() - .unwrap() - .create() - .name("source") - .property("location", "/home/savanni/Music/tavern-music.ogg") - // .property( - // "location", - // "/home/savanni/Music/Night at Work _ Instrumental Chill Music Mix [n9Y2Eb4BaSg].m4a", - // ) - .build() - .unwrap(); - let decoder = gstreamer::ElementFactory::find("decodebin") - .unwrap() - .load() - .unwrap() - .create() - .name("decoder") - .build() - .unwrap(); + let pipeline = gstreamer::Pipeline::new(); + let pipeline_object = pipeline.clone().upcast::(); + let sinkfactory = gstreamer::ElementFactory::find("pulsesink") .unwrap() .load() .unwrap(); - let defaultsink = sinkfactory.create().name("sink").build().unwrap(); + let audio_template = sinkfactory + .static_pad_templates() + .iter() + .next() + .map(|template| template.get()) + .unwrap(); + + let audio_output = sinkfactory.create().name("sink").build().unwrap(); + pipeline.add(&audio_output).unwrap(); + + let funnel = gstreamer::ElementFactory::find("audiomixer") + .unwrap() + .load() + .unwrap() + .create() + .build() + .unwrap(); + pipeline.add(&funnel).unwrap(); + + let convert = gstreamer::ElementFactory::find("audioconvert") + .unwrap() + .load() + .unwrap() + .create() + .build() + .unwrap(); + pipeline.add(&convert).unwrap(); + funnel.link(&convert).unwrap(); + convert.link(&audio_output).unwrap(); + + /* + setup_file_reader( + &pipeline, + funnel.clone(), + "/home/savanni/Music/technical-station.ogg", + ); + */ + setup_file_reader(&pipeline, funnel.clone(), "/home/savanni/Music/techno-city-day.ogg"); + + let bus = pipeline.bus().unwrap(); + + /* let btsink = sinkfactory .create() .name("sink") .property("device", "bluez_output.0C_A6_94_75_6E_8F.1") .build() .unwrap(); - let convert = gstreamer::ElementFactory::find("audioconvert") - .unwrap() - .load() - .unwrap() - .create() - .name("convert") - .build() - .unwrap(); - let resample = gstreamer::ElementFactory::find("audioresample") - .unwrap() - .load() - .unwrap() - .create() - .name("resample") - .build() - .unwrap(); - - let pipeline = gstreamer::Pipeline::new(); - pipeline.add(&source).unwrap(); - pipeline.add(&defaultsink).unwrap(); - pipeline.add(&decoder).unwrap(); - pipeline.add(&convert).unwrap(); - pipeline.add(&resample).unwrap(); - - source.link(&decoder).unwrap(); - convert.link(&resample).unwrap(); - resample.link(&defaultsink).unwrap(); - - decoder.connect_pad_added(move |element, pad| handle_pad_added(element, pad, &convert)); - - // println!("Sink target: {:?}", sink.property::("current-device")); - // println!("Sink target: {:?}", sink.property::("device-name")); - - /* - println!("source: {:?}", source); - source.foreach_pad(|_, pad| { - println!("\tpad: {:?} {}, {:?}", pad, pad.name(), pad.direction()); - true - }); - let source_pad = source.get_pad(pad.name()); - source_pad - */ - - // source.connect_pad_added(handle_pad_added); - // source.link(&convert).unwrap(); + */ pipeline.set_state(gstreamer::State::Playing).unwrap(); let pipeline_object = pipeline.clone().upcast::(); /* - std::thread::sleep(Duration::from_secs(5)); - println!("switching audio outputs"); - - pipeline.set_state(gstreamer::State::Paused).unwrap(); - resample.unlink(&defaultsink); - pipeline.remove(&defaultsink).unwrap(); - pipeline.add(&btsink).unwrap(); - resample.link(&btsink).unwrap(); - pipeline.set_state(gstreamer::State::Playing).unwrap(); - println!("switch complete"); - */ - - let bus = pipeline.bus().unwrap(); - // let msg = bus.timed_pop_filtered( - // gstreamer::ClockTime::NONE, - // &[gstreamer::MessageType::Error, gstreamer::MessageType::Eos], - // ); - /* - */ std::thread::spawn({ let bus = bus.clone(); let pipeline = pipeline.clone(); @@ -118,6 +78,9 @@ fn main() { swap_audio_output(bus, pipeline, resample, defaultsink, btsink); } }); + */ + + pipeline.set_state(gstreamer::State::Playing).unwrap(); let mut playing = false; @@ -163,67 +126,75 @@ fn main() { pipeline.set_state(gstreamer::State::Null).unwrap(); } -fn handle_pad_added(element: &Element, pad: &Pad, converter: &Element) { +fn handle_pad_added(element: &Element, pad: &Pad, next_element: &Element, template: &PadTemplate) { println!("handle_pad_added"); println!("\t{:?}", element); println!("\t{:?}, {:?}", pad, pad.current_caps()); - let converter_pad = converter.static_pad("sink").unwrap(); - pad.link(&converter_pad).unwrap(); -} - -fn swap_audio_output( - bus: Bus, - pipeline: Pipeline, - upstream: Element, - oldoutput: Element, - newoutput: Element, -) { - let source_pad = upstream.static_pad("src").unwrap(); - source_pad.add_probe(gstreamer::PadProbeType::BLOCK, move |pad, info| { - println!("upstream pad blocked"); - /* - if let Some(probe_id) = info.id { - pad.remove_probe(probe_id); - } + /* + let audio_caps = gstreamer::caps::Caps::builder() + .field("audio", "audio/x-raw, + .build(); */ - - /* - if let Some(old_element_output) = oldelement.static_pad("src") { - old_element_output.add_probe(gstreamer::PadProbeType::BLOCK, wait_for_eos); - let old_element_input = oldelement.static_pad("sink").unwrap(); - old_element_input.send_event(gstreamer::event::Eos::new()); - } else { - /* This is a final sink, so we don't have to wait for EOS to go through. */ - } - */ - let oldoutput_pad = oldoutput.static_pad("sink").unwrap(); - // oldoutput_pad.send_event(gstreamer::event::Eos::new()); - // oldoutput.set_state(gstreamer::State::Null).unwrap(); - pipeline.remove(&oldoutput).unwrap(); - - pipeline.add(&newoutput).unwrap(); - upstream.link(&newoutput).unwrap(); - - PadProbeReturn::Remove - }); + /* + let audio_pad_template = PadTemplate::new( + "audio-pad-template", + PadDirection::Sink, + PadPresence::Request, + &pad.current_caps().unwrap(), + ) + .unwrap(); + */ + let next_pad = next_element.request_pad(template, None, None).unwrap(); + // let converter_pad = converter.static_pad("sink").unwrap(); + pad.link(&next_pad).unwrap(); } -fn wait_for_eos(pad: &Pad, info: &mut PadProbeInfo) -> PadProbeReturn { -/* - match &info.data { - Some(PadProbeData::Event(event)) if event.type_() == EventType::Eos => { - /* Do a lot of things now that this pad is empty of data. */ - element.set_state(gstreamer::State::Null); - pipeline.remove(element); - pipeline.add(new_element); - let new_pad = new_element.static_pad("src"); - pad.link(new_pad); +fn setup_file_reader(pipeline: &Pipeline, dest: Element, path: &str) { + let source = gstreamer::ElementFactory::find("filesrc") + .unwrap() + .load() + .unwrap() + .create() + .property("location", path) + .build() + .unwrap(); - println!("EOS detected"); - } - _ => return PadProbeReturn::Ok, - }; -*/ + let decoder = gstreamer::ElementFactory::find("decodebin") + .unwrap() + .load() + .unwrap() + .create() + .build() + .unwrap(); - return PadProbeReturn::Ok; + let volume = gstreamer::ElementFactory::find("volume") + .unwrap() + .load() + .unwrap() + .create() + .property("mute", false) + .property("volume", 0.5) + .build() + .unwrap(); + + pipeline.add(&source).unwrap(); + pipeline.add(&decoder).unwrap(); + pipeline.add(&volume).unwrap(); + + source.link(&decoder).unwrap(); + + let next_pad = dest.request_pad_simple("sink_%u").unwrap(); + let volume_output = volume.static_pad("src").unwrap(); + volume_output.link(&next_pad).unwrap(); + + decoder.connect_pad_added( + move |element, pad| handle_decoder_started(element, pad, volume.clone()) + ); +} + +fn handle_decoder_started(_: &Element, pad: &Pad, next: Element) { + println!("connecting file decoder to converter stream"); + // let next_pad = next.request_pad_simple("sink_%u").unwrap(); + let next_pad = next.static_pad("sink").unwrap(); + pad.link(&next_pad).unwrap(); }