manual changes post ObjectExt improvements

This commit is contained in:
Bilal Elmoussaoui 2021-11-06 18:09:27 +01:00
parent 0d009bca31
commit 213020165a
43 changed files with 421 additions and 516 deletions

View file

@ -81,7 +81,7 @@ fn example_main() -> Result<(), Error> {
gst::ElementFactory::make("decodebin", None).map_err(|_| MissingElement("decodebin"))?; gst::ElementFactory::make("decodebin", None).map_err(|_| MissingElement("decodebin"))?;
// Tell the filesrc what file to load // Tell the filesrc what file to load
src.set_property("location", uri)?; 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])?;

View file

@ -76,9 +76,7 @@ fn configure_encodebin(encodebin: &gst::Element) {
.build(); .build();
// Finally, apply the EncodingProfile onto our encodebin element. // Finally, apply the EncodingProfile onto our encodebin element.
encodebin encodebin.set_property("profile", &container_profile);
.set_property("profile", &container_profile)
.expect("set profile property failed");
} }
fn example_main() -> Result<(), Error> { fn example_main() -> Result<(), Error> {
@ -104,10 +102,8 @@ fn example_main() -> Result<(), Error> {
let sink = let sink =
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?; gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
src.set_property("uri", uri) src.set_property("uri", uri);
.expect("setting URI Property failed"); sink.set_property("location", output_file);
sink.set_property("location", output_file)
.expect("setting location property failed");
// 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.

View file

@ -33,11 +33,11 @@ fn create_ui(app: &gtk::Application) {
// 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", None).unwrap();
glsinkbin.set_property("sink", &gtkglsink).unwrap(); glsinkbin.set_property("sink", &gtkglsink);
// 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("widget").unwrap(); let widget = gtkglsink.property::<gtk::Widget>("widget");
(glsinkbin, widget.get::<gtk::Widget>().unwrap()) (glsinkbin, widget)
} else { } else {
// 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
@ -45,8 +45,8 @@ fn create_ui(app: &gtk::Application) {
let sink = gst::ElementFactory::make("gtksink", None).unwrap(); let sink = gst::ElementFactory::make("gtksink", None).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("widget").unwrap(); let widget = sink.property::<gtk::Widget>("widget");
(sink, widget.get::<gtk::Widget>().unwrap()) (sink, widget)
}; };
pipeline.add_many(&[&src, &sink]).unwrap(); pipeline.add_many(&[&src, &sink]).unwrap();

View file

@ -85,7 +85,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.field("height", 800i32) .field("height", 800i32)
.field("framerate", gst::Fraction::new(15, 1)) .field("framerate", gst::Fraction::new(15, 1))
.build(); .build();
capsfilter.set_property("caps", &caps).unwrap(); capsfilter.set_property("caps", &caps);
// The videotestsrc supports multiple test patterns. In this example, we will use the // 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. // pattern with a white ball moving around the video's center point.
@ -131,142 +131,146 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// //
// In this case, the signal passes the gst::Element and a gst::Sample with // In this case, the signal passes the gst::Element and a gst::Sample with
// the current buffer // the current buffer
overlay overlay.connect("draw", false, move |args| {
.connect("draw", false, move |args| { use std::f64::consts::PI;
use std::f64::consts::PI;
let drawer = &drawer_clone; let drawer = &drawer_clone;
let drawer = drawer.lock().unwrap(); let drawer = drawer.lock().unwrap();
// Get the signal's arguments // Get the signal's arguments
let _overlay = args[0].get::<gst::Element>().unwrap(); let _overlay = args[0].get::<gst::Element>().unwrap();
let sample = args[1].get::<gst::Sample>().unwrap(); let sample = args[1].get::<gst::Sample>().unwrap();
let buffer = sample.buffer().unwrap(); let buffer = sample.buffer().unwrap();
let timestamp = buffer.pts().unwrap(); let timestamp = buffer.pts().unwrap();
let info = drawer.info.as_ref().unwrap(); let info = drawer.info.as_ref().unwrap();
let layout = drawer.layout.borrow(); let layout = drawer.layout.borrow();
let angle = 2.0 let angle = 2.0 * PI * (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64
* PI / (10.0 * gst::ClockTime::SECOND.nseconds() as f64);
* (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64
/ (10.0 * gst::ClockTime::SECOND.nseconds() as f64);
/* Create a gst::Buffer for Cairo to draw into */ /* Create a gst::Buffer for Cairo to draw into */
let frame_width = info.width() as usize; let frame_width = info.width() as usize;
let frame_height = info.height() as usize; let frame_height = info.height() as usize;
let stride = 4 * frame_width; let stride = 4 * frame_width;
let frame_size = stride * frame_height; let frame_size = stride * frame_height;
/* Create an RGBA buffer, and add a video meta that the videooverlaycomposition expects */ /* Create an RGBA buffer, and add a video meta that the videooverlaycomposition expects */
let mut buffer = gst::Buffer::with_size(frame_size).unwrap(); let mut buffer = gst::Buffer::with_size(frame_size).unwrap();
gst_video::VideoMeta::add( gst_video::VideoMeta::add(
buffer.get_mut().unwrap(), buffer.get_mut().unwrap(),
gst_video::VideoFrameFlags::empty(), gst_video::VideoFrameFlags::empty(),
gst_video::VideoFormat::Bgra, gst_video::VideoFormat::Bgra,
frame_width as u32, frame_width as u32,
frame_height as u32, frame_height as u32,
).unwrap(); )
let buffer = buffer.into_mapped_buffer_writable().unwrap();
let buffer = {
let buffer_ptr = unsafe { buffer.buffer().as_ptr() };
let surface = cairo::ImageSurface::create_for_data(
buffer,
cairo::Format::ARgb32,
frame_width as i32,
frame_height as i32,
stride as i32,
)
.unwrap();
let cr = cairo::Context::new(&surface).expect("Failed to create cairo context");
cr.save().expect("Failed to save state");
cr.set_operator(cairo::Operator::Clear);
cr.paint().expect("Failed to clear background");
cr.restore().expect("Failed to restore state");
// The image we draw (the text) will be static, but we will change the
// transformation on the drawing context, which rotates and shifts everything
// that we draw afterwards. Like this, we have no complicated calulations
// in the actual drawing below.
// Calling multiple transformation methods after each other will apply the
// new transformation on top. If you repeat the cr.rotate(angle) line below
// this a second time, everything in the canvas will rotate twice as fast.
cr.translate(
f64::from(info.width()) / 2.0,
f64::from(info.height()) / 2.0,
);
cr.rotate(angle);
// This loop will render 10 times the string "GStreamer" in a circle
for i in 0..10 {
// Cairo, like most rendering frameworks, is using a stack for transformations
// with this, we push our current transformation onto this stack - allowing us
// to make temporary changes / render something / and then returning to the
// previous transformations.
cr.save().expect("Failed to save state");
let angle = (360. * f64::from(i)) / 10.0;
let red = (1.0 + f64::cos((angle - 60.0) * PI / 180.0)) / 2.0;
cr.set_source_rgb(red, 0.0, 1.0 - red);
cr.rotate(angle * PI / 180.0);
// Update the text layout. This function is only updating pango's internal state.
// So e.g. that after a 90 degree rotation it knows that what was previously going
// to end up as a 200x100 rectangle would now be 100x200.
pangocairo::functions::update_layout(&cr, &**layout);
let (width, _height) = layout.size();
// Using width and height of the text, we can properly possition it within
// our canvas.
cr.move_to(
-(f64::from(width) / f64::from(pango::SCALE)) / 2.0,
-(f64::from(info.height())) / 2.0,
);
// After telling the layout object where to draw itself, we actually tell
// it to draw itself into our cairo context.
pangocairo::functions::show_layout(&cr, &**layout);
// Here we go one step up in our stack of transformations, removing any
// changes we did to them since the last call to cr.save();
cr.restore().expect("Failed to restore state");
}
// Safety: The surface still owns a mutable reference to the buffer but our reference
// to the surface here is the last one. After dropping the surface the buffer would be
// freed, so we keep an additional strong reference here before dropping the surface,
// which is then returned. As such it's guaranteed that nothing is using the buffer
// anymore mutably.
drop(cr);
unsafe {
assert_eq!(
cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()),
1
);
let buffer = glib::translate::from_glib_none(buffer_ptr);
drop(surface);
buffer
}
};
/* Turn the buffer into a VideoOverlayRectangle, then place
* that into a VideoOverlayComposition and return it.
*
* A VideoOverlayComposition can take a Vec of such rectangles
* spaced around the video frame, but we're just outputting 1
* here */
let rect = gst_video::VideoOverlayRectangle::new_raw(
&buffer,
0, 0, frame_width as u32, frame_height as u32,
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
);
Some(gst_video::VideoOverlayComposition::new(Some(&rect)).unwrap().to_value())
})
.unwrap(); .unwrap();
let buffer = buffer.into_mapped_buffer_writable().unwrap();
let buffer = {
let buffer_ptr = unsafe { buffer.buffer().as_ptr() };
let surface = cairo::ImageSurface::create_for_data(
buffer,
cairo::Format::ARgb32,
frame_width as i32,
frame_height as i32,
stride as i32,
)
.unwrap();
let cr = cairo::Context::new(&surface).expect("Failed to create cairo context");
cr.save().expect("Failed to save state");
cr.set_operator(cairo::Operator::Clear);
cr.paint().expect("Failed to clear background");
cr.restore().expect("Failed to restore state");
// The image we draw (the text) will be static, but we will change the
// transformation on the drawing context, which rotates and shifts everything
// that we draw afterwards. Like this, we have no complicated calulations
// in the actual drawing below.
// Calling multiple transformation methods after each other will apply the
// new transformation on top. If you repeat the cr.rotate(angle) line below
// this a second time, everything in the canvas will rotate twice as fast.
cr.translate(
f64::from(info.width()) / 2.0,
f64::from(info.height()) / 2.0,
);
cr.rotate(angle);
// This loop will render 10 times the string "GStreamer" in a circle
for i in 0..10 {
// Cairo, like most rendering frameworks, is using a stack for transformations
// with this, we push our current transformation onto this stack - allowing us
// to make temporary changes / render something / and then returning to the
// previous transformations.
cr.save().expect("Failed to save state");
let angle = (360. * f64::from(i)) / 10.0;
let red = (1.0 + f64::cos((angle - 60.0) * PI / 180.0)) / 2.0;
cr.set_source_rgb(red, 0.0, 1.0 - red);
cr.rotate(angle * PI / 180.0);
// Update the text layout. This function is only updating pango's internal state.
// So e.g. that after a 90 degree rotation it knows that what was previously going
// to end up as a 200x100 rectangle would now be 100x200.
pangocairo::functions::update_layout(&cr, &**layout);
let (width, _height) = layout.size();
// Using width and height of the text, we can properly possition it within
// our canvas.
cr.move_to(
-(f64::from(width) / f64::from(pango::SCALE)) / 2.0,
-(f64::from(info.height())) / 2.0,
);
// After telling the layout object where to draw itself, we actually tell
// it to draw itself into our cairo context.
pangocairo::functions::show_layout(&cr, &**layout);
// Here we go one step up in our stack of transformations, removing any
// changes we did to them since the last call to cr.save();
cr.restore().expect("Failed to restore state");
}
// Safety: The surface still owns a mutable reference to the buffer but our reference
// to the surface here is the last one. After dropping the surface the buffer would be
// freed, so we keep an additional strong reference here before dropping the surface,
// which is then returned. As such it's guaranteed that nothing is using the buffer
// anymore mutably.
drop(cr);
unsafe {
assert_eq!(
cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()),
1
);
let buffer = glib::translate::from_glib_none(buffer_ptr);
drop(surface);
buffer
}
};
/* Turn the buffer into a VideoOverlayRectangle, then place
* that into a VideoOverlayComposition and return it.
*
* A VideoOverlayComposition can take a Vec of such rectangles
* spaced around the video frame, but we're just outputting 1
* here */
let rect = gst_video::VideoOverlayRectangle::new_raw(
&buffer,
0,
0,
frame_width as u32,
frame_height as u32,
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
);
Some(
gst_video::VideoOverlayComposition::new(Some(&rect))
.unwrap()
.to_value(),
)
});
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g. // Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
// be called when the sink that we render to does not support resizing the image // be called when the sink that we render to does not support resizing the image
// itself - but the user just changed the window-size. The element after the overlay // itself - but the user just changed the window-size. The element after the overlay
@ -274,17 +278,15 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// resize our canvas's size. // resize our canvas's size.
// Another possibility for when this might happen is, when our video is a network // Another possibility for when this might happen is, when our video is a network
// stream that dynamically changes resolution when enough bandwith is available. // stream that dynamically changes resolution when enough bandwith is available.
overlay overlay.connect("caps-changed", false, move |args| {
.connect("caps-changed", false, move |args| { let _overlay = args[0].get::<gst::Element>().unwrap();
let _overlay = args[0].get::<gst::Element>().unwrap(); let caps = args[1].get::<gst::Caps>().unwrap();
let caps = args[1].get::<gst::Caps>().unwrap();
let mut drawer = drawer.lock().unwrap(); let mut drawer = drawer.lock().unwrap();
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap()); drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
None None
}) });
.unwrap();
Ok(pipeline) Ok(pipeline)
} }

View file

@ -83,7 +83,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
.field("width", 800i32) .field("width", 800i32)
.field("height", 800i32) .field("height", 800i32)
.build(); .build();
capsfilter.set_property("caps", &caps).unwrap(); capsfilter.set_property("caps", &caps);
// The videotestsrc supports multiple test patterns. In this example, we will use the // 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. // pattern with a white ball moving around the video's center point.
@ -125,76 +125,74 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// passed as array of glib::Value. For a documentation about the actual arguments // passed as array of glib::Value. For a documentation about the actual arguments
// it is always a good idea to either check the element's signals using either // it is always a good idea to either check the element's signals using either
// gst-inspect, or the online documentation. // gst-inspect, or the online documentation.
overlay overlay.connect("draw", false, move |args| {
.connect("draw", false, move |args| { use std::f64::consts::PI;
use std::f64::consts::PI;
let drawer = &drawer_clone; let drawer = &drawer_clone;
let drawer = drawer.lock().unwrap(); let drawer = drawer.lock().unwrap();
// Get the signal's arguments // Get the signal's arguments
let _overlay = args[0].get::<gst::Element>().unwrap(); let _overlay = args[0].get::<gst::Element>().unwrap();
// This is the cairo context. This is the root of all of cairo's // This is the cairo context. This is the root of all of cairo's
// drawing functionality. // drawing functionality.
let cr = args[1].get::<cairo::Context>().unwrap(); let cr = args[1].get::<cairo::Context>().unwrap();
let timestamp = args[2].get::<gst::ClockTime>().unwrap(); let timestamp = args[2].get::<gst::ClockTime>().unwrap();
let _duration = args[3].get::<gst::ClockTime>().unwrap(); let _duration = args[3].get::<gst::ClockTime>().unwrap();
let info = drawer.info.as_ref().unwrap(); let info = drawer.info.as_ref().unwrap();
let layout = drawer.layout.borrow(); let layout = drawer.layout.borrow();
let angle = 2.0 * PI * (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64 let angle = 2.0 * PI * (timestamp % (10 * gst::ClockTime::SECOND)).nseconds() as f64
/ (10.0 * gst::ClockTime::SECOND.nseconds() as f64); / (10.0 * gst::ClockTime::SECOND.nseconds() as f64);
// The image we draw (the text) will be static, but we will change the // The image we draw (the text) will be static, but we will change the
// transformation on the drawing context, which rotates and shifts everything // transformation on the drawing context, which rotates and shifts everything
// that we draw afterwards. Like this, we have no complicated calulations // that we draw afterwards. Like this, we have no complicated calulations
// in the actual drawing below. // in the actual drawing below.
// Calling multiple transformation methods after each other will apply the // Calling multiple transformation methods after each other will apply the
// new transformation on top. If you repeat the cr.rotate(angle) line below // new transformation on top. If you repeat the cr.rotate(angle) line below
// this a second time, everything in the canvas will rotate twice as fast. // this a second time, everything in the canvas will rotate twice as fast.
cr.translate( cr.translate(
f64::from(info.width()) / 2.0, f64::from(info.width()) / 2.0,
f64::from(info.height()) / 2.0, f64::from(info.height()) / 2.0,
);
cr.rotate(angle);
// This loop will render 10 times the string "GStreamer" in a circle
for i in 0..10 {
// Cairo, like most rendering frameworks, is using a stack for transformations
// with this, we push our current transformation onto this stack - allowing us
// to make temporary changes / render something / and then returning to the
// previous transformations.
cr.save().expect("Failed to save state");
let angle = (360. * f64::from(i)) / 10.0;
let red = (1.0 + f64::cos((angle - 60.0) * PI / 180.0)) / 2.0;
cr.set_source_rgb(red, 0.0, 1.0 - red);
cr.rotate(angle * PI / 180.0);
// Update the text layout. This function is only updating pango's internal state.
// So e.g. that after a 90 degree rotation it knows that what was previously going
// to end up as a 200x100 rectangle would now be 100x200.
pangocairo::functions::update_layout(&cr, &**layout);
let (width, _height) = layout.size();
// Using width and height of the text, we can properly possition it within
// our canvas.
cr.move_to(
-(f64::from(width) / f64::from(pango::SCALE)) / 2.0,
-(f64::from(info.height())) / 2.0,
); );
cr.rotate(angle); // After telling the layout object where to draw itself, we actually tell
// it to draw itself into our cairo context.
pangocairo::functions::show_layout(&cr, &**layout);
// This loop will render 10 times the string "GStreamer" in a circle // Here we go one step up in our stack of transformations, removing any
for i in 0..10 { // changes we did to them since the last call to cr.save();
// Cairo, like most rendering frameworks, is using a stack for transformations cr.restore().expect("Failed to restore state");
// with this, we push our current transformation onto this stack - allowing us }
// to make temporary changes / render something / and then returning to the
// previous transformations.
cr.save().expect("Failed to save state");
let angle = (360. * f64::from(i)) / 10.0; None
let red = (1.0 + f64::cos((angle - 60.0) * PI / 180.0)) / 2.0; });
cr.set_source_rgb(red, 0.0, 1.0 - red);
cr.rotate(angle * PI / 180.0);
// Update the text layout. This function is only updating pango's internal state.
// So e.g. that after a 90 degree rotation it knows that what was previously going
// to end up as a 200x100 rectangle would now be 100x200.
pangocairo::functions::update_layout(&cr, &**layout);
let (width, _height) = layout.size();
// Using width and height of the text, we can properly possition it within
// our canvas.
cr.move_to(
-(f64::from(width) / f64::from(pango::SCALE)) / 2.0,
-(f64::from(info.height())) / 2.0,
);
// After telling the layout object where to draw itself, we actually tell
// it to draw itself into our cairo context.
pangocairo::functions::show_layout(&cr, &**layout);
// Here we go one step up in our stack of transformations, removing any
// changes we did to them since the last call to cr.save();
cr.restore().expect("Failed to restore state");
}
None
})
.unwrap();
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g. // Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
// be called when the sink that we render to does not support resizing the image // be called when the sink that we render to does not support resizing the image
@ -203,17 +201,15 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
// resize our canvas's size. // resize our canvas's size.
// Another possibility for when this might happen is, when our video is a network // Another possibility for when this might happen is, when our video is a network
// stream that dynamically changes resolution when enough bandwith is available. // stream that dynamically changes resolution when enough bandwith is available.
overlay overlay.connect("caps-changed", false, move |args| {
.connect("caps-changed", false, move |args| { let _overlay = args[0].get::<gst::Element>().unwrap();
let _overlay = args[0].get::<gst::Element>().unwrap(); let caps = args[1].get::<gst::Caps>().unwrap();
let caps = args[1].get::<gst::Caps>().unwrap();
let mut drawer = drawer.lock().unwrap(); let mut drawer = drawer.lock().unwrap();
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap()); drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
None None
}) });
.unwrap();
Ok(pipeline) Ok(pipeline)
} }

