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

View file

@ -18,10 +18,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
@ -38,11 +34,9 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?;
let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("appsrc", None).map_err(|_| MissingElement("appsrc"))?;
let videoconvert = gst::ElementFactory::make("videoconvert", None)
.map_err(|_| MissingElement("videoconvert"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
let src = gst::ElementFactory::make("appsrc").build()?;
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
pipeline.add_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.
let pipeline = gst::Pipeline::new(None);
let src1 = gst::ElementFactory::make("videotestsrc", None).context("Creating videotestsrc")?;
let src2 = gst::ElementFactory::make("videotestsrc", None).context("Creating videotestsrc")?;
let src1 = gst::ElementFactory::make("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 conv = gst::ElementFactory::make("videoconvert", None).context("Creating videoconvert")?;
let sink =
gst::ElementFactory::make("autovideosink", None).context("Creating autovideosink")?;
let conv = gst::ElementFactory::make("videoconvert").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
comp.set_property("background-color", 0xff_33_33_33u32);
pipeline.add_many(&[&src1, &src2, comp.upcast_ref(), &conv, &sink])?;
@ -603,11 +608,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.context("Linking converter")?;
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
let xmax = 1280.0 - 320.0f64;
let ymax = 720.0 - 240.0f64;

View file

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

View file

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

View file

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

View file

@ -31,10 +31,6 @@ use std::{
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
@ -51,13 +47,11 @@ fn create_receiver_pipeline(
let caps = video_info.to_caps()?;
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 convert = gst::ElementFactory::make("videoconvert", None)
.map_err(|_| MissingElement("videoconvert"))?;
let queue = gst::ElementFactory::make("queue", None).map_err(|_| MissingElement("queue"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.map_err(|_| MissingElement("autovideosink"))?;
let convert = gst::ElementFactory::make("videoconvert").build()?;
let queue = gst::ElementFactory::make("queue").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
src.downcast_ref::<gst_app::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 pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None)
.map_err(|_| MissingElement("videotestsrc"))?;
let sink = gst::ElementFactory::make("appsink", None).map_err(|_| MissingElement("appsink"))?;
src.set_property("num-buffers", 250i32);
let src = gst::ElementFactory::make("videotestsrc")
.property("num-buffers", 250i32)
.build()?;
let sink = gst::ElementFactory::make("appsink").build()?;
sink.downcast_ref::<gst_app::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) {
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
// in the pipeline, and the widget will be embedded in our gui.
// 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.
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
// low cpu usage. :)
// 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
// 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.
let glsinkbin = gst::ElementFactory::make("glsinkbin", None).unwrap();
glsinkbin.set_property("sink", &gtkglsink);
let glsinkbin = gst::ElementFactory::make("glsinkbin")
.property("sink", &gtkglsink)
.build()
.unwrap();
// 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.
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
// our frames manually, using the CPU. An example why this may fail is, when
// 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.
// So we get it and use it later to add it to our gui.
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
// X11 protocol's XV extension, which allows to overlay regions
// 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"))]
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 {
// On Mac, this is done by overlaying a window region with an
// 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"))]
@ -102,7 +102,7 @@ fn set_window_handle(video_overlay: &gst_video::VideoOverlay, gdk_window: &gdk::
fn create_ui(app: &gtk::Application) {
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
// 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.
// This element does nothing, really. We also never add it to a pipeline.
// 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.
let mut iter = identity.iterate_pads();
loop {

View file

@ -21,10 +21,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
@ -58,19 +54,14 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?;
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])?;
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
// The videotestsrc supports multiple test patterns. In this example, we will use the
// 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
// 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)
.framerate((15, 1).into())
.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
// pattern with a white ball moving around the video's center point.
src.set_property_from_str("pattern", "ball");
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
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.
let fontmap = pangocairo::FontMap::new();

View file

@ -24,10 +24,6 @@ use derive_more::{Display, Error};
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
@ -61,19 +57,12 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?;
let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("videotestsrc", None)
.map_err(|_| MissingElement("videotestsrc"))?;
let overlay = gst::ElementFactory::make("cairooverlay", None)
.map_err(|_| MissingElement("cairooverlay"))?;
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])?;
gst::Element::link_many(&[&src, &overlay, &capsfilter, &videoconvert, &sink])?;
let src = gst::ElementFactory::make("videotestsrc")
// The videotestsrc supports multiple test patterns. In this example, we will use the
// pattern with a white ball moving around the video's center point.
.property_from_str("pattern", "ball")
.build()?;
let overlay = gst::ElementFactory::make("cairooverlay").build()?;
// Plug in a capsfilter element that will force the videotestsrc and the cairooverlay to work
// with images of the size 800x800.
@ -81,11 +70,15 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.width(800)
.height(800)
.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
// pattern with a white ball moving around the video's center point.
src.set_property_from_str("pattern", "ball");
let videoconvert = gst::ElementFactory::make("videoconvert").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
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.
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.
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
playbin.set_property("uri", uri);
let playbin = gst::ElementFactory::make("playbin")
.property("uri", uri)
.build()
.unwrap();
// For flags handling
// 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 derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "No such pad {} in {}", _0, _1)]
struct NoSuchPad(#[error(not(source))] &'static str, String);
@ -34,17 +30,6 @@ struct ErrorMessage {
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> {
match element.static_pad(pad_name) {
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> {
match element.request_pad_simple(pad_name) {
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> {
let fecdec = make_element("rtpulpfecdec", None)?;
let internal_storage = rtpbin.emit_by_name::<glib::Object>("get-internal-storage", &[&sess_id]);
fecdec.set_property("storage", &internal_storage);
fecdec.set_property("pt", 100u32);
let fecdec = gst::ElementFactory::make("rtpulpfecdec")
.property("storage", &internal_storage)
.property("pt", 100u32)
.build()?;
Ok(fecdec)
}
@ -104,33 +88,54 @@ fn example_main() -> Result<(), Error> {
let drop_probability = args[2].parse::<f32>()?;
let pipeline = gst::Pipeline::new(None);
let src = make_element("udpsrc", None)?;
let netsim = make_element("netsim", None)?;
let rtpbin = make_element("rtpbin", None)?;
let depay = make_element("rtpvp8depay", None)?;
let dec = make_element("vp8dec", None)?;
let conv = make_element("videoconvert", None)?;
let scale = make_element("videoscale", None)?;
let filter = make_element("capsfilter", None)?;
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();
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])?;
gst::Element::link_many(&[&depay, &dec, &conv, &scale, &filter])?;
match args[1].as_str() {
"play" => {
let sink = make_element("autovideosink", None)?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
pipeline.add(&sink)?;
filter.link(&sink)?;
}
"record" => {
let enc = make_element("x264enc", None)?;
let mux = make_element("matroskamux", None)?;
let sink = make_element("filesink", None)?;
let enc = gst::ElementFactory::make("x264enc")
.property_from_str("tune", "zerolatency")
.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])?;
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");
}
_ => 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
.bus()
.expect("Pipeline without bus. Shouldn't happen!");

View file

@ -9,10 +9,6 @@ use std::env;
use anyhow::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)]
#[display(fmt = "No such pad {} in {}", _0, _1)]
struct NoSuchPad(&'static str, String);
@ -30,17 +26,6 @@ struct ErrorMessage {
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> {
match element.static_pad(pad_name) {
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> {
match element.request_pad_simple(pad_name) {
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> {
let fecenc = make_element("rtpulpfecenc", None)?;
fecenc.set_property("pt", 100u32);
fecenc.set_property("multipacket", true);
fecenc.set_property("percentage", fec_percentage);
let fecenc = gst::ElementFactory::make("rtpulpfecenc")
.property("pt", 100u32)
.property("multipacket", true)
.property("percentage", fec_percentage)
.build()?;
Ok(fecenc)
}
@ -91,15 +75,31 @@ fn example_main() -> Result<(), Error> {
let uri = &args[1];
let fec_percentage = args[2].parse::<u32>()?;
let video_caps = gst::Caps::builder("video/x-raw").build();
let pipeline = gst::Pipeline::new(None);
let src = make_element("uridecodebin", None)?;
let conv = make_element("videoconvert", None)?;
let q1 = make_element("queue", None)?;
let enc = make_element("vp8enc", None)?;
let q2 = make_element("queue", None)?;
let pay = make_element("rtpvp8pay", None)?;
let rtpbin = make_element("rtpbin", None)?;
let sink = make_element("udpsink", None)?;
let src = gst::ElementFactory::make("uridecodebin")
.property_from_str("pattern", "ball")
.property("expose-all-streams", false)
.property("caps", video_caps)
.property("uri", uri)
.build()?;
let conv = gst::ElementFactory::make("videoconvert").build()?;
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])?;
@ -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
.bus()
.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> {
// Create a simple VP8 videotestsrc input
let bin = gst::Bin::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap();
let enc = gst::ElementFactory::make("vp8enc", None).unwrap();
let src = gst::ElementFactory::make("videotestsrc")
// 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
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
// Configure the videotestsrc live
src.set_property("is-live", true);
// Produce encoded data as fast as possible
enc.set_property("deadline", 1i64);
let pay = gst::ElementFactory::make("rtpvp8pay")
.name("pay0")
.build()
.unwrap();
bin.add_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)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
@ -249,21 +245,18 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// Create our pipeline with the custom element
let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("audiotestsrc", None)
.map_err(|_| MissingElement("audiotestsrc"))?;
let src = gst::ElementFactory::make("audiotestsrc")
.property_from_str("wave", "white-noise")
.build()?;
let filter = fir_filter::FirFilter::new(None);
let conv = gst::ElementFactory::make("audioconvert", None)
.map_err(|_| MissingElement("audioconvert"))?;
let sink = gst::ElementFactory::make("autoaudiosink", None)
.map_err(|_| MissingElement("autoaudiosink"))?;
let conv = gst::ElementFactory::make("audioconvert").build()?;
let sink = gst::ElementFactory::make("autoaudiosink").build()?;
pipeline.add_many(&[&src, filter.upcast_ref(), &conv, &sink])?;
src.link(&filter)?;
filter.link(&conv)?;
conv.link(&sink)?;
src.set_property_from_str("wave", "white-noise");
// Create a windowed sinc lowpass filter at 1/64 sample rate,
// i.e. 689Hz for 44.1kHz sample rate
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"]
mod examples_common;
#[derive(Debug, Display, Error)]
#[display(fmt = "Missing element {}", _0)]
struct MissingElement(#[error(not(source))] &'static str);
#[derive(Debug, Display, Error)]
#[display(fmt = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {

View file

@ -28,10 +28,11 @@ fn example_main() {
};
let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("filesrc", None).unwrap();
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
src.set_property("location", uri);
let src = gst::ElementFactory::make("filesrc")
.property("location", uri)
.build()
.unwrap();
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
pipeline.add_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
// we simply pipe every encountered stream into a fakesink, essentially
// throwing away the data.
let queue = gst::ElementFactory::make("queue", None).unwrap();
let sink = gst::ElementFactory::make("fakesink", None).unwrap();
let queue = gst::ElementFactory::make("queue").build().unwrap();
let sink = gst::ElementFactory::make("fakesink").build().unwrap();
let elements = &[&queue, &sink];
pipeline.add_many(elements).unwrap();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,6 @@
// DO NOT EDIT
use crate::Caps;
use crate::Element;
use crate::Object;
use crate::PluginFeature;
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 = "get_element_type")]
pub fn element_type(&self) -> glib::types::Type {
@ -140,18 +128,6 @@ impl ElementFactory {
assert_initialized_main_thread!();
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 {}

View file

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

View file

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

View file

@ -25,64 +25,12 @@ impl ElementFactory {
) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!();
// 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 = 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)
let mut builder = self.create();
builder.properties = properties
.iter()
.map(|(name, value)| (*name, ValueOrStr::Value(value.to_value())))
.collect();
builder.build()
}
#[doc(alias = "gst_element_factory_make_with_properties")]
@ -93,22 +41,59 @@ impl ElementFactory {
) -> Result<Element, glib::BoolError> {
assert_initialized_main_thread!();
crate::log!(
crate::CAT_RUST,
"gstelementfactory: make \"{}\"",
factoryname
);
let mut builder = Self::make(factoryname);
builder.properties = properties
.iter()
.map(|(name, value)| (*name, ValueOrStr::Value(value.to_value())))
.collect();
builder.build()
}
let factory = Self::find(factoryname).ok_or_else(|| {
crate::warning!(
crate::CAT_RUST,
"gstelementfactory: make \"{}\"",
factoryname
);
glib::bool_error!("Failed to create element from factory name")
})?;
#[doc(alias = "gst_element_factory_create")]
#[doc(alias = "gst_element_factory_create_with_properties")]
#[track_caller]
pub fn create(&self) -> ElementBuilder {
ElementBuilder {
name_or_factory: NameOrFactory::Factory(self),
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")]
@ -196,3 +181,219 @@ impl ElementFactory {
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 {
#[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"))]
{
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() {
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");
let v = fakesink.property_value("state-error");
let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();

View file

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

View file

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

View file

@ -136,7 +136,10 @@ mod tests {
crate::init().unwrap();
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();
let notify = Arc::new(Mutex::new(None));

View file

@ -55,6 +55,6 @@ mod tests {
let factory = crate::ElementFactory::find("identity").unwrap();
let loaded = factory.load().unwrap();
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 src = ElementFactory::make("fakesrc", None).unwrap();
let sink = ElementFactory::make("fakesink", None).unwrap();
src.set_property("num-buffers", 100i32);
let src = ElementFactory::make("fakesrc")
.property("num-buffers", 100i32)
.build()
.unwrap();
let sink = ElementFactory::make("fakesink").build().unwrap();
pipeline
.add_many(&[&src, element.upcast_ref(), &sink])

View file

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

View file

@ -7,14 +7,27 @@ fn tutorial_main() {
// Initialize GStreamer
gst::init().unwrap();
let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
// 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.");
let convert = gst::ElementFactory::make("audioconvert", Some("convert"))
let convert = gst::ElementFactory::make("audioconvert")
.name("convert")
.build()
.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.");
let resample = gst::ElementFactory::make("audioresample", Some("resample"))
let resample = gst::ElementFactory::make("audioresample")
.name("resample")
.build()
.expect("Could not create resample element.");
// Create the empty pipeline
@ -27,11 +40,6 @@ fn tutorial_main() {
.unwrap();
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
source.connect_pad_added(move |src, src_pad| {
println!("Received new pad {} from {}", src_pad.name(), src.name());

View file

@ -24,14 +24,16 @@ fn tutorial_main() {
// Initialize GStreamer
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 =
"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
playbin

View file

@ -301,8 +301,10 @@ mod tutorial5 {
let uri = "https://www.freedesktop.org/software/gstreamer-sdk/\
data/media/sintel_trailer-480p.webm";
let playbin = gst::ElementFactory::make("playbin", None).unwrap();
playbin.set_property("uri", uri);
let playbin = gst::ElementFactory::make("playbin")
.property("uri", uri)
.build()
.unwrap();
playbin.connect("video-tags-changed", false, |args| {
let pipeline = args[0]

View file

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

View file

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

View file

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

View file

@ -107,13 +107,17 @@ fn tutorial_main() -> Result<(), Error> {
// Initialize GStreamer
gst::init()?;
// Create PlayBin element
let playbin = gst::ElementFactory::make("playbin", Some("playbin"))?;
// Set URI to play
let uri =
"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
let flags = playbin.property_value("flags");
@ -129,9 +133,6 @@ fn tutorial_main() -> Result<(), Error> {
.unwrap();
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
let playbin_clone = playbin.clone();
let main_loop_clone = main_loop.clone();

View file

@ -109,19 +109,20 @@ fn tutorial_main() -> Result<(), Error> {
// Initialize GStreamer
gst::init()?;
// Create PlayBin element
let playbin = gst::ElementFactory::make("playbin", Some("playbin"))?;
// Set URI to play
let uri =
"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 =
"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
let flags = playbin.property_value("flags");

View file

@ -19,8 +19,10 @@ fn tutorial_main() -> Result<(), Error> {
// Build the pipeline
let uri =
"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
let pipeline = gst::ElementFactory::make("playbin", None)?;
pipeline.set_property("uri", uri);
let pipeline = gst::ElementFactory::make("playbin")
.name("playbin")
.property("uri", uri)
.build()?;
// Set the download flag
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
let name = vis_factory.longname();
println!("Selected {}", name);
let vis_plugin = vis_factory.create(None).unwrap();
let vis_plugin = vis_factory.create().build().unwrap();
// Build the pipeline
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")?;
// 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.");
let convert = gst::ElementFactory::make("audioconvert", Some("convert"))
let convert = gst::ElementFactory::make("audioconvert")
.name("convert")
.build()
.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.");
// Create the sink bin, add the elements and link them