From 50e6e2e108fb067570ef5d5cab2086eb04e156fb Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Thu, 3 Aug 2017 15:52:08 +0100 Subject: [PATCH] examples: appsrc: improved error handling The program should now exit gracefully with human readable messages. --- examples/src/bin/appsrc.rs | 105 ++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 24 deletions(-) diff --git a/examples/src/bin/appsrc.rs b/examples/src/bin/appsrc.rs index 627de2f3b..d8b1a6345 100644 --- a/examples/src/bin/appsrc.rs +++ b/examples/src/bin/appsrc.rs @@ -3,24 +3,67 @@ use gst::*; extern crate gstreamer_app as gst_app; use gst_app::*; +extern crate glib; + +use std::fmt; use std::u64; use std::thread; const WIDTH: usize = 320; const HEIGHT: usize = 240; -fn main() { - gst::init().unwrap(); +#[derive(Debug)] +enum AppSrcExError { + InitFailed(glib::Error), + ElementNotFound(&'static str), + ElementLinkFailed(&'static str, &'static str), + SetStateError(&'static str), + ElementError(std::string::String, glib::Error, std::string::String), +} + +impl fmt::Display for AppSrcExError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + AppSrcExError::InitFailed(ref e) => { + write!(f, "GStreamer initialization failed: {:?}", e) + } + AppSrcExError::ElementNotFound(ref e) => write!(f, "Element {} not found", e), + AppSrcExError::ElementLinkFailed(ref e1, ref e2) => { + write!(f, "Link failed between {} and {}", e1, e2) + } + AppSrcExError::SetStateError(ref state) => { + write!(f, "Pipeline failed to switch to state {}", state) + } + AppSrcExError::ElementError(ref element, ref err, ref debug) => { + write!(f, "Error from {}: {} ({:?})", element, err, debug) + } + } + } +} + +fn create_pipeline() -> Result<(Pipeline, AppSrc), AppSrcExError> { + gst::init().map_err(|e| AppSrcExError::InitFailed(e))?; let pipeline = gst::Pipeline::new(None); - let src = gst::ElementFactory::make("appsrc", None).unwrap(); - let videoconvert = gst::ElementFactory::make("videoconvert", None).unwrap(); - let sink = gst::ElementFactory::make("autovideosink", None).unwrap(); + let src = gst::ElementFactory::make("appsrc", None) + .ok_or(AppSrcExError::ElementNotFound("appsrc"))?; - pipeline.add_many(&[&src, &videoconvert, &sink]).unwrap(); - gst::Element::link_many(&[&src, &videoconvert, &sink]).unwrap(); + let videoconvert = gst::ElementFactory::make("videoconvert", None) + .ok_or(AppSrcExError::ElementNotFound("videoconvert"))?; + let sink = gst::ElementFactory::make("autovideosink", None) + .ok_or(AppSrcExError::ElementNotFound("autovideosink"))?; - let appsrc = src.clone().dynamic_cast::().unwrap(); + pipeline + .add_many(&[&src, &videoconvert, &sink]) + .expect("Unable to add elements in the pipeline"); + gst::Element::link(&src, &videoconvert) + .map_err(|_| AppSrcExError::ElementLinkFailed("src", "videoconvert"))?; + gst::Element::link(&videoconvert, &sink) + .map_err(|_| AppSrcExError::ElementLinkFailed("videoconvert", "sink"))?; + + let appsrc = src.clone() + .dynamic_cast::() + .expect("Source element is expected to be an appsrc!"); appsrc.set_caps(&Caps::new_simple( "video/x-raw", &[ @@ -34,6 +77,12 @@ fn main() { appsrc.set_max_bytes(1); appsrc.set_property_block(true); + Ok((pipeline, appsrc)) +} + +fn main_loop() -> Result<(), AppSrcExError> { + let (pipeline, appsrc) = create_pipeline()?; + thread::spawn(move || { for i in 0..100 { println!("Producing frame {}", i); @@ -44,14 +93,14 @@ fn main() { let g = if i % 3 == 0 { 0 } else { 255 }; let b = if i % 5 == 0 { 0 } else { 255 }; - for _ in 0..(320 * 240) { + for _ in 0..(WIDTH * HEIGHT) { vec.push(b); vec.push(g); vec.push(r); vec.push(0); } - let mut buffer = Buffer::from_vec(vec).unwrap(); + let mut buffer = Buffer::from_vec(vec).expect("Unable to create a Buffer"); buffer.get_mut().unwrap().set_pts(i * 500_000_000); if appsrc.push_buffer(buffer) != FlowReturn::Ok { @@ -62,12 +111,13 @@ fn main() { appsrc.end_of_stream(); }); - assert_ne!( - pipeline.set_state(gst::State::Playing), - gst::StateChangeReturn::Failure - ); + if let gst::StateChangeReturn::Failure = pipeline.set_state(gst::State::Playing) { + return Err(AppSrcExError::SetStateError("playing")); + } - let bus = pipeline.get_bus().unwrap(); + let bus = pipeline + .get_bus() + .expect("Pipeline without bus. Shouldn't happen!"); loop { let msg = match bus.timed_pop(u64::MAX) { @@ -78,20 +128,27 @@ fn main() { match msg.view() { MessageView::Eos(..) => break, MessageView::Error(err) => { - println!( - "Error from {}: {} ({:?})", + pipeline.set_state(gst::State::Null); + return Err(AppSrcExError::ElementError( msg.get_src().get_path_string(), err.get_error(), - err.get_debug() - ); - break; + err.get_debug().unwrap(), + )); } _ => (), } } - assert_ne!( - pipeline.set_state(gst::State::Null), - gst::StateChangeReturn::Failure - ); + if let gst::StateChangeReturn::Failure = pipeline.set_state(gst::State::Null) { + return Err(AppSrcExError::SetStateError("null")); + } + + Ok(()) +} + +fn main() { + match main_loop() { + Ok(r) => r, + Err(e) => eprintln!("Error! {}", e), + } }