1572 lines
48 KiB
Diff
1572 lines
48 KiB
Diff
From 33fa3db5ac3c5e3c82df9573dec334ef7bf77ee3 Mon Sep 17 00:00:00 2001
|
|
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
Date: Fri, 17 Dec 2021 14:02:51 +0200
|
|
Subject: [PATCH 16/16] Add new gtkwaylandsink element
|
|
|
|
Upstream-Status: Inappropriate [DEY specific]
|
|
---
|
|
ext/gtk/gstgtkutils.c | 71 +++++
|
|
ext/gtk/gstgtkutils.h | 29 ++
|
|
ext/gtk/gstgtkwaylandsink.c | 460 +++++++++++++++++++++++++++++
|
|
ext/gtk/gstgtkwaylandsink.h | 51 ++++
|
|
ext/gtk/gstplugin.c | 43 +++
|
|
ext/gtk/gtkgstbasewidget.c | 542 ++++++++++++++++++++++++++++++++++
|
|
ext/gtk/gtkgstbasewidget.h | 100 +++++++
|
|
ext/gtk/gtkgstwaylandwidget.c | 67 +++++
|
|
ext/gtk/gtkgstwaylandwidget.h | 68 +++++
|
|
ext/gtk/meson.build | 22 ++
|
|
ext/meson.build | 1 +
|
|
meson_options.txt | 1 +
|
|
12 files changed, 1455 insertions(+)
|
|
create mode 100644 ext/gtk/gstgtkutils.c
|
|
create mode 100644 ext/gtk/gstgtkutils.h
|
|
create mode 100644 ext/gtk/gstgtkwaylandsink.c
|
|
create mode 100644 ext/gtk/gstgtkwaylandsink.h
|
|
create mode 100644 ext/gtk/gstplugin.c
|
|
create mode 100644 ext/gtk/gtkgstbasewidget.c
|
|
create mode 100644 ext/gtk/gtkgstbasewidget.h
|
|
create mode 100644 ext/gtk/gtkgstwaylandwidget.c
|
|
create mode 100644 ext/gtk/gtkgstwaylandwidget.h
|
|
create mode 100644 ext/gtk/meson.build
|
|
|
|
diff --git a/ext/gtk/gstgtkutils.c b/ext/gtk/gstgtkutils.c
|
|
new file mode 100644
|
|
index 0000000..c730f01
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gstgtkutils.c
|
|
@@ -0,0 +1,71 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#include "gstgtkutils.h"
|
|
+
|
|
+struct invoke_context
|
|
+{
|
|
+ GThreadFunc func;
|
|
+ gpointer data;
|
|
+ GMutex lock;
|
|
+ GCond cond;
|
|
+ gboolean fired;
|
|
+
|
|
+ gpointer res;
|
|
+};
|
|
+
|
|
+static gboolean
|
|
+gst_gtk_invoke_func (struct invoke_context *info)
|
|
+{
|
|
+ g_mutex_lock (&info->lock);
|
|
+ info->res = info->func (info->data);
|
|
+ info->fired = TRUE;
|
|
+ g_cond_signal (&info->cond);
|
|
+ g_mutex_unlock (&info->lock);
|
|
+
|
|
+ return G_SOURCE_REMOVE;
|
|
+}
|
|
+
|
|
+gpointer
|
|
+gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
|
|
+{
|
|
+ GMainContext *main_context = g_main_context_default ();
|
|
+ struct invoke_context info;
|
|
+
|
|
+ g_mutex_init (&info.lock);
|
|
+ g_cond_init (&info.cond);
|
|
+ info.fired = FALSE;
|
|
+ info.func = func;
|
|
+ info.data = data;
|
|
+
|
|
+ g_main_context_invoke (main_context, (GSourceFunc) gst_gtk_invoke_func,
|
|
+ &info);
|
|
+
|
|
+ g_mutex_lock (&info.lock);
|
|
+ while (!info.fired)
|
|
+ g_cond_wait (&info.cond, &info.lock);
|
|
+ g_mutex_unlock (&info.lock);
|
|
+
|
|
+ g_mutex_clear (&info.lock);
|
|
+ g_cond_clear (&info.cond);
|
|
+
|
|
+ return info.res;
|
|
+}
|
|
diff --git a/ext/gtk/gstgtkutils.h b/ext/gtk/gstgtkutils.h
|
|
new file mode 100644
|
|
index 0000000..7584ae2
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gstgtkutils.h
|
|
@@ -0,0 +1,29 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifndef __GST_GTK_UTILS_H__
|
|
+#define __GST_GTK_UTILS_H__
|
|
+
|
|
+#include <glib.h>
|
|
+
|
|
+gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
|
+
|
|
+#endif /* __GST_GTK_UTILS_H__ */
|
|
diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c
|
|
new file mode 100644
|
|
index 0000000..c38ea51
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gstgtkwaylandsink.c
|
|
@@ -0,0 +1,460 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ * Copyright (C) 2021 Collabora Ltd.
|
|
+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include "gstgtkwaylandsink.h"
|
|
+#include "gtkgstwaylandwidget.h"
|
|
+#include "gstgtkutils.h"
|
|
+
|
|
+#include <gst/video/video.h>
|
|
+#include <gst/wayland/wayland.h>
|
|
+
|
|
+#include <gdk/gdk.h>
|
|
+
|
|
+#ifdef GDK_WINDOWING_WAYLAND
|
|
+#include <gdk/gdkwayland.h>
|
|
+#else
|
|
+#error "Wayland is not supported in GTK+"
|
|
+#endif
|
|
+
|
|
+GST_DEBUG_CATEGORY (gst_debug_gtk_wayland_sink);
|
|
+#define GST_CAT_DEFAULT gst_debug_gtk_wayland_sink
|
|
+
|
|
+#ifndef GST_CAPS_FEATURE_MEMORY_DMABUF
|
|
+#define GST_CAPS_FEATURE_MEMORY_DMABUF "memory:DMABuf"
|
|
+#endif
|
|
+
|
|
+#define WL_VIDEO_FORMATS \
|
|
+ "{ BGRx, BGRA, RGBx, xBGR, xRGB, RGBA, ABGR, ARGB, RGB, BGR, " \
|
|
+ "RGB16, BGR16, YUY2, YVYU, UYVY, AYUV, NV12, NV21, NV16, NV61, " \
|
|
+ "YUV9, YVU9, Y41B, I420, YV12, Y42B, v308 }"
|
|
+
|
|
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
|
+ GST_PAD_SINK,
|
|
+ GST_PAD_ALWAYS,
|
|
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (WL_VIDEO_FORMATS) ";"
|
|
+ GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF,
|
|
+ WL_VIDEO_FORMATS))
|
|
+ );
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_navigation_interface_init (GstNavigationInterface * iface);
|
|
+
|
|
+static GstPadProbeReturn sink_pad_probe_cb (GstPad * pad,
|
|
+ GstPadProbeInfo * info, gpointer user_data);
|
|
+
|
|
+enum
|
|
+{
|
|
+ PROP_0,
|
|
+ PROP_WIDGET,
|
|
+};
|
|
+
|
|
+#define gst_gtk_wayland_sink_parent_class parent_class
|
|
+G_DEFINE_TYPE_WITH_CODE (GstGtkWaylandSink, gst_gtk_wayland_sink, GST_TYPE_BIN,
|
|
+ G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
|
+ gst_gtk_wayland_sink_navigation_interface_init);
|
|
+ GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_wayland_sink, "gtkwaylandsink", 0,
|
|
+ "Gtk Wayland Video Sink"));
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_init (GstGtkWaylandSink * self)
|
|
+{
|
|
+ self->waylandsink = gst_element_factory_make ("waylandsink", NULL);
|
|
+ if (self->waylandsink) {
|
|
+ GstPadTemplate *tmpl;
|
|
+ GstPad *pad;
|
|
+
|
|
+ gst_bin_add (GST_BIN (self), self->waylandsink);
|
|
+
|
|
+ tmpl = gst_static_pad_template_get (&sink_template);
|
|
+ pad = gst_element_get_static_pad (self->waylandsink, "sink");
|
|
+ self->ghostpad = gst_ghost_pad_new_from_template ("sink", pad, tmpl);
|
|
+
|
|
+ gst_pad_add_probe (self->ghostpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
|
+ sink_pad_probe_cb, self, NULL);
|
|
+ gst_element_add_pad (GST_ELEMENT (self), self->ghostpad);
|
|
+
|
|
+ gst_object_unref (pad);
|
|
+ gst_object_unref (tmpl);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_finalize (GObject * object)
|
|
+{
|
|
+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object);
|
|
+
|
|
+ g_clear_object (&self->widget);
|
|
+
|
|
+ G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+widget_destroy_cb (GtkWidget * widget, GstGtkWaylandSink * self)
|
|
+{
|
|
+ GST_OBJECT_LOCK (self);
|
|
+ g_clear_object (&self->widget);
|
|
+ GST_OBJECT_UNLOCK (self);
|
|
+}
|
|
+
|
|
+static void
|
|
+window_destroy_cb (GtkWidget * widget, GstGtkWaylandSink * self)
|
|
+{
|
|
+ GST_OBJECT_LOCK (self);
|
|
+ self->window = NULL;
|
|
+ GST_OBJECT_UNLOCK (self);
|
|
+
|
|
+ GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("Window was closed"), (NULL));
|
|
+}
|
|
+
|
|
+/* We use the "draw" callback to change the size of the sink
|
|
+ * because the "configure-event" is only sent to top-level widgets. */
|
|
+static gboolean
|
|
+widget_draw_cb (GtkWidget * widget, cairo_t * cr, gpointer user_data)
|
|
+{
|
|
+ GstGtkWaylandSink *self = user_data;
|
|
+ GtkAllocation allocation;
|
|
+
|
|
+ gtk_widget_get_allocation (widget, &allocation);
|
|
+ gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (self->waylandsink),
|
|
+ allocation.x, allocation.y, allocation.width, allocation.height);
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static GtkWidget *
|
|
+gst_gtk_wayland_sink_get_widget (GstGtkWaylandSink * self)
|
|
+{
|
|
+ if (self->widget != NULL)
|
|
+ return g_object_ref (self->widget);
|
|
+
|
|
+ /* Ensure GTK is initialized, this has no side effect if it was already
|
|
+ * initialized. Also, we do that lazily, so the application can be first */
|
|
+ if (!gtk_init_check (NULL, NULL)) {
|
|
+ GST_INFO_OBJECT (self, "Could not ensure GTK initialization.");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ self->widget = gtk_gst_wayland_widget_new ();
|
|
+ g_signal_connect_object (self->widget, "draw",
|
|
+ G_CALLBACK (widget_draw_cb), self, 0);
|
|
+ gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (self->widget),
|
|
+ GST_ELEMENT (self));
|
|
+
|
|
+ /* Take the floating ref, other wise the destruction of the container will
|
|
+ * make this widget disappear possibly before we are done. */
|
|
+ g_object_ref_sink (self->widget);
|
|
+ g_signal_connect_object (self->widget, "destroy",
|
|
+ G_CALLBACK (widget_destroy_cb), self, 0);
|
|
+
|
|
+ return g_object_ref (self->widget);
|
|
+}
|
|
+
|
|
+static GtkWidget *
|
|
+gst_gtk_wayland_sink_acquire_widget (GstGtkWaylandSink * self)
|
|
+{
|
|
+ gpointer widget = NULL;
|
|
+
|
|
+ GST_OBJECT_LOCK (self);
|
|
+ if (self->widget != NULL)
|
|
+ widget = g_object_ref (self->widget);
|
|
+ GST_OBJECT_UNLOCK (self);
|
|
+
|
|
+ if (!widget)
|
|
+ widget =
|
|
+ gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_wayland_sink_get_widget,
|
|
+ self);
|
|
+
|
|
+ return widget;
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_get_property (GObject * object, guint prop_id,
|
|
+ GValue * value, GParamSpec * pspec)
|
|
+{
|
|
+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (object);
|
|
+
|
|
+ switch (prop_id) {
|
|
+ case PROP_WIDGET:
|
|
+ {
|
|
+ g_value_take_object (value, gst_gtk_wayland_sink_acquire_widget (self));
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gst_gtk_wayland_sink_start_on_main (GstGtkWaylandSink * self)
|
|
+{
|
|
+ GtkWidget *toplevel;
|
|
+ GstContext *context;
|
|
+ GdkDisplay *display;
|
|
+ struct wl_display *display_handle;
|
|
+
|
|
+ if ((toplevel = gst_gtk_wayland_sink_get_widget (self)) == NULL) {
|
|
+ GST_ERROR_OBJECT (self, "Could not ensure GTK initialization.");
|
|
+ return FALSE;
|
|
+ }
|
|
+ g_object_unref (toplevel);
|
|
+
|
|
+ /* After this point, self->widget will always be set */
|
|
+
|
|
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->widget));
|
|
+ if (!gtk_widget_is_toplevel (toplevel)) {
|
|
+ /* User did not add widget its own UI, let's popup a new GtkWindow to
|
|
+ * make gst-launch-1.0 work. */
|
|
+ self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
+ gtk_window_set_default_size (GTK_WINDOW (self->window), 640, 480);
|
|
+ gtk_window_set_title (GTK_WINDOW (self->window), "Gst GTK Wayland Sink");
|
|
+ gtk_container_add (GTK_CONTAINER (self->window), toplevel);
|
|
+ self->window_destroy_id = g_signal_connect (self->window, "destroy",
|
|
+ G_CALLBACK (window_destroy_cb), self);
|
|
+ }
|
|
+
|
|
+ display = gtk_widget_get_display (self->widget);
|
|
+ if (!GDK_IS_WAYLAND_DISPLAY (display)) {
|
|
+ GST_ERROR_OBJECT (self, "GDK is not using its wayland backend.");
|
|
+ return FALSE;
|
|
+ }
|
|
+ display_handle = gdk_wayland_display_get_wl_display (display);
|
|
+ context = gst_wayland_display_handle_context_new (display_handle);
|
|
+ gst_element_set_context (self->waylandsink, context);
|
|
+ gst_context_unref (context);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gst_gtk_wayland_sink_stop_on_main (GstGtkWaylandSink * self)
|
|
+{
|
|
+ if (self->window) {
|
|
+ if (self->window_destroy_id)
|
|
+ g_signal_handler_disconnect (self->window, self->window_destroy_id);
|
|
+ self->window_destroy_id = 0;
|
|
+ gtk_widget_destroy (self->window);
|
|
+ self->window = NULL;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_widget_show_all_and_unref (GtkWidget * widget)
|
|
+{
|
|
+ gtk_widget_show_all (widget);
|
|
+ g_object_unref (widget);
|
|
+}
|
|
+
|
|
+static GstStateChangeReturn
|
|
+gst_gtk_wayland_sink_change_state (GstElement * element,
|
|
+ GstStateChange transition)
|
|
+{
|
|
+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (element);
|
|
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
|
+
|
|
+ switch (transition) {
|
|
+ case GST_STATE_CHANGE_NULL_TO_READY:
|
|
+ if (!self->waylandsink) {
|
|
+ GST_ERROR_OBJECT (self, "Failed to load waylandsink");
|
|
+ return GST_STATE_CHANGE_FAILURE;
|
|
+ }
|
|
+ if (!gst_gtk_invoke_on_main ((GThreadFunc)
|
|
+ gst_gtk_wayland_sink_start_on_main, element))
|
|
+ return GST_STATE_CHANGE_FAILURE;
|
|
+ break;
|
|
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
+ {
|
|
+ GtkWindow *window = NULL;
|
|
+
|
|
+ GST_OBJECT_LOCK (self);
|
|
+ if (self->window)
|
|
+ window = g_object_ref (GTK_WINDOW (self->window));
|
|
+ GST_OBJECT_UNLOCK (self);
|
|
+
|
|
+ if (window)
|
|
+ gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_widget_show_all_and_unref,
|
|
+ window);
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
+ if (ret != GST_STATE_CHANGE_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ switch (transition) {
|
|
+ case GST_STATE_CHANGE_READY_TO_NULL:
|
|
+ case GST_STATE_CHANGE_NULL_TO_NULL:
|
|
+ gst_gtk_invoke_on_main ((GThreadFunc)
|
|
+ gst_gtk_wayland_sink_stop_on_main, element);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static GstPadProbeReturn
|
|
+sink_pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
|
|
+{
|
|
+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (user_data);
|
|
+ if (GST_IS_EVENT (info->data) &&
|
|
+ GST_EVENT_TYPE (info->data) == GST_EVENT_CAPS) {
|
|
+ GstVideoInfo v_info;
|
|
+ GstCaps *caps;
|
|
+ GtkWidget *widget;
|
|
+
|
|
+ widget = gst_gtk_wayland_sink_acquire_widget (self);
|
|
+ if (widget) {
|
|
+ gst_event_parse_caps (GST_EVENT (info->data), &caps);
|
|
+ gst_video_info_from_caps (&v_info, caps);
|
|
+ gtk_gst_base_widget_set_format (GTK_GST_BASE_WIDGET (widget), &v_info);
|
|
+ g_object_unref (widget);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return GST_PAD_PROBE_OK;
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_set_window_handle_on_main (GstGtkWaylandSink * self)
|
|
+{
|
|
+ GtkAllocation allocation;
|
|
+ GdkWindow *window;
|
|
+ struct wl_surface *window_handle;
|
|
+
|
|
+ gtk_widget_get_allocation (self->widget, &allocation);
|
|
+ window = gtk_widget_get_window (self->widget);
|
|
+ window_handle = gdk_wayland_window_get_wl_surface (window);
|
|
+
|
|
+ GST_INFO_OBJECT (self, "setting window handle and size (%d x %d)",
|
|
+ allocation.width, allocation.height);
|
|
+
|
|
+ gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (self->waylandsink),
|
|
+ (guintptr) window_handle);
|
|
+ gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (self->waylandsink),
|
|
+ allocation.x, allocation.y, allocation.width, allocation.height);
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_handle_message (GstBin * bin, GstMessage * message)
|
|
+{
|
|
+ GstGtkWaylandSink *self = GST_GTK_WAYLAND_SINK (bin);
|
|
+
|
|
+ if (gst_is_video_overlay_prepare_window_handle_message (message)) {
|
|
+ gst_gtk_invoke_on_main ((GThreadFunc)
|
|
+ gst_gtk_wayland_sink_set_window_handle_on_main, self);
|
|
+ gst_message_unref (message);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ GST_BIN_CLASS (parent_class)->handle_message (bin, message);
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_class_init (GstGtkWaylandSinkClass * klass)
|
|
+{
|
|
+ GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
|
|
+ GstBinClass *gstbin_class = (GstBinClass *) klass;
|
|
+
|
|
+ gobject_class->finalize = gst_gtk_wayland_sink_finalize;
|
|
+ gobject_class->get_property = gst_gtk_wayland_sink_get_property;
|
|
+ gstelement_class->change_state = gst_gtk_wayland_sink_change_state;
|
|
+ gstbin_class->handle_message = gst_gtk_wayland_sink_handle_message;
|
|
+
|
|
+ g_object_class_install_property (gobject_class, PROP_WIDGET,
|
|
+ g_param_spec_object ("widget", "Gtk Widget",
|
|
+ "The GtkWidget to place in the widget hierarchy "
|
|
+ "(must only be get from the GTK main thread)",
|
|
+ GTK_TYPE_WIDGET,
|
|
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
+
|
|
+ gst_element_class_set_metadata (gstelement_class, "Gtk Wayland Video Sink",
|
|
+ "Sink/Video",
|
|
+ "A video sink that renders to a GtkWidget using Wayland API",
|
|
+ "George Kiagiadakis <george.kiagiadakis@collabora.com>");
|
|
+
|
|
+ gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_navigation_send_event (GstNavigation * navigation,
|
|
+ GstStructure * structure)
|
|
+{
|
|
+ GstGtkWaylandSink *sink = GST_GTK_WAYLAND_SINK (navigation);
|
|
+ GstEvent *event;
|
|
+ GstPad *pad;
|
|
+ gdouble x, y;
|
|
+
|
|
+ if (gst_structure_get_double (structure, "pointer_x", &x) &&
|
|
+ gst_structure_get_double (structure, "pointer_y", &y)) {
|
|
+ GtkGstBaseWidget *widget =
|
|
+ (GtkGstBaseWidget *) gst_gtk_wayland_sink_acquire_widget (sink);
|
|
+ gdouble stream_x, stream_y;
|
|
+
|
|
+ if (widget == NULL) {
|
|
+ GST_ERROR_OBJECT (sink, "Could not ensure GTK initialization.");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ gtk_gst_base_widget_display_size_to_stream_size (widget,
|
|
+ x, y, &stream_x, &stream_y);
|
|
+ gst_structure_set (structure,
|
|
+ "pointer_x", G_TYPE_DOUBLE, (gdouble) stream_x,
|
|
+ "pointer_y", G_TYPE_DOUBLE, (gdouble) stream_y, NULL);
|
|
+
|
|
+ g_object_unref (widget);
|
|
+ }
|
|
+
|
|
+ event = gst_event_new_navigation (structure);
|
|
+ pad = gst_pad_get_peer (sink->ghostpad);
|
|
+
|
|
+ GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure);
|
|
+
|
|
+ if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
|
|
+ if (!gst_pad_send_event (pad, gst_event_ref (event))) {
|
|
+ /* If upstream didn't handle the event we'll post a message with it
|
|
+ * for the application in case it wants to do something with it */
|
|
+ gst_element_post_message (GST_ELEMENT_CAST (sink),
|
|
+ gst_navigation_message_new_event (GST_OBJECT_CAST (sink), event));
|
|
+ }
|
|
+ gst_event_unref (event);
|
|
+ gst_object_unref (pad);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gst_gtk_wayland_sink_navigation_interface_init (GstNavigationInterface * iface)
|
|
+{
|
|
+ iface->send_event = gst_gtk_wayland_sink_navigation_send_event;
|
|
+}
|
|
diff --git a/ext/gtk/gstgtkwaylandsink.h b/ext/gtk/gstgtkwaylandsink.h
|
|
new file mode 100644
|
|
index 0000000..33fc96f
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gstgtkwaylandsink.h
|
|
@@ -0,0 +1,51 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2021 Collabora Ltd.
|
|
+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifndef __GST_GTK_WAYLAND_SINK_H__
|
|
+#define __GST_GTK_WAYLAND_SINK_H__
|
|
+
|
|
+#include <gtk/gtk.h>
|
|
+#include <gst/gst.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define GST_TYPE_GTK_WAYLAND_SINK (gst_gtk_wayland_sink_get_type())
|
|
+G_DECLARE_FINAL_TYPE (GstGtkWaylandSink, gst_gtk_wayland_sink, GST, GTK_WAYLAND_SINK, GstBin)
|
|
+
|
|
+/**
|
|
+ * GstGtkWaylandSink:
|
|
+ *
|
|
+ * Opaque #GstGtkWaylandSink object
|
|
+ */
|
|
+struct _GstGtkWaylandSink
|
|
+{
|
|
+ /* <private> */
|
|
+ GstBin parent;
|
|
+ GstElement *waylandsink;
|
|
+ GstPad *ghostpad;
|
|
+ GtkWidget *widget;
|
|
+ GtkWidget *window;
|
|
+ gulong window_destroy_id;
|
|
+};
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __GST_GTK_WAYLAND_SINK_H__ */
|
|
diff --git a/ext/gtk/gstplugin.c b/ext/gtk/gstplugin.c
|
|
new file mode 100644
|
|
index 0000000..f154b08
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gstplugin.c
|
|
@@ -0,0 +1,43 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include "gstgtkwaylandsink.h"
|
|
+
|
|
+static gboolean
|
|
+plugin_init (GstPlugin * plugin)
|
|
+{
|
|
+ gboolean ret = FALSE;
|
|
+
|
|
+ ret |= gst_element_register (plugin, "gtkwaylandsink", GST_RANK_NONE,
|
|
+ GST_TYPE_GTK_WAYLAND_SINK);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
+ GST_VERSION_MINOR,
|
|
+ gtkwayland,
|
|
+ "Gtk+ wayland sink",
|
|
+ plugin_init, PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME,
|
|
+ GST_PACKAGE_ORIGIN)
|
|
diff --git a/ext/gtk/gtkgstbasewidget.c b/ext/gtk/gtkgstbasewidget.c
|
|
new file mode 100644
|
|
index 0000000..053640b
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gtkgstbasewidget.c
|
|
@@ -0,0 +1,542 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include <stdio.h>
|
|
+
|
|
+#include "gtkgstbasewidget.h"
|
|
+
|
|
+GST_DEBUG_CATEGORY (gst_debug_gtk_base_widget);
|
|
+#define GST_CAT_DEFAULT gst_debug_gtk_base_widget
|
|
+
|
|
+#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
|
+#define DEFAULT_PAR_N 0
|
|
+#define DEFAULT_PAR_D 1
|
|
+#define DEFAULT_IGNORE_ALPHA TRUE
|
|
+
|
|
+enum
|
|
+{
|
|
+ PROP_0,
|
|
+ PROP_FORCE_ASPECT_RATIO,
|
|
+ PROP_PIXEL_ASPECT_RATIO,
|
|
+ PROP_IGNORE_ALPHA,
|
|
+};
|
|
+
|
|
+static void
|
|
+gtk_gst_base_widget_get_preferred_width (GtkWidget * widget, gint * min,
|
|
+ gint * natural)
|
|
+{
|
|
+ GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
|
|
+ gint video_width = gst_widget->display_width;
|
|
+
|
|
+ if (!gst_widget->negotiated)
|
|
+ video_width = 10;
|
|
+
|
|
+ if (min)
|
|
+ *min = 1;
|
|
+ if (natural)
|
|
+ *natural = video_width;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min,
|
|
+ gint * natural)
|
|
+{
|
|
+ GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
|
|
+ gint video_height = gst_widget->display_height;
|
|
+
|
|
+ if (!gst_widget->negotiated)
|
|
+ video_height = 10;
|
|
+
|
|
+ if (min)
|
|
+ *min = 1;
|
|
+ if (natural)
|
|
+ *natural = video_height;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_gst_base_widget_set_property (GObject * object, guint prop_id,
|
|
+ const GValue * value, GParamSpec * pspec)
|
|
+{
|
|
+ GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
|
|
+
|
|
+ switch (prop_id) {
|
|
+ case PROP_FORCE_ASPECT_RATIO:
|
|
+ gtk_widget->force_aspect_ratio = g_value_get_boolean (value);
|
|
+ break;
|
|
+ case PROP_PIXEL_ASPECT_RATIO:
|
|
+ gtk_widget->par_n = gst_value_get_fraction_numerator (value);
|
|
+ gtk_widget->par_d = gst_value_get_fraction_denominator (value);
|
|
+ break;
|
|
+ case PROP_IGNORE_ALPHA:
|
|
+ gtk_widget->ignore_alpha = g_value_get_boolean (value);
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_gst_base_widget_get_property (GObject * object, guint prop_id,
|
|
+ GValue * value, GParamSpec * pspec)
|
|
+{
|
|
+ GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
|
|
+
|
|
+ switch (prop_id) {
|
|
+ case PROP_FORCE_ASPECT_RATIO:
|
|
+ g_value_set_boolean (value, gtk_widget->force_aspect_ratio);
|
|
+ break;
|
|
+ case PROP_PIXEL_ASPECT_RATIO:
|
|
+ gst_value_set_fraction (value, gtk_widget->par_n, gtk_widget->par_d);
|
|
+ break;
|
|
+ case PROP_IGNORE_ALPHA:
|
|
+ g_value_set_boolean (value, gtk_widget->ignore_alpha);
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_calculate_par (GtkGstBaseWidget * widget, GstVideoInfo * info)
|
|
+{
|
|
+ gboolean ok;
|
|
+ gint width, height;
|
|
+ gint par_n, par_d;
|
|
+ gint display_par_n, display_par_d;
|
|
+
|
|
+ width = GST_VIDEO_INFO_WIDTH (info);
|
|
+ height = GST_VIDEO_INFO_HEIGHT (info);
|
|
+
|
|
+ par_n = GST_VIDEO_INFO_PAR_N (info);
|
|
+ par_d = GST_VIDEO_INFO_PAR_D (info);
|
|
+
|
|
+ if (!par_n)
|
|
+ par_n = 1;
|
|
+
|
|
+ /* get display's PAR */
|
|
+ if (widget->par_n != 0 && widget->par_d != 0) {
|
|
+ display_par_n = widget->par_n;
|
|
+ display_par_d = widget->par_d;
|
|
+ } else {
|
|
+ display_par_n = 1;
|
|
+ display_par_d = 1;
|
|
+ }
|
|
+
|
|
+
|
|
+ ok = gst_video_calculate_display_ratio (&widget->display_ratio_num,
|
|
+ &widget->display_ratio_den, width, height, par_n, par_d, display_par_n,
|
|
+ display_par_d);
|
|
+
|
|
+ if (ok) {
|
|
+ GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
|
|
+ display_par_d);
|
|
+ return TRUE;
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
+_apply_par (GtkGstBaseWidget * widget)
|
|
+{
|
|
+ guint display_ratio_num, display_ratio_den;
|
|
+ gint width, height;
|
|
+
|
|
+ width = GST_VIDEO_INFO_WIDTH (&widget->v_info);
|
|
+ height = GST_VIDEO_INFO_HEIGHT (&widget->v_info);
|
|
+
|
|
+ display_ratio_num = widget->display_ratio_num;
|
|
+ display_ratio_den = widget->display_ratio_den;
|
|
+
|
|
+ if (height % display_ratio_den == 0) {
|
|
+ GST_DEBUG ("keeping video height");
|
|
+ widget->display_width = (guint)
|
|
+ gst_util_uint64_scale_int (height, display_ratio_num,
|
|
+ display_ratio_den);
|
|
+ widget->display_height = height;
|
|
+ } else if (width % display_ratio_num == 0) {
|
|
+ GST_DEBUG ("keeping video width");
|
|
+ widget->display_width = width;
|
|
+ widget->display_height = (guint)
|
|
+ gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
|
|
+ } else {
|
|
+ GST_DEBUG ("approximating while keeping video height");
|
|
+ widget->display_width = (guint)
|
|
+ gst_util_uint64_scale_int (height, display_ratio_num,
|
|
+ display_ratio_den);
|
|
+ widget->display_height = height;
|
|
+ }
|
|
+
|
|
+ GST_DEBUG ("scaling to %dx%d", widget->display_width, widget->display_height);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+_queue_draw (GtkGstBaseWidget * widget)
|
|
+{
|
|
+ GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
+ widget->draw_id = 0;
|
|
+
|
|
+ if (widget->pending_resize) {
|
|
+ widget->pending_resize = FALSE;
|
|
+
|
|
+ widget->v_info = widget->pending_v_info;
|
|
+ widget->negotiated = TRUE;
|
|
+
|
|
+ _apply_par (widget);
|
|
+
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (widget));
|
|
+ } else {
|
|
+ gtk_widget_queue_draw (GTK_WIDGET (widget));
|
|
+ }
|
|
+
|
|
+ GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
+
|
|
+ return G_SOURCE_REMOVE;
|
|
+}
|
|
+
|
|
+static const gchar *
|
|
+_gdk_key_to_navigation_string (guint keyval)
|
|
+{
|
|
+ /* TODO: expand */
|
|
+ switch (keyval) {
|
|
+#define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key)
|
|
+ KEY (Up);
|
|
+ KEY (Down);
|
|
+ KEY (Left);
|
|
+ KEY (Right);
|
|
+ KEY (Home);
|
|
+ KEY (End);
|
|
+#undef KEY
|
|
+ default:
|
|
+ return NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event)
|
|
+{
|
|
+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
+ GstElement *element;
|
|
+
|
|
+ if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
+ if (GST_IS_NAVIGATION (element)) {
|
|
+ const gchar *str = _gdk_key_to_navigation_string (event->keyval);
|
|
+ const gchar *key_type =
|
|
+ event->type == GDK_KEY_PRESS ? "key-press" : "key-release";
|
|
+
|
|
+ if (!str)
|
|
+ str = event->string;
|
|
+
|
|
+ gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
|
|
+ }
|
|
+ g_object_unref (element);
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
+_fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget,
|
|
+ GtkAllocation * allocation, GstVideoRectangle * result)
|
|
+{
|
|
+ if (base_widget->force_aspect_ratio) {
|
|
+ GstVideoRectangle src, dst;
|
|
+
|
|
+ src.x = 0;
|
|
+ src.y = 0;
|
|
+ src.w = base_widget->display_width;
|
|
+ src.h = base_widget->display_height;
|
|
+
|
|
+ dst.x = 0;
|
|
+ dst.y = 0;
|
|
+ dst.w = allocation->width;
|
|
+ dst.h = allocation->height;
|
|
+
|
|
+ gst_video_sink_center_rect (src, dst, result, TRUE);
|
|
+ } else {
|
|
+ result->x = 0;
|
|
+ result->y = 0;
|
|
+ result->w = allocation->width;
|
|
+ result->h = allocation->height;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_display_size_to_stream_size (GtkGstBaseWidget * base_widget,
|
|
+ gdouble x, gdouble y, gdouble * stream_x, gdouble * stream_y)
|
|
+{
|
|
+ gdouble stream_width, stream_height;
|
|
+ GtkAllocation allocation;
|
|
+ GstVideoRectangle result;
|
|
+
|
|
+ gtk_widget_get_allocation (GTK_WIDGET (base_widget), &allocation);
|
|
+ _fit_stream_to_allocated_size (base_widget, &allocation, &result);
|
|
+
|
|
+ stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
|
|
+ stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
|
|
+
|
|
+ /* from display coordinates to stream coordinates */
|
|
+ if (result.w > 0)
|
|
+ *stream_x = (x - result.x) / result.w * stream_width;
|
|
+ else
|
|
+ *stream_x = 0.;
|
|
+
|
|
+ /* clip to stream size */
|
|
+ if (*stream_x < 0.)
|
|
+ *stream_x = 0.;
|
|
+ if (*stream_x > GST_VIDEO_INFO_WIDTH (&base_widget->v_info))
|
|
+ *stream_x = GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
|
|
+
|
|
+ /* same for y-axis */
|
|
+ if (result.h > 0)
|
|
+ *stream_y = (y - result.y) / result.h * stream_height;
|
|
+ else
|
|
+ *stream_y = 0.;
|
|
+
|
|
+ if (*stream_y < 0.)
|
|
+ *stream_y = 0.;
|
|
+ if (*stream_y > GST_VIDEO_INFO_HEIGHT (&base_widget->v_info))
|
|
+ *stream_y = GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
|
|
+
|
|
+ GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event)
|
|
+{
|
|
+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
+ GstElement *element;
|
|
+
|
|
+ if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
+ if (GST_IS_NAVIGATION (element)) {
|
|
+ const gchar *key_type =
|
|
+ event->type ==
|
|
+ GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release";
|
|
+ gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
|
|
+ event->button, event->x, event->y);
|
|
+ }
|
|
+ g_object_unref (element);
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event)
|
|
+{
|
|
+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
+ GstElement *element;
|
|
+
|
|
+ if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
+ if (GST_IS_NAVIGATION (element)) {
|
|
+ gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
|
|
+ 0, event->x, event->y);
|
|
+ }
|
|
+ g_object_unref (element);
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_gst_base_widget_scroll_event (GtkWidget * widget, GdkEventScroll * event)
|
|
+{
|
|
+#if GST_CHECK_VERSION(1, 18, 0)
|
|
+ GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
+ GstElement *element;
|
|
+
|
|
+ if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
+ if (GST_IS_NAVIGATION (element)) {
|
|
+ gdouble x, y, delta_x, delta_y;
|
|
+
|
|
+ gtk_gst_base_widget_display_size_to_stream_size (base_widget, event->x,
|
|
+ event->y, &x, &y);
|
|
+
|
|
+ if (!gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) {
|
|
+ gdouble offset = 20;
|
|
+
|
|
+ delta_x = event->delta_x;
|
|
+ delta_y = event->delta_y;
|
|
+
|
|
+ switch (event->direction) {
|
|
+ case GDK_SCROLL_UP:
|
|
+ delta_y = offset;
|
|
+ break;
|
|
+ case GDK_SCROLL_DOWN:
|
|
+ delta_y = -offset;
|
|
+ break;
|
|
+ case GDK_SCROLL_LEFT:
|
|
+ delta_x = -offset;
|
|
+ break;
|
|
+ case GDK_SCROLL_RIGHT:
|
|
+ delta_x = offset;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ gst_navigation_send_mouse_scroll_event (GST_NAVIGATION (element),
|
|
+ x, y, delta_x, delta_y);
|
|
+ }
|
|
+ g_object_unref (element);
|
|
+ }
|
|
+#endif
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
|
|
+{
|
|
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
+ GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
|
|
+
|
|
+ gobject_klass->set_property = gtk_gst_base_widget_set_property;
|
|
+ gobject_klass->get_property = gtk_gst_base_widget_get_property;
|
|
+
|
|
+ g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
|
|
+ g_param_spec_boolean ("force-aspect-ratio",
|
|
+ "Force aspect ratio",
|
|
+ "When enabled, scaling will respect original aspect ratio",
|
|
+ DEFAULT_FORCE_ASPECT_RATIO,
|
|
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
+
|
|
+ g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO,
|
|
+ gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
|
+ "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
|
+ G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
+
|
|
+ g_object_class_install_property (gobject_klass, PROP_IGNORE_ALPHA,
|
|
+ g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
|
|
+ "When enabled, alpha will be ignored and converted to black",
|
|
+ DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
+
|
|
+ widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
|
|
+ widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
|
|
+ widget_klass->key_press_event = gtk_gst_base_widget_key_event;
|
|
+ widget_klass->key_release_event = gtk_gst_base_widget_key_event;
|
|
+ widget_klass->button_press_event = gtk_gst_base_widget_button_event;
|
|
+ widget_klass->button_release_event = gtk_gst_base_widget_button_event;
|
|
+ widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event;
|
|
+ widget_klass->scroll_event = gtk_gst_base_widget_scroll_event;
|
|
+
|
|
+ GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0,
|
|
+ "Gtk Video Base Widget");
|
|
+}
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
|
|
+{
|
|
+ int event_mask;
|
|
+
|
|
+ widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
|
+ widget->par_n = DEFAULT_PAR_N;
|
|
+ widget->par_d = DEFAULT_PAR_D;
|
|
+ widget->ignore_alpha = DEFAULT_IGNORE_ALPHA;
|
|
+
|
|
+ gst_video_info_init (&widget->v_info);
|
|
+ gst_video_info_init (&widget->pending_v_info);
|
|
+
|
|
+ g_weak_ref_init (&widget->element, NULL);
|
|
+ g_mutex_init (&widget->lock);
|
|
+
|
|
+ gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
|
|
+ event_mask = gtk_widget_get_events (GTK_WIDGET (widget));
|
|
+ event_mask |= GDK_KEY_PRESS_MASK
|
|
+ | GDK_KEY_RELEASE_MASK
|
|
+ | GDK_BUTTON_PRESS_MASK
|
|
+ | GDK_BUTTON_RELEASE_MASK
|
|
+ | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK;
|
|
+ gtk_widget_set_events (GTK_WIDGET (widget), event_mask);
|
|
+}
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_finalize (GObject * object)
|
|
+{
|
|
+ GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
|
|
+
|
|
+ gst_buffer_replace (&widget->pending_buffer, NULL);
|
|
+ gst_buffer_replace (&widget->buffer, NULL);
|
|
+ g_mutex_clear (&widget->lock);
|
|
+ g_weak_ref_clear (&widget->element);
|
|
+
|
|
+ if (widget->draw_id)
|
|
+ g_source_remove (widget->draw_id);
|
|
+}
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget,
|
|
+ GstElement * element)
|
|
+{
|
|
+ g_weak_ref_set (&widget->element, element);
|
|
+}
|
|
+
|
|
+gboolean
|
|
+gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget,
|
|
+ GstVideoInfo * v_info)
|
|
+{
|
|
+ GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
+
|
|
+ if (gst_video_info_is_equal (&widget->pending_v_info, v_info)) {
|
|
+ GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
+ return TRUE;
|
|
+ }
|
|
+
|
|
+ if (!_calculate_par (widget, v_info)) {
|
|
+ GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ widget->pending_resize = TRUE;
|
|
+ widget->pending_v_info = *v_info;
|
|
+
|
|
+ if (!widget->draw_id) {
|
|
+ widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
|
+ (GSourceFunc) _queue_draw, widget, NULL);
|
|
+ }
|
|
+
|
|
+ GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+void
|
|
+gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer)
|
|
+{
|
|
+ /* As we have no type, this is better then no check */
|
|
+ g_return_if_fail (GTK_IS_WIDGET (widget));
|
|
+
|
|
+ GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
+
|
|
+ gst_buffer_replace (&widget->pending_buffer, buffer);
|
|
+
|
|
+ if (!widget->draw_id) {
|
|
+ widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
|
+ (GSourceFunc) _queue_draw, widget, NULL);
|
|
+ }
|
|
+
|
|
+ GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
+}
|
|
diff --git a/ext/gtk/gtkgstbasewidget.h b/ext/gtk/gtkgstbasewidget.h
|
|
new file mode 100644
|
|
index 0000000..c10ed38
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gtkgstbasewidget.h
|
|
@@ -0,0 +1,100 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifndef __GTK_GST_BASE_WIDGET_H__
|
|
+#define __GTK_GST_BASE_WIDGET_H__
|
|
+
|
|
+#include <gtk/gtk.h>
|
|
+#include <gst/gst.h>
|
|
+#include <gst/video/video.h>
|
|
+
|
|
+#define GTK_GST_BASE_WIDGET(w) ((GtkGstBaseWidget *)(w))
|
|
+#define GTK_GST_BASE_WIDGET_CLASS(k) ((GtkGstBaseWidgetClass *)(k))
|
|
+#define GTK_GST_BASE_WIDGET_LOCK(w) g_mutex_lock(&((GtkGstBaseWidget*)(w))->lock)
|
|
+#define GTK_GST_BASE_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkGstBaseWidget*)(w))->lock)
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+typedef struct _GtkGstBaseWidget GtkGstBaseWidget;
|
|
+typedef struct _GtkGstBaseWidgetClass GtkGstBaseWidgetClass;
|
|
+
|
|
+struct _GtkGstBaseWidget
|
|
+{
|
|
+ union {
|
|
+ GtkDrawingArea drawing_area;
|
|
+#if GTK_CHECK_VERSION(3, 15, 0)
|
|
+ GtkGLArea gl_area;
|
|
+#endif
|
|
+ } parent;
|
|
+
|
|
+ /* properties */
|
|
+ gboolean force_aspect_ratio;
|
|
+ gint par_n, par_d;
|
|
+ gboolean ignore_alpha;
|
|
+
|
|
+ gint display_width;
|
|
+ gint display_height;
|
|
+
|
|
+ gboolean negotiated;
|
|
+ GstBuffer *pending_buffer;
|
|
+ GstBuffer *buffer;
|
|
+ GstVideoInfo v_info;
|
|
+
|
|
+ /* resize */
|
|
+ gboolean pending_resize;
|
|
+ GstVideoInfo pending_v_info;
|
|
+ guint display_ratio_num;
|
|
+ guint display_ratio_den;
|
|
+
|
|
+ /*< private >*/
|
|
+ GMutex lock;
|
|
+ GWeakRef element;
|
|
+
|
|
+ /* Pending draw idles callback */
|
|
+ guint draw_id;
|
|
+};
|
|
+
|
|
+struct _GtkGstBaseWidgetClass
|
|
+{
|
|
+ union {
|
|
+ GtkDrawingAreaClass drawing_area_class;
|
|
+#if GTK_CHECK_VERSION(3, 15, 0)
|
|
+ GtkGLAreaClass gl_area_class;
|
|
+#endif
|
|
+ } parent_class;
|
|
+};
|
|
+
|
|
+/* For implementer */
|
|
+void gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass);
|
|
+void gtk_gst_base_widget_init (GtkGstBaseWidget * widget);
|
|
+
|
|
+void gtk_gst_base_widget_finalize (GObject * object);
|
|
+
|
|
+/* API */
|
|
+gboolean gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget, GstVideoInfo * v_info);
|
|
+void gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer);
|
|
+void gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget, GstElement * element);
|
|
+void gtk_gst_base_widget_display_size_to_stream_size (GtkGstBaseWidget * base_widget,
|
|
+ gdouble x, gdouble y,
|
|
+ gdouble * stream_x, gdouble * stream_y);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __GTK_GST_BASE_WIDGET_H__ */
|
|
diff --git a/ext/gtk/gtkgstwaylandwidget.c b/ext/gtk/gtkgstwaylandwidget.c
|
|
new file mode 100644
|
|
index 0000000..df3d1d5
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gtkgstwaylandwidget.c
|
|
@@ -0,0 +1,67 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include "gtkgstwaylandwidget.h"
|
|
+#include <gst/video/video.h>
|
|
+
|
|
+/**
|
|
+ * SECTION:gtkgstwidget
|
|
+ * @title: GtkGstWaylandWidget
|
|
+ * @short_description: a #GtkWidget that renders GStreamer video #GstBuffers
|
|
+ * @see_also: #GtkDrawingArea, #GstBuffer
|
|
+ *
|
|
+ * #GtkGstWaylandWidget is an #GtkWidget that renders GStreamer video buffers.
|
|
+ */
|
|
+
|
|
+G_DEFINE_TYPE (GtkGstWaylandWidget, gtk_gst_wayland_widget,
|
|
+ GTK_TYPE_DRAWING_AREA);
|
|
+
|
|
+static void
|
|
+gtk_gst_wayland_widget_finalize (GObject * object)
|
|
+{
|
|
+ gtk_gst_base_widget_finalize (object);
|
|
+
|
|
+ G_OBJECT_CLASS (gtk_gst_wayland_widget_parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_gst_wayland_widget_class_init (GtkGstWaylandWidgetClass * klass)
|
|
+{
|
|
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
+
|
|
+ gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
|
|
+ gobject_klass->finalize = gtk_gst_wayland_widget_finalize;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_gst_wayland_widget_init (GtkGstWaylandWidget * widget)
|
|
+{
|
|
+ gtk_gst_base_widget_init (GTK_GST_BASE_WIDGET (widget));
|
|
+}
|
|
+
|
|
+GtkWidget *
|
|
+gtk_gst_wayland_widget_new (void)
|
|
+{
|
|
+ return (GtkWidget *) g_object_new (GTK_TYPE_GST_WAYLAND_WIDGET, NULL);
|
|
+}
|
|
diff --git a/ext/gtk/gtkgstwaylandwidget.h b/ext/gtk/gtkgstwaylandwidget.h
|
|
new file mode 100644
|
|
index 0000000..48cfe9e
|
|
--- /dev/null
|
|
+++ b/ext/gtk/gtkgstwaylandwidget.h
|
|
@@ -0,0 +1,68 @@
|
|
+/*
|
|
+ * GStreamer
|
|
+ * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
+ *
|
|
+ * This library is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU Library General Public
|
|
+ * License as published by the Free Software Foundation; either
|
|
+ * version 2 of the License, or (at your option) any later version.
|
|
+ *
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * Library General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Library General Public
|
|
+ * License along with this library; if not, write to the
|
|
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
+ * Boston, MA 02110-1301, USA.
|
|
+ */
|
|
+
|
|
+#ifndef __GTK_GST_WAYLAND_WIDGET_H__
|
|
+#define __GTK_GST_WAYLAND_WIDGET_H__
|
|
+
|
|
+#include <gtk/gtk.h>
|
|
+#include <gst/gst.h>
|
|
+
|
|
+#include "gtkgstbasewidget.h"
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+GType gtk_gst_wayland_widget_get_type (void);
|
|
+#define GTK_TYPE_GST_WAYLAND_WIDGET (gtk_gst_wayland_widget_get_type())
|
|
+#define GTK_GST_WAYLAND_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_WAYLAND_WIDGET,GtkGstWaylandWidget))
|
|
+#define GTK_GST_WAYLAND_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_WAYLAND_WIDGET,GtkGstWaylandWidgetClass))
|
|
+#define GTK_IS_GST_WAYLAND_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_WAYLAND_WIDGET))
|
|
+#define GTK_IS_GST_WAYLAND_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_WAYLAND_WIDGET))
|
|
+#define GTK_GST_WAYLAND_WIDGET_CAST(obj) ((GtkGstWaylandWidget*)(obj))
|
|
+
|
|
+typedef struct _GtkGstWaylandWidget GtkGstWaylandWidget;
|
|
+typedef struct _GtkGstWaylandWidgetClass GtkGstWaylandWidgetClass;
|
|
+
|
|
+/**
|
|
+ * GtkGstWaylandWidget:
|
|
+ *
|
|
+ * Opaque #GtkGstWaylandWidget object
|
|
+ */
|
|
+struct _GtkGstWaylandWidget
|
|
+{
|
|
+ /* <private> */
|
|
+ GtkGstBaseWidget base;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * GtkGstWaylandWidgetClass:
|
|
+ *
|
|
+ * The #GtkGstWaylandWidgetClass struct only contains private data
|
|
+ */
|
|
+struct _GtkGstWaylandWidgetClass
|
|
+{
|
|
+ /* <private> */
|
|
+ GtkGstBaseWidgetClass base_class;
|
|
+};
|
|
+
|
|
+GtkWidget * gtk_gst_wayland_widget_new (void);
|
|
+
|
|
+G_END_DECLS
|
|
+
|
|
+#endif /* __GTK_GST_WAYLAND_WIDGET_H__ */
|
|
diff --git a/ext/gtk/meson.build b/ext/gtk/meson.build
|
|
new file mode 100644
|
|
index 0000000..199188d
|
|
--- /dev/null
|
|
+++ b/ext/gtk/meson.build
|
|
@@ -0,0 +1,22 @@
|
|
+gtkwayland_sources = [
|
|
+ 'gstplugin.c',
|
|
+ 'gstgtkutils.c',
|
|
+ 'gstgtkwaylandsink.c',
|
|
+ 'gtkgstbasewidget.c',
|
|
+ 'gtkgstwaylandwidget.c',
|
|
+]
|
|
+
|
|
+gtk_dep = dependency('gtk+-3.0', required : get_option('gtk3'))
|
|
+gtk_wayland_dep = dependency('gtk+-wayland-3.0', required : get_option('gtk3'))
|
|
+
|
|
+if gtk_dep.found() and gtk_wayland_dep.found() and use_wayland
|
|
+ gstgtkwayland = library('gstgtkwayland',
|
|
+ gtkwayland_sources,
|
|
+ c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'],
|
|
+ include_directories : [configinc],
|
|
+ dependencies : [gtk_dep, gstvideo_dep, gstwayland_dep],
|
|
+ install : true,
|
|
+ install_dir : plugins_install_dir,
|
|
+ )
|
|
+ pkgconfig.generate(gstgtkwayland, install_dir : plugins_pkgconfig_install_dir)
|
|
+endif
|
|
diff --git a/ext/meson.build b/ext/meson.build
|
|
index 1e40ace..17195f8 100644
|
|
--- a/ext/meson.build
|
|
+++ b/ext/meson.build
|
|
@@ -21,6 +21,7 @@ subdir('fluidsynth')
|
|
subdir('gme')
|
|
subdir('gs')
|
|
subdir('gsm')
|
|
+subdir('gtk')
|
|
subdir('hls')
|
|
subdir('iqa')
|
|
subdir('isac')
|
|
diff --git a/meson_options.txt b/meson_options.txt
|
|
index b347dcb..c64f292 100644
|
|
--- a/meson_options.txt
|
|
+++ b/meson_options.txt
|
|
@@ -29,6 +29,7 @@ option('frei0r', type : 'feature', value : 'auto')
|
|
option('gaudieffects', type : 'feature', value : 'auto')
|
|
option('gdp', type : 'feature', value : 'auto')
|
|
option('geometrictransform', type : 'feature', value : 'auto')
|
|
+option('gtk3', type : 'feature', value : 'auto')
|
|
option('id3tag', type : 'feature', value : 'auto')
|
|
option('inter', type : 'feature', value : 'auto')
|
|
option('interlace', type : 'feature', value : 'auto')
|
|
--
|
|
2.34.1
|
|
|