Set up a set of pipelines that mix two different file sources
This commit is contained in:
parent
6b245ac9a0
commit
7467e8d5b2
|
@ -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::<gstreamer::Object>();
|
||||
|
||||
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::<String>("current-device"));
|
||||
// println!("Sink target: {:?}", sink.property::<String>("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::<gstreamer::Object>();
|
||||
|
||||
/*
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue