Fix reference cycles and minor related problems in all examples and tutorials

These are now all leak-free.
This commit is contained in:
Sebastian Dröge 2018-07-27 13:07:24 +03:00
parent ec8b55ec30
commit c0422acf66
18 changed files with 222 additions and 88 deletions

View file

@ -47,7 +47,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?; src.link(&sink)?;
let appsink = sink.clone() let appsink = sink
.dynamic_cast::<gst_app::AppSink>() .dynamic_cast::<gst_app::AppSink>()
.expect("Sink element is expected to be an appsink!"); .expect("Sink element is expected to be an appsink!");

View file

@ -46,7 +46,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
pipeline.add_many(&[&src, &videoconvert, &sink])?; pipeline.add_many(&[&src, &videoconvert, &sink])?;
gst::Element::link_many(&[&src, &videoconvert, &sink])?; gst::Element::link_many(&[&src, &videoconvert, &sink])?;
let appsrc = src.clone() let appsrc = src
.dynamic_cast::<gst_app::AppSrc>() .dynamic_cast::<gst_app::AppSrc>()
.expect("Source element is expected to be an appsrc!"); .expect("Source element is expected to be an appsrc!");

View file

@ -53,10 +53,12 @@ fn example_main() -> Result<(), Error> {
pipeline.add_many(&[&src, &decodebin])?; pipeline.add_many(&[&src, &decodebin])?;
gst::Element::link_many(&[&src, &decodebin])?; gst::Element::link_many(&[&src, &decodebin])?;
// Need to move a new reference into the closure let pipeline_weak = pipeline.downgrade();
let pipeline_clone = pipeline.clone();
decodebin.connect_pad_added(move |dbin, src_pad| { decodebin.connect_pad_added(move |dbin, src_pad| {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return,
};
let (is_audio, is_video) = { let (is_audio, is_video) = {
let media_type = src_pad.get_current_caps().and_then(|caps| { let media_type = src_pad.get_current_caps().and_then(|caps| {

View file

@ -17,11 +17,12 @@ fn example_main() {
let ret = pipeline.set_state(gst::State::Playing); let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
let main_loop_clone = main_loop.clone(); let pipeline_weak = pipeline.downgrade();
let pipeline_clone = pipeline.clone();
glib::timeout_add_seconds(5, move || { glib::timeout_add_seconds(5, move || {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return glib::Continue(false),
};
println!("sending eos"); println!("sending eos");
@ -33,6 +34,7 @@ fn example_main() {
//bus.add_signal_watch(); //bus.add_signal_watch();
//bus.connect_message(move |_, msg| { //bus.connect_message(move |_, msg| {
let main_loop_clone = main_loop.clone();
bus.add_watch(move |_, msg| { bus.add_watch(move |_, msg| {
use gst::MessageView; use gst::MessageView;
@ -61,6 +63,8 @@ fn example_main() {
let ret = pipeline.set_state(gst::State::Null); let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
bus.remove_watch();
} }
fn main() { fn main() {

View file

@ -10,9 +10,7 @@ extern crate gtk;
use gtk::prelude::*; use gtk::prelude::*;
use std::env; use std::env;
use std::cell::RefCell;
extern crate send_cell;
use send_cell::SendCell;
fn create_ui(app: &gtk::Application) { fn create_ui(app: &gtk::Application) {
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
@ -45,9 +43,13 @@ fn create_ui(app: &gtk::Application) {
app.add_window(&window); app.add_window(&window);
let pipeline_clone = pipeline.clone(); let pipeline_weak = pipeline.downgrade();
gtk::timeout_add(500, move || { let timeout_id = gtk::timeout_add(500, move || {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return glib::Continue(true),
};
let position = pipeline let position = pipeline
.query_position::<gst::ClockTime>() .query_position::<gst::ClockTime>()
.unwrap_or_else(|| 0.into()); .unwrap_or_else(|| 0.into());
@ -56,9 +58,13 @@ fn create_ui(app: &gtk::Application) {
glib::Continue(true) glib::Continue(true)
}); });
let app_clone = app.clone(); let app_weak = app.downgrade();
window.connect_delete_event(move |_, _| { window.connect_delete_event(move |_, _| {
let app = &app_clone; let app = match app_weak.upgrade() {
Some(app) => app,
None => return Inhibit(false),
};
app.quit(); app.quit();
Inhibit(false) Inhibit(false)
}); });
@ -68,11 +74,15 @@ fn create_ui(app: &gtk::Application) {
let ret = pipeline.set_state(gst::State::Playing); let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
let app_clone = SendCell::new(app.clone()); let app_weak = glib::SendWeakRef::from(app.downgrade());
bus.add_watch(move |_, msg| { bus.add_watch(move |_, msg| {
use gst::MessageView; use gst::MessageView;
let app = app_clone.borrow(); let app = match app_weak.upgrade() {
Some(app) => app,
None => return glib::Continue(false),
};
match msg.view() { match msg.view() {
MessageView::Eos(..) => gtk::main_quit(), MessageView::Eos(..) => gtk::main_quit(),
MessageView::Error(err) => { MessageView::Error(err) => {
@ -90,11 +100,17 @@ fn create_ui(app: &gtk::Application) {
glib::Continue(true) glib::Continue(true)
}); });
let pipeline_clone = pipeline.clone(); // Pipeline reference is owned by the closure below, so will be
// destroyed once the app is destroyed
let timeout_id = RefCell::new(Some(timeout_id));
app.connect_shutdown(move |_| { app.connect_shutdown(move |_| {
let pipeline = &pipeline_clone;
let ret = pipeline.set_state(gst::State::Null); let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
bus.remove_watch();
if let Some(timeout_id) = timeout_id.borrow_mut().take() {
glib::source_remove(timeout_id);
}
}); });
} }

View file

@ -20,8 +20,7 @@ use std::env;
use std::os::raw::c_void; use std::os::raw::c_void;
extern crate send_cell; use std::cell::RefCell;
use send_cell::SendCell;
use std::process; use std::process;
@ -49,9 +48,13 @@ fn create_ui(app: &gtk::Application) {
video_window.set_size_request(320, 240); video_window.set_size_request(320, 240);
let video_overlay = sink.clone() let video_overlay = sink.clone()
.dynamic_cast::<gst_video::VideoOverlay>() .dynamic_cast::<gst_video::VideoOverlay>()
.unwrap(); .unwrap()
.downgrade();
video_window.connect_realize(move |video_window| { video_window.connect_realize(move |video_window| {
let video_overlay = &video_overlay; let video_overlay = match video_overlay.upgrade() {
Some(video_overlay) => video_overlay,
None => return,
};
let gdk_window = video_window.get_window().unwrap(); let gdk_window = video_window.get_window().unwrap();
@ -108,9 +111,13 @@ fn create_ui(app: &gtk::Application) {
app.add_window(&window); app.add_window(&window);
let pipeline_clone = pipeline.clone(); let pipeline_weak = pipeline.downgrade();
gtk::timeout_add(500, move || { let timeout_id = gtk::timeout_add(500, move || {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return glib::Continue(true),
};
let position = pipeline let position = pipeline
.query_position::<gst::ClockTime>() .query_position::<gst::ClockTime>()
.unwrap_or_else(|| 0.into()); .unwrap_or_else(|| 0.into());
@ -119,9 +126,13 @@ fn create_ui(app: &gtk::Application) {
glib::Continue(true) glib::Continue(true)
}); });
let app_clone = app.clone(); let app_weak = app.downgrade();
window.connect_delete_event(move |_, _| { window.connect_delete_event(move |_, _| {
let app = &app_clone; let app = match app_weak.upgrade() {
Some(app) => app,
None => return Inhibit(false),
};
app.quit(); app.quit();
Inhibit(false) Inhibit(false)
}); });
@ -131,11 +142,15 @@ fn create_ui(app: &gtk::Application) {
let ret = pipeline.set_state(gst::State::Playing); let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
let app_clone = SendCell::new(app.clone()); let app_weak = glib::SendWeakRef::from(app.downgrade());
bus.add_watch(move |_, msg| { bus.add_watch(move |_, msg| {
use gst::MessageView; use gst::MessageView;
let app = app_clone.borrow(); let app = match app_weak.upgrade() {
Some(app) => app,
None => return glib::Continue(false),
};
match msg.view() { match msg.view() {
MessageView::Eos(..) => gtk::main_quit(), MessageView::Eos(..) => gtk::main_quit(),
MessageView::Error(err) => { MessageView::Error(err) => {
@ -153,11 +168,17 @@ fn create_ui(app: &gtk::Application) {
glib::Continue(true) glib::Continue(true)
}); });
let pipeline_clone = pipeline.clone(); // Pipeline reference is owned by the closure below, so will be
// destroyed once the app is destroyed
let timeout_id = RefCell::new(Some(timeout_id));
app.connect_shutdown(move |_| { app.connect_shutdown(move |_| {
let pipeline = &pipeline_clone;
let ret = pipeline.set_state(gst::State::Null); let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
bus.remove_watch();
if let Some(timeout_id) = timeout_id.borrow_mut().take() {
glib::source_remove(timeout_id);
}
}); });
} }

View file

@ -50,6 +50,8 @@ fn example_main() {
let ret = pipeline.set_state(gst::State::Null); let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
bus.remove_watch();
} }
fn main() { fn main() {

View file

@ -17,12 +17,11 @@ fn example_main() {
"audiotestsrc name=src ! audio/x-raw,format={},channels=1 ! fakesink", "audiotestsrc name=src ! audio/x-raw,format={},channels=1 ! fakesink",
gst_audio::AUDIO_FORMAT_S16.to_string() gst_audio::AUDIO_FORMAT_S16.to_string()
)).unwrap(); )).unwrap();
let bus = pipeline.get_bus().unwrap(); let pipeline = pipeline
.dynamic_cast::<gst::Pipeline>()
.unwrap();
let src = pipeline let src = pipeline
.clone()
.dynamic_cast::<gst::Bin>()
.unwrap()
.get_by_name("src") .get_by_name("src")
.unwrap(); .unwrap();
let src_pad = src.get_static_pad("src").unwrap(); let src_pad = src.get_static_pad("src").unwrap();
@ -53,6 +52,7 @@ fn example_main() {
let ret = pipeline.set_state(gst::State::Playing); let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
let bus = pipeline.get_bus().unwrap();
while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) { while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
use gst::MessageView; use gst::MessageView;

View file

@ -32,21 +32,17 @@ fn main_loop(uri: &str) -> Result<(), Error> {
let error = Arc::new(Mutex::new(Ok(()))); let error = Arc::new(Mutex::new(Ok(())));
let player_clone = player.clone();
let main_loop_clone = main_loop.clone(); let main_loop_clone = main_loop.clone();
player.connect_end_of_stream(move |_| { player.connect_end_of_stream(move |player| {
let main_loop = &main_loop_clone; let main_loop = &main_loop_clone;
let player = &player_clone;
player.stop(); player.stop();
main_loop.quit(); main_loop.quit();
}); });
let player_clone = player.clone();
let main_loop_clone = main_loop.clone(); let main_loop_clone = main_loop.clone();
let error_clone = Arc::clone(&error); let error_clone = Arc::clone(&error);
player.connect_error(move |_, err| { player.connect_error(move |player, err| {
let main_loop = &main_loop_clone; let main_loop = &main_loop_clone;
let player = &player_clone;
let error = &error_clone; let error = &error_clone;
*error.lock().unwrap() = Err(err.clone()); *error.lock().unwrap() = Err(err.clone());

View file

@ -23,9 +23,12 @@ fn example_main() {
let main_loop_clone = main_loop.clone(); let main_loop_clone = main_loop.clone();
let pipeline_clone = pipeline.clone(); let pipeline_weak = pipeline.downgrade();
glib::timeout_add_seconds(1, move || { let timeout_id = glib::timeout_add_seconds(1, move || {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return glib::Continue(true),
};
//let pos = pipeline.query_position(gst::Format::Time).unwrap_or(-1); //let pos = pipeline.query_position(gst::Format::Time).unwrap_or(-1);
//let dur = pipeline.query_duration(gst::Format::Time).unwrap_or(-1); //let dur = pipeline.query_duration(gst::Format::Time).unwrap_or(-1);
@ -81,6 +84,9 @@ fn example_main() {
let ret = pipeline.set_state(gst::State::Null); let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure); assert_ne!(ret, gst::StateChangeReturn::Failure);
bus.remove_watch();
glib::source_remove(timeout_id);
} }
fn main() { fn main() {

View file

@ -205,9 +205,14 @@ fn example_main() -> Result<(), Error> {
let sinkpad = get_request_pad(&rtpbin, "recv_rtp_sink_0")?; let sinkpad = get_request_pad(&rtpbin, "recv_rtp_sink_0")?;
srcpad.link(&sinkpad).into_result()?; srcpad.link(&sinkpad).into_result()?;
let depay_clone = depay.clone(); let depay_weak = depay.downgrade();
rtpbin.connect_pad_added(move |rtpbin, src_pad| { rtpbin.connect_pad_added(move |rtpbin, src_pad| {
match connect_rtpbin_srcpad(&src_pad, &depay_clone) { let depay = match depay_weak.upgrade() {
Some(depay) => depay,
None => return,
};
match connect_rtpbin_srcpad(&src_pad, &depay) {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
gst_element_error!( gst_element_error!(
@ -245,6 +250,9 @@ fn example_main() -> Result<(), Error> {
match msg.view() { match msg.view() {
MessageView::Eos(..) => break, MessageView::Eos(..) => break,
MessageView::Error(err) => { MessageView::Error(err) => {
let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure);
return Err(ErrorMessage { return Err(ErrorMessage {
src: msg.get_src() src: msg.get_src()
.map(|s| s.get_path_string()) .map(|s| s.get_path_string())

View file

@ -183,6 +183,9 @@ fn example_main() -> Result<(), Error> {
match msg.view() { match msg.view() {
MessageView::Eos(..) => break, MessageView::Eos(..) => break,
MessageView::Error(err) => { MessageView::Error(err) => {
let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure);
return Err(ErrorMessage { return Err(ErrorMessage {
src: msg.get_src() src: msg.get_src()
.map(|s| s.get_path_string()) .map(|s| s.get_path_string())

View file

@ -94,7 +94,7 @@ fn main_loop() -> Result<(), Error> {
mounts.add_factory("/test", &factory); mounts.add_factory("/test", &factory);
server.attach(None); let id = server.attach(None);
println!( println!(
"Stream ready at rtsps://127.0.0.1:{}/test", "Stream ready at rtsps://127.0.0.1:{}/test",
@ -103,6 +103,8 @@ fn main_loop() -> Result<(), Error> {
main_loop.run(); main_loop.run();
glib::source_remove(id);
Ok(()) Ok(())
} }

View file

@ -41,7 +41,7 @@ fn main_loop() -> Result<(), Error> {
mounts.add_factory("/test", &factory); mounts.add_factory("/test", &factory);
server.attach(None); let id = server.attach(None);
println!( println!(
"Stream ready at rtsp://127.0.0.1:{}/test", "Stream ready at rtsp://127.0.0.1:{}/test",
@ -50,6 +50,8 @@ fn main_loop() -> Result<(), Error> {
main_loop.run(); main_loop.run();
glib::source_remove(id);
Ok(()) Ok(())
} }

View file

@ -30,9 +30,12 @@ fn example_main() {
gst::Element::link_many(&[&src, &decodebin]).unwrap(); gst::Element::link_many(&[&src, &decodebin]).unwrap();
// Need to move a new reference into the closure // Need to move a new reference into the closure
let pipeline_clone = pipeline.clone(); let pipeline_weak = pipeline.downgrade();
decodebin.connect_pad_added(move |_, src_pad| { decodebin.connect_pad_added(move |_, src_pad| {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return,
};
let queue = gst::ElementFactory::make("queue", None).unwrap(); let queue = gst::ElementFactory::make("queue", None).unwrap();
let sink = gst::ElementFactory::make("fakesink", None).unwrap(); let sink = gst::ElementFactory::make("fakesink", None).unwrap();

View file

@ -32,11 +32,18 @@ fn tutorial_main() {
.expect("Can't set uri property on uridecodebin"); .expect("Can't set uri property on uridecodebin");
// Connect the pad-added signal // Connect the pad-added signal
let pipeline_clone = pipeline.clone(); let pipeline_weak = pipeline.downgrade();
let convert_clone = convert.clone(); let convert_weak = convert.downgrade();
source.connect_pad_added(move |_, src_pad| { source.connect_pad_added(move |_, src_pad| {
let pipeline = &pipeline_clone; let pipeline = match pipeline_weak.upgrade() {
let convert = &convert_clone; Some(pipeline) => pipeline,
None => return,
};
let convert = match convert_weak.upgrade() {
Some(convert) => convert,
None => return,
};
println!( println!(
"Received new pad {} from {}", "Received new pad {} from {}",

View file

@ -20,6 +20,33 @@ mod tutorial5 {
extern crate gstreamer_video as gst_video; extern crate gstreamer_video as gst_video;
use self::gst_video::prelude::*; use self::gst_video::prelude::*;
use std::ops;
// Custom struct to keep our window reference alive
// and to store the timeout id so that we can remove
// it from the main context again later and drop the
// references it keeps inside its closures
struct AppWindow {
main_window: Window,
timeout_id: Option<glib::SourceId>,
}
impl ops::Deref for AppWindow {
type Target = Window;
fn deref(&self) -> &Window {
&self.main_window
}
}
impl Drop for AppWindow {
fn drop(&mut self) {
if let Some(source_id) = self.timeout_id.take() {
glib::source_remove(source_id);
}
}
}
// Extract tags from streams of @stype and add the info in the UI. // Extract tags from streams of @stype and add the info in the UI.
fn add_streams_info( fn add_streams_info(
playbin: &gst::Element, playbin: &gst::Element,
@ -79,7 +106,7 @@ mod tutorial5 {
} }
// This creates all the GTK+ widgets that compose our application, and registers the callbacks // This creates all the GTK+ widgets that compose our application, and registers the callbacks
pub fn create_ui(playbin: &gst::Element) { fn create_ui(playbin: &gst::Element) -> AppWindow {
let main_window = Window::new(WindowType::Toplevel); let main_window = Window::new(WindowType::Toplevel);
main_window.connect_delete_event(|_, _| { main_window.connect_delete_event(|_, _| {
gtk::main_quit(); gtk::main_quit();
@ -144,7 +171,7 @@ mod tutorial5 {
let pipeline = playbin.clone(); let pipeline = playbin.clone();
let lslider = slider.clone(); let lslider = slider.clone();
// Update the UI (seekbar) every second // Update the UI (seekbar) every second
gtk::timeout_add_seconds(1, move || { let timeout_id = gtk::timeout_add_seconds(1, move || {
let pipeline = &pipeline; let pipeline = &pipeline;
let lslider = &lslider; let lslider = &lslider;
@ -228,7 +255,7 @@ mod tutorial5 {
let streams_list = gtk::TextView::new(); let streams_list = gtk::TextView::new();
streams_list.set_editable(false); streams_list.set_editable(false);
let pipeline = playbin.clone(); let pipeline_weak = playbin.downgrade();
let textbuf = SendCell::new( let textbuf = SendCell::new(
streams_list streams_list
.get_buffer() .get_buffer()
@ -239,6 +266,11 @@ mod tutorial5 {
.unwrap() .unwrap()
.connect_message(move |_, msg| match msg.view() { .connect_message(move |_, msg| match msg.view() {
gst::MessageView::Application(application) => { gst::MessageView::Application(application) => {
let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return,
};
if application.get_structure().map(|s| s.get_name()) == Some("tags-changed") { if application.get_structure().map(|s| s.get_name()) == Some("tags-changed") {
analyze_streams(&pipeline, &textbuf); analyze_streams(&pipeline, &textbuf);
} }
@ -257,6 +289,11 @@ mod tutorial5 {
main_window.set_default_size(640, 480); main_window.set_default_size(640, 480);
main_window.show_all(); main_window.show_all();
AppWindow {
main_window,
timeout_id: Some(timeout_id),
}
} }
// We are possibly in a GStreamer working thread, so we notify the main // We are possibly in a GStreamer working thread, so we notify the main
@ -290,43 +327,45 @@ mod tutorial5 {
data/media/sintel_trailer-480p.webm"; data/media/sintel_trailer-480p.webm";
let playbin = gst::ElementFactory::make("playbin", None).unwrap(); let playbin = gst::ElementFactory::make("playbin", None).unwrap();
playbin playbin
.set_property("uri", &glib::Value::from(uri)) .set_property("uri", &uri)
.unwrap(); .unwrap();
let pipeline = playbin.clone();
playbin playbin
.connect("video-tags-changed", false, move |_| { .connect("video-tags-changed", false, |args| {
let pipeline = args[0].get::<gst::Element>().unwrap();
post_app_message(&pipeline); post_app_message(&pipeline);
None None
}) })
.unwrap(); .unwrap();
let pipeline = playbin.clone();
playbin playbin
.connect("audio-tags-changed", false, move |_| { .connect("audio-tags-changed", false, |args| {
let pipeline = args[0].get::<gst::Element>().unwrap();
post_app_message(&pipeline); post_app_message(&pipeline);
None None
}) })
.unwrap(); .unwrap();
let pipeline = playbin.clone();
playbin playbin
.connect("text-tags-changed", false, move |_| { .connect("text-tags-changed", false, move |args| {
let pipeline = args[0].get::<gst::Element>().unwrap();
post_app_message(&pipeline); post_app_message(&pipeline);
None None
}) })
.unwrap(); .unwrap();
create_ui(&playbin); let window = create_ui(&playbin);
playbin
.set_state(gst::State::Playing)
.into_result()
.unwrap();
let bus = playbin.get_bus().unwrap(); let bus = playbin.get_bus().unwrap();
let pipeline = playbin.clone();
bus.add_signal_watch(); bus.add_signal_watch();
playbin.get_bus().unwrap().connect_message(move |_, msg| {
let pipeline_weak = playbin.downgrade();
bus.connect_message(move |_, msg| {
let pipeline = match pipeline_weak.upgrade() {
Some(pipeline) => pipeline,
None => return,
};
match msg.view() { match msg.view() {
// This is called when an End-Of-Stream message is posted on the bus. // This is called when an End-Of-Stream message is posted on the bus.
// We just set the pipeline to READY (which stops playback). // We just set the pipeline to READY (which stops playback).
@ -359,8 +398,16 @@ mod tutorial5 {
} }
}); });
playbin
.set_state(gst::State::Playing)
.into_result()
.unwrap();
gtk::main(); gtk::main();
window.hide();
playbin.set_state(gst::State::Null).into_result().unwrap(); playbin.set_state(gst::State::Null).into_result().unwrap();
bus.remove_signal_watch();
} }
} }

View file

@ -127,30 +127,34 @@ fn main() {
let audio_caps = info.to_caps().unwrap(); let audio_caps = info.to_caps().unwrap();
let appsrc = appsrc let appsrc = appsrc
.clone()
.dynamic_cast::<AppSrc>() .dynamic_cast::<AppSrc>()
.expect("Source element is expected to be an appsrc!"); .expect("Source element is expected to be an appsrc!");
appsrc.set_caps(&audio_caps); appsrc.set_caps(&audio_caps);
appsrc.set_property_format(gst::Format::Time); appsrc.set_property_format(gst::Format::Time);
let appsink = appsink let appsink = appsink
.clone()
.dynamic_cast::<AppSink>() .dynamic_cast::<AppSink>()
.expect("Sink element is expected to be an appsink!"); .expect("Sink element is expected to be an appsink!");
let data: Arc<Mutex<CustomData>> = Arc::new(Mutex::new(CustomData::new(&appsrc, &appsink))); let data: Arc<Mutex<CustomData>> = Arc::new(Mutex::new(CustomData::new(&appsrc, &appsink)));
let data_clone = Arc::clone(&data); let data_weak = Arc::downgrade(&data);
appsrc.connect_need_data(move |_, _size| { appsrc.connect_need_data(move |_, _size| {
let data = &data_clone; let data = match data_weak.upgrade() {
Some(data) => data,
None => return,
};
let mut d = data.lock().unwrap(); let mut d = data.lock().unwrap();
if d.source_id.is_none() { if d.source_id.is_none() {
println!("start feeding"); println!("start feeding");
let data_clone = Arc::clone(data); let data_weak = Arc::downgrade(&data);
d.source_id = Some(glib::source::idle_add(move || { d.source_id = Some(glib::source::idle_add(move || {
let data = &data_clone; let data = match data_weak.upgrade() {
Some(data) => data,
None => return glib::Continue(false),
};
let (appsrc, buffer) = { let (appsrc, buffer) = {
let mut data = data.lock().unwrap(); let mut data = data.lock().unwrap();
@ -198,9 +202,12 @@ fn main() {
} }
}); });
let data_clone = Arc::clone(&data); let data_weak = Arc::downgrade(&data);
appsrc.connect_enough_data(move |_| { appsrc.connect_enough_data(move |_| {
let data = &data_clone; let data = match data_weak.upgrade() {
Some(data) => data,
None => return,
};
let mut data = data.lock().unwrap(); let mut data = data.lock().unwrap();
if let Some(source) = data.source_id.take() { if let Some(source) = data.source_id.take() {
@ -213,10 +220,15 @@ fn main() {
appsink.set_emit_signals(true); appsink.set_emit_signals(true);
appsink.set_caps(&audio_caps); appsink.set_caps(&audio_caps);
let data_clone = Arc::clone(&data); let data_weak = Arc::downgrade(&data);
appsink.connect_new_sample(move |_| { appsink.connect_new_sample(move |_| {
let data = match data_weak.upgrade() {
Some(data) => data,
None => return gst::FlowReturn::Ok,
};
let appsink = { let appsink = {
let data = &data_clone.lock().unwrap(); let data = data.lock().unwrap();
data.appsink.clone() data.appsink.clone()
}; };
@ -230,10 +242,6 @@ fn main() {
gst::FlowReturn::Ok gst::FlowReturn::Ok
}); });
pipeline
.set_state(gst::State::Playing)
.into_result()
.expect("Unable to set the pipeline to the Playing state.");
let main_loop = glib::MainLoop::new(None, false); let main_loop = glib::MainLoop::new(None, false);
let main_loop_clone = main_loop.clone(); let main_loop_clone = main_loop.clone();
let bus = pipeline.get_bus().unwrap(); let bus = pipeline.get_bus().unwrap();
@ -252,10 +260,17 @@ fn main() {
}); });
bus.add_signal_watch(); bus.add_signal_watch();
pipeline
.set_state(gst::State::Playing)
.into_result()
.expect("Unable to set the pipeline to the Playing state.");
main_loop.run(); main_loop.run();
pipeline pipeline
.set_state(gst::State::Null) .set_state(gst::State::Null)
.into_result() .into_result()
.expect("Unable to set the pipeline to the Null state."); .expect("Unable to set the pipeline to the Null state.");
bus.remove_signal_watch();
} }