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: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1375>
This commit is contained in:
Sebastian Dröge 2023-10-30 11:21:25 +02:00
parent b771afe8be
commit 74c04d79c9
2 changed files with 29 additions and 18 deletions

View file

@ -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", features = ["v1_16"], optional = true } gst_gl_wayland = { package = "gstreamer-gl-wayland", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"], optional = true }
gst_gl_x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"], optional = true } gst_gl_x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"], optional = true }
gst_gl_egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"], optional = true } gst_gl_egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"], optional = true }
async-channel = "2.0.0"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]

View file

@ -13,7 +13,7 @@ use super::SinkEvent;
use crate::sink::frame::Frame; use crate::sink::frame::Frame;
use crate::sink::paintable::Paintable; use crate::sink::paintable::Paintable;
use glib::{thread_guard::ThreadGuard, Sender}; use glib::thread_guard::ThreadGuard;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{gdk, glib}; use gtk::{gdk, glib};
@ -60,7 +60,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
pub struct PaintableSink { pub struct PaintableSink {
paintable: Mutex<Option<ThreadGuard<Paintable>>>, paintable: Mutex<Option<ThreadGuard<Paintable>>>,
info: Mutex<Option<gst_video::VideoInfo>>, info: Mutex<Option<gst_video::VideoInfo>>,
sender: Mutex<Option<Sender<SinkEvent>>>, sender: Mutex<Option<async_channel::Sender<SinkEvent>>>,
pending_frame: Mutex<Option<Frame>>, pending_frame: Mutex<Option<Frame>>,
cached_caps: Mutex<Option<gst::Caps>>, cached_caps: Mutex<Option<gst::Caps>>,
} }
@ -449,10 +449,16 @@ impl VideoSinkImpl for PaintableSink {
gst::FlowError::Flushing gst::FlowError::Flushing
})?; })?;
sender.send(SinkEvent::FrameChanged).map_err(|_| { 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"); gst::error!(CAT, imp: self, "Have main thread receiver shut down");
gst::FlowError::Flushing return Err(gst::FlowError::Flushing);
})?; }
}
Ok(gst::FlowSuccess::Ok) Ok(gst::FlowSuccess::Ok)
} }
@ -521,20 +527,24 @@ impl PaintableSink {
gst::debug!(CAT, imp: self, "Initializing paintable"); gst::debug!(CAT, imp: self, "Initializing paintable");
// The channel for the SinkEvents // The channel for the SinkEvents
let (sender, receiver) = glib::MainContext::channel(glib::Priority::DEFAULT); let (sender, receiver) = async_channel::bounded(3);
let self_ = self.to_owned();
// 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 || { 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"))] #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{ {
let gdk_context = if let GLContext::Initialized { gdk_context, .. } = let gdk_context = if let GLContext::Initialized { gdk_context, .. } =