View file

@ -29,7 +29,7 @@ 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", None).unwrap();
playbin.set_property("uri", uri).unwrap(); playbin.set_property("uri", uri);
// 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
@ -50,53 +50,48 @@ fn example_main() {
// - Live streams (such as internet radios) update this metadata during the stream // - Live streams (such as internet radios) update this metadata during the stream
// Note that this signal will be emitted from the streaming threads usually, // Note that this signal will be emitted from the streaming threads usually,
// not the application's threads! // not the application's threads!
playbin playbin.connect("audio-tags-changed", false, |values| {
.connect("audio-tags-changed", false, |values| { // The metadata of any of the contained audio streams changed
// The metadata of any of the contained audio streams changed // In the case of a live-stream from an internet radio, this could for example
// In the case of a live-stream from an internet radio, this could for example // mark the beginning of a new track, or a new DJ.
// mark the beginning of a new track, or a new DJ. let playbin = values[0]
let playbin = values[0] .get::<glib::Object>()
.get::<glib::Object>() .expect("playbin \"audio-tags-changed\" signal values[1]");
.expect("playbin \"audio-tags-changed\" signal values[1]"); // This gets the index of the stream that changed. This is neccessary, since
// This gets the index of the stream that changed. This is neccessary, since // there could e.g. be multiple audio streams (english, spanish, ...).
// there could e.g. be multiple audio streams (english, spanish, ...). let idx = values[1]
let idx = values[1] .get::<i32>()
.get::<i32>() .expect("playbin \"audio-tags-changed\" signal values[1]");
.expect("playbin \"audio-tags-changed\" signal values[1]");
println!("audio tags of audio stream {} changed:", idx); println!("audio tags of audio stream {} changed:", idx);
// HELP: is this correct? // HELP: is this correct?
// We were only notified about the change of metadata. If we want to do // We were only notified about the change of metadata. If we want to do
// something with it, we first need to actually query the metadata from the playbin. // something with it, we first need to actually query the metadata from the playbin.
// We do this by facilliating the get-audio-tags action-signal on playbin. // We do this by facilliating the get-audio-tags action-signal on playbin.
// Sending an action-signal to an element essentially is a function call on the element. // Sending an action-signal to an element essentially is a function call on the element.
// It is done that way, because elements do not have their own function API, they are // It is done that way, because elements do not have their own function API, they are
// relying on GStreamer and GLib's API. The only way an element can communicate with an // relying on GStreamer and GLib's API. The only way an element can communicate with an
// application is via properties, signals or action signals (or custom messages, events, queries). // application is via properties, signals or action signals (or custom messages, events, queries).
// So what the following code does, is essentially asking playbin to tell us its already // So what the following code does, is essentially asking playbin to tell us its already
// internally stored tag list for this stream index. // internally stored tag list for this stream index.
let tags = playbin let tags = playbin.emit_by_name("get-audio-tags", &[&idx]).unwrap();
.emit_by_name("get-audio-tags", &[&idx]) let tags = tags.get::<gst::TagList>().expect("tags");
.unwrap()
.unwrap();
let tags = tags.get::<gst::TagList>().expect("tags");
if let Some(artist) = tags.get::<gst::tags::Artist>() { if let Some(artist) = tags.get::<gst::tags::Artist>() {
println!(" Artist: {}", artist.get()); println!(" Artist: {}", artist.get());
} }
if let Some(title) = tags.get::<gst::tags::Title>() { if let Some(title) = tags.get::<gst::tags::Title>() {
println!(" Title: {}", title.get()); println!(" Title: {}", title.get());
} }
if let Some(album) = tags.get::<gst::tags::Album>() { if let Some(album) = tags.get::<gst::tags::Album>() {
println!(" Album: {}", album.get()); println!(" Album: {}", album.get());
} }
None None
}) });
.unwrap();
// The playbin element itself is a playbin, so it can be used as one, despite being // The playbin element itself is a playbin, so it can be used as one, despite being
// created from an element factory. // created from an element factory.

View file

@ -87,12 +87,11 @@ fn make_fec_decoder(rtpbin: &gst::Element, sess_id: u32) -> Result<gst::Element,
let internal_storage = rtpbin let internal_storage = rtpbin
.emit_by_name("get-internal-storage", &[&sess_id]) .emit_by_name("get-internal-storage", &[&sess_id])
.unwrap() .unwrap()
.unwrap()
.get::<glib::Object>() .get::<glib::Object>()
.unwrap(); .unwrap();
fecdec.set_property("storage", &internal_storage)?; fecdec.set_property("storage", &internal_storage);
fecdec.set_property("pt", 100u32)?; fecdec.set_property("pt", 100u32);
Ok(fecdec) Ok(fecdec)
} }
@ -134,7 +133,7 @@ fn example_main() -> Result<(), Error> {
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")?; sink.set_property("location", "out.mkv");
enc.set_property_from_str("tune", "zerolatency")?; enc.set_property_from_str("tune", "zerolatency")?;
eprintln!("Recording to out.mkv"); eprintln!("Recording to out.mkv");
} }
@ -147,10 +146,10 @@ fn example_main() -> Result<(), Error> {
let storage = values[1] let storage = values[1]
.get::<gst::Element>() .get::<gst::Element>()
.expect("rtpbin \"new-storage\" signal values[1]"); .expect("rtpbin \"new-storage\" signal values[1]");
storage.set_property("size-time", 250_000_000u64).unwrap(); storage.set_property("size-time", 250_000_000u64);
None None
})?; });
rtpbin.connect("request-pt-map", false, |values| { rtpbin.connect("request-pt-map", false, |values| {
let pt = values[2] let pt = values[2]
@ -175,7 +174,7 @@ fn example_main() -> Result<(), Error> {
), ),
_ => None, _ => None,
} }
})?; });
rtpbin.connect("request-fec-decoder", false, |values| { rtpbin.connect("request-fec-decoder", false, |values| {
let rtpbin = values[0] let rtpbin = values[0]
@ -197,7 +196,7 @@ fn example_main() -> Result<(), Error> {
None None
} }
} }
})?; });
let srcpad = static_pad(&netsim, "src")?; let srcpad = static_pad(&netsim, "src")?;
let sinkpad = request_pad(&rtpbin, "recv_rtp_sink_0")?; let sinkpad = request_pad(&rtpbin, "recv_rtp_sink_0")?;
@ -232,11 +231,11 @@ fn example_main() -> Result<(), Error> {
.field("height", 1080i32) .field("height", 1080i32)
.build(); .build();
src.set_property("address", "127.0.0.1")?; src.set_property("address", "127.0.0.1");
src.set_property("caps", &rtp_caps)?; src.set_property("caps", &rtp_caps);
netsim.set_property("drop-probability", drop_probability)?; netsim.set_property("drop-probability", drop_probability);
rtpbin.set_property("do-lost", true)?; rtpbin.set_property("do-lost", true);
filter.set_property("caps", &video_caps)?; filter.set_property("caps", &video_caps);
let bus = pipeline let bus = pipeline
.bus() .bus()

View file

@ -72,9 +72,9 @@ 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 = make_element("rtpulpfecenc", None)?;
fecenc.set_property("pt", 100u32)?; fecenc.set_property("pt", 100u32);
fecenc.set_property("multipacket", true)?; fecenc.set_property("multipacket", true);
fecenc.set_property("percentage", fec_percentage)?; fecenc.set_property("percentage", fec_percentage);
Ok(fecenc) Ok(fecenc)
} }
@ -125,7 +125,7 @@ fn example_main() -> Result<(), Error> {
None None
} }
} }
})?; });
let srcpad = static_pad(&q2, "src")?; let srcpad = static_pad(&q2, "src")?;
let sinkpad = request_pad(&rtpbin, "send_rtp_sink_0")?; let sinkpad = request_pad(&rtpbin, "send_rtp_sink_0")?;
@ -152,16 +152,16 @@ fn example_main() -> Result<(), Error> {
let video_caps = gst::Caps::builder("video/x-raw").build(); let video_caps = gst::Caps::builder("video/x-raw").build();
src.set_property_from_str("pattern", "ball")?; src.set_property_from_str("pattern", "ball")?;
sink.set_property("host", "127.0.0.1")?; sink.set_property("host", "127.0.0.1");
sink.set_property("sync", true)?; sink.set_property("sync", true);
enc.set_property("keyframe-max-dist", 30i32)?; enc.set_property("keyframe-max-dist", 30i32);
enc.set_property("threads", 12i32)?; enc.set_property("threads", 12i32);
enc.set_property("cpu-used", -16i32)?; enc.set_property("cpu-used", -16i32);
enc.set_property("deadline", 1i64)?; enc.set_property("deadline", 1i64);
enc.set_property_from_str("error-resilient", "default")?; enc.set_property_from_str("error-resilient", "default")?;
src.set_property("expose-all-streams", false)?; src.set_property("expose-all-streams", false);
src.set_property("caps", video_caps)?; src.set_property("caps", video_caps);
src.set_property("uri", uri)?; src.set_property("uri", uri);
let bus = pipeline let bus = pipeline
.bus() .bus()

View file

@ -130,10 +130,10 @@ mod media_factory {
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap(); let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
// Configure the videotestsrc live // Configure the videotestsrc live
src.set_property("is-live", true).unwrap(); src.set_property("is-live", true);
// Produce encoded data as fast as possible // Produce encoded data as fast as possible
enc.set_property("deadline", 1i64).unwrap(); 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

@ -47,7 +47,7 @@ fn create_pipeline(uri: String, out_path: std::path::PathBuf) -> Result<gst::Pip
.expect("Sink element is expected to be an appsink!"); .expect("Sink element is expected to be an appsink!");
// Don't synchronize on the clock, we only want a snapshot asap. // Don't synchronize on the clock, we only want a snapshot asap.
appsink.set_property("sync", false).unwrap(); appsink.set_property("sync", false);
// Tell the appsink what format we want. // Tell the appsink what format we want.
// This can be set after linking the two objects, because format negotiation between // This can be set after linking the two objects, because format negotiation between

View file

@ -31,7 +31,7 @@ fn example_main() {
let src = gst::ElementFactory::make("filesrc", None).unwrap(); let src = gst::ElementFactory::make("filesrc", None).unwrap();
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap(); let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
src.set_property("location", uri).unwrap(); src.set_property("location", uri);
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();

View file

@ -68,18 +68,11 @@ fn example_main() -> Result<(), Error> {
let sink = let sink =
gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?; gst::ElementFactory::make("filesink", None).map_err(|_| MissingElement("filesink"))?;
sink.set_property("location", output_file) sink.set_property("location", output_file);
.expect("setting location property failed");
// Increase the queue capacity to 100MB to avoid a stalling pipeline // Increase the queue capacity to 100MB to avoid a stalling pipeline
queue queue.set_property("max-size-buffers", 0u32);
.set_property("max-size-buffers", 0u32) queue.set_property("max-size-time", 0u64);
.expect("changing capacity of multiqueue failed"); queue.set_property("max-size-bytes", 1024u32 * 1024 * 100);
queue
.set_property("max-size-time", 0u64)
.expect("changing capacity of multiqueue failed");
queue
.set_property("max-size-bytes", 1024u32 * 1024 * 100)
.expect("changing capacity of multiqueue failed");
pipeline pipeline
.add_many(&[&src, &typefinder, &queue, &muxer, &sink]) .add_many(&[&src, &typefinder, &queue, &muxer, &sink])
@ -90,55 +83,53 @@ fn example_main() -> Result<(), Error> {
let pipeline_clone = pipeline.clone(); let pipeline_clone = pipeline.clone();
let typefinder_clone = typefinder.clone(); let typefinder_clone = typefinder.clone();
typefinder typefinder.connect("have-type", false, move |values| {
.connect("have-type", false, move |values| { let (pipeline, typefinder) = (&pipeline_clone, &typefinder_clone);
let (pipeline, typefinder) = (&pipeline_clone, &typefinder_clone);
// Use the detected format to select between a small set of supported demuxers // Use the detected format to select between a small set of supported demuxers
// Hint: This should probably never be done manually, for stuff like this, // Hint: This should probably never be done manually, for stuff like this,
// the decodebin should be used, that does this stuff automatically and handles // the decodebin should be used, that does this stuff automatically and handles
// much more corner-cases. This is just for the sake of being an example. // much more corner-cases. This is just for the sake of being an example.
let caps = values[2] let caps = values[2]
.get::<gst::Caps>() .get::<gst::Caps>()
.expect("typefinder \"have-type\" signal values[2]"); .expect("typefinder \"have-type\" signal values[2]");
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", None).expect("matroskademux missing") gst::ElementFactory::make("matroskademux", None).expect("matroskademux missing")
} }
"video/quicktime" => { "video/quicktime" => {
gst::ElementFactory::make("qtdemux", None).expect("qtdemux missing") gst::ElementFactory::make("qtdemux", None).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);
} }
}; };
// We found a supported format and created the appropriate demuxer -> link it // We found a supported format and created the appropriate demuxer -> link it
pipeline pipeline
.add(&demuxer) .add(&demuxer)
.expect("Failed to build remux pipeline"); .expect("Failed to build remux pipeline");
// We simply keep the typefinder element and pipe the data through it. // We simply keep the typefinder element and pipe the data through it.
// Removing is non-trivial since it started reading data from the pipeline // Removing is non-trivial since it started reading data from the pipeline
// that the next element (the format specific demuxer) would need. // that the next element (the format specific demuxer) would need.
typefinder typefinder
.link(&demuxer) .link(&demuxer)
.expect("Failed to build remux pipeline"); .expect("Failed to build remux pipeline");
let queue_clone = queue.clone(); let queue_clone = queue.clone();
let muxer_clone = muxer.clone(); let muxer_clone = muxer.clone();
demuxer.connect_pad_added(move |demux, src_pad| { demuxer.connect_pad_added(move |demux, src_pad| {
handle_demux_pad_added(demux, src_pad, &queue_clone, &muxer_clone) handle_demux_pad_added(demux, src_pad, &queue_clone, &muxer_clone)
}); });
demuxer demuxer
.sync_state_with_parent() .sync_state_with_parent()
.expect("Failed to build remux pipeline"); .expect("Failed to build remux pipeline");
None None
}) });
.expect("Failed to register have-type signal of typefind");
pipeline.set_state(gst::State::Playing)?; pipeline.set_state(gst::State::Playing)?;

View file

@ -24,10 +24,10 @@ fn example_main() {
/* Completely contrived example that takes the 4:3 input video, cuts out a 5:4 frame /* Completely contrived example that takes the 4:3 input video, cuts out a 5:4 frame
* and then adds pillarbox borders to place it in a 16:9 target area */ * and then adds pillarbox borders to place it in a 16:9 target area */
/* The output will be the full frame: */ /* The output will be the full frame: */
sinkpad.set_property("xpos", 0i32).unwrap(); sinkpad.set_property("xpos", 0i32);
sinkpad.set_property("ypos", 0i32).unwrap(); sinkpad.set_property("ypos", 0i32);
sinkpad.set_property("width", 1280i32).unwrap(); sinkpad.set_property("width", 1280i32);
sinkpad.set_property("height", 720i32).unwrap(); sinkpad.set_property("height", 720i32);
let mut converter_config = gst_video::VideoConverterConfig::new(); let mut converter_config = gst_video::VideoConverterConfig::new();
/* Crop the input frame to 5:4: */ /* Crop the input frame to 5:4: */
@ -41,9 +41,7 @@ fn example_main() {
converter_config.set_dest_y(0); converter_config.set_dest_y(0);
converter_config.set_dest_height(Some(720)); converter_config.set_dest_height(Some(720));
sinkpad sinkpad.set_property("converter-config", &*converter_config);
.set_property("converter-config", &*converter_config)
.unwrap();
pipeline pipeline
.set_state(gst::State::Playing) .set_state(gst::State::Playing)
.expect("Unable to set the pipeline to the `Playing` state"); .expect("Unable to set the pipeline to the `Playing` state");

View file

@ -543,9 +543,9 @@ impl App {
.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_property("enable-last-sample", false);
appsink.set_property("emit-signals", false)?; appsink.set_property("emit-signals", false);
appsink.set_property("max-buffers", 1u32)?; appsink.set_property("max-buffers", 1u32);
let caps = gst::Caps::builder("video/x-raw") let caps = gst::Caps::builder("video/x-raw")
.features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY]) .features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY])
@ -571,7 +571,7 @@ impl App {
let sink = gst::ElementFactory::make("glsinkbin", None) let sink = gst::ElementFactory::make("glsinkbin", None)
.map_err(|_| MissingElement("glsinkbin"))?; .map_err(|_| MissingElement("glsinkbin"))?;
sink.set_property("sink", &appsink)?; sink.set_property("sink", &appsink);
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?; src.link(&sink)?;
@ -680,11 +680,7 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> {
{ {
if gst_gl_context.is_none() { if gst_gl_context.is_none() {
gst_gl_context = glupload gst_gl_context = glupload.property::<Option<gst_gl::GLContext>>("context");
.property("context")
.unwrap()
.get::<Option<gst_gl::GLContext>>()
.unwrap();
} }
let sync_meta = buffer.meta::<gst_gl::GLSyncMeta>().unwrap(); let sync_meta = buffer.meta::<gst_gl::GLSyncMeta>().unwrap();

View file

@ -1035,7 +1035,7 @@ mod tests {
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap(); let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap();
let appsink = gst::ElementFactory::make("appsink", None).unwrap(); let appsink = gst::ElementFactory::make("appsink", None).unwrap();
videotestsrc.set_property("num-buffers", 5).unwrap(); videotestsrc.set_property("num-buffers", 5);
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
pipeline.add(&videotestsrc).unwrap(); pipeline.add(&videotestsrc).unwrap();

View file

@ -454,7 +454,7 @@ mod tests {
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap(); let appsrc = gst::ElementFactory::make("appsrc", None).unwrap();
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap(); let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
fakesink.set_property("signal-handoffs", true).unwrap(); fakesink.set_property("signal-handoffs", true);
let pipeline = gst::Pipeline::new(None); let pipeline = gst::Pipeline::new(None);
pipeline.add(&appsrc).unwrap(); pipeline.add(&appsrc).unwrap();
@ -475,17 +475,15 @@ mod tests {
let handoff_count_reference = Arc::new(AtomicUsize::new(0)); let handoff_count_reference = Arc::new(AtomicUsize::new(0));
fakesink fakesink.connect("handoff", false, {
.connect("handoff", false, { let handoff_count_reference = Arc::clone(&handoff_count_reference);
let handoff_count_reference = Arc::clone(&handoff_count_reference);
move |_| { move |_| {
handoff_count_reference.fetch_add(1, Ordering::AcqRel); handoff_count_reference.fetch_add(1, Ordering::AcqRel);
None None
} }
}) });
.unwrap();
pipeline.set_state(gst::State::Playing).unwrap(); pipeline.set_state(gst::State::Playing).unwrap();

View file

@ -17,6 +17,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -8,9 +8,6 @@ use glib::signal::{connect_raw, SignalHandlerId};
use glib::translate::*; use glib::translate::*;
#[cfg(any(feature = "v1_16", feature = "dox"))] #[cfg(any(feature = "v1_16", feature = "dox"))]
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
use glib::Value;
#[cfg(any(feature = "v1_16", feature = "dox"))]
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
use std::boxed::Box as Box_; use std::boxed::Box as Box_;
use std::mem; use std::mem;
#[cfg(any(feature = "v1_16", feature = "dox"))] #[cfg(any(feature = "v1_16", feature = "dox"))]
@ -129,29 +126,14 @@ impl<O: IsA<Aggregator>> AggregatorExtManual for O {
#[cfg(any(feature = "v1_16", feature = "dox"))] #[cfg(any(feature = "v1_16", feature = "dox"))]
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
fn min_upstream_latency(&self) -> gst::ClockTime { fn min_upstream_latency(&self) -> gst::ClockTime {
unsafe { self.as_ref().property("min-upstream-latency")
let mut value = Value::from_type(<gst::ClockTime as StaticType>::static_type());
glib::gobject_ffi::g_object_get_property(
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
b"min-upstream-latency\0".as_ptr() as *const _,
value.to_glib_none_mut().0,
);
value
.get()
.expect("AggregatorExtManual::min_upstream_latency")
}
} }
#[cfg(any(feature = "v1_16", feature = "dox"))] #[cfg(any(feature = "v1_16", feature = "dox"))]
#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))]
fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) { fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) {
unsafe { self.as_ref()
glib::gobject_ffi::g_object_set_property( .set_property("min-upstream-latency", &min_upstream_latency);
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
b"min-upstream-latency\0".as_ptr() as *const _,
Value::from(&min_upstream_latency).to_glib_none().0,
);
}
} }
#[cfg(any(feature = "v1_16", feature = "dox"))] #[cfg(any(feature = "v1_16", feature = "dox"))]

View file

@ -25,6 +25,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::functions::*; pub use crate::auto::functions::*;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -21,6 +21,7 @@ macro_rules! assert_initialized_main_thread {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -24,6 +24,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
mod control_point; mod control_point;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -57,6 +57,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(deprecated)] #[allow(deprecated)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -24,6 +24,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;
mod net_client_clock; mod net_client_clock;

View file

@ -6,32 +6,17 @@ use glib::prelude::*;
use glib::signal::connect_raw; use glib::signal::connect_raw;
use glib::signal::SignalHandlerId; use glib::signal::SignalHandlerId;
use glib::translate::*; use glib::translate::*;
use glib::Value;
use std::boxed::Box as Box_; use std::boxed::Box as Box_;
use std::mem::transmute; use std::mem::transmute;
impl Discoverer { impl Discoverer {
pub fn set_timeout(&self, timeout: gst::ClockTime) { pub fn set_timeout(&self, timeout: gst::ClockTime) {
unsafe { self.set_property("timeout", &timeout);
glib::gobject_ffi::g_object_set_property(
self.as_ptr() as *mut _,
"timeout".to_glib_none().0,
Value::from(&timeout).to_glib_none().0,
);
}
} }
pub fn timeout(&self) -> gst::ClockTime { pub fn timeout(&self) -> gst::ClockTime {
let mut value = Value::from(&0u64); self.property("timeout")
unsafe {
glib::gobject_ffi::g_object_get_property(
self.as_ptr() as *mut _,
"timeout".to_glib_none().0,
value.to_glib_none_mut().0,
);
}
value.get().expect("undefined timeout")
} }
#[doc(alias = "timeout")] #[doc(alias = "timeout")]

View file

@ -32,6 +32,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::functions::*; pub use crate::auto::functions::*;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -23,6 +23,7 @@ macro_rules! assert_initialized_main_thread {
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -30,6 +30,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::let_and_return)] #[allow(clippy::let_and_return)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -26,6 +26,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -29,7 +29,7 @@ impl<O: IsA<glib::Object>> GObjectExtManualGst for O {
} }
}; };
self.set_property_from_value(name, &value) self.try_set_property_from_value(name, &value)
} }
} }
@ -45,7 +45,7 @@ mod tests {
fakesink fakesink
.set_property_from_str("state-error", "ready-to-paused") .set_property_from_str("state-error", "ready-to-paused")
.unwrap(); .unwrap();
let v = fakesink.property("state-error").unwrap(); let v = fakesink.property_value("state-error");
let e = glib::EnumValue::from_value(&v).unwrap(); let e = glib::EnumValue::from_value(&v).unwrap();
assert_eq!(e.nick(), "ready-to-paused"); assert_eq!(e.nick(), "ready-to-paused");
} }

