2020-05-27 12:41:02 +00:00
|
|
|
// This example demonstrates how custom application-specific events can be
|
|
|
|
// created, sent in a pipeline and retrieved later.
|
|
|
|
//
|
|
|
|
// It uses a queue that contains several seconds worth of data. When the event
|
|
|
|
// is sent on the sink pad, we expect to see it emerge on the other side when
|
|
|
|
// the data in front of it has exited.
|
|
|
|
|
|
|
|
use gst::prelude::*;
|
|
|
|
|
|
|
|
#[path = "../examples-common.rs"]
|
|
|
|
mod examples_common;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ExampleCustomEvent {
|
|
|
|
pub send_eos: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExampleCustomEvent {
|
|
|
|
const EVENT_NAME: &'static str = "example-custom-event";
|
|
|
|
|
2020-06-25 16:22:25 +00:00
|
|
|
#[allow(clippy::new_ret_no_self)]
|
|
|
|
pub fn new(send_eos: bool) -> gst::Event {
|
2020-05-27 12:41:02 +00:00
|
|
|
let s = gst::Structure::builder(Self::EVENT_NAME)
|
2022-11-01 09:10:57 +00:00
|
|
|
.field("send_eos", send_eos)
|
2020-05-27 12:41:02 +00:00
|
|
|
.build();
|
2020-06-23 15:52:51 +00:00
|
|
|
gst::event::CustomDownstream::new(s)
|
2020-05-27 12:41:02 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 16:22:25 +00:00
|
|
|
pub fn parse(ev: &gst::EventRef) -> Option<ExampleCustomEvent> {
|
2020-05-27 12:41:02 +00:00
|
|
|
match ev.view() {
|
|
|
|
gst::EventView::CustomDownstream(e) => {
|
2021-04-11 19:39:50 +00:00
|
|
|
let s = match e.structure() {
|
|
|
|
Some(s) if s.name() == Self::EVENT_NAME => s,
|
2020-05-27 12:41:02 +00:00
|
|
|
_ => return None, // No structure in this event, or the name didn't match
|
|
|
|
};
|
|
|
|
|
2021-04-20 07:19:02 +00:00
|
|
|
let send_eos = s.get::<bool>("send_eos").unwrap();
|
2020-05-27 12:41:02 +00:00
|
|
|
Some(ExampleCustomEvent { send_eos })
|
|
|
|
}
|
|
|
|
_ => None, // Not a custom event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn example_main() {
|
|
|
|
gst::init().unwrap();
|
|
|
|
|
|
|
|
let main_loop = glib::MainLoop::new(None, false);
|
|
|
|
|
|
|
|
// This creates a pipeline by parsing the gst-launch pipeline syntax.
|
|
|
|
let pipeline = gst::parse_launch(
|
|
|
|
"audiotestsrc name=src ! queue max-size-time=2000000000 ! fakesink name=sink sync=true",
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-04-11 19:39:50 +00:00
|
|
|
let bus = pipeline.bus().unwrap();
|
2020-05-27 12:41:02 +00:00
|
|
|
|
|
|
|
pipeline
|
|
|
|
.set_state(gst::State::Playing)
|
|
|
|
.expect("Unable to set the pipeline to the `Playing` state");
|
|
|
|
let pipeline = pipeline.dynamic_cast::<gst::Pipeline>().unwrap();
|
|
|
|
|
2021-04-20 10:24:17 +00:00
|
|
|
let sink = pipeline.by_name("sink").unwrap();
|
|
|
|
let sinkpad = sink.static_pad("sink").unwrap();
|
2020-05-27 12:41:02 +00:00
|
|
|
|
|
|
|
// Need to move a new reference into the closure.
|
|
|
|
// !!ATTENTION!!:
|
|
|
|
// It might seem appealing to use pipeline.clone() here, because that greatly
|
|
|
|
// simplifies the code within the callback. What this actually does, however, is creating
|
|
|
|
// a memory leak. The clone of a pipeline is a new strong reference on the pipeline.
|
|
|
|
// Storing this strong reference of the pipeline within the callback (we are moving it in!),
|
|
|
|
// which is in turn stored in another strong reference on the pipeline is creating a
|
|
|
|
// reference cycle.
|
|
|
|
// DO NOT USE pipeline.clone() TO USE THE PIPELINE WITHIN A CALLBACK
|
|
|
|
let pipeline_weak = pipeline.downgrade();
|
|
|
|
// Add a pad probe on the sink pad and catch the custom event we sent, then send
|
|
|
|
// an EOS event on the pipeline.
|
|
|
|
sinkpad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, move |_, probe_info| {
|
|
|
|
match probe_info.data {
|
|
|
|
Some(gst::PadProbeData::Event(ref ev))
|
2021-04-11 19:39:50 +00:00
|
|
|
if ev.type_() == gst::EventType::CustomDownstream =>
|
2020-05-27 12:41:02 +00:00
|
|
|
{
|
2020-06-25 16:22:25 +00:00
|
|
|
if let Some(custom_event) = ExampleCustomEvent::parse(ev) {
|
2020-05-27 12:41:02 +00:00
|
|
|
if let Some(pipeline) = pipeline_weak.upgrade() {
|
|
|
|
if custom_event.send_eos {
|
|
|
|
/* Send EOS event to shut down the pipeline, but from an async callback, as we're
|
|
|
|
* in a pad probe blocking the stream thread here... */
|
|
|
|
println!("Got custom event with send_eos=true. Sending EOS");
|
2020-06-23 15:52:51 +00:00
|
|
|
let ev = gst::event::Eos::new();
|
2020-05-27 12:41:02 +00:00
|
|
|
let pipeline_weak = pipeline_weak.clone();
|
|
|
|
pipeline.call_async(move |_| {
|
|
|
|
if let Some(pipeline) = pipeline_weak.upgrade() {
|
|
|
|
pipeline.send_event(ev);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
println!("Got custom event, with send_eos=false. Ignoring");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
gst::PadProbeReturn::Ok
|
|
|
|
});
|
|
|
|
|
|
|
|
println!("Pipeline is running. Waiting 2 seconds");
|
|
|
|
|
|
|
|
/* Send 2 events into the pipeline - one with send_eos = false, followed
|
|
|
|
* by 1 with send_eos = true. Use a timeout event to send them in a few seconds
|
|
|
|
* when the pipeline has filled. */
|
|
|
|
for (i, send_eos) in [false, true].iter().enumerate() {
|
|
|
|
let pipeline_weak = pipeline.downgrade();
|
|
|
|
glib::timeout_add_seconds(2 + i as u32, move || {
|
|
|
|
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
|
|
|
// we moved into this callback.
|
|
|
|
let pipeline = match pipeline_weak.upgrade() {
|
|
|
|
Some(pipeline) => pipeline,
|
2023-07-06 13:15:14 +00:00
|
|
|
None => return glib::ControlFlow::Break,
|
2020-05-27 12:41:02 +00:00
|
|
|
};
|
2023-01-25 08:09:45 +00:00
|
|
|
println!("Sending custom event to the pipeline with send_eos={send_eos}");
|
2020-06-25 16:22:25 +00:00
|
|
|
let ev = ExampleCustomEvent::new(*send_eos);
|
2020-05-27 12:41:02 +00:00
|
|
|
if !pipeline.send_event(ev) {
|
|
|
|
println!("Warning: Failed to send custom event");
|
|
|
|
}
|
|
|
|
// Remove this handler, the pipeline will shutdown once our pad probe catches the custom
|
|
|
|
// event and sends EOS
|
2023-07-06 13:15:14 +00:00
|
|
|
glib::ControlFlow::Break
|
2020-05-27 12:41:02 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let main_loop_clone = main_loop.clone();
|
|
|
|
// This sets the bus's signal handler (don't be mislead by the "add", there can only be one).
|
|
|
|
// Every message from the bus is passed through this function. Its returnvalue determines
|
2023-07-06 13:15:14 +00:00
|
|
|
// whether the handler wants to be called again. If glib::ControlFlow::Break is returned, the
|
2020-05-27 12:41:02 +00:00
|
|
|
// handler is removed and will never be called again. The mainloop still runs though.
|
2023-04-12 08:28:44 +00:00
|
|
|
let _bus_watch = bus
|
|
|
|
.add_watch(move |_, msg| {
|
|
|
|
use gst::MessageView;
|
|
|
|
|
|
|
|
let main_loop = &main_loop_clone;
|
|
|
|
match msg.view() {
|
|
|
|
MessageView::Eos(..) => {
|
|
|
|
println!("received eos");
|
|
|
|
// An EndOfStream event was sent to the pipeline, so we tell our main loop
|
|
|
|
// to stop execution here.
|
|
|
|
main_loop.quit()
|
|
|
|
}
|
|
|
|
MessageView::Error(err) => {
|
|
|
|
println!(
|
|
|
|
"Error from {:?}: {} ({:?})",
|
|
|
|
err.src().map(|s| s.path_string()),
|
|
|
|
err.error(),
|
|
|
|
err.debug()
|
|
|
|
);
|
|
|
|
main_loop.quit();
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
};
|
2020-05-27 12:41:02 +00:00
|
|
|
|
2023-04-12 08:28:44 +00:00
|
|
|
// Tell the mainloop to continue executing this callback.
|
2023-07-06 13:15:14 +00:00
|
|
|
glib::ControlFlow::Continue
|
2023-04-12 08:28:44 +00:00
|
|
|
})
|
|
|
|
.expect("Failed to add bus watch");
|
2020-05-27 12:41:02 +00:00
|
|
|
|
|
|
|
// Operate GStreamer's bus, facilitating GLib's mainloop here.
|
|
|
|
// This function call will block until you tell the mainloop to quit
|
|
|
|
// (see above for how to do this).
|
|
|
|
main_loop.run();
|
|
|
|
|
|
|
|
pipeline
|
|
|
|
.set_state(gst::State::Null)
|
|
|
|
.expect("Unable to set the pipeline to the `Null` state");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2021-04-10 11:42:04 +00:00
|
|
|
// tutorials_common::run is only required to set up the application environment on macOS
|
|
|
|
// (but not necessary in normal Cocoa applications where this is set up automatically)
|
2020-05-27 12:41:02 +00:00
|
|
|
examples_common::run(example_main);
|
|
|
|
}
|