forked from mirrors/gstreamer-rs
examples/glupload: Update glutin to 0.26 with winit 0.24
Winit 0.19 uses uninitialized variables which is invalid since Rust 1.48, leading to a runtime panic [1]. Updating to the latest version resolves these issues but requires significant refactoring since the event loop now runs entirely within a closure. [1]: https://github.com/rust-windowing/winit/issues/1811
This commit is contained in:
parent
8ab8f00005
commit
5e8634e9eb
3 changed files with 74 additions and 71 deletions
|
@ -30,6 +30,11 @@ name = "gstreamer-rs-lgpl-docs"
|
||||||
multiple-versions = "deny"
|
multiple-versions = "deny"
|
||||||
wildcards = "allow"
|
wildcards = "allow"
|
||||||
highlight = "all"
|
highlight = "all"
|
||||||
|
skip-tree = [
|
||||||
|
# Winit introduces quite a few duplicate (outdated!) dependencies together
|
||||||
|
# with its direct dependant glutin.
|
||||||
|
{ name = "winit", version = "0.24" }
|
||||||
|
]
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
unknown-registry = "deny"
|
unknown-registry = "deny"
|
||||||
|
|
|
@ -32,8 +32,7 @@ byte-slice-cast = "1"
|
||||||
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs", features=["use_glib"], optional = true }
|
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs", features=["use_glib"], optional = true }
|
||||||
pango = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
pango = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
||||||
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
||||||
glutin = { version = "0.21", optional = true }
|
glutin = { version = "0.26", optional = true }
|
||||||
winit = { version = "0.19", optional = true }
|
|
||||||
once_cell = "1.0"
|
once_cell = "1.0"
|
||||||
image = { version="0.23", optional = true }
|
image = { version="0.23", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use gst_gl::prelude::*;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::sync;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
@ -184,7 +185,7 @@ impl Gl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&self, size: glutin::dpi::PhysicalSize) {
|
fn resize(&self, size: glutin::dpi::PhysicalSize<u32>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.gl
|
self.gl
|
||||||
.Viewport(0, 0, size.width as i32, size.height as i32);
|
.Viewport(0, 0, size.width as i32, size.height as i32);
|
||||||
|
@ -318,7 +319,7 @@ struct App {
|
||||||
appsink: gst_app::AppSink,
|
appsink: gst_app::AppSink,
|
||||||
glupload: gst::Element,
|
glupload: gst::Element,
|
||||||
bus: gst::Bus,
|
bus: gst::Bus,
|
||||||
events_loop: glutin::EventsLoop,
|
event_loop: glutin::event_loop::EventLoop<()>,
|
||||||
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
|
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
|
||||||
shared_context: gst_gl::GLContext,
|
shared_context: gst_gl::GLContext,
|
||||||
}
|
}
|
||||||
|
@ -332,11 +333,11 @@ impl App {
|
||||||
.get_bus()
|
.get_bus()
|
||||||
.expect("Pipeline without bus. Shouldn't happen!");
|
.expect("Pipeline without bus. Shouldn't happen!");
|
||||||
|
|
||||||
let events_loop = glutin::EventsLoop::new();
|
let event_loop = glutin::event_loop::EventLoop::new();
|
||||||
let window = glutin::WindowBuilder::new().with_title("GL rendering");
|
let window = glutin::window::WindowBuilder::new().with_title("GL rendering");
|
||||||
let windowed_context = glutin::ContextBuilder::new()
|
let windowed_context = glutin::ContextBuilder::new()
|
||||||
.with_vsync(true)
|
.with_vsync(true)
|
||||||
.build_windowed(window, &events_loop)?;
|
.build_windowed(window, &event_loop)?;
|
||||||
|
|
||||||
let windowed_context = unsafe { windowed_context.make_current().map_err(|(_, err)| err)? };
|
let windowed_context = unsafe { windowed_context.make_current().map_err(|(_, err)| err)? };
|
||||||
|
|
||||||
|
@ -345,10 +346,10 @@ impl App {
|
||||||
|
|
||||||
let shared_context: gst_gl::GLContext;
|
let shared_context: gst_gl::GLContext;
|
||||||
if cfg!(target_os = "linux") {
|
if cfg!(target_os = "linux") {
|
||||||
use glutin::os::unix::RawHandle;
|
use glutin::platform::unix::RawHandle;
|
||||||
#[cfg(any(feature = "gst-gl-x11", feature = "gst-gl-wayland"))]
|
#[cfg(any(feature = "gst-gl-x11", feature = "gst-gl-wayland"))]
|
||||||
use glutin::os::unix::WindowExt;
|
use glutin::platform::unix::WindowExtUnix;
|
||||||
use glutin::os::ContextTraitExt;
|
use glutin::platform::ContextTraitExt;
|
||||||
|
|
||||||
let api = App::map_gl_api(windowed_context.get_api());
|
let api = App::map_gl_api(windowed_context.get_api());
|
||||||
|
|
||||||
|
@ -368,7 +369,7 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "gst-gl-wayland")]
|
#[cfg(feature = "gst-gl-wayland")]
|
||||||
if let Some(display) = inner_window.get_wayland_display() {
|
if let Some(display) = inner_window.wayland_display() {
|
||||||
gl_display = Some(
|
gl_display = Some(
|
||||||
unsafe {
|
unsafe {
|
||||||
gst_gl_wayland::GLDisplayWayland::with_display(display as usize)
|
gst_gl_wayland::GLDisplayWayland::with_display(display as usize)
|
||||||
|
@ -386,7 +387,7 @@ impl App {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gst-gl-x11")]
|
#[cfg(feature = "gst-gl-x11")]
|
||||||
RawHandle::Glx(glx_context) => {
|
RawHandle::Glx(glx_context) => {
|
||||||
let gl_display = if let Some(display) = inner_window.get_xlib_display() {
|
let gl_display = if let Some(display) = inner_window.xlib_display() {
|
||||||
unsafe { gst_gl_x11::GLDisplayX11::with_display(display as usize) }.unwrap()
|
unsafe { gst_gl_x11::GLDisplayX11::with_display(display as usize) }.unwrap()
|
||||||
} else {
|
} else {
|
||||||
panic!("X11 window without X Display");
|
panic!("X11 window without X Display");
|
||||||
|
@ -413,7 +414,7 @@ impl App {
|
||||||
shared_context.fill_info()?;
|
shared_context.fill_info()?;
|
||||||
|
|
||||||
let gl_context = shared_context.clone();
|
let gl_context = shared_context.clone();
|
||||||
let events_proxy = events_loop.create_proxy();
|
let event_proxy = sync::Mutex::new(event_loop.create_proxy());
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
bus.set_sync_handler(move |_, msg| {
|
bus.set_sync_handler(move |_, msg| {
|
||||||
|
@ -446,7 +447,9 @@ impl App {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = events_proxy.wakeup();
|
if let Err(e) = event_proxy.lock().unwrap().send_event(()) {
|
||||||
|
eprintln!("Failed to send BusEvent to event proxy: {}", e)
|
||||||
|
}
|
||||||
|
|
||||||
gst::BusSyncReply::Pass
|
gst::BusSyncReply::Pass
|
||||||
});
|
});
|
||||||
|
@ -459,7 +462,7 @@ impl App {
|
||||||
appsink,
|
appsink,
|
||||||
glupload,
|
glupload,
|
||||||
bus,
|
bus,
|
||||||
events_loop,
|
event_loop,
|
||||||
windowed_context,
|
windowed_context,
|
||||||
shared_context,
|
shared_context,
|
||||||
})
|
})
|
||||||
|
@ -467,9 +470,9 @@ impl App {
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
&self,
|
&self,
|
||||||
events_loop: &glutin::EventsLoop,
|
event_loop: &glutin::event_loop::EventLoop<()>,
|
||||||
) -> Result<mpsc::Receiver<gst::Sample>, Error> {
|
) -> Result<mpsc::Receiver<gst::Sample>, Error> {
|
||||||
let events_proxy = events_loop.create_proxy();
|
let events_proxy = event_loop.create_proxy();
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
self.appsink.set_callbacks(
|
self.appsink.set_callbacks(
|
||||||
gst_app::AppSinkCallbacks::builder()
|
gst_app::AppSinkCallbacks::builder()
|
||||||
|
@ -506,7 +509,7 @@ impl App {
|
||||||
.map(|_| gst::FlowSuccess::Ok)
|
.map(|_| gst::FlowSuccess::Ok)
|
||||||
.map_err(|_| gst::FlowError::Error)?;
|
.map_err(|_| gst::FlowError::Error)?;
|
||||||
|
|
||||||
let _ = events_proxy.wakeup();
|
let _ = events_proxy.send_event(());
|
||||||
|
|
||||||
Ok(gst::FlowSuccess::Ok)
|
Ok(gst::FlowSuccess::Ok)
|
||||||
})
|
})
|
||||||
|
@ -595,13 +598,11 @@ impl App {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_context(self: App) -> glutin::WindowedContext<glutin::PossiblyCurrent> {
|
|
||||||
self.windowed_context
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_loop(mut app: App) -> Result<glutin::WindowedContext<glutin::PossiblyCurrent>, Error> {
|
fn main_loop(app: App) -> Result<(), Error> {
|
||||||
|
let receiver = app.setup(&app.event_loop)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Pixel format of the window's GL context {:?}",
|
"Pixel format of the window's GL context {:?}",
|
||||||
app.windowed_context.get_pixel_format()
|
app.windowed_context.get_pixel_format()
|
||||||
|
@ -609,42 +610,52 @@ fn main_loop(mut app: App) -> Result<glutin::WindowedContext<glutin::PossiblyCur
|
||||||
|
|
||||||
let gl = load(&app.windowed_context);
|
let gl = load(&app.windowed_context);
|
||||||
|
|
||||||
let receiver = app.setup(&app.events_loop)?;
|
|
||||||
|
|
||||||
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 running = true;
|
|
||||||
let mut gst_gl_context: Option<gst_gl::GLContext> = None;
|
let mut gst_gl_context: Option<gst_gl::GLContext> = None;
|
||||||
let events_loop = &mut app.events_loop;
|
|
||||||
let windowed_context = &mut app.windowed_context;
|
|
||||||
let bus = &app.bus;
|
|
||||||
|
|
||||||
while running {
|
let App {
|
||||||
#[allow(clippy::single_match)]
|
bus,
|
||||||
events_loop.poll_events(|event| match event {
|
event_loop,
|
||||||
glutin::Event::WindowEvent { event, .. } => match event {
|
glupload,
|
||||||
glutin::WindowEvent::CloseRequested
|
pipeline,
|
||||||
| glutin::WindowEvent::KeyboardInput {
|
shared_context,
|
||||||
|
windowed_context,
|
||||||
|
..
|
||||||
|
} = app;
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, cf| {
|
||||||
|
*cf = glutin::event_loop::ControlFlow::Wait;
|
||||||
|
|
||||||
|
let mut needs_redraw = false;
|
||||||
|
match event {
|
||||||
|
glutin::event::Event::LoopDestroyed => {
|
||||||
|
pipeline.send_event(gst::event::Eos::new());
|
||||||
|
pipeline.set_state(gst::State::Null).unwrap();
|
||||||
|
}
|
||||||
|
glutin::event::Event::WindowEvent { event, .. } => match event {
|
||||||
|
glutin::event::WindowEvent::CloseRequested
|
||||||
|
| glutin::event::WindowEvent::KeyboardInput {
|
||||||
input:
|
input:
|
||||||
glutin::KeyboardInput {
|
glutin::event::KeyboardInput {
|
||||||
state: glutin::ElementState::Released,
|
state: glutin::event::ElementState::Released,
|
||||||
virtual_keycode: Some(glutin::VirtualKeyCode::Escape),
|
virtual_keycode: Some(glutin::event::VirtualKeyCode::Escape),
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
} => running = false,
|
} => *cf = glutin::event_loop::ControlFlow::Exit,
|
||||||
glutin::WindowEvent::Resized(logical_size) => {
|
glutin::event::WindowEvent::Resized(physical_size) => {
|
||||||
let dpi_factor = windowed_context.window().get_hidpi_factor();
|
windowed_context.resize(physical_size);
|
||||||
windowed_context.resize(logical_size.to_physical(dpi_factor));
|
gl.resize(physical_size);
|
||||||
gl.resize(logical_size.to_physical(dpi_factor));
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
glutin::event::Event::RedrawRequested(_) => needs_redraw = true,
|
||||||
_ => (),
|
_ => (),
|
||||||
});
|
}
|
||||||
|
|
||||||
// Handle all pending messages. Whenever there is a message we will
|
// Handle all pending messages. Whenever there is a message we will
|
||||||
// wake up the events loop above
|
// wake up the events loop above
|
||||||
App::handle_messages(&bus)?;
|
App::handle_messages(&bus).unwrap();
|
||||||
|
|
||||||
// get the last frame in channel
|
// get the last frame in channel
|
||||||
if let Some(sample) = receiver.try_iter().last() {
|
if let Some(sample) = receiver.try_iter().last() {
|
||||||
|
@ -656,8 +667,7 @@ fn main_loop(mut app: App) -> Result<glutin::WindowedContext<glutin::PossiblyCur
|
||||||
|
|
||||||
{
|
{
|
||||||
if gst_gl_context.is_none() {
|
if gst_gl_context.is_none() {
|
||||||
gst_gl_context = app
|
gst_gl_context = glupload
|
||||||
.glupload
|
|
||||||
.get_property("context")
|
.get_property("context")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get::<gst_gl::GLContext>()
|
.get::<gst_gl::GLContext>()
|
||||||
|
@ -670,38 +680,27 @@ fn main_loop(mut app: App) -> Result<glutin::WindowedContext<glutin::PossiblyCur
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(frame) = curr_frame.as_ref() {
|
if needs_redraw {
|
||||||
let sync_meta = frame.buffer().get_meta::<gst_gl::GLSyncMeta>().unwrap();
|
if let Some(frame) = curr_frame.as_ref() {
|
||||||
sync_meta.wait(&app.shared_context);
|
let sync_meta = frame.buffer().get_meta::<gst_gl::GLSyncMeta>().unwrap();
|
||||||
if let Some(texture) = frame.get_texture_id(0) {
|
sync_meta.wait(&shared_context);
|
||||||
gl.draw_frame(texture as gl::types::GLuint);
|
if let Some(texture) = frame.get_texture_id(0) {
|
||||||
|
gl.draw_frame(texture as gl::types::GLuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
windowed_context.swap_buffers().unwrap();
|
||||||
}
|
}
|
||||||
windowed_context.swap_buffers()?;
|
})
|
||||||
}
|
|
||||||
|
|
||||||
app.pipeline.send_event(gst::event::Eos::new());
|
|
||||||
app.pipeline.set_state(gst::State::Null)?;
|
|
||||||
|
|
||||||
Ok(app.into_context())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup(_windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>) {
|
|
||||||
// To ensure that the context stays alive longer than the pipeline or any reference
|
|
||||||
// inside GStreamer to the GL context, its display or anything else. See
|
|
||||||
// https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/196
|
|
||||||
//
|
|
||||||
// We might do any window/GL specific cleanup here as needed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn example_main() {
|
fn example_main() {
|
||||||
match App::new().and_then(main_loop).map(cleanup) {
|
App::new()
|
||||||
Ok(r) => r,
|
.and_then(main_loop)
|
||||||
Err(e) => eprintln!("Error! {}", e),
|
.unwrap_or_else(|e| eprintln!("Error! {}", e))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
Loading…
Reference in a new issue