examples: glupload: Set sync point on the GL buffer as soon as possible

And also add API for getting the GL context from a `GLBaseMemory`.
This commit is contained in:
Sebastian Dröge 2022-10-24 16:51:54 +03:00 committed by Sebastian Dröge
parent c6cbf86012
commit 951f000622
2 changed files with 41 additions and 51 deletions

View file

@ -308,14 +308,13 @@ fn load(gl_context: &glutin::WindowedContext<glutin::PossiblyCurrent>) -> Gl {
#[derive(Debug)] #[derive(Debug)]
enum Message { enum Message {
Sample(gst::Sample), Frame(gst_video::VideoInfo, gst::Buffer),
BusEvent, BusEvent,
} }
pub(crate) struct App { pub(crate) struct App {
pipeline: gst::Pipeline, pipeline: gst::Pipeline,
appsink: gst_app::AppSink, appsink: gst_app::AppSink,
glupload: gst::Element,
bus: gst::Bus, bus: gst::Bus,
event_loop: glutin::event_loop::EventLoop<Message>, event_loop: glutin::event_loop::EventLoop<Message>,
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>, windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
@ -326,7 +325,7 @@ impl App {
pub(crate) fn new(gl_element: Option<&gst::Element>) -> Result<App, Error> { pub(crate) fn new(gl_element: Option<&gst::Element>) -> Result<App, Error> {
gst::init()?; gst::init()?;
let (pipeline, appsink, glupload) = App::create_pipeline(gl_element)?; let (pipeline, appsink) = App::create_pipeline(gl_element)?;
let bus = pipeline let bus = pipeline
.bus() .bus()
.expect("Pipeline without bus. Shouldn't happen!"); .expect("Pipeline without bus. Shouldn't happen!");
@ -458,7 +457,6 @@ impl App {
Ok(App { Ok(App {
pipeline, pipeline,
appsink, appsink,
glupload,
bus, bus,
event_loop, event_loop,
windowed_context, windowed_context,
@ -473,33 +471,49 @@ impl App {
.new_sample(move |appsink| { .new_sample(move |appsink| {
let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?; let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?;
{ let info = sample
let _buffer = sample.buffer().ok_or_else(|| { .caps()
.and_then(|caps| gst_video::VideoInfo::from_caps(caps).ok())
.ok_or_else(|| {
element_error!( element_error!(
appsink, appsink,
gst::ResourceError::Failed, gst::ResourceError::Failed,
("Failed to get buffer from appsink") ("Failed to get video info from sample")
); );
gst::FlowError::Error gst::FlowError::NotNegotiated
})?; })?;
let _info = sample let mut buffer = sample.buffer_owned().unwrap();
.caps() {
.and_then(|caps| gst_video::VideoInfo::from_caps(caps).ok()) let context = match (buffer.n_memory() > 0)
.ok_or_else(|| { .then(|| buffer.peek_memory(0))
.and_then(|m| m.downcast_memory_ref::<gst_gl::GLBaseMemory>())
.map(|m| m.context())
{
Some(context) => context.clone(),
None => {
element_error!( element_error!(
appsink, appsink,
gst::ResourceError::Failed, gst::ResourceError::Failed,
("Failed to get video info from sample") ("Failed to get GL context from buffer")
); );
gst::FlowError::Error return Err(gst::FlowError::Error);
})?; }
};
if let Some(meta) = buffer.meta::<gst_gl::GLSyncMeta>() {
meta.set_sync_point(&context);
} else {
let buffer = buffer.make_mut();
let meta = gst_gl::GLSyncMeta::add(buffer, &context);
meta.set_sync_point(&context);
}
} }
event_proxy event_proxy
.send_event(Message::Sample(sample)) .send_event(Message::Frame(info, buffer))
.map(|()| gst::FlowSuccess::Ok) .map(|()| gst::FlowSuccess::Ok)
.map_err(|e| { .map_err(|e| {
element_error!( element_error!(
@ -529,7 +543,7 @@ impl App {
fn create_pipeline( fn create_pipeline(
gl_element: Option<&gst::Element>, gl_element: Option<&gst::Element>,
) -> Result<(gst::Pipeline, gst_app::AppSink, gst::Element), Error> { ) -> Result<(gst::Pipeline, gst_app::AppSink), Error> {
let pipeline = gst::Pipeline::default(); let pipeline = gst::Pipeline::default();
let src = gst::ElementFactory::make("videotestsrc").build()?; let src = gst::ElementFactory::make("videotestsrc").build()?;
@ -556,7 +570,7 @@ impl App {
glupload.link(gl_element)?; glupload.link(gl_element)?;
gl_element.link(&appsink)?; gl_element.link(&appsink)?;
Ok((pipeline, appsink, glupload)) Ok((pipeline, appsink))
} else { } else {
let sink = gst::ElementFactory::make("glsinkbin") let sink = gst::ElementFactory::make("glsinkbin")
.property("sink", &appsink) .property("sink", &appsink)
@ -565,21 +579,7 @@ impl App {
pipeline.add_many(&[&src, &sink])?; pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?; src.link(&sink)?;
// get the glupload element to extract later the used context in it Ok((pipeline, appsink))
let mut iter = sink.downcast_ref::<gst::Bin>().unwrap().iterate_elements();
let glupload = loop {
match iter.next() {
Ok(Some(element)) => {
if element.factory().map_or(false, |f| f.name() == "glupload") {
break Some(element);
}
}
Err(gst::IteratorError::Resync) => iter.resync(),
_ => break None,
}
};
Ok((pipeline, appsink, glupload.unwrap()))
} }
} }
@ -620,12 +620,10 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> {
let gl = load(&app.windowed_context); let gl = load(&app.windowed_context);
let mut curr_frame: Option<gst_video::VideoFrame<gst_video::video_frame::Readable>> = None; let mut curr_frame: Option<gst_video::VideoFrame<gst_video::video_frame::Readable>> = None;
let mut gst_gl_context: Option<gst_gl::GLContext> = None;
let App { let App {
bus, bus,
event_loop, event_loop,
glupload,
pipeline, pipeline,
shared_context, shared_context,
windowed_context, windowed_context,
@ -660,22 +658,7 @@ pub(crate) fn main_loop(app: App) -> Result<(), Error> {
}, },
glutin::event::Event::RedrawRequested(_) => needs_redraw = true, glutin::event::Event::RedrawRequested(_) => needs_redraw = true,
// Receive a frame // Receive a frame
glutin::event::Event::UserEvent(Message::Sample(sample)) => { glutin::event::Event::UserEvent(Message::Frame(info, buffer)) => {
let buffer = sample.buffer_owned().unwrap();
let info = sample
.caps()
.and_then(|caps| gst_video::VideoInfo::from_caps(caps).ok())
.unwrap();
{
if gst_gl_context.is_none() {
gst_gl_context = glupload.property::<Option<gst_gl::GLContext>>("context");
}
let sync_meta = buffer.meta::<gst_gl::GLSyncMeta>().unwrap();
sync_meta.set_sync_point(gst_gl_context.as_ref().unwrap());
}
if let Ok(frame) = gst_video::VideoFrame::from_buffer_readable_gl(buffer, &info) { if let Ok(frame) = gst_video::VideoFrame::from_buffer_readable_gl(buffer, &info) {
curr_frame = Some(frame); curr_frame = Some(frame);
needs_redraw = true; needs_redraw = true;

View file

@ -86,4 +86,11 @@ impl GLBaseMemoryRef {
ffi::gst_gl_base_memory_init_once(); ffi::gst_gl_base_memory_init_once();
} }
} }
pub fn context(&self) -> &crate::GLContext {
unsafe {
&*(&(*self.as_ptr()).context as *const *mut ffi::GstGLContext
as *const crate::GLContext)
}
}
} }