Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/video
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian@centricular.com>2022-12-23 22:23:42 +0300
committerJayson Reis <santosdosreis@gmail.com>2023-06-22 08:43:57 +0300
commitdcb80ac105ca3c61505ba909865f7210bdc10b1f (patch)
tree6f07210f94f9da26c8b3791b738330ac1007ba98 /video
parent2be14b95b306cf23bd52a192106c17108d573a03 (diff)
gtk4: Add support for GL on Windows
This implements all the workarounds for Windows-specific complications that the GTK GStreamer mediafile implementation also does. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1255>
Diffstat (limited to 'video')
-rw-r--r--video/gtk4/Cargo.toml7
-rw-r--r--video/gtk4/src/sink/frame.rs18
-rw-r--r--video/gtk4/src/sink/imp.rs213
3 files changed, 212 insertions, 26 deletions
diff --git a/video/gtk4/Cargo.toml b/video/gtk4/Cargo.toml
index a87d71174..1036af9cc 100644
--- a/video/gtk4/Cargo.toml
+++ b/video/gtk4/Cargo.toml
@@ -28,6 +28,12 @@ once_cell = "1.0"
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_6"] }
gst_gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
+[target.'cfg(target_os = "windows")'.dependencies]
+gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_6"] }
+gst_gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_20"] }
+gdk_win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_4"]}
+windows-sys = { version = "0.42", features = ["Win32_Graphics_OpenGL", "Win32_Foundation", "Win32_Graphics_Gdi"] }
+
[lib]
name = "gstgtk4"
crate-type = ["cdylib", "rlib"]
@@ -42,6 +48,7 @@ static = []
wayland = ["gtk/v4_6", "gdk_wayland", "gst_gl", "gst_gl_wayland"]
x11glx = ["gtk/v4_6", "gdk_x11", "gst_gl", "gst_gl_x11"]
x11egl = ["gtk/v4_6", "gdk_x11", "gst_gl", "gst_gl_egl"]
+winegl = ["gdk_win32/egl", "gst_gl_egl"]
capi = []
doc = ["gst/v1_18"]
diff --git a/video/gtk4/src/sink/frame.rs b/video/gtk4/src/sink/frame.rs
index 6074792b5..4a53e1a07 100644
--- a/video/gtk4/src/sink/frame.rs
+++ b/video/gtk4/src/sink/frame.rs
@@ -11,7 +11,7 @@
use gst_video::prelude::*;
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
use gst_gl::prelude::*;
use gtk::{gdk, glib};
use std::collections::{HashMap, HashSet};
@@ -20,7 +20,7 @@ use std::collections::{HashMap, HashSet};
pub(crate) struct Frame {
frame: gst_video::VideoFrame<gst_video::video_frame::Readable>,
overlays: Vec<Overlay>,
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
wrapped_context: Option<gst_gl::GLContext>,
}
@@ -94,7 +94,7 @@ fn video_frame_to_memory_texture(
(texture, pixel_aspect_ratio)
}
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
fn video_frame_to_gl_texture(
frame: gst_video::VideoFrame<gst_video::video_frame::Readable>,
cached_textures: &mut HashMap<usize, gdk::Texture>,
@@ -150,11 +150,11 @@ impl Frame {
let width = self.frame.width();
let height = self.frame.height();
let (texture, pixel_aspect_ratio) = {
- #[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
+ #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst_gl")))]
{
video_frame_to_memory_texture(self.frame, cached_textures, &mut used_textures)
}
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
if let (Some(gdk_ctx), Some(wrapped_ctx)) =
(gdk_context, self.wrapped_context.as_ref())
@@ -210,11 +210,11 @@ impl Frame {
pub(crate) fn new(
buffer: &gst::Buffer,
info: &gst_video::VideoInfo,
- #[cfg(any(target_os = "macos", feature = "gst_gl"))] wrapped_context: Option<
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))] wrapped_context: Option<
&gst_gl::GLContext,
>,
#[allow(unused_variables)]
- #[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
+ #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst_gl")))]
wrapped_context: Option<&()>,
) -> Result<Self, gst::FlowError> {
// Empty buffers get filtered out in show_frame
@@ -222,7 +222,7 @@ impl Frame {
let mut frame;
- #[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
+ #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst_gl")))]
{
frame = Self {
frame: gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info)
@@ -230,7 +230,7 @@ impl Frame {
overlays: vec![],
};
}
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
// Check we received a buffer with GL memory and if the context of that memory
// can share with the wrapped context around the GDK GL context.
diff --git a/video/gtk4/src/sink/imp.rs b/video/gtk4/src/sink/imp.rs
index a8b21e3ab..0aacc2968 100644
--- a/video/gtk4/src/sink/imp.rs
+++ b/video/gtk4/src/sink/imp.rs
@@ -26,15 +26,15 @@ use std::sync::{Mutex, MutexGuard};
use crate::utils;
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
use gst_gl::prelude::GLContextExt as GstGLContextExt;
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
use gst_gl::prelude::*;
// Global GL context that is created by the first sink and kept around until the end of the
// process. This is provided to other elements in the pipeline to make sure they create GL contexts
// that are sharing with the GTK GL context.
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
enum GLContext {
Uninitialized,
Unsupported,
@@ -45,7 +45,7 @@ enum GLContext {
},
}
-#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
static GL_CONTEXT: Mutex<GLContext> = Mutex::new(GLContext::Uninitialized);
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
@@ -154,12 +154,12 @@ impl ElementImpl for PaintableSink {
for features in [
None,
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
Some(gst::CapsFeatures::new([
"memory:GLMemory",
"meta:GstVideoOverlayComposition",
])),
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
Some(gst::CapsFeatures::new(["memory:GLMemory"])),
Some(gst::CapsFeatures::new([
"memory:SystemMemory",
@@ -225,7 +225,7 @@ impl ElementImpl for PaintableSink {
// Notify the pipeline about the GL display and wrapped context so that any other
// elements in the pipeline ideally use the same / create GL contexts that are
// sharing with this one.
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
let gl_context = GL_CONTEXT.lock().unwrap();
if let GLContext::Initialized {
@@ -331,7 +331,7 @@ impl BaseSinkImpl for PaintableSink {
// TODO: Provide a preferred "window size" here for higher-resolution rendering
query.add_allocation_meta::<gst_video::VideoOverlayCompositionMeta>(None);
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
if let GLContext::Initialized {
wrapped_context, ..
@@ -352,7 +352,7 @@ impl BaseSinkImpl for PaintableSink {
gst::log!(CAT, imp: self, "Handling query {:?}", query);
match query.view_mut() {
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
gst::QueryViewMut::Context(q) => {
// Avoid holding the locks while we respond to the query
// The objects are ref-counted anyway.
@@ -408,11 +408,11 @@ impl VideoSinkImpl for PaintableSink {
})?;
let wrapped_context = {
- #[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
+ #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst_gl")))]
{
None
}
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
let gl_context = GL_CONTEXT.lock().unwrap();
if let GLContext::Initialized {
@@ -474,7 +474,7 @@ impl PaintableSink {
#[allow(unused_mut)]
let mut tmp_caps = Self::pad_templates()[0].caps().clone();
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
// Filter out GL caps from the template pads if we have no context
if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) {
@@ -493,7 +493,7 @@ impl PaintableSink {
}
fn create_paintable(&self, paintable_storage: &mut MutexGuard<Option<ThreadGuard<Paintable>>>) {
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
self.initialize_gl_context();
}
@@ -523,7 +523,7 @@ impl PaintableSink {
),
);
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
{
let gdk_context = if let GLContext::Initialized { gdk_context, .. } =
&*GL_CONTEXT.lock().unwrap()
@@ -534,7 +534,7 @@ impl PaintableSink {
};
ThreadGuard::new(Paintable::new(gdk_context))
}
- #[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
+ #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst_gl")))]
{
ThreadGuard::new(Paintable::new(None))
}
@@ -545,7 +545,7 @@ impl PaintableSink {
*self.sender.lock().unwrap() = Some(sender);
}
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
fn initialize_gl_context(&self) {
gst::debug!(CAT, imp: self, "Realizing GDK GL Context");
@@ -555,7 +555,7 @@ impl PaintableSink {
});
}
- #[cfg(any(target_os = "macos", feature = "gst_gl"))]
+ #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst_gl"))]
fn initialize_gl_context_main(&self) {
gst::debug!(CAT, imp: self, "Realizing GDK GL Context from main thread");
@@ -603,6 +603,10 @@ impl PaintableSink {
"GdkWaylandGLContext" => (),
#[cfg(target_os = "macos")]
"GdkMacosGLContext" => (),
+ #[cfg(target_os = "windows")]
+ "GdkWin32GLContext" => (),
+ #[cfg(all(windows, feature = "winegl"))]
+ "GdkWin32GLContextEGL" => (),
display => {
gst::error!(CAT, imp: self, "Unsupported GDK display {display} for GL");
return;
@@ -618,6 +622,7 @@ impl PaintableSink {
gst::info!(CAT, imp: self, "Successfully realized GDK GL Context");
+ handle_wgl_makecurrent(&ctx);
gdk_context.make_current();
let res = match gdk_context.type_().name() {
@@ -629,6 +634,14 @@ impl PaintableSink {
"GdkWaylandGLContext" => self.initialize_waylandegl(gdk_display),
#[cfg(target_os = "macos")]
"GdkMacosGLContext" => self.initialize_macosgl(gdk_display),
+ #[cfg(target_os = "windows")]
+ "GdkWin32GLContext" => {
+ self.initialize_wgl(gdk_display, &gdk_ctx);
+ }
+ #[cfg(all(target_os = "windows", feature = "winegl"))]
+ "GdkWin32GLContextEGL" => {
+ self.initialize_winegl(gdk_display);
+ }
display_type => {
unreachable!("Unsupported GDK display {display_type} for GL");
}
@@ -666,6 +679,11 @@ impl PaintableSink {
return;
}
+ // FIXME: Is this all necessary?
+ deactivate_gdk_wgl_context(&ctx);
+ handle_wgl_makecurrent(&ctx);
+ reactivate_gdk_wgl_context(&ctx);
+
gst::info!(CAT, imp: self, "Successfully initialized GL Context");
*gl_context_guard = GLContext::Initialized {
@@ -864,4 +882,165 @@ impl PaintableSink {
Some((gst_display, wrapped_context))
}
}
+
+ #[cfg(target_os = "windows")]
+ fn initialize_wgl(
+ &self,
+ display: gdk::Display,
+ context: &gdk::GLContext,
+ ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
+ gst::info!(
+ CAT,
+ imp: self,
+ "Initializing GL with for Windows WGL backend and display."
+ );
+
+ let platform = gst_gl::GLPlatform::WGL;
+
+ let gl_api = if context.is_legacy() {
+ gst_gl::GLAPI::OPENGL
+ } else {
+ gst_gl::GLAPI::OPENGL3
+ };
+ let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
+
+ if gl_ctx == 0 {
+ gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",);
+ return None;
+ }
+
+ unsafe {
+ let gst_gl_display = gst_gl::Display::with_type(gst_gl::DisplayType::WIN32);
+ if gst_gl_display.is_none() {
+ gst::error!(CAT, imp: self, "Failed to get GL display");
+ return None;
+ }
+
+ gst_gl_display.filter_gl_api(gl_api);
+
+ let wrapped_context =
+ gst_gl::GLContext::new_wrapped(&gst_gl_display, gl_ctx, platform, gl_api);
+ let wrapped_context = match wrapped_context {
+ None => {
+ gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
+ return None;
+ }
+ Some(wrapped_context) => wrapped_context,
+ };
+
+ Some((gst_gl_display, wrapped_context))
+ }
+ }
+
+ #[cfg(all(target_os = "windows", feature = "winegl"))]
+ fn initialize_winegl(
+ &self,
+ display: gdk::Display,
+ ) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
+ gst::info!(
+ CAT,
+ imp: self,
+ "Initializing GL with for Windows EGL backend and display."
+ );
+
+ let platform = gst_gl::GLPlatform::EGL;
+
+ let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform);
+ let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
+
+ if gl_ctx == 0 {
+ gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext",);
+ return None;
+ }
+
+ // FIXME: bindings
+ unsafe {
+ use gdk_win32::prelude::*;
+
+ let d = display.downcast::<gdk_win32::Win32Display>().unwrap();
+ let egl_display = display.egl_display().unwrap().as_ptr();
+
+ let gst_gl_display =
+ gst_gl_egl::ffi::gst_gl_display_egl_new_with_egl_display(egl_display);
+ if gst_gl_display.is_null() {
+ gst::error!(CAT, imp: self, "Failed to get EGL display");
+ return None;
+ }
+ let gst_gl_display: gst_gl::GLDisplay =
+ from_glib_full(gst_gl_display as *mut gst_gl::ffi::GstGLDisplay);
+
+ gst_gl_display.filter_gl_api(gl_api);
+
+ let wrapped_context =
+ gst_gl::GLContext::new_wrapped(&gst_gl_display, gl_ctx, platform, gl_api);
+
+ let wrapped_context = match wrapped_context {
+ None => {
+ gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
+ return None;
+ }
+ Some(wrapped_context) => wrapped_context,
+ };
+
+ Some((gst_gl_display, wrapped_context))
+ }
+ }
}
+
+// Workaround for Windows specific GL context problems
+
+#[cfg(target_os = "windows")]
+fn handle_wgl_makecurrent(ctx: &gdk::GLContext) {
+ if ctx.type_().name() != "GdkWin32GLContext" {
+ return;
+ }
+
+ extern "C" {
+ fn epoxy_handle_external_wglMakeCurrent();
+ }
+
+ unsafe {
+ epoxy_handle_external_wglMakeCurrent();
+ }
+}
+
+#[cfg(target_os = "windows")]
+fn deactivate_gdk_wgl_context(ctx: &gdk::GLContext) {
+ if ctx.type_().name() != "GdkWin32GLContext" {
+ return;
+ }
+
+ unsafe {
+ use gdk_win32::prelude::*;
+
+ let surface = context
+ .surface()
+ .unwrap()
+ .downcast::<gdk_win32::Win32Surface>()
+ .unwrap();
+ let hwnd = surface.handle();
+ let hdc = windows_sys::Win32::Graphics::Gdi::GetDC(hwnd);
+ windows_sys::Win32::Graphics::OpenGL::wglMakeCurrent(hdc, 0);
+ }
+}
+
+#[cfg(target_os = "windows")]
+fn reactivate_gdk_wgl_context(ctx: &gdk::GLContext) {
+ if ctx.type_().name() != "GdkWin32GLContext" {
+ return;
+ }
+
+ context.make_current();
+}
+
+#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(not(target_os = "windows"))]
+fn handle_wgl_makecurrent(_ctx: &gdk::GLContext) {}
+
+#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(not(target_os = "windows"))]
+fn deactivate_gdk_wgl_context(_ctx: &gdk::GLContext) {}
+
+#[cfg(any(target_os = "macos", feature = "gst_gl"))]
+#[cfg(not(target_os = "windows"))]
+fn reactivate_gdk_wgl_context(_ctx: &gdk::GLContext) {}