From 33fa3db5ac3c5e3c82df9573dec334ef7bf77ee3 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis 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 + * Copyright (C) 2015 Thibault Saunier + * + * 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 + * Copyright (C) 2015 Thibault Saunier + * + * 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 + +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 + * Copyright (C) 2021 Collabora Ltd. + * @author George Kiagiadakis + * + * 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 +#include + +#include + +#ifdef GDK_WINDOWING_WAYLAND +#include +#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 "); + + 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 + * + * 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 +#include + +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 +{ + /* */ + 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 + * + * 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 + * + * 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 + +#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 + * + * 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 +#include +#include + +#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 + * + * 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 + +/** + * 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 + * + * 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 +#include + +#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 +{ + /* */ + GtkGstBaseWidget base; +}; + +/** + * GtkGstWaylandWidgetClass: + * + * The #GtkGstWaylandWidgetClass struct only contains private data + */ +struct _GtkGstWaylandWidgetClass +{ + /* */ + 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