mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2025-09-03 10:23:48 +00:00
examples: overlay-composition: Use cairo's new ImageSurfaceDataOwned
to get rid of unsafe code
This commit is contained in:
parent
2bb500db0b
commit
ce1a25a128
2 changed files with 74 additions and 98 deletions
|
@ -7,10 +7,6 @@
|
||||||
// {videotestsrc} - {overlaycomposition} - {capsfilter} - {videoconvert} - {autovideosink}
|
// {videotestsrc} - {overlaycomposition} - {capsfilter} - {videoconvert} - {autovideosink}
|
||||||
// The capsfilter element allows us to dictate the video resolution we want for the
|
// The capsfilter element allows us to dictate the video resolution we want for the
|
||||||
// videotestsrc and the overlaycomposition element.
|
// videotestsrc and the overlaycomposition element.
|
||||||
//
|
|
||||||
// There is a small amount of unsafe code that demonstrates how to work around
|
|
||||||
// Cairo's internal refcounting of the target buffer surface
|
|
||||||
#![allow(clippy::non_send_fields_in_send_ty)]
|
|
||||||
|
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
|
||||||
|
@ -150,36 +146,13 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
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);
|
||||||
|
|
||||||
/* Create a gst::Buffer for Cairo to draw into */
|
/* Create a Cairo image surface to draw into and the context around it. */
|
||||||
let frame_width = info.width() as usize;
|
let surface = cairo::ImageSurface::create(
|
||||||
let frame_height = info.height() as usize;
|
|
||||||
let stride = 4 * frame_width;
|
|
||||||
let frame_size = stride * frame_height;
|
|
||||||
|
|
||||||
/* Create an RGBA buffer, and add a video meta that the videooverlaycomposition expects */
|
|
||||||
let mut buffer = gst::Buffer::with_size(frame_size).unwrap();
|
|
||||||
|
|
||||||
gst_video::VideoMeta::add(
|
|
||||||
buffer.get_mut().unwrap(),
|
|
||||||
gst_video::VideoFrameFlags::empty(),
|
|
||||||
gst_video::VideoFormat::Bgra,
|
|
||||||
frame_width 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,
|
cairo::Format::ARgb32,
|
||||||
frame_width as i32,
|
info.width() as i32,
|
||||||
frame_height as i32,
|
info.height() as i32,
|
||||||
stride as i32,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cr = cairo::Context::new(&surface).expect("Failed to create cairo context");
|
let cr = cairo::Context::new(&surface).expect("Failed to create cairo context");
|
||||||
|
|
||||||
cr.save().expect("Failed to save state");
|
cr.save().expect("Failed to save state");
|
||||||
|
@ -233,22 +206,26 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
cr.restore().expect("Failed to restore state");
|
cr.restore().expect("Failed to restore state");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: The surface still owns a mutable reference to the buffer but our reference
|
/* Drop the Cairo context to release the additional reference to the data and
|
||||||
// to the surface here is the last one. After dropping the surface the buffer would be
|
* then take ownership of the data. This only works if we have the one and only
|
||||||
// freed, so we keep an additional strong reference here before dropping the surface,
|
* reference to the image surface */
|
||||||
// which is then returned. As such it's guaranteed that nothing is using the buffer
|
|
||||||
// anymore mutably.
|
|
||||||
drop(cr);
|
drop(cr);
|
||||||
unsafe {
|
let stride = surface.stride();
|
||||||
assert_eq!(
|
let data = surface.take_data().unwrap();
|
||||||
cairo::ffi::cairo_surface_get_reference_count(surface.to_raw_none()),
|
|
||||||
1
|
/* Create an RGBA buffer, and add a video meta that the videooverlaycomposition expects */
|
||||||
);
|
let mut buffer = gst::Buffer::from_mut_slice(data);
|
||||||
let buffer = glib::translate::from_glib_none(buffer_ptr);
|
|
||||||
drop(surface);
|
gst_video::VideoMeta::add_full(
|
||||||
buffer
|
buffer.get_mut().unwrap(),
|
||||||
}
|
gst_video::VideoFrameFlags::empty(),
|
||||||
};
|
gst_video::VideoFormat::Bgra,
|
||||||
|
info.width(),
|
||||||
|
info.height(),
|
||||||
|
&[0],
|
||||||
|
&[stride],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
/* Turn the buffer into a VideoOverlayRectangle, then place
|
/* Turn the buffer into a VideoOverlayRectangle, then place
|
||||||
* that into a VideoOverlayComposition and return it.
|
* that into a VideoOverlayComposition and return it.
|
||||||
|
@ -260,8 +237,8 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
||||||
&buffer,
|
&buffer,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
frame_width as u32,
|
info.width(),
|
||||||
frame_height as u32,
|
info.height(),
|
||||||
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
|
gst_video::VideoOverlayFormatFlags::PREMULTIPLIED_ALPHA,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
// {videotestsrc} - {cairooverlay} - {capsfilter} - {videoconvert} - {autovideosink}
|
// {videotestsrc} - {cairooverlay} - {capsfilter} - {videoconvert} - {autovideosink}
|
||||||
// The capsfilter element allows us to dictate the video resolution we want for the
|
// The capsfilter element allows us to dictate the video resolution we want for the
|
||||||
// videotestsrc and the cairooverlay element.
|
// videotestsrc and the cairooverlay element.
|
||||||
#![allow(clippy::non_send_fields_in_send_ty)]
|
|
||||||
|
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue