elementfactory: Change make() / create() to builders and keep the old variants as create_with_name() / make_with_name()

As a side-effect, this also now includes the element factory name in the
error messages instead of giving the same error string for every
factory.

Partially fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/318

Also let them all go through the same, single object construction code.
This commit is contained in:
Sebastian Dröge 2022-10-19 14:13:57 +03:00
parent 7ad75d4b1f
commit 7423b1dea6
46 changed files with 723 additions and 518 deletions

View file

@ -23,10 +23,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -40,9 +36,8 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?; gst::init()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("audiotestsrc", None) let src = gst::ElementFactory::make("audiotestsrc").build()?;
.map_err(|_| MissingElement("audiotestsrc"))?; let sink = gst::ElementFactory::make("appsink").build()?;
let sink = gst::ElementFactory::make("appsink", None).map_err(|_| MissingElement("appsink"))?;
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?; src.link(&sink)?;

View file

@ -18,10 +18,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -38,11 +34,9 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?; gst::init()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("appsrc", None).map_err(|_| MissingElement("appsrc"))?; let src = gst::ElementFactory::make("appsrc").build()?;
let videoconvert = gst::ElementFactory::make("videoconvert", None) let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
.map_err(|_| MissingElement("videoconvert"))?; let sink = gst::ElementFactory::make("autovideosink").build()?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
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])?;

View file

@ -567,12 +567,17 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// Create our pipeline with the compositor and two input streams. // Create our pipeline with the compositor and two input streams.
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src1 = gst::ElementFactory::make("videotestsrc", None).context("Creating videotestsrc")?; let src1 = gst::ElementFactory::make("videotestsrc")
let src2 = gst::ElementFactory::make("videotestsrc", None).context("Creating videotestsrc")?; .property_from_str("pattern", "ball")
.build()?;
let src2 = gst::ElementFactory::make("videotestsrc")
.property_from_str("pattern", "smpte")
.build()?;
let comp = cairo_compositor::CairoCompositor::new(None); let comp = cairo_compositor::CairoCompositor::new(None);
let conv = gst::ElementFactory::make("videoconvert", None).context("Creating videoconvert")?; let conv = gst::ElementFactory::make("videoconvert").build()?;
let sink = let sink = gst::ElementFactory::make("autovideosink").build()?;
gst::ElementFactory::make("autovideosink", None).context("Creating autovideosink")?;
comp.set_property("background-color", 0xff_33_33_33u32);
pipeline.add_many(&[&src1, &src2, comp.upcast_ref(), &conv, &sink])?; pipeline.add_many(&[&src1, &src2, comp.upcast_ref(), &conv, &sink])?;
@ -603,11 +608,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.context("Linking converter")?; .context("Linking converter")?;
conv.link(&sink).context("Linking sink")?; conv.link(&sink).context("Linking sink")?;
src1.set_property_from_str("pattern", "ball");
src2.set_property_from_str("pattern", "smpte");
comp.set_property("background-color", 0xff_33_33_33u32);
// Change positions etc of both inputs based on a timer // Change positions etc of both inputs based on a timer
let xmax = 1280.0 - 320.0f64; let xmax = 1280.0 - 320.0f64;
let ymax = 720.0 - 240.0f64; let ymax = 720.0 - 240.0f64;

View file

@ -46,7 +46,6 @@ mod custom_meta {
} }
// Retrieve the stored label. // Retrieve the stored label.
#[doc(alias = "get_label")]
pub fn label(&self) -> &str { pub fn label(&self) -> &str {
self.0.label.as_str() self.0.label.as_str()
} }
@ -183,11 +182,13 @@ fn example_main() {
// This creates a pipeline with appsrc and appsink. // This creates a pipeline with appsrc and appsink.
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let appsrc = gst::ElementFactory::make("appsrc", None) let appsrc = gst::ElementFactory::make("appsrc")
.build()
.unwrap() .unwrap()
.downcast::<gst_app::AppSrc>() .downcast::<gst_app::AppSrc>()
.unwrap(); .unwrap();
let appsink = gst::ElementFactory::make("appsink", None) let appsink = gst::ElementFactory::make("appsink")
.build()
.unwrap() .unwrap()
.downcast::<gst_app::AppSink>() .downcast::<gst_app::AppSink>()
.unwrap(); .unwrap();

View file

@ -42,10 +42,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -71,12 +67,10 @@ fn example_main() -> Result<(), Error> {
}; };
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("filesrc", None).map_err(|_| MissingElement("filesrc"))?; let src = gst::ElementFactory::make("filesrc")
let decodebin = .property("location", uri)
gst::ElementFactory::make("decodebin", None).map_err(|_| MissingElement("decodebin"))?; .build()?;
let decodebin = gst::ElementFactory::make("decodebin").build()?;
// Tell the filesrc what file to load
src.set_property("location", uri);
pipeline.add_many(&[&src, &decodebin])?; pipeline.add_many(&[&src, &decodebin])?;
gst::Element::link_many(&[&src, &decodebin])?; gst::Element::link_many(&[&src, &decodebin])?;
@ -135,14 +129,10 @@ fn example_main() -> Result<(), Error> {
if is_audio { if is_audio {
// decodebin found a raw audiostream, so we build the follow-up pipeline to // decodebin found a raw audiostream, so we build the follow-up pipeline to
// play it on the default audio playback device (using autoaudiosink). // play it on the default audio playback device (using autoaudiosink).
let queue = gst::ElementFactory::make("queue", None) let queue = gst::ElementFactory::make("queue").build()?;
.map_err(|_| MissingElement("queue"))?; let convert = gst::ElementFactory::make("audioconvert").build()?;
let convert = gst::ElementFactory::make("audioconvert", None) let resample = gst::ElementFactory::make("audioresample").build()?;
.map_err(|_| MissingElement("audioconvert"))?; let sink = gst::ElementFactory::make("autoaudiosink").build()?;
let resample = gst::ElementFactory::make("audioresample", None)
.map_err(|_| MissingElement("audioresample"))?;
let sink = gst::ElementFactory::make("autoaudiosink", None)
.map_err(|_| MissingElement("autoaudiosink"))?;
let elements = &[&queue, &convert, &resample, &sink]; let elements = &[&queue, &convert, &resample, &sink];
pipeline.add_many(elements)?; pipeline.add_many(elements)?;
@ -163,14 +153,10 @@ fn example_main() -> Result<(), Error> {
} else if is_video { } else if is_video {
// decodebin found a raw videostream, so we build the follow-up pipeline to // decodebin found a raw videostream, so we build the follow-up pipeline to
// display it using the autovideosink. // display it using the autovideosink.
let queue = gst::ElementFactory::make("queue", None) let queue = gst::ElementFactory::make("queue").build()?;
.map_err(|_| MissingElement("queue"))?; let convert = gst::ElementFactory::make("videoconvert").build()?;
let convert = gst::ElementFactory::make("videoconvert", None) let scale = gst::ElementFactory::make("videoscale").build()?;
.map_err(|_| MissingElement("videoconvert"))?; let sink = gst::ElementFactory::make("autovideosink").build()?;
let scale = gst::ElementFactory::make("videoscale", None)
.map_err(|_| MissingElement("videoscale"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
let elements = &[&queue, &convert, &scale, &sink]; let elements = &[&queue, &convert, &scale, &sink];
pipeline.add_many(elements)?; pipeline.add_many(elements)?;

View file

@ -26,10 +26,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -90,15 +86,13 @@ fn example_main() -> Result<(), Error> {
}; };
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("uridecodebin", None) let src = gst::ElementFactory::make("uridecodebin")
.map_err(|_| MissingElement("uridecodebin"))?; .property("uri", uri)
let encodebin = .build()?;
gst::ElementFactory::make("encodebin", None).map_err(|_| MissingElement("encodebin"))?; let encodebin = gst::ElementFactory::make("encodebin").build()?;
let sink = let sink = gst::ElementFactory::make("filesink")
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?; .property("location", output_file)
.build()?;
src.set_property("uri", uri);
sink.set_property("location", output_file);
// Configure the encodebin. // Configure the encodebin.
// Here we tell the bin what format we expect it to create at its output. // Here we tell the bin what format we expect it to create at its output.
@ -157,12 +151,9 @@ fn example_main() -> Result<(), Error> {
let link_to_encodebin = |is_audio, is_video| -> Result<(), Error> { let link_to_encodebin = |is_audio, is_video| -> Result<(), Error> {
if is_audio { if is_audio {
let queue = gst::ElementFactory::make("queue", None) let queue = gst::ElementFactory::make("queue").build()?;
.map_err(|_| MissingElement("queue"))?; let convert = gst::ElementFactory::make("audioconvert").build()?;
let convert = gst::ElementFactory::make("audioconvert", None) let resample = gst::ElementFactory::make("audioresample").build()?;
.map_err(|_| MissingElement("audioconvert"))?;
let resample = gst::ElementFactory::make("audioresample", None)
.map_err(|_| MissingElement("audioresample"))?;
let elements = &[&queue, &convert, &resample]; let elements = &[&queue, &convert, &resample];
pipeline pipeline
@ -188,12 +179,9 @@ fn example_main() -> Result<(), Error> {
let sink_pad = queue.static_pad("sink").expect("queue has no sinkpad"); let sink_pad = queue.static_pad("sink").expect("queue has no sinkpad");
dbin_src_pad.link(&sink_pad)?; dbin_src_pad.link(&sink_pad)?;
} else if is_video { } else if is_video {
let queue = gst::ElementFactory::make("queue", None) let queue = gst::ElementFactory::make("queue").build()?;
.map_err(|_| MissingElement("queue"))?; let convert = gst::ElementFactory::make("videoconvert").build()?;
let convert = gst::ElementFactory::make("videoconvert", None) let scale = gst::ElementFactory::make("videoscale").build()?;
.map_err(|_| MissingElement("videoconvert"))?;
let scale = gst::ElementFactory::make("videoscale", None)
.map_err(|_| MissingElement("videoscale"))?;
let elements = &[&queue, &convert, &scale]; let elements = &[&queue, &convert, &scale];
pipeline pipeline

View file

@ -31,10 +31,6 @@ use std::{
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -51,13 +47,11 @@ fn create_receiver_pipeline(
let caps = video_info.to_caps()?; let caps = video_info.to_caps()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("appsrc", None).map_err(|_| MissingElement("appsrc"))?; let src = gst::ElementFactory::make("appsrc").build()?;
let filter = video_filter::FdMemoryFadeInVideoFilter::default().upcast::<gst::Element>(); let filter = video_filter::FdMemoryFadeInVideoFilter::default().upcast::<gst::Element>();
let convert = gst::ElementFactory::make("videoconvert", None) let convert = gst::ElementFactory::make("videoconvert").build()?;
.map_err(|_| MissingElement("videoconvert"))?; let queue = gst::ElementFactory::make("queue").build()?;
let queue = gst::ElementFactory::make("queue", None).map_err(|_| MissingElement("queue"))?; let sink = gst::ElementFactory::make("autovideosink").build()?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
src.downcast_ref::<gst_app::AppSrc>() src.downcast_ref::<gst_app::AppSrc>()
.ok_or_else(|| anyhow::anyhow!("is not a appsrc"))? .ok_or_else(|| anyhow::anyhow!("is not a appsrc"))?
@ -131,11 +125,10 @@ fn create_sender_pipeline(
let caps = video_info.to_caps()?; let caps = video_info.to_caps()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None) let src = gst::ElementFactory::make("videotestsrc")
.map_err(|_| MissingElement("videotestsrc"))?; .property("num-buffers", 250i32)
let sink = gst::ElementFactory::make("appsink", None).map_err(|_| MissingElement("appsink"))?; .build()?;
let sink = gst::ElementFactory::make("appsink").build()?;
src.set_property("num-buffers", 250i32);
sink.downcast_ref::<gst_app::AppSink>() sink.downcast_ref::<gst_app::AppSink>()
.ok_or_else(|| anyhow::anyhow!("is not a appsink"))? .ok_or_else(|| anyhow::anyhow!("is not a appsink"))?

View file

@ -20,20 +20,22 @@ use std::cell::RefCell;
fn create_ui(app: &gtk::Application) { fn create_ui(app: &gtk::Application) {
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap(); let src = gst::ElementFactory::make("videotestsrc").build().unwrap();
// Create the gtk sink and retrieve the widget from it. The sink element will be used // Create the gtk sink and retrieve the widget from it. The sink element will be used
// in the pipeline, and the widget will be embedded in our gui. // in the pipeline, and the widget will be embedded in our gui.
// Gstreamer then displays frames in the gtk widget. // Gstreamer then displays frames in the gtk widget.
// First, we try to use the OpenGL version - and if that fails, we fall back to non-OpenGL. // First, we try to use the OpenGL version - and if that fails, we fall back to non-OpenGL.
let (sink, widget) = if let Ok(gtkglsink) = gst::ElementFactory::make("gtkglsink", None) { let (sink, widget) = if let Ok(gtkglsink) = gst::ElementFactory::make("gtkglsink").build() {
// Using the OpenGL widget succeeded, so we are in for a nice playback experience with // Using the OpenGL widget succeeded, so we are in for a nice playback experience with
// low cpu usage. :) // low cpu usage. :)
// The gtkglsink essentially allocates an OpenGL texture on the GPU, that it will display. // The gtkglsink essentially allocates an OpenGL texture on the GPU, that it will display.
// Now we create the glsinkbin element, which is responsible for conversions and for uploading // Now we create the glsinkbin element, which is responsible for conversions and for uploading
// video frames to our texture (if they are not already in the GPU). Now we tell the OpenGL-sink // video frames to our texture (if they are not already in the GPU). Now we tell the OpenGL-sink
// about our gtkglsink element, form where it will retrieve the OpenGL texture to fill. // about our gtkglsink element, form where it will retrieve the OpenGL texture to fill.
let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap(); let glsinkbin = gst::ElementFactory::make("glsinkbin")
glsinkbin.set_property("sink", &gtkglsink); .property("sink", &gtkglsink)
.build()
.unwrap();
// The gtkglsink creates the gtk widget for us. This is accessible through a property. // The gtkglsink creates the gtk widget for us. This is accessible through a property.
// So we get it and use it later to add it to our gui. // So we get it and use it later to add it to our gui.
let widget = gtkglsink.property::<gtk::Widget>("widget"); let widget = gtkglsink.property::<gtk::Widget>("widget");
@ -42,7 +44,7 @@ fn create_ui(app: &gtk::Application) {
// Unfortunately, using the OpenGL widget didn't work out, so we will have to render // Unfortunately, using the OpenGL widget didn't work out, so we will have to render
// our frames manually, using the CPU. An example why this may fail is, when // our frames manually, using the CPU. An example why this may fail is, when
// the PC doesn't have proper graphics drivers installed. // the PC doesn't have proper graphics drivers installed.
let sink = gst::ElementFactory::make("gtksink", None).unwrap(); let sink = gst::ElementFactory::make("gtksink").build().unwrap();
// The gtksink creates the gtk widget for us. This is accessible through a property. // The gtksink creates the gtk widget for us. This is accessible through a property.
// So we get it and use it later to add it to our gui. // So we get it and use it later to add it to our gui.
let widget = sink.property::<gtk::Widget>("widget"); let widget = sink.property::<gtk::Widget>("widget");

View file

@ -34,7 +34,7 @@ fn create_video_sink() -> gst::Element {
// When we are on linux with the Xorg display server, we use the // When we are on linux with the Xorg display server, we use the
// X11 protocol's XV extension, which allows to overlay regions // X11 protocol's XV extension, which allows to overlay regions
// with video streams. For this, we use the xvimagesink element. // with video streams. For this, we use the xvimagesink element.
gst::ElementFactory::make("xvimagesink", None).unwrap() gst::ElementFactory::make("xvimagesink").build().unwrap()
} }
#[cfg(all(target_os = "linux", feature = "gtkvideooverlay-x11"))] #[cfg(all(target_os = "linux", feature = "gtkvideooverlay-x11"))]
fn set_window_handle(video_overlay: &gst_video::VideoOverlay, gdk_window: &gdk::Window) { fn set_window_handle(video_overlay: &gst_video::VideoOverlay, gdk_window: &gdk::Window) {
@ -68,7 +68,7 @@ fn set_window_handle(video_overlay: &gst_video::VideoOverlay, gdk_window: &gdk::
fn create_video_sink() -> gst::Element { fn create_video_sink() -> gst::Element {
// On Mac, this is done by overlaying a window region with an // On Mac, this is done by overlaying a window region with an
// OpenGL-texture, using the glimagesink element. // OpenGL-texture, using the glimagesink element.
gst::ElementFactory::make("glimagesink", None).unwrap() gst::ElementFactory::make("glimagesink").build().unwrap()
} }
#[cfg(all(target_os = "macos", feature = "gtkvideooverlay-quartz"))] #[cfg(all(target_os = "macos", feature = "gtkvideooverlay-quartz"))]
@ -102,7 +102,7 @@ fn set_window_handle(video_overlay: &gst_video::VideoOverlay, gdk_window: &gdk::
fn create_ui(app: &gtk::Application) { fn create_ui(app: &gtk::Application) {
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap(); let src = gst::ElementFactory::make("videotestsrc").build().unwrap();
// Since using the window system to overlay our gui window is making // Since using the window system to overlay our gui window is making
// direct contact with the windowing system, this is highly platform- // direct contact with the windowing system, this is highly platform-

View file

@ -13,7 +13,7 @@ fn example_main() {
// Create and use an identity element here. // Create and use an identity element here.
// This element does nothing, really. We also never add it to a pipeline. // This element does nothing, really. We also never add it to a pipeline.
// We just want to iterate the identity element's pads. // We just want to iterate the identity element's pads.
let identity = gst::ElementFactory::make("identity", None).unwrap(); let identity = gst::ElementFactory::make("identity").build().unwrap();
// Get an iterator over all pads of the identity-element. // Get an iterator over all pads of the identity-element.
let mut iter = identity.iterate_pads(); let mut iter = identity.iterate_pads();
loop { loop {

View file

@ -21,10 +21,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -58,19 +54,14 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?; gst::init()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None)
.map_err(|_| MissingElement("videotestsrc"))?;
let overlay = gst::ElementFactory::make("overlaycomposition", None)
.map_err(|_| MissingElement("overlaycomposition"))?;
let capsfilter =
gst::ElementFactory::make("capsfilter", None).map_err(|_| MissingElement("capsfilter"))?;
let videoconvert = gst::ElementFactory::make("videoconvert", None)
.map_err(|_| MissingElement("videoconvert"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?; // The videotestsrc supports multiple test patterns. In this example, we will use the
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?; // pattern with a white ball moving around the video's center point.
let src = gst::ElementFactory::make("videotestsrc")
.property_from_str("pattern", "ball")
.build()?;
let overlay = gst::ElementFactory::make("overlaycomposition").build()?;
// Plug in a capsfilter element that will force the videotestsrc and the overlay to work // Plug in a capsfilter element that will force the videotestsrc and the overlay to work
// with images of the size 800x800, and framerate of 15 fps, since my laptop struggles // with images of the size 800x800, and framerate of 15 fps, since my laptop struggles
@ -80,11 +71,15 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.height(800) .height(800)
.framerate((15, 1).into()) .framerate((15, 1).into())
.build(); .build();
capsfilter.set_property("caps", &caps); let capsfilter = gst::ElementFactory::make("capsfilter")
.property("caps", &caps)
.build()?;
// The videotestsrc supports multiple test patterns. In this example, we will use the let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
// pattern with a white ball moving around the video's center point. let sink = gst::ElementFactory::make("autovideosink").build()?;
src.set_property_from_str("pattern", "ball");
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
// The PangoFontMap represents the set of fonts available for a particular rendering system. // The PangoFontMap represents the set of fonts available for a particular rendering system.
let fontmap = pangocairo::FontMap::new(); let fontmap = pangocairo::FontMap::new();

View file

@ -24,10 +24,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -61,19 +57,12 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?; gst::init()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None) let src = gst::ElementFactory::make("videotestsrc")
.map_err(|_| MissingElement("videotestsrc"))?; // The videotestsrc supports multiple test patterns. In this example, we will use the
let overlay = gst::ElementFactory::make("cairooverlay", None) // pattern with a white ball moving around the video's center point.
.map_err(|_| MissingElement("cairooverlay"))?; .property_from_str("pattern", "ball")
let capsfilter = .build()?;
gst::ElementFactory::make("capsfilter", None).map_err(|_| MissingElement("capsfilter"))?; let overlay = gst::ElementFactory::make("cairooverlay").build()?;
let videoconvert = gst::ElementFactory::make("videoconvert", None)
.map_err(|_| MissingElement("videoconvert"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
// Plug in a capsfilter element that will force the videotestsrc and the cairooverlay to work // Plug in a capsfilter element that will force the videotestsrc and the cairooverlay to work
// with images of the size 800x800. // with images of the size 800x800.
@ -81,11 +70,15 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.width(800) .width(800)
.height(800) .height(800)
.build(); .build();
capsfilter.set_property("caps", &caps); let capsfilter = gst::ElementFactory::make("capsfilter")
.property("caps", &caps)
.build()?;
// The videotestsrc supports multiple test patterns. In this example, we will use the let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
// pattern with a white ball moving around the video's center point. let sink = gst::ElementFactory::make("autovideosink").build()?;
src.set_property_from_str("pattern", "ball");
pipeline.add_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
// The PangoFontMap represents the set of fonts available for a particular rendering system. // The PangoFontMap represents the set of fonts available for a particular rendering system.
let fontmap = pangocairo::FontMap::new(); let fontmap = pangocairo::FontMap::new();

View file

@ -28,8 +28,10 @@ fn example_main() {
}; };
// Create a new playbin element, and tell it what uri to play back. // Create a new playbin element, and tell it what uri to play back.
let playbin = gst::ElementFactory::make("playbin", None).unwrap(); let playbin = gst::ElementFactory::make("playbin")
playbin.set_property("uri", uri); .property("uri", uri)
.build()
.unwrap();
// For flags handling // For flags handling
// With flags, one can configure playbin's behavior such as whether it // With flags, one can configure playbin's behavior such as whether it

View file

@ -9,10 +9,6 @@ mod examples_common;
use anyhow::Error; use anyhow::Error;
use derive_more::{Display, Error}; use derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "No such pad {} in {}", _0, _1)] #[display(fmt = "No such pad {} in {}", _0, _1)]
struct NoSuchPad(#[error(not(source))] &'static str, String); struct NoSuchPad(#[error(not(source))] &'static str, String);
@ -34,17 +30,6 @@ struct ErrorMessage {
source: glib::Error, source: glib::Error,
} }
fn make_element(
factory_name: &'static str,
element_name: Option<&str>,
) -> Result<gst::Element, Error> {
match gst::ElementFactory::make(factory_name, element_name) {
Ok(elem) => Ok(elem),
Err(_) => Err(Error::from(MissingElement(factory_name))),
}
}
#[doc(alias = "get_static_pad")]
fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> { fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> {
match element.static_pad(pad_name) { match element.static_pad(pad_name) {
Some(pad) => Ok(pad), Some(pad) => Ok(pad),
@ -55,7 +40,6 @@ fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad
} }
} }
#[doc(alias = "get_request_pad")]
fn request_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> { fn request_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> {
match element.request_pad_simple(pad_name) { match element.request_pad_simple(pad_name) {
Some(pad) => Ok(pad), Some(pad) => Ok(pad),
@ -83,11 +67,11 @@ fn connect_rtpbin_srcpad(src_pad: &gst::Pad, sink: &gst::Element) -> Result<(),
} }
fn make_fec_decoder(rtpbin: &gst::Element, sess_id: u32) -> Result<gst::Element, Error> { fn make_fec_decoder(rtpbin: &gst::Element, sess_id: u32) -> Result<gst::Element, Error> {
let fecdec = make_element("rtpulpfecdec", None)?;
let internal_storage = rtpbin.emit_by_name::<glib::Object>("get-internal-storage", &[&sess_id]); let internal_storage = rtpbin.emit_by_name::<glib::Object>("get-internal-storage", &[&sess_id]);
let fecdec = gst::ElementFactory::make("rtpulpfecdec")
fecdec.set_property("storage", &internal_storage); .property("storage", &internal_storage)
fecdec.set_property("pt", 100u32); .property("pt", 100u32)
.build()?;
Ok(fecdec) Ok(fecdec)
} }
@ -104,33 +88,54 @@ fn example_main() -> Result<(), Error> {
let drop_probability = args[2].parse::<f32>()?; let drop_probability = args[2].parse::<f32>()?;
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = make_element("udpsrc", None)?;
let netsim = make_element("netsim", None)?; let rtp_caps = gst::Caps::builder("application/x-rtp")
let rtpbin = make_element("rtpbin", None)?; .field("clock-rate", 90000i32)
let depay = make_element("rtpvp8depay", None)?; .build();
let dec = make_element("vp8dec", None)?;
let conv = make_element("videoconvert", None)?; let video_caps = gst_video::VideoCapsBuilder::new()
let scale = make_element("videoscale", None)?; .width(1920)
let filter = make_element("capsfilter", None)?; .height(1080)
.build();
let src = gst::ElementFactory::make("udpsrc")
.property("address", "127.0.0.1")
.property("caps", &rtp_caps)
.build()?;
let netsim = gst::ElementFactory::make("netsim")
.property("drop-probability", drop_probability)
.build()?;
let rtpbin = gst::ElementFactory::make("rtpbin")
.property("do-lost", true)
.build()?;
let depay = gst::ElementFactory::make("rtpvp8depay").build()?;
let dec = gst::ElementFactory::make("vp8dec").build()?;
let conv = gst::ElementFactory::make("videoconvert").build()?;
let scale = gst::ElementFactory::make("videoscale").build()?;
let filter = gst::ElementFactory::make("capsfilter")
.property("caps", &video_caps)
.build()?;
pipeline.add_many(&[&src, &netsim, &rtpbin, &depay, &dec, &conv, &scale, &filter])?; pipeline.add_many(&[&src, &netsim, &rtpbin, &depay, &dec, &conv, &scale, &filter])?;
gst::Element::link_many(&[&depay, &dec, &conv, &scale, &filter])?; gst::Element::link_many(&[&depay, &dec, &conv, &scale, &filter])?;
match args[1].as_str() { match args[1].as_str() {
"play" => { "play" => {
let sink = make_element("autovideosink", None)?; let sink = gst::ElementFactory::make("autovideosink").build()?;
pipeline.add(&sink)?; pipeline.add(&sink)?;
filter.link(&sink)?; filter.link(&sink)?;
} }
"record" => { "record" => {
let enc = make_element("x264enc", None)?; let enc = gst::ElementFactory::make("x264enc")
let mux = make_element("matroskamux", None)?; .property_from_str("tune", "zerolatency")
let sink = make_element("filesink", None)?; .build()?;
let mux = gst::ElementFactory::make("matroskamux").build()?;
let sink = gst::ElementFactory::make("filesink")
.property("location", "out.mkv")
.build()?;
pipeline.add_many(&[&enc, &mux, &sink])?; pipeline.add_many(&[&enc, &mux, &sink])?;
gst::Element::link_many(&[&filter, &enc, &mux, &sink])?; gst::Element::link_many(&[&filter, &enc, &mux, &sink])?;
sink.set_property("location", "out.mkv");
enc.set_property_from_str("tune", "zerolatency");
eprintln!("Recording to out.mkv"); eprintln!("Recording to out.mkv");
} }
_ => return Err(Error::from(UsageError(args[0].clone()))), _ => return Err(Error::from(UsageError(args[0].clone()))),
@ -218,21 +223,6 @@ fn example_main() -> Result<(), Error> {
} }
}); });
let rtp_caps = gst::Caps::builder("application/x-rtp")
.field("clock-rate", 90000i32)
.build();
let video_caps = gst_video::VideoCapsBuilder::new()
.width(1920)
.height(1080)
.build();
src.set_property("address", "127.0.0.1");
src.set_property("caps", &rtp_caps);
netsim.set_property("drop-probability", drop_probability);
rtpbin.set_property("do-lost", true);
filter.set_property("caps", &video_caps);
let bus = pipeline let bus = pipeline
.bus() .bus()
.expect("Pipeline without bus. Shouldn't happen!"); .expect("Pipeline without bus. Shouldn't happen!");

View file

@ -9,10 +9,6 @@ use std::env;
use anyhow::Error; use anyhow::Error;
use derive_more::{Display, Error}; use derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "No such pad {} in {}", _0, _1)] #[display(fmt = "No such pad {} in {}", _0, _1)]
struct NoSuchPad(&'static str, String); struct NoSuchPad(&'static str, String);
@ -30,17 +26,6 @@ struct ErrorMessage {
source: glib::Error, source: glib::Error,
} }
fn make_element(
factory_name: &'static str,
element_name: Option<&str>,
) -> Result<gst::Element, Error> {
match gst::ElementFactory::make(factory_name, element_name) {
Ok(elem) => Ok(elem),
Err(_) => Err(Error::from(MissingElement(factory_name))),
}
}
#[doc(alias = "get_static_pad")]
fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> { fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> {
match element.static_pad(pad_name) { match element.static_pad(pad_name) {
Some(pad) => Ok(pad), Some(pad) => Ok(pad),
@ -51,7 +36,6 @@ fn static_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad
} }
} }
#[doc(alias = "get_request_pad")]
fn request_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> { fn request_pad(element: &gst::Element, pad_name: &'static str) -> Result<gst::Pad, Error> {
match element.request_pad_simple(pad_name) { match element.request_pad_simple(pad_name) {
Some(pad) => Ok(pad), Some(pad) => Ok(pad),
@ -70,11 +54,11 @@ fn connect_decodebin_pad(src_pad: &gst::Pad, sink: &gst::Element) -> Result<(),
} }
fn make_fec_encoder(fec_percentage: u32) -> Result<gst::Element, Error> { fn make_fec_encoder(fec_percentage: u32) -> Result<gst::Element, Error> {
let fecenc = make_element("rtpulpfecenc", None)?; let fecenc = gst::ElementFactory::make("rtpulpfecenc")
.property("pt", 100u32)
fecenc.set_property("pt", 100u32); .property("multipacket", true)
fecenc.set_property("multipacket", true); .property("percentage", fec_percentage)
fecenc.set_property("percentage", fec_percentage); .build()?;
Ok(fecenc) Ok(fecenc)
} }
@ -91,15 +75,31 @@ fn example_main() -> Result<(), Error> {
let uri = &args[1]; let uri = &args[1];
let fec_percentage = args[2].parse::<u32>()?; let fec_percentage = args[2].parse::<u32>()?;
let video_caps = gst::Caps::builder("video/x-raw").build();
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = make_element("uridecodebin", None)?; let src = gst::ElementFactory::make("uridecodebin")
let conv = make_element("videoconvert", None)?; .property_from_str("pattern", "ball")
let q1 = make_element("queue", None)?; .property("expose-all-streams", false)
let enc = make_element("vp8enc", None)?; .property("caps", video_caps)
let q2 = make_element("queue", None)?; .property("uri", uri)
let pay = make_element("rtpvp8pay", None)?; .build()?;
let rtpbin = make_element("rtpbin", None)?; let conv = gst::ElementFactory::make("videoconvert").build()?;
let sink = make_element("udpsink", None)?; let q1 = gst::ElementFactory::make("queue").build()?;
let enc = gst::ElementFactory::make("vp8enc")
.property("keyframe-max-dist", 30i32)
.property("threads", 12i32)
.property("cpu-used", -16i32)
.property("deadline", 1i64)
.property_from_str("error-resilient", "default")
.build()?;
let q2 = gst::ElementFactory::make("queue").build()?;
let pay = gst::ElementFactory::make("rtpvp8pay").build()?;
let rtpbin = gst::ElementFactory::make("rtpbin").build()?;
let sink = gst::ElementFactory::make("udpsink")
.property("host", "127.0.0.1")
.property("sync", true)
.build()?;
pipeline.add_many(&[&src, &conv, &q1, &enc, &q2, &pay, &rtpbin, &sink])?; pipeline.add_many(&[&src, &conv, &q1, &enc, &q2, &pay, &rtpbin, &sink])?;
@ -149,20 +149,6 @@ fn example_main() -> Result<(), Error> {
}, },
); );
let video_caps = gst::Caps::builder("video/x-raw").build();
src.set_property_from_str("pattern", "ball");
sink.set_property("host", "127.0.0.1");
sink.set_property("sync", true);
enc.set_property("keyframe-max-dist", 30i32);
enc.set_property("threads", 12i32);
enc.set_property("cpu-used", -16i32);
enc.set_property("deadline", 1i64);
enc.set_property_from_str("error-resilient", "default");
src.set_property("expose-all-streams", false);
src.set_property("caps", video_caps);
src.set_property("uri", uri);
let bus = pipeline let bus = pipeline
.bus() .bus()
.expect("Pipeline without bus. Shouldn't happen!"); .expect("Pipeline without bus. Shouldn't happen!");

View file

@ -121,17 +121,22 @@ mod media_factory {
fn create_element(&self, _url: &gst_rtsp::RTSPUrl) -> Option<gst::Element> { fn create_element(&self, _url: &gst_rtsp::RTSPUrl) -> Option<gst::Element> {
// Create a simple VP8 videotestsrc input // Create a simple VP8 videotestsrc input
let bin = gst::Bin::new(None); let bin = gst::Bin::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap(); let src = gst::ElementFactory::make("videotestsrc")
let enc = gst::ElementFactory::make("vp8enc", None).unwrap(); // Configure the videotestsrc live
.property("is-live", true)
.build()
.unwrap();
let enc = gst::ElementFactory::make("vp8enc")
// Produce encoded data as fast as possible
.property("deadline", 1i64)
.build()
.unwrap();
// The names of the payloaders must be payX // The names of the payloaders must be payX
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap(); let pay = gst::ElementFactory::make("rtpvp8pay")
.name("pay0")
// Configure the videotestsrc live .build()
src.set_property("is-live", true); .unwrap();
// Produce encoded data as fast as possible
enc.set_property("deadline", 1i64);
bin.add_many(&[&src, &enc, &pay]).unwrap(); bin.add_many(&[&src, &enc, &pay]).unwrap();
gst::Element::link_many(&[&src, &enc, &pay]).unwrap(); gst::Element::link_many(&[&src, &enc, &pay]).unwrap();

View file

@ -231,10 +231,6 @@ mod fir_filter {
} }
} }
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -249,21 +245,18 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// Create our pipeline with the custom element // Create our pipeline with the custom element
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("audiotestsrc", None) let src = gst::ElementFactory::make("audiotestsrc")
.map_err(|_| MissingElement("audiotestsrc"))?; .property_from_str("wave", "white-noise")
.build()?;
let filter = fir_filter::FirFilter::new(None); let filter = fir_filter::FirFilter::new(None);
let conv = gst::ElementFactory::make("audioconvert", None) let conv = gst::ElementFactory::make("audioconvert").build()?;
.map_err(|_| MissingElement("audioconvert"))?; let sink = gst::ElementFactory::make("autoaudiosink").build()?;
let sink = gst::ElementFactory::make("autoaudiosink", None)
.map_err(|_| MissingElement("autoaudiosink"))?;
pipeline.add_many(&[&src, filter.upcast_ref(), &conv, &sink])?; pipeline.add_many(&[&src, filter.upcast_ref(), &conv, &sink])?;
src.link(&filter)?; src.link(&filter)?;
filter.link(&conv)?; filter.link(&conv)?;
conv.link(&sink)?; conv.link(&sink)?;
src.set_property_from_str("wave", "white-noise");
// Create a windowed sinc lowpass filter at 1/64 sample rate, // Create a windowed sinc lowpass filter at 1/64 sample rate,
// i.e. 689Hz for 44.1kHz sample rate // i.e. 689Hz for 44.1kHz sample rate
let w = 2.0 * std::f32::consts::PI / 64.0; let w = 2.0 * std::f32::consts::PI / 64.0;

View file

@ -16,10 +16,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {

View file

@ -28,10 +28,11 @@ fn example_main() {
}; };
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("filesrc", None).unwrap(); let src = gst::ElementFactory::make("filesrc")
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap(); .property("location", uri)
.build()
src.set_property("location", uri); .unwrap();
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
pipeline.add_many(&[&src, &decodebin]).unwrap(); pipeline.add_many(&[&src, &decodebin]).unwrap();
gst::Element::link_many(&[&src, &decodebin]).unwrap(); gst::Element::link_many(&[&src, &decodebin]).unwrap();
@ -59,8 +60,8 @@ fn example_main() {
// In this example, we are only interested about parsing the ToC, so // In this example, we are only interested about parsing the ToC, so
// we simply pipe every encountered stream into a fakesink, essentially // we simply pipe every encountered stream into a fakesink, essentially
// throwing away the data. // throwing away the data.
let queue = gst::ElementFactory::make("queue", None).unwrap(); let queue = gst::ElementFactory::make("queue").build().unwrap();
let sink = gst::ElementFactory::make("fakesink", None).unwrap(); let sink = gst::ElementFactory::make("fakesink").build().unwrap();
let elements = &[&queue, &sink]; let elements = &[&queue, &sink];
pipeline.add_many(elements).unwrap(); pipeline.add_many(elements).unwrap();

View file

@ -28,10 +28,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"] #[path = "../examples-common.rs"]
mod examples_common; mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -59,20 +55,18 @@ fn example_main() -> Result<(), Error> {
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::Element::make_from_uri(gst::URIType::Src, uri, None) let src = gst::Element::make_from_uri(gst::URIType::Src, uri, None)
.expect("We do not seem to support this uri"); .expect("We do not seem to support this uri");
let typefinder = let typefinder = gst::ElementFactory::make("typefind").build()?;
gst::ElementFactory::make("typefind", None).map_err(|_| MissingElement("typefind"))?; let queue = gst::ElementFactory::make("multiqueue")
let queue = .property("max-size-buffers", 0u32)
gst::ElementFactory::make("multiqueue", None).map_err(|_| MissingElement("multiqueue"))?; .property("max-size-time", 0u64)
let muxer = gst::ElementFactory::make("matroskamux", None) .property("max-size-bytes", 1024u32 * 1024 * 100)
.map_err(|_| MissingElement("matroskamux"))?; .build()?;
let sink = let muxer = gst::ElementFactory::make("matroskamux").build()?;
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?; let sink = gst::ElementFactory::make("filesink")
.property("location", output_file)
.build()?;
sink.set_property("location", output_file);
// Increase the queue capacity to 100MB to avoid a stalling pipeline // Increase the queue capacity to 100MB to avoid a stalling pipeline
queue.set_property("max-size-buffers", 0u32);
queue.set_property("max-size-time", 0u64);
queue.set_property("max-size-bytes", 1024u32 * 1024 * 100);
pipeline pipeline
.add_many(&[&src, &typefinder, &queue, &muxer, &sink]) .add_many(&[&src, &typefinder, &queue, &muxer, &sink])
@ -96,12 +90,12 @@ fn example_main() -> Result<(), Error> {
let format_name = caps.structure(0).expect("Failed to get format name").name(); let format_name = caps.structure(0).expect("Failed to get format name").name();
let demuxer = match format_name { let demuxer = match format_name {
"video/x-matroska" | "video/webm" => { "video/x-matroska" | "video/webm" => gst::ElementFactory::make("matroskademux")
gst::ElementFactory::make("matroskademux", None).expect("matroskademux missing") .build()
} .expect("matroskademux missing"),
"video/quicktime" => { "video/quicktime" => gst::ElementFactory::make("qtdemux")
gst::ElementFactory::make("qtdemux", None).expect("qtdemux missing") .build()
} .expect("qtdemux missing"),
_ => { _ => {
eprintln!("Sorry, this format is not supported by this example."); eprintln!("Sorry, this format is not supported by this example.");
std::process::exit(-1); std::process::exit(-1);

View file

@ -16,10 +16,6 @@ use std::sync;
use anyhow::Error; use anyhow::Error;
use derive_more::{Display, Error}; use derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)] #[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage { struct ErrorMessage {
@ -535,18 +531,15 @@ impl App {
gl_element: Option<&gst::Element>, gl_element: Option<&gst::Element>,
) -> Result<(gst::Pipeline, gst_app::AppSink, gst::Element), Error> { ) -> Result<(gst::Pipeline, gst_app::AppSink, gst::Element), Error> {
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None) let src = gst::ElementFactory::make("videotestsrc").build()?;
.map_err(|_| MissingElement("videotestsrc"))?;
let appsink = gst::ElementFactory::make("appsink", None) let appsink = gst::ElementFactory::make("appsink")
.map_err(|_| MissingElement("appsink"))? .build()?
.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!");
appsink.set_property("enable-last-sample", false); appsink.set_enable_last_sample(true);
appsink.set_property("emit-signals", false); appsink.set_max_buffers(1);
appsink.set_property("max-buffers", 1u32);
let caps = gst_video::VideoCapsBuilder::new() let caps = gst_video::VideoCapsBuilder::new()
.features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY]) .features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY])
.format(gst_video::VideoFormat::Rgba) .format(gst_video::VideoFormat::Rgba)
@ -555,8 +548,7 @@ impl App {
appsink.set_caps(Some(&caps)); appsink.set_caps(Some(&caps));
if let Some(gl_element) = gl_element { if let Some(gl_element) = gl_element {
let glupload = gst::ElementFactory::make("glupload", None) let glupload = gst::ElementFactory::make("glupload").build()?;
.map_err(|_| MissingElement("glupload"))?;
pipeline.add_many(&[&src, &glupload])?; pipeline.add_many(&[&src, &glupload])?;
pipeline.add(gl_element)?; pipeline.add(gl_element)?;
@ -568,10 +560,9 @@ impl App {
Ok((pipeline, appsink, glupload)) Ok((pipeline, appsink, glupload))
} else { } else {
let sink = gst::ElementFactory::make("glsinkbin", None) let sink = gst::ElementFactory::make("glsinkbin")
.map_err(|_| MissingElement("glsinkbin"))?; .property("sink", &appsink)
.build()?;
sink.set_property("sink", &appsink);
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?; src.link(&sink)?;

View file

@ -1006,10 +1006,11 @@ mod tests {
fn test_app_sink_stream() { fn test_app_sink_stream() {
gst::init().unwrap(); gst::init().unwrap();
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap(); let videotestsrc = gst::ElementFactory::make("videotestsrc")
let appsink = gst::ElementFactory::make("appsink", None).unwrap(); .property("num-buffers", 5)
.build()
videotestsrc.set_property("num-buffers", 5); .unwrap();
let appsink = gst::ElementFactory::make("appsink").build().unwrap();
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
pipeline.add(&videotestsrc).unwrap(); pipeline.add(&videotestsrc).unwrap();

View file

@ -458,10 +458,11 @@ mod tests {
fn test_app_src_sink() { fn test_app_src_sink() {
gst::init().unwrap(); gst::init().unwrap();
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap(); let appsrc = gst::ElementFactory::make("appsrc").build().unwrap();
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap(); let fakesink = gst::ElementFactory::make("fakesink")
.property("signal-handoffs", true)
fakesink.set_property("signal-handoffs", true); .build()
.unwrap();
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
pipeline.add(&appsrc).unwrap(); pipeline.add(&appsrc).unwrap();

View file

@ -1248,13 +1248,13 @@ status = "generate"
final_type = true final_type = true
[[object.function]] [[object.function]]
name = "create" name = "create"
[object.function.return] rename = "create_with_name"
nullable_return_is_error = "Failed to create element from factory" manual = true
[[object.function]] [[object.function]]
name = "make" name = "make"
[object.function.return] rename = "make_with_name"
nullable_return_is_error = "Failed to create element from factory name" manual = true
[[object.function]] [[object.function]]
name = "create_full" name = "create_full"

View file

@ -4,7 +4,6 @@
// DO NOT EDIT // DO NOT EDIT
use crate::Caps; use crate::Caps;
use crate::Element;
use crate::Object; use crate::Object;
use crate::PluginFeature; use crate::PluginFeature;
use crate::URIType; use crate::URIType;
@ -60,17 +59,6 @@ impl ElementFactory {
} }
} }
#[doc(alias = "gst_element_factory_create")]
pub fn create(&self, name: Option<&str>) -> Result<Element, glib::BoolError> {
unsafe {
Option::<_>::from_glib_none(ffi::gst_element_factory_create(
self.to_glib_none().0,
name.to_glib_none().0,
))
.ok_or_else(|| glib::bool_error!("Failed to create element from factory"))
}
}
#[doc(alias = "gst_element_factory_get_element_type")] #[doc(alias = "gst_element_factory_get_element_type")]
#[doc(alias = "get_element_type")] #[doc(alias = "get_element_type")]
pub fn element_type(&self) -> glib::types::Type { pub fn element_type(&self) -> glib::types::Type {
@ -140,18 +128,6 @@ impl ElementFactory {
assert_initialized_main_thread!(); assert_initialized_main_thread!();
unsafe { from_glib_full(ffi::gst_element_factory_find(name.to_glib_none().0)) } unsafe { from_glib_full(ffi::gst_element_factory_find(name.to_glib_none().0)) }
} }
#[doc(alias = "gst_element_factory_make")]
pub fn make(factoryname: &str, name: Option<&str>) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!();
unsafe {
Option::<_>::from_glib_none(ffi::gst_element_factory_make(
factoryname.to_glib_none().0,
name.to_glib_none().0,
))
.ok_or_else(|| glib::bool_error!("Failed to create element from factory name"))
}
}
} }
unsafe impl Send for ElementFactory {} unsafe impl Send for ElementFactory {}

View file

@ -246,10 +246,20 @@ mod tests {
crate::init().unwrap(); crate::init().unwrap();
let bin = crate::Bin::new(None); let bin = crate::Bin::new(None);
bin.add(&crate::ElementFactory::make("identity", Some("identity0")).unwrap()) bin.add(
.unwrap(); &crate::ElementFactory::make("identity")
bin.add(&crate::ElementFactory::make("identity", Some("identity1")).unwrap()) .name("identity0")
.unwrap(); .build()
.unwrap(),
)
.unwrap();
bin.add(
&crate::ElementFactory::make("identity")
.name("identity1")
.build()
.unwrap(),
)
.unwrap();
let mut child_names = bin let mut child_names = bin
.children() .children()

View file

@ -1595,7 +1595,7 @@ mod tests {
fn test_get_pads() { fn test_get_pads() {
crate::init().unwrap(); crate::init().unwrap();
let identity = crate::ElementFactory::make("identity", None).unwrap(); let identity = crate::ElementFactory::make("identity").build().unwrap();
let mut pad_names = identity let mut pad_names = identity
.pads() .pads()
@ -1626,7 +1626,7 @@ mod tests {
fn test_foreach_pad() { fn test_foreach_pad() {
crate::init().unwrap(); crate::init().unwrap();
let identity = crate::ElementFactory::make("identity", None).unwrap(); let identity = crate::ElementFactory::make("identity").build().unwrap();
let mut pad_names = Vec::new(); let mut pad_names = Vec::new();
identity.foreach_pad(|_element, pad| { identity.foreach_pad(|_element, pad| {
@ -1642,7 +1642,7 @@ mod tests {
fn test_call_async() { fn test_call_async() {
crate::init().unwrap(); crate::init().unwrap();
let identity = crate::ElementFactory::make("identity", None).unwrap(); let identity = crate::ElementFactory::make("identity").build().unwrap();
let (sender, receiver) = channel(); let (sender, receiver) = channel();
identity.call_async(move |_| { identity.call_async(move |_| {

View file

@ -25,64 +25,12 @@ impl ElementFactory {
) -> Result<Element, glib::BoolError> { ) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!(); assert_initialized_main_thread!();
// The below is basically a reimplementation of the C function. We want to call let mut builder = self.create();
// glib::Object::with_type() ourselves here for checking properties and their values builder.properties = properties
// correctly and to provide consistent behaviour. .iter()
use crate::prelude::{ .map(|(name, value)| (*name, ValueOrStr::Value(value.to_value())))
ElementExtManual, GstObjectExt, GstObjectExtManual, PluginFeatureExtManual, .collect();
}; builder.build()
let factory = self.load().map_err(|_| {
crate::warning!(crate::CAT_RUST, obj: self, "loading plugin returned None");
glib::bool_error!("Failed to create element from factory")
})?;
let element_type = factory.element_type();
if !element_type.is_valid() {
crate::warning!(crate::CAT_RUST, obj: self, "factory has no type");
return Err(glib::bool_error!("Failed to create element from factory"));
}
let element = glib::Object::with_type(element_type, properties)
.downcast::<crate::Element>()
.unwrap();
unsafe {
use std::sync::atomic;
let klass = element.element_class();
let factory_ptr: &atomic::AtomicPtr<ffi::GstElementFactory> =
&*(&klass.as_ref().elementfactory as *const *mut ffi::GstElementFactory
as *const atomic::AtomicPtr<ffi::GstElementFactory>);
if factory_ptr
.compare_exchange(
std::ptr::null_mut(),
factory.as_ptr(),
atomic::Ordering::SeqCst,
atomic::Ordering::SeqCst,
)
.is_ok()
{
factory.set_object_flags(crate::ObjectFlags::MAY_BE_LEAKED);
}
if glib::gobject_ffi::g_object_is_floating(factory.as_ptr() as *mut _)
!= glib::ffi::GFALSE
{
glib::g_critical!(
"GStreamer",
"The created element should be floating, this is probably caused by faulty bindings",
);
}
}
crate::log!(
crate::CAT_RUST,
obj: self,
"created element \"{}\"",
factory.name()
);
Ok(element)
} }
#[doc(alias = "gst_element_factory_make_with_properties")] #[doc(alias = "gst_element_factory_make_with_properties")]
@ -93,22 +41,59 @@ impl ElementFactory {
) -> Result<Element, glib::BoolError> { ) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!(); assert_initialized_main_thread!();
crate::log!( let mut builder = Self::make(factoryname);
crate::CAT_RUST, builder.properties = properties
"gstelementfactory: make \"{}\"", .iter()
factoryname .map(|(name, value)| (*name, ValueOrStr::Value(value.to_value())))
); .collect();
builder.build()
}
let factory = Self::find(factoryname).ok_or_else(|| { #[doc(alias = "gst_element_factory_create")]
crate::warning!( #[doc(alias = "gst_element_factory_create_with_properties")]
crate::CAT_RUST, #[track_caller]
"gstelementfactory: make \"{}\"", pub fn create(&self) -> ElementBuilder {
factoryname ElementBuilder {
); name_or_factory: NameOrFactory::Factory(self),
glib::bool_error!("Failed to create element from factory name") properties: Vec::new(),
})?; }
}
factory.create_with_properties(properties) #[doc(alias = "gst_element_factory_make")]
#[doc(alias = "gst_element_factory_make_with_properties")]
#[track_caller]
pub fn make(factoryname: &str) -> ElementBuilder {
assert_initialized_main_thread!();
ElementBuilder {
name_or_factory: NameOrFactory::Name(factoryname),
properties: Vec::new(),
}
}
#[doc(alias = "gst_element_factory_create")]
#[track_caller]
pub fn create_with_name(&self, name: Option<&str>) -> Result<Element, glib::BoolError> {
let mut builder = self.create();
if let Some(name) = name {
builder = builder.name(name);
}
builder.build()
}
#[doc(alias = "gst_element_factory_make")]
#[track_caller]
pub fn make_with_name(
factoryname: &str,
name: Option<&str>,
) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!();
let mut builder = Self::make(factoryname);
if let Some(name) = name {
builder = builder.name(name);
}
builder.build()
} }
#[doc(alias = "gst_element_factory_get_static_pad_templates")] #[doc(alias = "gst_element_factory_get_static_pad_templates")]
@ -196,3 +181,219 @@ impl ElementFactory {
self.metadata(&ELEMENT_METADATA_ICON_NAME) self.metadata(&ELEMENT_METADATA_ICON_NAME)
} }
} }
// rustdoc-stripper-ignore-next
/// Builder for `Element`s.
#[must_use = "The builder must be built to be used"]
pub struct ElementBuilder<'a> {
name_or_factory: NameOrFactory<'a>,
properties: Vec<(&'a str, ValueOrStr<'a>)>,
}
#[derive(Copy, Clone)]
enum NameOrFactory<'a> {
Name(&'a str),
Factory(&'a ElementFactory),
}
enum ValueOrStr<'a> {
Value(glib::Value),
Str(&'a str),
}
impl<'a> ElementBuilder<'a> {
// rustdoc-stripper-ignore-next
/// Sets the name property to the given `name`.
pub fn name(self, name: &'a str) -> Self {
self.property("name", name)
}
// rustdoc-stripper-ignore-next
/// Set property `name` to the given value `value`.
pub fn property<T: glib::ToValue + 'a>(self, name: &'a str, value: T) -> Self {
Self {
name_or_factory: self.name_or_factory,
properties: {
let mut properties = self.properties;
properties.push((name, ValueOrStr::Value(value.to_value())));
properties
},
}
}
// rustdoc-stripper-ignore-next
/// Set property `name` to the given string value `value`.
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
Self {
name_or_factory: self.name_or_factory,
properties: {
let mut properties = self.properties;
properties.push((name, ValueOrStr::Str(value)));
properties
},
}
}
// rustdoc-stripper-ignore-next
/// Build the element with the provided properties.
///
/// This fails if there is no such element factory or the element factory can't be loaded.
///
/// # Panics
///
/// This panics if the element is not instantiable, doesn't have all the given properties or
/// property values of the wrong type are provided.
#[track_caller]
#[must_use = "Building the element without using it has no effect"]
pub fn build(self) -> Result<Element, glib::BoolError> {
let mut _factory_found = None;
let factory = match self.name_or_factory {
NameOrFactory::Name(name) => {
let factory = ElementFactory::find(name).ok_or_else(|| {
crate::warning!(crate::CAT_RUST, "element factory '{}' not found", name);
glib::bool_error!(
"Failed to find element factory with name '{}' for creating element",
name
)
})?;
_factory_found = Some(factory);
_factory_found.as_ref().unwrap()
}
NameOrFactory::Factory(factory) => factory,
};
// The below is basically a reimplementation of the C function. We want to call
// glib::Object::with_type() ourselves here for checking properties and their values
// correctly and to provide consistent behaviour.
use crate::prelude::{
ElementExtManual, GstObjectExt, GstObjectExtManual, PluginFeatureExtManual,
};
let factory = factory.load().map_err(|_| {
crate::warning!(
crate::CAT_RUST,
obj: factory,
"loading element factory '{}' failed",
factory.name(),
);
glib::bool_error!(
"Failed to load element factory '{}' for creating element",
factory.name()
)
})?;
let element_type = factory.element_type();
if !element_type.is_valid() {
crate::warning!(
crate::CAT_RUST,
obj: &factory,
"element factory '{}' has no type",
factory.name()
);
return Err(glib::bool_error!(
"Failed to create element from factory '{}'",
factory.name()
));
}
let mut properties = Vec::with_capacity(self.properties.len());
let klass = glib::Class::<Element>::from_type(element_type).unwrap();
for (name, value) in self.properties {
match value {
ValueOrStr::Value(value) => {
properties.push((name, value));
}
ValueOrStr::Str(value) => {
use crate::value::GstValueExt;
let pspec = match klass.find_property(name) {
Some(pspec) => pspec,
None => {
panic!(
"property '{}' of element factory '{}' not found",
name,
factory.name()
);
}
};
let value = {
if pspec.value_type() == crate::Structure::static_type() && value == "NULL"
{
None::<crate::Structure>.to_value()
} else {
#[cfg(feature = "v1_20")]
{
glib::Value::deserialize_with_pspec(value, &pspec)
.unwrap_or_else(|_| {
panic!(
"property '{}' of element factory '{}' can't be set from string '{}'",
name,
factory.name(),
value,
)
})
}
#[cfg(not(feature = "v1_20"))]
{
glib::Value::deserialize(value, pspec.value_type())
.unwrap_or_else(|_| {
panic!(
"property '{}' of element factory '{}' can't be set from string '{}'",
name,
factory.name(),
value,
)
})
}
}
};
properties.push((name, value));
}
}
}
let element = glib::Object::with_values(element_type, &properties)
.downcast::<crate::Element>()
.unwrap();
unsafe {
use std::sync::atomic;
let klass = element.element_class();
let factory_ptr: &atomic::AtomicPtr<ffi::GstElementFactory> =
&*(&klass.as_ref().elementfactory as *const *mut ffi::GstElementFactory
as *const atomic::AtomicPtr<ffi::GstElementFactory>);
if factory_ptr
.compare_exchange(
std::ptr::null_mut(),
factory.as_ptr(),
atomic::Ordering::SeqCst,
atomic::Ordering::SeqCst,
)
.is_ok()
{
factory.set_object_flags(crate::ObjectFlags::MAY_BE_LEAKED);
}
if glib::gobject_ffi::g_object_is_floating(factory.as_ptr() as *mut _)
!= glib::ffi::GFALSE
{
glib::g_critical!(
"GStreamer",
"The created element should be floating, this is probably caused by faulty bindings",
);
}
}
crate::log!(
crate::CAT_RUST,
obj: &factory,
"created element \"{}\"",
factory.name()
);
Ok(element)
}
}

View file

@ -21,11 +21,25 @@ impl<O: IsA<glib::Object>> GObjectExtManualGst for O {
} else { } else {
#[cfg(feature = "v1_20")] #[cfg(feature = "v1_20")]
{ {
glib::Value::deserialize_with_pspec(value, &pspec).unwrap() glib::Value::deserialize_with_pspec(value, &pspec).unwrap_or_else(|_| {
panic!(
"property '{}' of type '{}' can't be set from string '{}'",
name,
self.type_(),
value,
)
})
} }
#[cfg(not(feature = "v1_20"))] #[cfg(not(feature = "v1_20"))]
{ {
glib::Value::deserialize(value, pspec.value_type()).unwrap() glib::Value::deserialize(value, pspec.value_type()).unwrap_or_else(|_| {
panic!(
"property '{}' of type '{}' can't be set from string '{}'",
name,
self.type_(),
value,
)
})
} }
} }
}; };
@ -42,7 +56,7 @@ mod tests {
fn test_set_property_from_str() { fn test_set_property_from_str() {
crate::init().unwrap(); crate::init().unwrap();
let fakesink = crate::ElementFactory::make("fakesink", None).unwrap(); let fakesink = crate::ElementFactory::make("fakesink").build().unwrap();
fakesink.set_property_from_str("state-error", "ready-to-paused"); fakesink.set_property_from_str("state-error", "ready-to-paused");
let v = fakesink.property_value("state-error"); let v = fakesink.property_value("state-error");
let (_klass, e) = glib::EnumValue::from_value(&v).unwrap(); let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();

View file

@ -798,8 +798,8 @@ mod tests {
crate::init().unwrap(); crate::init().unwrap();
let bin = crate::Bin::new(None); let bin = crate::Bin::new(None);
let id1 = crate::ElementFactory::make("identity", None).unwrap(); let id1 = crate::ElementFactory::make("identity").build().unwrap();
let id2 = crate::ElementFactory::make("identity", None).unwrap(); let id2 = crate::ElementFactory::make("identity").build().unwrap();
bin.add(&id1).unwrap(); bin.add(&id1).unwrap();
@ -829,8 +829,8 @@ mod tests {
crate::init().unwrap(); crate::init().unwrap();
let bin = crate::Bin::new(None); let bin = crate::Bin::new(None);
let id1 = crate::ElementFactory::make("identity", None).unwrap(); let id1 = crate::ElementFactory::make("identity").build().unwrap();
let id2 = crate::ElementFactory::make("identity", None).unwrap(); let id2 = crate::ElementFactory::make("identity").build().unwrap();
bin.add(&id1).unwrap(); bin.add(&id1).unwrap();

View file

@ -145,7 +145,7 @@ pub use promise::{Promise, PromiseError};
pub mod bus; pub mod bus;
mod element; mod element;
mod element_factory; pub mod element_factory;
mod bin; mod bin;

View file

@ -136,7 +136,10 @@ mod tests {
crate::init().unwrap(); crate::init().unwrap();
let bin = crate::Bin::new(None); let bin = crate::Bin::new(None);
let identity = crate::ElementFactory::make("identity", Some("id")).unwrap(); let identity = crate::ElementFactory::make("identity")
.name("id")
.build()
.unwrap();
bin.add(&identity).unwrap(); bin.add(&identity).unwrap();
let notify = Arc::new(Mutex::new(None)); let notify = Arc::new(Mutex::new(None));

View file

@ -55,6 +55,6 @@ mod tests {
let factory = crate::ElementFactory::find("identity").unwrap(); let factory = crate::ElementFactory::find("identity").unwrap();
let loaded = factory.load().unwrap(); let loaded = factory.load().unwrap();
assert_eq!(factory.type_(), loaded.type_()); assert_eq!(factory.type_(), loaded.type_());
let _element = loaded.create(None).unwrap(); let _element = loaded.create().build().unwrap();
} }
} }

View file

@ -781,10 +781,11 @@ mod tests {
); );
let pipeline = crate::Pipeline::new(None); let pipeline = crate::Pipeline::new(None);
let src = ElementFactory::make("fakesrc", None).unwrap(); let src = ElementFactory::make("fakesrc")
let sink = ElementFactory::make("fakesink", None).unwrap(); .property("num-buffers", 100i32)
.build()
src.set_property("num-buffers", 100i32); .unwrap();
let sink = ElementFactory::make("fakesink").build().unwrap();
pipeline pipeline
.add_many(&[&src, element.upcast_ref(), &sink]) .add_many(&[&src, element.upcast_ref(), &sink])

View file

@ -8,9 +8,14 @@ fn tutorial_main() {
gst::init().unwrap(); gst::init().unwrap();
// Create the elements // Create the elements
let source = gst::ElementFactory::make("videotestsrc", Some("source")) let source = gst::ElementFactory::make("videotestsrc")
.name("source")
.property_from_str("pattern", "smpte")
.build()
.expect("Could not create source element."); .expect("Could not create source element.");
let sink = gst::ElementFactory::make("autovideosink", Some("sink")) let sink = gst::ElementFactory::make("autovideosink")
.name("sink")
.build()
.expect("Could not create sink element"); .expect("Could not create sink element");
// Create the empty pipeline // Create the empty pipeline
@ -20,9 +25,6 @@ fn tutorial_main() {
pipeline.add_many(&[&source, &sink]).unwrap(); pipeline.add_many(&[&source, &sink]).unwrap();
source.link(&sink).expect("Elements could not be linked."); source.link(&sink).expect("Elements could not be linked.");
// Modify the source's properties
source.set_property_from_str("pattern", "smpte");
// Start playing // Start playing
pipeline pipeline
.set_state(gst::State::Playing) .set_state(gst::State::Playing)

View file

@ -7,14 +7,27 @@ fn tutorial_main() {
// Initialize GStreamer // Initialize GStreamer
gst::init().unwrap(); gst::init().unwrap();
let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
// Create the elements // Create the elements
let source = gst::ElementFactory::make("uridecodebin", Some("source")) let source = gst::ElementFactory::make("uridecodebin")
.name("source")
// Set the URI to play
.property("uri", uri)
.build()
.expect("Could not create uridecodebin element."); .expect("Could not create uridecodebin element.");
let convert = gst::ElementFactory::make("audioconvert", Some("convert")) let convert = gst::ElementFactory::make("audioconvert")
.name("convert")
.build()
.expect("Could not create convert element."); .expect("Could not create convert element.");
let sink = gst::ElementFactory::make("autoaudiosink", Some("sink")) let sink = gst::ElementFactory::make("autoaudiosink")
.name("sink")
.build()
.expect("Could not create sink element."); .expect("Could not create sink element.");
let resample = gst::ElementFactory::make("audioresample", Some("resample")) let resample = gst::ElementFactory::make("audioresample")
.name("resample")
.build()
.expect("Could not create resample element."); .expect("Could not create resample element.");
// Create the empty pipeline // Create the empty pipeline
@ -27,11 +40,6 @@ fn tutorial_main() {
.unwrap(); .unwrap();
gst::Element::link_many(&[&convert, &resample, &sink]).expect("Elements could not be linked."); gst::Element::link_many(&[&convert, &resample, &sink]).expect("Elements could not be linked.");
// Set the URI to play
let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
source.set_property("uri", uri);
// Connect the pad-added signal // Connect the pad-added signal
source.connect_pad_added(move |src, src_pad| { source.connect_pad_added(move |src, src_pad| {
println!("Received new pad {} from {}", src_pad.name(), src.name()); println!("Received new pad {} from {}", src_pad.name(), src.name());

View file

@ -24,14 +24,16 @@ fn tutorial_main() {
// Initialize GStreamer // Initialize GStreamer
gst::init().unwrap(); gst::init().unwrap();
// Creat the playbin element
let playbin = gst::ElementFactory::make("playbin", Some("playbin"))
.expect("Failed to create playbin element");
// Set the URI to play
let uri = let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"; "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
playbin.set_property("uri", uri);
// Creat the playbin element
let playbin = gst::ElementFactory::make("playbin")
.name("playbin")
// Set the URI to play
.property("uri", uri)
.build()
.expect("Failed to create playbin element");
// Start playing // Start playing
playbin playbin

View file

@ -301,8 +301,10 @@ mod tutorial5 {
let uri = "https://www.freedesktop.org/software/gstreamer-sdk/\ let uri = "https://www.freedesktop.org/software/gstreamer-sdk/\
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")
playbin.set_property("uri", uri); .property("uri", uri)
.build()
.unwrap();
playbin.connect("video-tags-changed", false, |args| { playbin.connect("video-tags-changed", false, |args| {
let pipeline = args[0] let pipeline = args[0]

View file

@ -90,10 +90,14 @@ fn tutorial_main() {
// Ask the factories to instantiate actual elements // Ask the factories to instantiate actual elements
let source = source_factory let source = source_factory
.create(Some("source")) .create()
.name("source")
.build()
.expect("Failed to create source element"); .expect("Failed to create source element");
let sink = sink_factory let sink = sink_factory
.create(Some("sink")) .create()
.name("sink")
.build()
.expect("Failed to create sink element"); .expect("Failed to create sink element");
// Create the empty pipeline // Create the empty pipeline

View file

@ -10,24 +10,52 @@ fn tutorial_main() {
return; return;
} }
let audio_source = gst::ElementFactory::make("audiotestsrc", Some("audio_source")).unwrap(); let audio_source = gst::ElementFactory::make("audiotestsrc")
let tee = gst::ElementFactory::make("tee", Some("tee")).unwrap(); .name("audio_source")
let audio_queue = gst::ElementFactory::make("queue", Some("audio_queue")).unwrap(); .property("freq", 215.0)
let audio_convert = gst::ElementFactory::make("audioconvert", Some("audio_convert")).unwrap(); .build()
let audio_resample = .unwrap();
gst::ElementFactory::make("audioresample", Some("audio_resample")).unwrap(); let tee = gst::ElementFactory::make("tee")
let audio_sink = gst::ElementFactory::make("autoaudiosink", Some("audio_sink")).unwrap(); .name("tee")
let video_queue = gst::ElementFactory::make("queue", Some("video_queue")).unwrap(); .build()
let visual = gst::ElementFactory::make("wavescope", Some("visual")).unwrap(); .unwrap();
let video_convert = gst::ElementFactory::make("videoconvert", Some("video_convert")).unwrap(); let audio_queue = gst::ElementFactory::make("queue")
let video_sink = gst::ElementFactory::make("autovideosink", Some("video_sink")).unwrap(); .name("audio_queue")
.build()
.unwrap();
let audio_convert = gst::ElementFactory::make("audioconvert")
.name("audio_convert")
.build()
.unwrap();
let audio_resample = gst::ElementFactory::make("audioresample")
.name("audio_resample")
.build()
.unwrap();
let audio_sink = gst::ElementFactory::make("autoaudiosink")
.name("audio_sink")
.build()
.unwrap();
let video_queue = gst::ElementFactory::make("queue")
.name("video_queue")
.build()
.unwrap();
let visual = gst::ElementFactory::make("wavescope")
.name("visual")
.property_from_str("shader", "none")
.property_from_str("style", "lines")
.build()
.unwrap();
let video_convert = gst::ElementFactory::make("videoconvert")
.name("video_convert")
.build()
.unwrap();
let video_sink = gst::ElementFactory::make("autovideosink")
.name("video_sink")
.build()
.unwrap();
let pipeline = gst::Pipeline::new(Some("test-pipeline")); let pipeline = gst::Pipeline::new(Some("test-pipeline"));
audio_source.set_property("freq", 215.0);
visual.set_property_from_str("shader", "none");
visual.set_property_from_str("style", "lines");
pipeline pipeline
.add_many(&[ .add_many(&[
&audio_source, &audio_source,

View file

@ -47,26 +47,63 @@ fn main() {
return; return;
} }
let appsrc = gst::ElementFactory::make("appsrc", Some("audio_source")).unwrap(); let appsrc = gst::ElementFactory::make("appsrc")
let tee = gst::ElementFactory::make("tee", Some("tee")).unwrap(); .name("audio_source")
let audio_queue = gst::ElementFactory::make("queue", Some("audio_queue")).unwrap(); .build()
let audio_convert1 = gst::ElementFactory::make("audioconvert", Some("audio_convert1")).unwrap(); .unwrap();
let audio_resample = let tee = gst::ElementFactory::make("tee")
gst::ElementFactory::make("audioresample", Some("audio_resample")).unwrap(); .name("tee")
let audio_sink = gst::ElementFactory::make("autoaudiosink", Some("audio_sink")).unwrap(); .build()
let video_queue = gst::ElementFactory::make("queue", Some("video_queue")).unwrap(); .unwrap();
let audio_convert2 = gst::ElementFactory::make("audioconvert", Some("audio_convert2")).unwrap(); let audio_queue = gst::ElementFactory::make("queue")
let visual = gst::ElementFactory::make("wavescope", Some("visual")).unwrap(); .name("audio_queue")
let video_convert = gst::ElementFactory::make("videoconvert", Some("video_convert")).unwrap(); .build()
let video_sink = gst::ElementFactory::make("autovideosink", Some("video_sink")).unwrap(); .unwrap();
let app_queue = gst::ElementFactory::make("queue", Some("app_queue")).unwrap(); let audio_convert1 = gst::ElementFactory::make("audioconvert")
let appsink = gst::ElementFactory::make("appsink", Some("app_sink")).unwrap(); .name("audio_convert1")
.build()
.unwrap();
let audio_resample = gst::ElementFactory::make("audioresample")
.name("audio_resample")
.build()
.unwrap();
let audio_sink = gst::ElementFactory::make("autoaudiosink")
.name("audio_sink")
.build()
.unwrap();
let video_queue = gst::ElementFactory::make("queue")
.name("video_queue")
.build()
.unwrap();
let audio_convert2 = gst::ElementFactory::make("audioconvert")
.name("audio_convert2")
.build()
.unwrap();
let visual = gst::ElementFactory::make("wavescope")
.name("visual")
.property_from_str("shader", "none")
.property_from_str("style", "lines")
.build()
.unwrap();
let video_convert = gst::ElementFactory::make("videoconvert")
.name("video_convert")
.build()
.unwrap();
let video_sink = gst::ElementFactory::make("autovideosink")
.name("video_sink")
.build()
.unwrap();
let app_queue = gst::ElementFactory::make("queue")
.name("app_queue")
.build()
.unwrap();
let appsink = gst::ElementFactory::make("appsink")
.name("app_sink")
.build()
.unwrap();
let pipeline = gst::Pipeline::new(Some("test-pipeline")); let pipeline = gst::Pipeline::new(Some("test-pipeline"));
visual.set_property_from_str("shader", "none");
visual.set_property_from_str("style", "lines");
pipeline pipeline
.add_many(&[ .add_many(&[
&appsrc, &appsrc,

View file

@ -107,13 +107,17 @@ fn tutorial_main() -> Result<(), Error> {
// Initialize GStreamer // Initialize GStreamer
gst::init()?; gst::init()?;
// Create PlayBin element
let playbin = gst::ElementFactory::make("playbin", Some("playbin"))?;
// Set URI to play
let uri = let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm"; "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm";
playbin.set_property("uri", uri);
// Create PlayBin element
let playbin = gst::ElementFactory::make("playbin")
.name("playbin")
// Set URI to play
.property("uri", uri)
// Set connection speed. This will affect some internal decisions of playbin
.property("connection-speed", 56u64)
.build()?;
// Set flags to show Audio and Video but ignore Subtitles // Set flags to show Audio and Video but ignore Subtitles
let flags = playbin.property_value("flags"); let flags = playbin.property_value("flags");
@ -129,9 +133,6 @@ fn tutorial_main() -> Result<(), Error> {
.unwrap(); .unwrap();
playbin.set_property_from_value("flags", &flags); playbin.set_property_from_value("flags", &flags);
// Set connection speed. This will affect some internal decisions of playbin
playbin.set_property("connection-speed", 56u64);
// Handle keyboard input // Handle keyboard input
let playbin_clone = playbin.clone(); let playbin_clone = playbin.clone();
let main_loop_clone = main_loop.clone(); let main_loop_clone = main_loop.clone();

View file

@ -109,19 +109,20 @@ fn tutorial_main() -> Result<(), Error> {
// Initialize GStreamer // Initialize GStreamer
gst::init()?; gst::init()?;
// Create PlayBin element
let playbin = gst::ElementFactory::make("playbin", Some("playbin"))?;
// Set URI to play
let uri = let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv"; "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv";
playbin.set_property("uri", uri);
// Set the subtitle URI and font description
let subtitle_uri = let subtitle_uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt"; "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt";
playbin.set_property("suburi", subtitle_uri);
playbin.set_property("subtitle-font-desc", "Sans, 18"); // Create PlayBin element
let playbin = gst::ElementFactory::make("playbin")
.name("playbin")
// Set URI to play
.property("uri", uri)
// Set the subtitle URI and font description
.property("suburi", subtitle_uri)
.property("subtitle-font-desc", "Sans, 18")
.build()?;
// Set flags to show Audio, Video and Subtitles // Set flags to show Audio, Video and Subtitles
let flags = playbin.property_value("flags"); let flags = playbin.property_value("flags");

View file

@ -19,8 +19,10 @@ fn tutorial_main() -> Result<(), Error> {
// Build the pipeline // Build the pipeline
let uri = let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"; "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
let pipeline = gst::ElementFactory::make("playbin", None)?; let pipeline = gst::ElementFactory::make("playbin")
pipeline.set_property("uri", uri); .name("playbin")
.property("uri", uri)
.build()?;
// Set the download flag // Set the download flag
let flags = pipeline.property_value("flags"); let flags = pipeline.property_value("flags");

View file

@ -43,7 +43,7 @@ fn tutorial_main() -> Result<(), Error> {
// We have now selected a factory for the visualization element // We have now selected a factory for the visualization element
let name = vis_factory.longname(); let name = vis_factory.longname();
println!("Selected {}", name); println!("Selected {}", name);
let vis_plugin = vis_factory.create(None).unwrap(); let vis_plugin = vis_factory.create().build().unwrap();
// Build the pipeline // Build the pipeline
let pipeline = gst::parse_launch("playbin uri=http://radio.hbr1.com:19800/ambient.ogg")?; let pipeline = gst::parse_launch("playbin uri=http://radio.hbr1.com:19800/ambient.ogg")?;

View file

@ -14,11 +14,17 @@ fn tutorial_main() -> Result<(), Error> {
"playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm")?; "playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm")?;
// Create elements that go inside the sink bin // Create elements that go inside the sink bin
let equalizer = gst::ElementFactory::make("equalizer-3bands", Some("equalizer")) let equalizer = gst::ElementFactory::make("equalizer-3bands")
.name("equalizer")
.build()
.expect("Could not create equalizer element."); .expect("Could not create equalizer element.");
let convert = gst::ElementFactory::make("audioconvert", Some("convert")) let convert = gst::ElementFactory::make("audioconvert")
.name("convert")
.build()
.expect("Could not create audioconvert element."); .expect("Could not create audioconvert element.");
let sink = gst::ElementFactory::make("autoaudiosink", Some("audio_sink")) let sink = gst::ElementFactory::make("autoaudiosink")
.name("audio_sink")
.build()
.expect("Could not create autoaudiosink element."); .expect("Could not create autoaudiosink element.");
// Create the sink bin, add the elements and link them // Create the sink bin, add the elements and link them