From ed5b090318aee1197ea9776e29deec2006c389ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 30 Oct 2023 11:21:25 +0200 Subject: [PATCH] gtk4: Use async-channel instead of the glib MainContext channel The latter will be removed in favour of using async code in the future, and async code generally allows for more flexible message handling than the callback based MainContext channel. Part-of: --- video/gtk4/Cargo.toml | 1 + video/gtk4/src/sink/imp.rs | 47 +++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/video/gtk4/Cargo.toml b/video/gtk4/Cargo.toml index 7048590a..baf37c8c 100644 --- a/video/gtk4/Cargo.toml +++ b/video/gtk4/Cargo.toml @@ -21,6 +21,7 @@ gst_gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstre gst_gl_wayland = { package = "gstreamer-gl-wayland", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.21", version = "0.21", features = ["v1_16"], optional = true } gst_gl_x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.21", version = "0.21", features = ["v1_16"], optional = true } gst_gl_egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.21", version = "0.21", features = ["v1_16"], optional = true } +async-channel = "2.0.0" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/video/gtk4/src/sink/imp.rs b/video/gtk4/src/sink/imp.rs index 6b23b71d..fd5b7fd4 100644 --- a/video/gtk4/src/sink/imp.rs +++ b/video/gtk4/src/sink/imp.rs @@ -13,8 +13,7 @@ use super::SinkEvent; use crate::sink::frame::Frame; use crate::sink::paintable::Paintable; -use glib::{thread_guard::ThreadGuard, Sender}; -use gtk::prelude::GLContextExt; +use glib::thread_guard::ThreadGuard; use gtk::prelude::*; use gtk::{gdk, glib}; @@ -61,7 +60,7 @@ static CAT: Lazy = Lazy::new(|| { pub struct PaintableSink { paintable: Mutex>>, info: Mutex>, - sender: Mutex>>, + sender: Mutex>>, pending_frame: Mutex>, cached_caps: Mutex>, } @@ -450,10 +449,16 @@ impl VideoSinkImpl for PaintableSink { gst::FlowError::Flushing })?; - sender.send(SinkEvent::FrameChanged).map_err(|_| { - gst::error!(CAT, imp: self, "Have main thread receiver shut down"); - gst::FlowError::Flushing - })?; + match sender.try_send(SinkEvent::FrameChanged) { + Ok(_) => (), + Err(async_channel::TrySendError::Full(_)) => { + gst::warning!(CAT, imp: self, "Have too many pending frames"); + } + Err(async_channel::TrySendError::Closed(_)) => { + gst::error!(CAT, imp: self, "Have main thread receiver shut down"); + return Err(gst::FlowError::Flushing); + } + } Ok(gst::FlowSuccess::Ok) } @@ -522,20 +527,24 @@ impl PaintableSink { gst::debug!(CAT, imp: self, "Initializing paintable"); // The channel for the SinkEvents - let (sender, receiver) = glib::MainContext::channel(glib::Priority::DEFAULT); - let self_ = self.to_owned(); + let (sender, receiver) = async_channel::bounded(3); + // Spawn an async task on the main context to handle the channel messages + let main_context = glib::MainContext::default(); + + let self_ = self.downgrade(); + main_context.spawn(async move { + while let Ok(action) = receiver.recv().await { + let Some(self_) = self_.upgrade() else { + break; + }; + + self_.do_action(action); + } + }); + + // Create the paintable from the main thread let paintable = utils::invoke_on_main_thread(move || { - // Attach the receiver from the main thread to make sure it is called - // from a place where it can acquire the default main context. - receiver.attach( - Some(&glib::MainContext::default()), - glib::clone!( - @weak self_ => @default-return glib::ControlFlow::Break, - move |action| self_.do_action(action) - ), - ); - #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))] { let gdk_context = if let GLContext::Initialized { gdk_context, .. } =