View file

@ -31,6 +31,7 @@ macro_rules! skip_assert_initialized {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::use_self)] #[allow(clippy::use_self)]
#[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrow)]
#[allow(unused_imports)]
mod auto; mod auto;
pub use crate::auto::functions::*; pub use crate::auto::functions::*;
pub use crate::auto::*; pub use crate::auto::*;

View file

@ -74,7 +74,6 @@ impl<O: IsA<crate::Object>> GstObjectExtManual for O {
None None
}) })
.unwrap()
} }
fn set_object_flags(&self, flags: ObjectFlags) { fn set_object_flags(&self, flags: ObjectFlags) {
@ -145,7 +144,7 @@ mod tests {
*notify_clone.lock().unwrap() = Some((id.clone(), prop.name())); *notify_clone.lock().unwrap() = Some((id.clone(), prop.name()));
}); });
identity.set_property("silent", false).unwrap(); identity.set_property("silent", false);
assert_eq!( assert_eq!(
*notify.lock().unwrap(), *notify.lock().unwrap(),
Some((identity.upcast::<crate::Object>(), "silent")) Some((identity.upcast::<crate::Object>(), "silent"))

View file

@ -1643,11 +1643,7 @@ impl<T: IsA<Pad> + IsA<glib::Object> + glib::object::IsClass> PadBuilder<T> {
// Since 1.14 templates can keep a pad GType with them, so we need to do some // Since 1.14 templates can keep a pad GType with them, so we need to do some
// additional checks here now // additional checks here now
if templ.has_property("gtype", Some(glib::Type::static_type())) { if templ.has_property("gtype", Some(glib::Type::static_type())) {
let gtype = templ let gtype = templ.property::<glib::Type>("gtype");
.property("gtype")
.unwrap()
.get::<glib::Type>()
.unwrap();
if gtype == glib::Type::UNIT { if gtype == glib::Type::UNIT {
// Nothing to be done, we can create any kind of pad // Nothing to be done, we can create any kind of pad

View file

@ -806,7 +806,7 @@ mod tests {
let src = ElementFactory::make("fakesrc", None).unwrap(); let src = ElementFactory::make("fakesrc", None).unwrap();
let sink = ElementFactory::make("fakesink", None).unwrap(); let sink = ElementFactory::make("fakesink", None).unwrap();
src.set_property("num-buffers", 100i32).unwrap(); src.set_property("num-buffers", 100i32);
pipeline pipeline
.add_many(&[&src, element.upcast_ref(), &sink]) .add_many(&[&src, element.upcast_ref(), &sink])

View file

@ -56,11 +56,7 @@ fn send_seek_event(pipeline: &Element, rate: f64) -> bool {
}; };
// If we have not done so, obtain the sink through which we will send the seek events // If we have not done so, obtain the sink through which we will send the seek events
if let Ok(Some(video_sink)) = pipeline if let Ok(Some(video_sink)) = pipeline.try_property::<Option<Element>>("video-sink") {
.property("video-sink")
.unwrap()
.get::<Option<Element>>()
{
println!("Current rate: {}\r", rate); println!("Current rate: {}\r", rate);
// Send the event // Send the event
video_sink.send_event(seek_event) video_sink.send_event(seek_event)
@ -175,10 +171,7 @@ USAGE: Choose one of the following options, then press enter:
} }
} }
Command::NextFrame => { Command::NextFrame => {
if let Ok(Some(video_sink)) = pipeline if let Ok(Some(video_sink)) = pipeline.try_property::<Option<Element>>("video-sink")
.property("video-sink")
.unwrap()
.get::<Option<Element>>()
{ {
// Send the event // Send the event
let step = Step::new(gst::format::Buffers(1), rate.abs(), true, false); let step = Step::new(gst::format::Buffers(1), rate.abs(), true, false);

View file

@ -30,9 +30,7 @@ fn tutorial_main() {
// Set the URI to play // 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";
source source.set_property("uri", uri);
.set_property("uri", uri)
.expect("Can't set uri property on uridecodebin");
// 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| {

View file

@ -31,9 +31,7 @@ fn tutorial_main() {
// Set the URI to play // 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 playbin.set_property("uri", uri);
.set_property("uri", uri)
.expect("Can't set uri property on playbin");
// Start playing // Start playing
playbin playbin

View file

@ -40,9 +40,9 @@ mod tutorial5 {
let propname: &str = &format!("n-{}", stype); let propname: &str = &format!("n-{}", stype);
let signame: &str = &format!("get-{}-tags", stype); let signame: &str = &format!("get-{}-tags", stype);
let x = playbin.property(propname).unwrap().get::<i32>().unwrap(); let x = playbin.property::<i32>(propname);
for i in 0..x { for i in 0..x {
let tags = playbin.emit_by_name(signame, &[&i]).unwrap().unwrap(); let tags = playbin.emit_by_name(signame, &[&i]).unwrap();
if let Ok(Some(tags)) = tags.get::<Option<gst::TagList>>() { if let Ok(Some(tags)) = tags.get::<Option<gst::TagList>>() {
textbuf.insert_at_cursor(&format!("{} stream {}:\n ", stype, i)); textbuf.insert_at_cursor(&format!("{} stream {}:\n ", stype, i));
@ -302,37 +302,31 @@ 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", None).unwrap();
playbin.set_property("uri", uri).unwrap(); playbin.set_property("uri", uri);
playbin playbin.connect("video-tags-changed", false, |args| {
.connect("video-tags-changed", false, |args| { let pipeline = args[0]
let pipeline = args[0] .get::<gst::Element>()
.get::<gst::Element>() .expect("playbin \"video-tags-changed\" args[0]");
.expect("playbin \"video-tags-changed\" args[0]"); post_app_message(&pipeline);
post_app_message(&pipeline); None
None });
})
.unwrap();
playbin playbin.connect("audio-tags-changed", false, |args| {
.connect("audio-tags-changed", false, |args| { let pipeline = args[0]
let pipeline = args[0] .get::<gst::Element>()
.get::<gst::Element>() .expect("playbin \"audio-tags-changed\" args[0]");
.expect("playbin \"audio-tags-changed\" args[0]"); post_app_message(&pipeline);
post_app_message(&pipeline); None
None });
})
.unwrap();
playbin playbin.connect("text-tags-changed", false, move |args| {
.connect("text-tags-changed", false, move |args| { let pipeline = args[0]
let pipeline = args[0] .get::<gst::Element>()
.get::<gst::Element>() .expect("playbin \"text-tags-changed\" args[0]");
.expect("playbin \"text-tags-changed\" args[0]"); post_app_message(&pipeline);
post_app_message(&pipeline); None
None });
})
.unwrap();
let window = create_ui(&playbin); let window = create_ui(&playbin);

View file

@ -24,7 +24,7 @@ fn tutorial_main() {
let pipeline = gst::Pipeline::new(Some("test-pipeline")); let pipeline = gst::Pipeline::new(Some("test-pipeline"));
audio_source.set_property("freq", 215.0).unwrap(); audio_source.set_property("freq", 215.0);
visual.set_property_from_str("shader", "none").unwrap(); visual.set_property_from_str("shader", "none").unwrap();
visual.set_property_from_str("style", "lines").unwrap(); visual.set_property_from_str("style", "lines").unwrap();

View file

@ -12,19 +12,16 @@ use std::{thread, time};
mod tutorials_common; mod tutorials_common;
fn analyze_streams(playbin: &gst::Element) { fn analyze_streams(playbin: &gst::Element) {
let n_video = playbin.property("n-video").unwrap().get::<i32>().unwrap(); let n_video = playbin.property::<i32>("n-video");
let n_audio = playbin.property("n-audio").unwrap().get::<i32>().unwrap(); let n_audio = playbin.property::<i32>("n-audio");
let n_text = playbin.property("n-text").unwrap().get::<i32>().unwrap(); let n_text = playbin.property::<i32>("n-text");
println!( println!(
"{} video stream(s), {} audio stream(s), {} text stream(s)", "{} video stream(s), {} audio stream(s), {} text stream(s)",
n_video, n_audio, n_text n_video, n_audio, n_text
); );
for i in 0..n_video { for i in 0..n_video {
let tags = playbin let tags = playbin.emit_by_name("get-video-tags", &[&i]).unwrap();
.emit_by_name("get-video-tags", &[&i])
.unwrap()
.unwrap();
if let Ok(tags) = tags.get::<gst::TagList>() { if let Ok(tags) = tags.get::<gst::TagList>() {
println!("video stream {}:", i); println!("video stream {}:", i);
@ -35,10 +32,7 @@ fn analyze_streams(playbin: &gst::Element) {
} }
for i in 0..n_audio { for i in 0..n_audio {
let tags = playbin let tags = playbin.emit_by_name("get-audio-tags", &[&i]).unwrap();
.emit_by_name("get-audio-tags", &[&i])
.unwrap()
.unwrap();
if let Ok(tags) = tags.get::<gst::TagList>() { if let Ok(tags) = tags.get::<gst::TagList>() {
println!("audio stream {}:", i); println!("audio stream {}:", i);
@ -55,10 +49,7 @@ fn analyze_streams(playbin: &gst::Element) {
} }
for i in 0..n_text { for i in 0..n_text {
let tags = playbin let tags = playbin.emit_by_name("get-text-tags", &[&i]).unwrap();
.emit_by_name("get-text-tags", &[&i])
.unwrap()
.unwrap();
if let Ok(tags) = tags.get::<gst::TagList>() { if let Ok(tags) = tags.get::<gst::TagList>() {
println!("subtitle stream {}:", i); println!("subtitle stream {}:", i);
@ -68,21 +59,9 @@ fn analyze_streams(playbin: &gst::Element) {
} }
} }
let current_video = playbin let current_video = playbin.property::<i32>("current-video");
.property("current-video") let current_audio = playbin.property::<i32>("current-audio");
.unwrap() let current_text = playbin.property::<i32>("current-text");
.get::<i32>()
.unwrap();
let current_audio = playbin
.property("current-audio")
.unwrap()
.get::<i32>()
.unwrap();
let current_text = playbin
.property("current-text")
.unwrap()
.get::<i32>()
.unwrap();
println!( println!(
"Currently playing video stream {}, audio stream {}, text stream {}", "Currently playing video stream {}, audio stream {}, text stream {}",
current_video, current_audio, current_text current_video, current_audio, current_text
@ -100,11 +79,11 @@ fn handle_keyboard(playbin: &gst::Element, main_loop: &glib::MainLoop) {
if let Some(index) = index.to_digit(10) { if let Some(index) = index.to_digit(10) {
// Here index can only be 0-9 // Here index can only be 0-9
let index = index as i32; let index = index as i32;
let n_audio = playbin.property("n-audio").unwrap().get::<i32>().unwrap(); let n_audio = playbin.property::<i32>("n-audio");
if index < n_audio { if index < n_audio {
println!("Setting current audio stream to {}", index); println!("Setting current audio stream to {}", index);
playbin.set_property("current-audio", index).unwrap(); playbin.set_property("current-audio", index);
} else { } else {
eprintln!("Index out of bounds"); eprintln!("Index out of bounds");
} }
@ -134,10 +113,10 @@ fn tutorial_main() -> Result<(), Error> {
// Set URI to play // 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)?; playbin.set_property("uri", uri);
// Set flags to show Audio and Video but ignore Subtitles // Set flags to show Audio and Video but ignore Subtitles
let flags = playbin.property("flags")?; let flags = playbin.property_value("flags");
let flags_class = FlagsClass::new(flags.type_()).unwrap(); let flags_class = FlagsClass::new(flags.type_()).unwrap();
let flags = flags_class let flags = flags_class
@ -148,10 +127,10 @@ fn tutorial_main() -> Result<(), Error> {
.unset_by_nick("text") .unset_by_nick("text")
.build() .build()
.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 // Set connection speed. This will affect some internal decisions of playbin
playbin.set_property("connection-speed", 56u64)?; playbin.set_property("connection-speed", 56u64);
// Handle keyboard input // Handle keyboard input
let playbin_clone = playbin.clone(); let playbin_clone = playbin.clone();

View file

@ -20,10 +20,10 @@ fn tutorial_main() -> Result<(), Error> {
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", None)?;
pipeline.set_property("uri", uri).unwrap(); pipeline.set_property("uri", uri);
// Set the download flag // Set the download flag
let flags = pipeline.property("flags")?; let flags = pipeline.property_value("flags");
let flags_class = FlagsClass::new(flags.type_()).unwrap(); let flags_class = FlagsClass::new(flags.type_()).unwrap();
let flags = flags_class let flags = flags_class
.builder_with_value(flags) .builder_with_value(flags)
@ -31,10 +31,10 @@ fn tutorial_main() -> Result<(), Error> {
.set_by_nick("download") .set_by_nick("download")
.build() .build()
.unwrap(); .unwrap();
pipeline.set_property_from_value("flags", &flags).unwrap(); pipeline.set_property_from_value("flags", &flags);
// Uncomment this line to limit the amount of downloaded data. // Uncomment this line to limit the amount of downloaded data.
// pipeline.set_property("ring-buffer-max-size", 4_000_000u64)?; // pipeline.set_property("ring-buffer-max-size", 4_000_000u64);
// Start playing // Start playing
let mut is_live = false; let mut is_live = false;
@ -103,16 +103,12 @@ fn tutorial_main() -> Result<(), Error> {
let download_buffer = args[1].get::<gst::Object>().unwrap(); let download_buffer = args[1].get::<gst::Object>().unwrap();
println!( println!(
"Temporary file: {:?}", "Temporary file: {:?}",
download_buffer download_buffer.property::<Option<String>>("temp-location")
.property("temp-location")
.unwrap()
.get::<Option<String>>()
.unwrap()
); );
// Uncomment this line to keep the temporary file after the program exists. // Uncomment this line to keep the temporary file after the program exists.
// download_buffer.set_property("temp-remove", false).ok(); // download_buffer.set_property("temp-remove", false).ok();
None None
})?; });
let pipeline_weak_ = pipeline.downgrade(); let pipeline_weak_ = pipeline.downgrade();
let timeout_id = glib::timeout_add_seconds(1, move || { let timeout_id = glib::timeout_add_seconds(1, move || {