mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 16:02:00 +02:00
@@ -30,7 +30,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gstclapper-gtk4-plugin.h"
|
#include "gstclapper-gtk4-plugin.h"
|
||||||
#include "gtk4/gstgtkglsink.h"
|
#include "gtk4/gstclapperglsink.h"
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -77,9 +77,7 @@ gst_clapper_gtk4_plugin_constructed (GObject * object)
|
|||||||
{
|
{
|
||||||
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
|
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
|
||||||
|
|
||||||
if (!self->video_sink)
|
self->video_sink = g_object_new (GST_TYPE_CLAPPER_GL_SINK, NULL);
|
||||||
self->video_sink = g_object_new (GST_TYPE_GTK_GL_SINK, NULL);
|
|
||||||
|
|
||||||
gst_object_ref_sink (self->video_sink);
|
gst_object_ref_sink (self->video_sink);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->constructed (object);
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
||||||
@@ -111,35 +109,15 @@ gst_clapper_gtk4_plugin_finalize (GObject * object)
|
|||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define C_ENUM(v) ((gint) v)
|
|
||||||
|
|
||||||
GType
|
|
||||||
gst_clapper_gtk4_plugin_type_get_type (void)
|
|
||||||
{
|
|
||||||
static gsize id = 0;
|
|
||||||
static const GEnumValue values[] = {
|
|
||||||
{C_ENUM (GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA), "GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA", "glarea"},
|
|
||||||
{0, NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (g_once_init_enter (&id)) {
|
|
||||||
GType tmp = g_enum_register_static ("GstClapperGtk4PluginType", values);
|
|
||||||
g_once_init_leave (&id, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (GType) id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_clapper_gtk4_plugin_new:
|
* gst_clapper_gtk4_plugin_new:
|
||||||
* @plugin_type: (allow-none): Requested GstClapperGtk4PluginType
|
|
||||||
*
|
*
|
||||||
* Creates a new GTK4 plugin.
|
* Creates a new GTK4 plugin.
|
||||||
*
|
*
|
||||||
* Returns: (transfer full): the new GstClapperGtk4Plugin
|
* Returns: (transfer full): the new GstClapperGtk4Plugin
|
||||||
*/
|
*/
|
||||||
GstClapperGtk4Plugin *
|
GstClapperGtk4Plugin *
|
||||||
gst_clapper_gtk4_plugin_new (G_GNUC_UNUSED const GstClapperGtk4PluginType plugin_type)
|
gst_clapper_gtk4_plugin_new (void)
|
||||||
{
|
{
|
||||||
return g_object_new (GST_TYPE_CLAPPER_GTK4_PLUGIN, NULL);
|
return g_object_new (GST_TYPE_CLAPPER_GTK4_PLUGIN, NULL);
|
||||||
}
|
}
|
||||||
|
@@ -26,20 +26,6 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/* PluginType */
|
|
||||||
GST_CLAPPER_API
|
|
||||||
GType gst_clapper_gtk4_plugin_type_get_type (void);
|
|
||||||
#define GST_TYPE_CLAPPER_GTK4_PLUGIN_TYPE (gst_clapper_gtk4_plugin_type_get_type ())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstClapperGtk4PluginType:
|
|
||||||
* @GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA: GTK4 GLArea sink.
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA,
|
|
||||||
} GstClapperGtk4PluginType;
|
|
||||||
|
|
||||||
#define GST_TYPE_CLAPPER_GTK4_PLUGIN (gst_clapper_gtk4_plugin_get_type ())
|
#define GST_TYPE_CLAPPER_GTK4_PLUGIN (gst_clapper_gtk4_plugin_get_type ())
|
||||||
#define GST_IS_CLAPPER_GTK4_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_GTK4_PLUGIN))
|
#define GST_IS_CLAPPER_GTK4_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_GTK4_PLUGIN))
|
||||||
#define GST_IS_CLAPPER_GTK4_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_GTK4_PLUGIN))
|
#define GST_IS_CLAPPER_GTK4_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_GTK4_PLUGIN))
|
||||||
@@ -79,7 +65,7 @@ GST_CLAPPER_API
|
|||||||
GType gst_clapper_gtk4_plugin_get_type (void);
|
GType gst_clapper_gtk4_plugin_get_type (void);
|
||||||
|
|
||||||
GST_CLAPPER_API
|
GST_CLAPPER_API
|
||||||
GstClapperGtk4Plugin * gst_clapper_gtk4_plugin_new (const GstClapperGtk4PluginType plugin_type);
|
GstClapperGtk4Plugin * gst_clapper_gtk4_plugin_new (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
732
lib/gst/clapper/gtk4/gstclapperglsink.c
Normal file
732
lib/gst/clapper/gtk4/gstclapperglsink.c
Normal file
@@ -0,0 +1,732 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||||
|
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:gstclapperglsink
|
||||||
|
* @title: GstClapperGLSink
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gst/gl/gstglfuncs.h>
|
||||||
|
|
||||||
|
#include "gstclapperglsink.h"
|
||||||
|
#include "gstgtkutils.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_sink);
|
||||||
|
#define GST_CAT_DEFAULT gst_debug_clapper_gl_sink
|
||||||
|
|
||||||
|
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||||
|
#define DEFAULT_PAR_N 0
|
||||||
|
#define DEFAULT_PAR_D 1
|
||||||
|
#define DEFAULT_IGNORE_TEXTURES FALSE
|
||||||
|
|
||||||
|
static GstStaticPadTemplate gst_clapper_gl_sink_template =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
GST_PAD_SINK,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||||
|
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
|
||||||
|
GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||||
|
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
|
||||||
|
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
|
||||||
|
|
||||||
|
static void gst_clapper_gl_sink_finalize (GObject * object);
|
||||||
|
static void gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * param_spec);
|
||||||
|
static void gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * param_spec);
|
||||||
|
|
||||||
|
static gboolean gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink,
|
||||||
|
GstQuery * query);
|
||||||
|
static gboolean gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
|
||||||
|
static gboolean gst_clapper_gl_sink_start (GstBaseSink * bsink);
|
||||||
|
static gboolean gst_clapper_gl_sink_stop (GstBaseSink * bsink);
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_clapper_gl_sink_change_state (GstElement * element,
|
||||||
|
GstStateChange transition);
|
||||||
|
|
||||||
|
static void gst_clapper_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
||||||
|
GstClockTime * start, GstClockTime * end);
|
||||||
|
static GstCaps *gst_clapper_gl_sink_get_caps (GstBaseSink * bsink,
|
||||||
|
GstCaps * filter);
|
||||||
|
static gboolean gst_clapper_gl_sink_set_caps (GstBaseSink * bsink,
|
||||||
|
GstCaps * caps);
|
||||||
|
static GstFlowReturn gst_clapper_gl_sink_show_frame (GstVideoSink * bsink,
|
||||||
|
GstBuffer * buf);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface);
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_WIDGET,
|
||||||
|
PROP_FORCE_ASPECT_RATIO,
|
||||||
|
PROP_PIXEL_ASPECT_RATIO,
|
||||||
|
PROP_IGNORE_TEXTURES,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define gst_clapper_gl_sink_parent_class parent_class
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (GstClapperGLSink, gst_clapper_gl_sink,
|
||||||
|
GST_TYPE_VIDEO_SINK,
|
||||||
|
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
||||||
|
gst_clapper_gl_sink_navigation_interface_init);
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_debug_clapper_gl_sink,
|
||||||
|
"clapperglsink", 0, "Clapper GL Sink"));
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstElementClass *gstelement_class;
|
||||||
|
GstBaseSinkClass *gstbasesink_class;
|
||||||
|
GstVideoSinkClass *gstvideosink_class;
|
||||||
|
GstClapperGLSinkClass *gstclapperglsink_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstelement_class = (GstElementClass *) klass;
|
||||||
|
gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||||
|
gstvideosink_class = (GstVideoSinkClass *) klass;
|
||||||
|
gstclapperglsink_class = (GstClapperGLSinkClass *) klass;
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_clapper_gl_sink_set_property;
|
||||||
|
gobject_class->get_property = gst_clapper_gl_sink_get_property;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, 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_class, 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_class, PROP_IGNORE_TEXTURES,
|
||||||
|
g_param_spec_boolean ("ignore-textures", "Ignore Textures",
|
||||||
|
"When enabled, textures will be ignored and not drawn",
|
||||||
|
DEFAULT_IGNORE_TEXTURES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_clapper_gl_sink_finalize;
|
||||||
|
|
||||||
|
gstelement_class->change_state = gst_clapper_gl_sink_change_state;
|
||||||
|
|
||||||
|
gstbasesink_class->get_caps = gst_clapper_gl_sink_get_caps;
|
||||||
|
gstbasesink_class->set_caps = gst_clapper_gl_sink_set_caps;
|
||||||
|
gstbasesink_class->get_times = gst_clapper_gl_sink_get_times;
|
||||||
|
gstbasesink_class->propose_allocation = gst_clapper_gl_sink_propose_allocation;
|
||||||
|
gstbasesink_class->query = gst_clapper_gl_sink_query;
|
||||||
|
gstbasesink_class->start = gst_clapper_gl_sink_start;
|
||||||
|
gstbasesink_class->stop = gst_clapper_gl_sink_stop;
|
||||||
|
|
||||||
|
gstvideosink_class->show_frame = gst_clapper_gl_sink_show_frame;
|
||||||
|
|
||||||
|
gstclapperglsink_class->create_widget = gtk_clapper_gl_widget_new;
|
||||||
|
gstclapperglsink_class->window_title = "GTK4 GL Renderer";
|
||||||
|
|
||||||
|
gst_element_class_set_metadata (gstelement_class,
|
||||||
|
"GTK4 GL Video Sink",
|
||||||
|
"Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
|
||||||
|
"Matthew Waters <matthew@centricular.com>, "
|
||||||
|
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||||
|
|
||||||
|
gst_element_class_add_static_pad_template (gstelement_class,
|
||||||
|
&gst_clapper_gl_sink_template);
|
||||||
|
|
||||||
|
gst_type_mark_as_plugin_api (GST_TYPE_CLAPPER_GL_SINK, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_init (GstClapperGLSink * clapper_sink)
|
||||||
|
{
|
||||||
|
clapper_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||||
|
clapper_sink->par_n = DEFAULT_PAR_N;
|
||||||
|
clapper_sink->par_d = DEFAULT_PAR_D;
|
||||||
|
clapper_sink->ignore_textures = DEFAULT_IGNORE_TEXTURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
|
||||||
|
|
||||||
|
GST_DEBUG ("Finalizing Clapper GL sink");
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
if (clapper_sink->window && clapper_sink->window_destroy_id)
|
||||||
|
g_signal_handler_disconnect (clapper_sink->window, clapper_sink->window_destroy_id);
|
||||||
|
if (clapper_sink->widget && clapper_sink->widget_destroy_id)
|
||||||
|
g_signal_handler_disconnect (clapper_sink->widget, clapper_sink->widget_destroy_id);
|
||||||
|
|
||||||
|
g_clear_object (&clapper_sink->widget);
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
widget_destroy_cb (GtkWidget * widget, GstClapperGLSink * clapper_sink)
|
||||||
|
{
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
g_clear_object (&clapper_sink->widget);
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
window_destroy_cb (GtkWidget * widget, GstClapperGLSink * clapper_sink)
|
||||||
|
{
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
if (clapper_sink->widget) {
|
||||||
|
if (clapper_sink->widget_destroy_id) {
|
||||||
|
g_signal_handler_disconnect (clapper_sink->widget,
|
||||||
|
clapper_sink->widget_destroy_id);
|
||||||
|
clapper_sink->widget_destroy_id = 0;
|
||||||
|
}
|
||||||
|
g_clear_object (&clapper_sink->widget);
|
||||||
|
}
|
||||||
|
clapper_sink->window = NULL;
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GtkClapperGLWidget *
|
||||||
|
gst_clapper_gl_sink_get_widget (GstClapperGLSink * clapper_sink)
|
||||||
|
{
|
||||||
|
if (clapper_sink->widget != NULL)
|
||||||
|
return clapper_sink->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 ()) {
|
||||||
|
GST_ERROR_OBJECT (clapper_sink, "Could not ensure GTK initialization.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert (GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget);
|
||||||
|
clapper_sink->widget = (GtkClapperGLWidget *)
|
||||||
|
GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget ();
|
||||||
|
|
||||||
|
clapper_sink->bind_aspect_ratio =
|
||||||
|
g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget,
|
||||||
|
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||||
|
clapper_sink->bind_pixel_aspect_ratio =
|
||||||
|
g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget,
|
||||||
|
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||||
|
clapper_sink->bind_ignore_textures =
|
||||||
|
g_object_bind_property (clapper_sink, "ignore-textures", clapper_sink->widget,
|
||||||
|
"ignore-textures", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||||
|
|
||||||
|
/* Take the floating ref, other wise the destruction of the container will
|
||||||
|
* make this widget disappear possibly before we are done. */
|
||||||
|
gst_object_ref_sink (clapper_sink->widget);
|
||||||
|
|
||||||
|
clapper_sink->widget_destroy_id = g_signal_connect (clapper_sink->widget, "destroy",
|
||||||
|
G_CALLBACK (widget_destroy_cb), clapper_sink);
|
||||||
|
|
||||||
|
/* back pointer */
|
||||||
|
gtk_clapper_gl_widget_set_element (GTK_CLAPPER_GL_WIDGET (clapper_sink->widget),
|
||||||
|
GST_ELEMENT (clapper_sink));
|
||||||
|
|
||||||
|
return clapper_sink->widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
|
||||||
|
GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_WIDGET:
|
||||||
|
{
|
||||||
|
GObject *widget = NULL;
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
if (clapper_sink->widget != NULL)
|
||||||
|
widget = G_OBJECT (clapper_sink->widget);
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
if (!widget)
|
||||||
|
widget =
|
||||||
|
gst_gtk_invoke_on_main ((GThreadFunc) gst_clapper_gl_sink_get_widget,
|
||||||
|
clapper_sink);
|
||||||
|
|
||||||
|
g_value_set_object (value, widget);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PROP_FORCE_ASPECT_RATIO:
|
||||||
|
g_value_set_boolean (value, clapper_sink->force_aspect_ratio);
|
||||||
|
break;
|
||||||
|
case PROP_PIXEL_ASPECT_RATIO:
|
||||||
|
gst_value_set_fraction (value, clapper_sink->par_n, clapper_sink->par_d);
|
||||||
|
break;
|
||||||
|
case PROP_IGNORE_TEXTURES:
|
||||||
|
g_value_set_boolean (value, clapper_sink->ignore_textures);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_FORCE_ASPECT_RATIO:
|
||||||
|
clapper_sink->force_aspect_ratio = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
case PROP_PIXEL_ASPECT_RATIO:
|
||||||
|
clapper_sink->par_n = gst_value_get_fraction_numerator (value);
|
||||||
|
clapper_sink->par_d = gst_value_get_fraction_denominator (value);
|
||||||
|
break;
|
||||||
|
case PROP_IGNORE_TEXTURES:
|
||||||
|
clapper_sink->ignore_textures = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_navigation_send_event (GstNavigation * navigation,
|
||||||
|
GstStructure * structure)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *sink = GST_CLAPPER_GL_SINK (navigation);
|
||||||
|
GstEvent *event;
|
||||||
|
GstPad *pad;
|
||||||
|
|
||||||
|
event = gst_event_new_navigation (structure);
|
||||||
|
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
||||||
|
|
||||||
|
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_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface)
|
||||||
|
{
|
||||||
|
iface->send_event = gst_clapper_gl_sink_navigation_send_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
GstBufferPool *pool = NULL;
|
||||||
|
GstStructure *config;
|
||||||
|
GstCaps *caps;
|
||||||
|
GstVideoInfo info;
|
||||||
|
guint size;
|
||||||
|
gboolean need_pool;
|
||||||
|
GstStructure *allocation_meta = NULL;
|
||||||
|
gint display_width, display_height;
|
||||||
|
|
||||||
|
if (!clapper_sink->display || !clapper_sink->context)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||||
|
|
||||||
|
if (caps == NULL)
|
||||||
|
goto no_caps;
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&info, caps))
|
||||||
|
goto invalid_caps;
|
||||||
|
|
||||||
|
/* the normal size of a frame */
|
||||||
|
size = info.size;
|
||||||
|
|
||||||
|
if (need_pool) {
|
||||||
|
GST_DEBUG_OBJECT (clapper_sink, "create new pool");
|
||||||
|
pool = gst_gl_buffer_pool_new (clapper_sink->context);
|
||||||
|
|
||||||
|
config = gst_buffer_pool_get_config (pool);
|
||||||
|
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
|
||||||
|
gst_buffer_pool_config_add_option (config,
|
||||||
|
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
||||||
|
|
||||||
|
if (!gst_buffer_pool_set_config (pool, config))
|
||||||
|
goto config_failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need at least 2 buffer because we hold on to the last one */
|
||||||
|
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
||||||
|
if (pool)
|
||||||
|
gst_object_unref (pool);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
display_width = clapper_sink->display_width;
|
||||||
|
display_height = clapper_sink->display_height;
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
if (display_width != 0 && display_height != 0) {
|
||||||
|
GST_DEBUG_OBJECT (clapper_sink, "sending alloc query with size %dx%d",
|
||||||
|
display_width, display_height);
|
||||||
|
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
|
||||||
|
"width", G_TYPE_UINT, display_width,
|
||||||
|
"height", G_TYPE_UINT, display_height, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_query_add_allocation_meta (query,
|
||||||
|
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
|
||||||
|
|
||||||
|
if (allocation_meta)
|
||||||
|
gst_structure_free (allocation_meta);
|
||||||
|
|
||||||
|
/* we also support various metadata */
|
||||||
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
|
||||||
|
|
||||||
|
if (clapper_sink->context->gl_vtable->FenceSync)
|
||||||
|
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_caps:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (bsink, "no caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
invalid_caps:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (bsink, "invalid caps specified");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
config_failed:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (bsink, "failed setting config");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
switch (GST_QUERY_TYPE (query)) {
|
||||||
|
case GST_QUERY_CONTEXT:
|
||||||
|
{
|
||||||
|
if (gst_gl_handle_context_query ((GstElement *) clapper_sink, query,
|
||||||
|
clapper_sink->display, clapper_sink->context, clapper_sink->gtk_context))
|
||||||
|
return TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_start_on_main (GstBaseSink * bsink)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *gst_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
GstClapperGLSinkClass *klass = GST_CLAPPER_GL_SINK_GET_CLASS (bsink);
|
||||||
|
GtkWidget *toplevel;
|
||||||
|
GtkRoot *root;
|
||||||
|
|
||||||
|
if (gst_clapper_gl_sink_get_widget (gst_sink) == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* After this point, clapper_sink->widget will always be set */
|
||||||
|
|
||||||
|
root = gtk_widget_get_root (GTK_WIDGET (gst_sink->widget));
|
||||||
|
if (!GTK_IS_ROOT (root)) {
|
||||||
|
GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (gst_sink->widget));
|
||||||
|
if (parent) {
|
||||||
|
GtkWidget *temp_parent;
|
||||||
|
while ((temp_parent = gtk_widget_get_parent (parent)))
|
||||||
|
parent = temp_parent;
|
||||||
|
}
|
||||||
|
toplevel = (parent) ? parent : GTK_WIDGET (gst_sink->widget);
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
g_assert (klass->window_title);
|
||||||
|
|
||||||
|
/* User did not add widget its own UI, let's popup a new GtkWindow to
|
||||||
|
* make gst-launch-1.0 work. */
|
||||||
|
gst_sink->window = gtk_window_new ();
|
||||||
|
gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480);
|
||||||
|
gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title);
|
||||||
|
gtk_window_set_child (GTK_WINDOW (gst_sink->window), toplevel);
|
||||||
|
|
||||||
|
gst_sink->window_destroy_id = g_signal_connect (
|
||||||
|
GTK_WINDOW (gst_sink->window),
|
||||||
|
"destroy", G_CALLBACK (window_destroy_cb), gst_sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_start (GstBaseSink * bsink)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
GtkClapperGLWidget *clapper_widget;
|
||||||
|
|
||||||
|
if (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||||
|
gst_clapper_gl_sink_start_on_main, bsink)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* After this point, clapper_sink->widget will always be set */
|
||||||
|
clapper_widget = GTK_CLAPPER_GL_WIDGET (clapper_sink->widget);
|
||||||
|
|
||||||
|
if (!gtk_clapper_gl_widget_init_winsys (clapper_widget)) {
|
||||||
|
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
|
||||||
|
"Failed to initialize OpenGL with GTK"), (NULL));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clapper_sink->display)
|
||||||
|
clapper_sink->display = gtk_clapper_gl_widget_get_display (clapper_widget);
|
||||||
|
if (!clapper_sink->context)
|
||||||
|
clapper_sink->context = gtk_clapper_gl_widget_get_context (clapper_widget);
|
||||||
|
if (!clapper_sink->gtk_context)
|
||||||
|
clapper_sink->gtk_context = gtk_clapper_gl_widget_get_gtk_context (clapper_widget);
|
||||||
|
|
||||||
|
if (!clapper_sink->display || !clapper_sink->context || !clapper_sink->gtk_context) {
|
||||||
|
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
|
||||||
|
"Failed to retrieve OpenGL context from GTK"), (NULL));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
|
||||||
|
clapper_sink->display);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_stop_on_main (GstBaseSink * bsink)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *gst_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
|
||||||
|
if (gst_sink->window) {
|
||||||
|
gtk_window_destroy (GTK_WINDOW (gst_sink->window));
|
||||||
|
gst_sink->window = NULL;
|
||||||
|
gst_sink->widget = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_stop (GstBaseSink * bsink)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
|
||||||
|
if (clapper_sink->display) {
|
||||||
|
gst_object_unref (clapper_sink->display);
|
||||||
|
clapper_sink->display = NULL;
|
||||||
|
}
|
||||||
|
if (clapper_sink->context) {
|
||||||
|
gst_object_unref (clapper_sink->context);
|
||||||
|
clapper_sink->context = NULL;
|
||||||
|
}
|
||||||
|
if (clapper_sink->gtk_context) {
|
||||||
|
gst_object_unref (clapper_sink->gtk_context);
|
||||||
|
clapper_sink->gtk_context = NULL;
|
||||||
|
}
|
||||||
|
if (clapper_sink->window)
|
||||||
|
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||||
|
gst_clapper_gl_sink_stop_on_main, bsink);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_gtk_window_show_all_and_unref (GtkWidget * window)
|
||||||
|
{
|
||||||
|
gtk_window_present (GTK_WINDOW (window));
|
||||||
|
g_object_unref (window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (element);
|
||||||
|
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
||||||
|
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
||||||
|
|
||||||
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
|
{
|
||||||
|
GtkWindow *window = NULL;
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
if (clapper_sink->window)
|
||||||
|
window = g_object_ref (GTK_WINDOW (clapper_sink->window));
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||||
|
gst_gtk_window_show_all_and_unref, window);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
if (clapper_sink->widget)
|
||||||
|
gtk_clapper_gl_widget_set_buffer (clapper_sink->widget, NULL);
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_clapper_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
||||||
|
GstClockTime * start, GstClockTime * end)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
|
||||||
|
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
|
||||||
|
*start = GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||||
|
*end = *start + GST_BUFFER_DURATION (buf);
|
||||||
|
else {
|
||||||
|
if (GST_VIDEO_INFO_FPS_N (&clapper_sink->v_info) > 0) {
|
||||||
|
*end = *start +
|
||||||
|
gst_util_uint64_scale_int (GST_SECOND,
|
||||||
|
GST_VIDEO_INFO_FPS_D (&clapper_sink->v_info),
|
||||||
|
GST_VIDEO_INFO_FPS_N (&clapper_sink->v_info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_clapper_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
||||||
|
{
|
||||||
|
GstCaps *tmp = NULL;
|
||||||
|
GstCaps *result = NULL;
|
||||||
|
|
||||||
|
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
|
||||||
|
filter);
|
||||||
|
|
||||||
|
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
||||||
|
gst_caps_unref (tmp);
|
||||||
|
} else {
|
||||||
|
result = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = gst_gl_overlay_compositor_add_caps (result);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_clapper_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||||
|
|
||||||
|
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
|
||||||
|
|
||||||
|
if (!gst_video_info_from_caps (&clapper_sink->v_info, caps))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
|
||||||
|
if (clapper_sink->widget == NULL) {
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
GST_ELEMENT_ERROR (clapper_sink, RESOURCE, NOT_FOUND,
|
||||||
|
("%s", "Output widget was destroyed"), (NULL));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gtk_clapper_gl_widget_set_format (clapper_sink->widget, &clapper_sink->v_info)) {
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_clapper_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
GstClapperGLSink *clapper_sink;
|
||||||
|
|
||||||
|
GST_TRACE ("rendering buffer:%p", buf);
|
||||||
|
|
||||||
|
clapper_sink = GST_CLAPPER_GL_SINK (vsink);
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (clapper_sink);
|
||||||
|
|
||||||
|
if (clapper_sink->widget == NULL) {
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
GST_ELEMENT_ERROR (clapper_sink, RESOURCE, NOT_FOUND,
|
||||||
|
("%s", "Output widget was destroyed"), (NULL));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_clapper_gl_widget_set_buffer (clapper_sink->widget, buf);
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (clapper_sink);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
106
lib/gst/clapper/gtk4/gstclapperglsink.h
Normal file
106
lib/gst/clapper/gtk4/gstclapperglsink.h
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||||
|
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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_CLAPPER_GL_SINK_H__
|
||||||
|
#define __GST_CLAPPER_GL_SINK_H__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/video/gstvideosink.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/gl/gl.h>
|
||||||
|
|
||||||
|
#include "gtkclapperglwidget.h"
|
||||||
|
|
||||||
|
#define GST_TYPE_CLAPPER_GL_SINK (gst_clapper_gl_sink_get_type())
|
||||||
|
#define GST_CLAPPER_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_GL_SINK,GstClapperGLSink))
|
||||||
|
#define GST_CLAPPER_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_GL_SINK,GstClapperGLClass))
|
||||||
|
#define GST_CLAPPER_GL_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_GL_SINK, GstClapperGLSinkClass))
|
||||||
|
#define GST_IS_CLAPPER_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_GL_SINK))
|
||||||
|
#define GST_IS_CLAPPER_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_GL_SINK))
|
||||||
|
#define GST_CLAPPER_GL_SINK_CAST(obj) ((GstClapperGLSink*)(obj))
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
typedef struct _GstClapperGLSink GstClapperGLSink;
|
||||||
|
typedef struct _GstClapperGLSinkClass GstClapperGLSinkClass;
|
||||||
|
|
||||||
|
GType gst_clapper_gl_sink_get_type (void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstClapperGLSink:
|
||||||
|
*
|
||||||
|
* Opaque #GstClapperGLSink object
|
||||||
|
*/
|
||||||
|
struct _GstClapperGLSink
|
||||||
|
{
|
||||||
|
/* <private> */
|
||||||
|
GstVideoSink parent;
|
||||||
|
|
||||||
|
GstVideoInfo v_info;
|
||||||
|
|
||||||
|
GtkClapperGLWidget *widget;
|
||||||
|
|
||||||
|
/* properties */
|
||||||
|
gboolean force_aspect_ratio;
|
||||||
|
GBinding *bind_aspect_ratio;
|
||||||
|
|
||||||
|
gint par_n, par_d;
|
||||||
|
GBinding *bind_pixel_aspect_ratio;
|
||||||
|
|
||||||
|
gboolean ignore_textures;
|
||||||
|
GBinding *bind_ignore_textures;
|
||||||
|
|
||||||
|
GtkWidget *window;
|
||||||
|
gulong widget_destroy_id;
|
||||||
|
gulong window_destroy_id;
|
||||||
|
|
||||||
|
GstGLDisplay *display;
|
||||||
|
GstGLContext *context;
|
||||||
|
GstGLContext *gtk_context;
|
||||||
|
|
||||||
|
GstGLUpload *upload;
|
||||||
|
GstBuffer *uploaded_buffer;
|
||||||
|
|
||||||
|
/* read/write with object lock */
|
||||||
|
gint display_width, display_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GstClapperGLSinkClass:
|
||||||
|
*
|
||||||
|
* The #GstClapperGLSinkClass struct only contains private data
|
||||||
|
*/
|
||||||
|
struct _GstClapperGLSinkClass
|
||||||
|
{
|
||||||
|
GstVideoSinkClass object_class;
|
||||||
|
|
||||||
|
/* metadata */
|
||||||
|
const gchar *window_title;
|
||||||
|
|
||||||
|
/* virtuals */
|
||||||
|
GtkWidget* (*create_widget) (void);
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperGLSink, gst_object_unref)
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_CLAPPER_GL_SINK_H__ */
|
@@ -1,574 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:gtkgstsink
|
|
||||||
* @title: GstGtkBaseSink
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "gstgtkbasesink.h"
|
|
||||||
#include "gstgtkutils.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_base_sink);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_base_sink
|
|
||||||
|
|
||||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
|
||||||
#define DEFAULT_PAR_N 0
|
|
||||||
#define DEFAULT_PAR_D 1
|
|
||||||
#define DEFAULT_IGNORE_ALPHA TRUE
|
|
||||||
#define DEFAULT_IGNORE_TEXTURES FALSE
|
|
||||||
|
|
||||||
static void gst_gtk_base_sink_finalize (GObject * object);
|
|
||||||
static void gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * param_spec);
|
|
||||||
static void gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * param_spec);
|
|
||||||
|
|
||||||
static gboolean gst_gtk_base_sink_start (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_base_sink_stop (GstBaseSink * bsink);
|
|
||||||
|
|
||||||
static GstStateChangeReturn
|
|
||||||
gst_gtk_base_sink_change_state (GstElement * element,
|
|
||||||
GstStateChange transition);
|
|
||||||
|
|
||||||
static void gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
|
||||||
GstClockTime * start, GstClockTime * end);
|
|
||||||
static gboolean gst_gtk_base_sink_set_caps (GstBaseSink * bsink,
|
|
||||||
GstCaps * caps);
|
|
||||||
static GstFlowReturn gst_gtk_base_sink_show_frame (GstVideoSink * bsink,
|
|
||||||
GstBuffer * buf);
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface);
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
PROP_WIDGET,
|
|
||||||
PROP_FORCE_ASPECT_RATIO,
|
|
||||||
PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
PROP_IGNORE_ALPHA,
|
|
||||||
PROP_IGNORE_TEXTURES,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define gst_gtk_base_sink_parent_class parent_class
|
|
||||||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink,
|
|
||||||
GST_TYPE_VIDEO_SINK,
|
|
||||||
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
|
||||||
gst_gtk_base_sink_navigation_interface_init);
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink,
|
|
||||||
"gtkbasesink", 0, "GTK Video Sink base class"));
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class;
|
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstBaseSinkClass *gstbasesink_class;
|
|
||||||
GstVideoSinkClass *gstvideosink_class;
|
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
|
||||||
gstbasesink_class = (GstBaseSinkClass *) klass;
|
|
||||||
gstvideosink_class = (GstVideoSinkClass *) klass;
|
|
||||||
|
|
||||||
gobject_class->set_property = gst_gtk_base_sink_set_property;
|
|
||||||
gobject_class->get_property = gst_gtk_base_sink_get_property;
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, 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_class, 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));
|
|
||||||
|
|
||||||
/* Disabling alpha was removed in GTK4 */
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
g_object_class_install_property (gobject_class, 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));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_IGNORE_TEXTURES,
|
|
||||||
g_param_spec_boolean ("ignore-textures", "Ignore Textures",
|
|
||||||
"When enabled, textures will be ignored and not drawn",
|
|
||||||
DEFAULT_IGNORE_TEXTURES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
gobject_class->finalize = gst_gtk_base_sink_finalize;
|
|
||||||
|
|
||||||
gstelement_class->change_state = gst_gtk_base_sink_change_state;
|
|
||||||
gstbasesink_class->set_caps = gst_gtk_base_sink_set_caps;
|
|
||||||
gstbasesink_class->get_times = gst_gtk_base_sink_get_times;
|
|
||||||
gstbasesink_class->start = gst_gtk_base_sink_start;
|
|
||||||
gstbasesink_class->stop = gst_gtk_base_sink_stop;
|
|
||||||
|
|
||||||
gstvideosink_class->show_frame = gst_gtk_base_sink_show_frame;
|
|
||||||
|
|
||||||
gst_type_mark_as_plugin_api (GST_TYPE_GTK_BASE_SINK, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_init (GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
|
||||||
gtk_sink->par_n = DEFAULT_PAR_N;
|
|
||||||
gtk_sink->par_d = DEFAULT_PAR_D;
|
|
||||||
gtk_sink->ignore_alpha = DEFAULT_IGNORE_ALPHA;
|
|
||||||
gtk_sink->ignore_textures = DEFAULT_IGNORE_TEXTURES;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
GST_DEBUG ("finalizing base sink");
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->window && gtk_sink->window_destroy_id)
|
|
||||||
g_signal_handler_disconnect (gtk_sink->window, gtk_sink->window_destroy_id);
|
|
||||||
if (gtk_sink->widget && gtk_sink->widget_destroy_id)
|
|
||||||
g_signal_handler_disconnect (gtk_sink->widget, gtk_sink->widget_destroy_id);
|
|
||||||
|
|
||||||
g_clear_object (>k_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
widget_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
g_clear_object (>k_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
window_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->widget) {
|
|
||||||
if (gtk_sink->widget_destroy_id) {
|
|
||||||
g_signal_handler_disconnect (gtk_sink->widget,
|
|
||||||
gtk_sink->widget_destroy_id);
|
|
||||||
gtk_sink->widget_destroy_id = 0;
|
|
||||||
}
|
|
||||||
g_clear_object (>k_sink->widget);
|
|
||||||
}
|
|
||||||
gtk_sink->window = NULL;
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkGstBaseWidget *
|
|
||||||
gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink)
|
|
||||||
{
|
|
||||||
if (gtk_sink->widget != NULL)
|
|
||||||
return gtk_sink->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 (
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
NULL, NULL
|
|
||||||
#endif
|
|
||||||
)) {
|
|
||||||
GST_ERROR_OBJECT (gtk_sink, "Could not ensure GTK initialization.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_assert (GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget);
|
|
||||||
gtk_sink->widget = (GtkGstBaseWidget *)
|
|
||||||
GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget ();
|
|
||||||
|
|
||||||
gtk_sink->bind_aspect_ratio =
|
|
||||||
g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget,
|
|
||||||
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
gtk_sink->bind_pixel_aspect_ratio =
|
|
||||||
g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget,
|
|
||||||
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_sink->bind_ignore_alpha =
|
|
||||||
g_object_bind_property (gtk_sink, "ignore-alpha", gtk_sink->widget,
|
|
||||||
"ignore-alpha", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gtk_sink->bind_ignore_textures =
|
|
||||||
g_object_bind_property (gtk_sink, "ignore-textures", gtk_sink->widget,
|
|
||||||
"ignore-textures", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
|
||||||
|
|
||||||
/* Take the floating ref, other wise the destruction of the container will
|
|
||||||
* make this widget disappear possibly before we are done. */
|
|
||||||
gst_object_ref_sink (gtk_sink->widget);
|
|
||||||
|
|
||||||
gtk_sink->widget_destroy_id = g_signal_connect (gtk_sink->widget, "destroy",
|
|
||||||
G_CALLBACK (widget_destroy_cb), gtk_sink);
|
|
||||||
|
|
||||||
/* back pointer */
|
|
||||||
gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (gtk_sink->widget),
|
|
||||||
GST_ELEMENT (gtk_sink));
|
|
||||||
|
|
||||||
return gtk_sink->widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
|
|
||||||
GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_WIDGET:
|
|
||||||
{
|
|
||||||
GObject *widget = NULL;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->widget != NULL)
|
|
||||||
widget = G_OBJECT (gtk_sink->widget);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (!widget)
|
|
||||||
widget =
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget,
|
|
||||||
gtk_sink);
|
|
||||||
|
|
||||||
g_value_set_object (value, widget);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
g_value_set_boolean (value, gtk_sink->force_aspect_ratio);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gst_value_set_fraction (value, gtk_sink->par_n, gtk_sink->par_d);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
g_value_set_boolean (value, gtk_sink->ignore_alpha);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_TEXTURES:
|
|
||||||
g_value_set_boolean (value, gtk_sink->ignore_textures);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
|
|
||||||
const GValue * value, GParamSpec * pspec)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_FORCE_ASPECT_RATIO:
|
|
||||||
gtk_sink->force_aspect_ratio = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_PIXEL_ASPECT_RATIO:
|
|
||||||
gtk_sink->par_n = gst_value_get_fraction_numerator (value);
|
|
||||||
gtk_sink->par_d = gst_value_get_fraction_denominator (value);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_ALPHA:
|
|
||||||
gtk_sink->ignore_alpha = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
case PROP_IGNORE_TEXTURES:
|
|
||||||
gtk_sink->ignore_textures = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_navigation_send_event (GstNavigation * navigation,
|
|
||||||
GstStructure * structure)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *sink = GST_GTK_BASE_SINK (navigation);
|
|
||||||
GstEvent *event;
|
|
||||||
GstPad *pad;
|
|
||||||
|
|
||||||
event = gst_event_new_navigation (structure);
|
|
||||||
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
|
||||||
|
|
||||||
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_base_sink_navigation_interface_init (GstNavigationInterface * iface)
|
|
||||||
{
|
|
||||||
iface->send_event = gst_gtk_base_sink_navigation_send_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_start_on_main (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
GstGtkBaseSinkClass *klass = GST_GTK_BASE_SINK_GET_CLASS (bsink);
|
|
||||||
GtkWidget *toplevel;
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
GtkRoot *root;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (gst_gtk_base_sink_get_widget (gst_sink) == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* After this point, gtk_sink->widget will always be set */
|
|
||||||
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
root = gtk_widget_get_root (GTK_WIDGET (gst_sink->widget));
|
|
||||||
if (!GTK_IS_ROOT (root)) {
|
|
||||||
GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (gst_sink->widget));
|
|
||||||
if (parent) {
|
|
||||||
GtkWidget *temp_parent;
|
|
||||||
while ((temp_parent = gtk_widget_get_parent (parent)))
|
|
||||||
parent = temp_parent;
|
|
||||||
}
|
|
||||||
toplevel = (parent) ? parent : GTK_WIDGET (gst_sink->widget);
|
|
||||||
#else
|
|
||||||
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gst_sink->widget));
|
|
||||||
if (!gtk_widget_is_toplevel (toplevel)) {
|
|
||||||
#endif
|
|
||||||
/* sanity check */
|
|
||||||
g_assert (klass->window_title);
|
|
||||||
|
|
||||||
/* User did not add widget its own UI, let's popup a new GtkWindow to
|
|
||||||
* make gst-launch-1.0 work. */
|
|
||||||
gst_sink->window = gtk_window_new (
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
GTK_WINDOW_TOPLEVEL
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480);
|
|
||||||
gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title);
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_window_set_child (GTK_WINDOW (
|
|
||||||
#else
|
|
||||||
gtk_container_add (GTK_CONTAINER (
|
|
||||||
#endif
|
|
||||||
gst_sink->window), toplevel);
|
|
||||||
|
|
||||||
gst_sink->window_destroy_id = g_signal_connect (
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
GTK_WINDOW (gst_sink->window),
|
|
||||||
#else
|
|
||||||
gst_sink->window,
|
|
||||||
#endif
|
|
||||||
"destroy", G_CALLBACK (window_destroy_cb), gst_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_start (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
|
||||||
gst_gtk_base_sink_start_on_main, bsink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_stop_on_main (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (gst_sink->window) {
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_window_destroy (GTK_WINDOW (gst_sink->window));
|
|
||||||
#else
|
|
||||||
gtk_widget_destroy (gst_sink->window);
|
|
||||||
#endif
|
|
||||||
gst_sink->window = NULL;
|
|
||||||
gst_sink->widget = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_base_sink_stop (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (gst_sink->window)
|
|
||||||
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
|
||||||
gst_gtk_base_sink_stop_on_main, bsink);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_window_show_all_and_unref (GtkWidget * window)
|
|
||||||
{
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_window_present (GTK_WINDOW (window));
|
|
||||||
#else
|
|
||||||
gtk_widget_show_all (window);
|
|
||||||
#endif
|
|
||||||
g_object_unref (window);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstStateChangeReturn
|
|
||||||
gst_gtk_base_sink_change_state (GstElement * element, GstStateChange transition)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (element);
|
|
||||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
|
|
||||||
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
|
||||||
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
|
||||||
|
|
||||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
||||||
{
|
|
||||||
GtkWindow *window = NULL;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->window)
|
|
||||||
window = g_object_ref (GTK_WINDOW (gtk_sink->window));
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (window) {
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
|
||||||
gst_gtk_window_show_all_and_unref, window);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
if (gtk_sink->widget)
|
|
||||||
gtk_gst_base_widget_set_buffer (gtk_sink->widget, NULL);
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
|
|
||||||
GstClockTime * start, GstClockTime * end)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink;
|
|
||||||
|
|
||||||
gtk_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
|
|
||||||
*start = GST_BUFFER_TIMESTAMP (buf);
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
|
||||||
*end = *start + GST_BUFFER_DURATION (buf);
|
|
||||||
else {
|
|
||||||
if (GST_VIDEO_INFO_FPS_N (>k_sink->v_info) > 0) {
|
|
||||||
*end = *start +
|
|
||||||
gst_util_uint64_scale_int (GST_SECOND,
|
|
||||||
GST_VIDEO_INFO_FPS_D (>k_sink->v_info),
|
|
||||||
GST_VIDEO_INFO_FPS_N (>k_sink->v_info));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gst_gtk_base_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
|
|
||||||
|
|
||||||
if (!gst_video_info_from_caps (>k_sink->v_info, caps))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (gtk_sink->widget == NULL) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
|
|
||||||
("%s", "Output widget was destroyed"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gtk_gst_base_widget_set_format (gtk_sink->widget, >k_sink->v_info)) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_gtk_base_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *gtk_sink;
|
|
||||||
|
|
||||||
GST_TRACE ("rendering buffer:%p", buf);
|
|
||||||
|
|
||||||
gtk_sink = GST_GTK_BASE_SINK (vsink);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (gtk_sink->widget == NULL) {
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
|
|
||||||
("%s", "Output widget was destroyed"), (NULL));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_gst_base_widget_set_buffer (gtk_sink->widget, buf);
|
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 __GST_GTK_BASE_SINK_H__
|
|
||||||
#define __GST_GTK_BASE_SINK_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/gstvideosink.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
#define GST_TYPE_GTK_BASE_SINK (gst_gtk_base_sink_get_type())
|
|
||||||
#define GST_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSink))
|
|
||||||
#define GST_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSinkClass))
|
|
||||||
#define GST_GTK_BASE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GTK_BASE_SINK, GstGtkBaseSinkClass))
|
|
||||||
#define GST_IS_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GTK_BASE_SINK))
|
|
||||||
#define GST_IS_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GTK_BASE_SINK))
|
|
||||||
#define GST_GTK_BASE_SINK_CAST(obj) ((GstGtkBaseSink*)(obj))
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _GstGtkBaseSink GstGtkBaseSink;
|
|
||||||
typedef struct _GstGtkBaseSinkClass GstGtkBaseSinkClass;
|
|
||||||
|
|
||||||
GType gst_gtk_base_sink_get_type (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkBaseSink:
|
|
||||||
*
|
|
||||||
* Opaque #GstGtkBaseSink object
|
|
||||||
*/
|
|
||||||
struct _GstGtkBaseSink
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstVideoSink parent;
|
|
||||||
|
|
||||||
GstVideoInfo v_info;
|
|
||||||
|
|
||||||
GtkGstBaseWidget *widget;
|
|
||||||
|
|
||||||
/* properties */
|
|
||||||
gboolean force_aspect_ratio;
|
|
||||||
GBinding *bind_aspect_ratio;
|
|
||||||
|
|
||||||
gint par_n;
|
|
||||||
gint par_d;
|
|
||||||
GBinding *bind_pixel_aspect_ratio;
|
|
||||||
|
|
||||||
gboolean ignore_alpha;
|
|
||||||
GBinding *bind_ignore_alpha;
|
|
||||||
|
|
||||||
gboolean ignore_textures;
|
|
||||||
GBinding *bind_ignore_textures;
|
|
||||||
|
|
||||||
GtkWidget *window;
|
|
||||||
gulong widget_destroy_id;
|
|
||||||
gulong window_destroy_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkBaseSinkClass:
|
|
||||||
*
|
|
||||||
* The #GstGtkBaseSinkClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GstGtkBaseSinkClass
|
|
||||||
{
|
|
||||||
GstVideoSinkClass object_class;
|
|
||||||
|
|
||||||
/* metadata */
|
|
||||||
const gchar *window_title;
|
|
||||||
|
|
||||||
/* virtuals */
|
|
||||||
GtkWidget* (*create_widget) (void);
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstGtkBaseSink, gst_object_unref)
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_BASE_SINK_H__ */
|
|
@@ -1,336 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:element-gtkglsink
|
|
||||||
* @title: gtkglsink
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:element-gtk4glsink
|
|
||||||
* @title: gtk4glsink
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <gst/gl/gstglfuncs.h>
|
|
||||||
|
|
||||||
#include "gtkconfig.h"
|
|
||||||
#include "gstgtkglsink.h"
|
|
||||||
#include "gtkgstglwidget.h"
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
|
|
||||||
#define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
|
|
||||||
|
|
||||||
static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
|
|
||||||
static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
|
|
||||||
static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
|
|
||||||
GstQuery * query);
|
|
||||||
static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
|
|
||||||
GstCaps * filter);
|
|
||||||
|
|
||||||
static void gst_gtk_gl_sink_finalize (GObject * object);
|
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_gtk_gl_sink_template =
|
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
||||||
GST_PAD_SINK,
|
|
||||||
GST_PAD_ALWAYS,
|
|
||||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
||||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
|
|
||||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
||||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
|
|
||||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
|
|
||||||
|
|
||||||
#define gst_gtk_gl_sink_parent_class parent_class
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
|
|
||||||
GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
|
|
||||||
GTKCONFIG_GLSINK, 0, GTKCONFIG_NAME " GL Video Sink"));
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class;
|
|
||||||
GstElementClass *gstelement_class;
|
|
||||||
GstBaseSinkClass *gstbasesink_class;
|
|
||||||
GstGtkBaseSinkClass *gstgtkbasesink_class;
|
|
||||||
|
|
||||||
gobject_class = (GObjectClass *) klass;
|
|
||||||
gstelement_class = (GstElementClass *) klass;
|
|
||||||
gstbasesink_class = (GstBaseSinkClass *) klass;
|
|
||||||
gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
|
|
||||||
|
|
||||||
gobject_class->finalize = gst_gtk_gl_sink_finalize;
|
|
||||||
|
|
||||||
gstbasesink_class->query = gst_gtk_gl_sink_query;
|
|
||||||
gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
|
|
||||||
gstbasesink_class->start = gst_gtk_gl_sink_start;
|
|
||||||
gstbasesink_class->stop = gst_gtk_gl_sink_stop;
|
|
||||||
gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
|
|
||||||
|
|
||||||
gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
|
|
||||||
gstgtkbasesink_class->window_title = GTKCONFIG_NAME " GL Renderer";
|
|
||||||
|
|
||||||
gst_element_class_set_metadata (gstelement_class,
|
|
||||||
GTKCONFIG_NAME " GL Video Sink",
|
|
||||||
"Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
|
|
||||||
"Matthew Waters <matthew@centricular.com>, "
|
|
||||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
|
||||||
|
|
||||||
gst_element_class_add_static_pad_template (gstelement_class,
|
|
||||||
&gst_gtk_gl_sink_template);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
switch (GST_QUERY_TYPE (query)) {
|
|
||||||
case GST_QUERY_CONTEXT:
|
|
||||||
{
|
|
||||||
if (gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
|
|
||||||
gtk_sink->display, gtk_sink->context, gtk_sink->gtk_context))
|
|
||||||
return TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
destroy_cb (GtkWidget * widget, GstGtkGLSink * gtk_sink)
|
|
||||||
{
|
|
||||||
if (gtk_sink->widget_destroy_sig_handler) {
|
|
||||||
g_signal_handler_disconnect (widget, gtk_sink->widget_destroy_sig_handler);
|
|
||||||
gtk_sink->widget_destroy_sig_handler = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_start (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
GtkGstGLWidget *gst_widget;
|
|
||||||
|
|
||||||
if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* After this point, gtk_sink->widget will always be set */
|
|
||||||
gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
|
|
||||||
|
|
||||||
if (!gtk_sink->widget_destroy_sig_handler) {
|
|
||||||
gtk_sink->widget_destroy_sig_handler =
|
|
||||||
g_signal_connect (gst_widget, "destroy", G_CALLBACK (destroy_cb),
|
|
||||||
gtk_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gtk_gst_gl_widget_init_winsys (gst_widget)) {
|
|
||||||
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
|
|
||||||
"Failed to initialize OpenGL with GTK"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gtk_sink->display)
|
|
||||||
gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
|
|
||||||
if (!gtk_sink->context)
|
|
||||||
gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
|
|
||||||
if (!gtk_sink->gtk_context)
|
|
||||||
gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
|
|
||||||
|
|
||||||
if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context) {
|
|
||||||
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
|
|
||||||
"Failed to retrieve OpenGL context from GTK"), (NULL));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
|
|
||||||
gtk_sink->display);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_stop (GstBaseSink * bsink)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
|
|
||||||
|
|
||||||
if (gtk_sink->display) {
|
|
||||||
gst_object_unref (gtk_sink->display);
|
|
||||||
gtk_sink->display = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtk_sink->context) {
|
|
||||||
gst_object_unref (gtk_sink->context);
|
|
||||||
gtk_sink->context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtk_sink->gtk_context) {
|
|
||||||
gst_object_unref (gtk_sink->gtk_context);
|
|
||||||
gtk_sink->gtk_context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
|
|
||||||
GstBufferPool *pool = NULL;
|
|
||||||
GstStructure *config;
|
|
||||||
GstCaps *caps;
|
|
||||||
GstVideoInfo info;
|
|
||||||
guint size;
|
|
||||||
gboolean need_pool;
|
|
||||||
GstStructure *allocation_meta = NULL;
|
|
||||||
gint display_width, display_height;
|
|
||||||
|
|
||||||
if (!gtk_sink->display || !gtk_sink->context)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
|
||||||
|
|
||||||
if (caps == NULL)
|
|
||||||
goto no_caps;
|
|
||||||
|
|
||||||
if (!gst_video_info_from_caps (&info, caps))
|
|
||||||
goto invalid_caps;
|
|
||||||
|
|
||||||
/* the normal size of a frame */
|
|
||||||
size = info.size;
|
|
||||||
|
|
||||||
if (need_pool) {
|
|
||||||
GST_DEBUG_OBJECT (gtk_sink, "create new pool");
|
|
||||||
pool = gst_gl_buffer_pool_new (gtk_sink->context);
|
|
||||||
|
|
||||||
config = gst_buffer_pool_get_config (pool);
|
|
||||||
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
|
|
||||||
gst_buffer_pool_config_add_option (config,
|
|
||||||
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
|
||||||
|
|
||||||
if (!gst_buffer_pool_set_config (pool, config))
|
|
||||||
goto config_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we need at least 2 buffer because we hold on to the last one */
|
|
||||||
gst_query_add_allocation_pool (query, pool, size, 2, 0);
|
|
||||||
if (pool)
|
|
||||||
gst_object_unref (pool);
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (gtk_sink);
|
|
||||||
display_width = gtk_sink->display_width;
|
|
||||||
display_height = gtk_sink->display_height;
|
|
||||||
GST_OBJECT_UNLOCK (gtk_sink);
|
|
||||||
|
|
||||||
if (display_width != 0 && display_height != 0) {
|
|
||||||
GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
|
|
||||||
display_width, display_height);
|
|
||||||
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
|
|
||||||
"width", G_TYPE_UINT, display_width,
|
|
||||||
"height", G_TYPE_UINT, display_height, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_query_add_allocation_meta (query,
|
|
||||||
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
|
|
||||||
|
|
||||||
if (allocation_meta)
|
|
||||||
gst_structure_free (allocation_meta);
|
|
||||||
|
|
||||||
/* we also support various metadata */
|
|
||||||
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
|
|
||||||
|
|
||||||
if (gtk_sink->context->gl_vtable->FenceSync)
|
|
||||||
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
no_caps:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "no caps specified");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
invalid_caps:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "invalid caps specified");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
config_failed:
|
|
||||||
{
|
|
||||||
GST_DEBUG_OBJECT (bsink, "failed setting config");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GstCaps *
|
|
||||||
gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
|
|
||||||
{
|
|
||||||
GstCaps *tmp = NULL;
|
|
||||||
GstCaps *result = NULL;
|
|
||||||
|
|
||||||
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
|
|
||||||
filter);
|
|
||||||
|
|
||||||
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
|
||||||
gst_caps_unref (tmp);
|
|
||||||
} else {
|
|
||||||
result = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = gst_gl_overlay_compositor_add_caps (result);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_gtk_gl_sink_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);
|
|
||||||
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (object);
|
|
||||||
|
|
||||||
if (gtk_sink->widget_destroy_sig_handler) {
|
|
||||||
g_signal_handler_disconnect (base_sink->widget,
|
|
||||||
gtk_sink->widget_destroy_sig_handler);
|
|
||||||
gtk_sink->widget_destroy_sig_handler = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
||||||
}
|
|
@@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 __GST_GTK_GL_SINK_H__
|
|
||||||
#define __GST_GTK_GL_SINK_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/video/gstvideosink.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
|
|
||||||
#include "gstgtkbasesink.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define GST_TYPE_GTK_GL_SINK (gst_gtk_gl_sink_get_type ())
|
|
||||||
G_DECLARE_FINAL_TYPE (GstGtkGLSink, gst_gtk_gl_sink, GST, GTK_GL_SINK,
|
|
||||||
GstGtkBaseSink);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GstGtkGLSink:
|
|
||||||
*
|
|
||||||
* Opaque #GstGtkGLSink object
|
|
||||||
*/
|
|
||||||
struct _GstGtkGLSink
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GstGtkBaseSink parent;
|
|
||||||
|
|
||||||
GstGLDisplay *display;
|
|
||||||
GstGLContext *context;
|
|
||||||
GstGLContext *gtk_context;
|
|
||||||
|
|
||||||
GstGLUpload *upload;
|
|
||||||
GstBuffer *uploaded_buffer;
|
|
||||||
|
|
||||||
/* read/write with object lock */
|
|
||||||
gint display_width;
|
|
||||||
gint display_height;
|
|
||||||
|
|
||||||
gulong widget_destroy_sig_handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GST_GTK_GL_SINK_H__ */
|
|
1083
lib/gst/clapper/gtk4/gtkclapperglwidget.c
Normal file
1083
lib/gst/clapper/gtk4/gtkclapperglwidget.c
Normal file
File diff suppressed because it is too large
Load Diff
107
lib/gst/clapper/gtk4/gtkclapperglwidget.h
Normal file
107
lib/gst/clapper/gtk4/gtkclapperglwidget.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||||
|
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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_CLAPPER_GL_WIDGET_H__
|
||||||
|
#define __GTK_CLAPPER_GL_WIDGET_H__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/gl/gl.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
GType gtk_clapper_gl_widget_get_type (void);
|
||||||
|
#define GTK_TYPE_CLAPPER_GL_WIDGET (gtk_clapper_gl_widget_get_type())
|
||||||
|
#define GTK_CLAPPER_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_CLAPPER_GL_WIDGET,GtkClapperGLWidget))
|
||||||
|
#define GTK_CLAPPER_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_CLAPPER_GL_WIDGET,GtkClapperGLWidgetClass))
|
||||||
|
#define GTK_IS_CLAPPER_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_CLAPPER_GL_WIDGET))
|
||||||
|
#define GTK_IS_CLAPPER_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_CLAPPER_GL_WIDGET))
|
||||||
|
#define GTK_CLAPPER_GL_WIDGET_CAST(obj) ((GtkClapperGLWidget*)(obj))
|
||||||
|
#define GTK_CLAPPER_GL_WIDGET_LOCK(w) g_mutex_lock(&((GtkClapperGLWidget*)(w))->lock)
|
||||||
|
#define GTK_CLAPPER_GL_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkClapperGLWidget*)(w))->lock)
|
||||||
|
|
||||||
|
typedef struct _GtkClapperGLWidget GtkClapperGLWidget;
|
||||||
|
typedef struct _GtkClapperGLWidgetClass GtkClapperGLWidgetClass;
|
||||||
|
typedef struct _GtkClapperGLWidgetPrivate GtkClapperGLWidgetPrivate;
|
||||||
|
|
||||||
|
struct _GtkClapperGLWidget
|
||||||
|
{
|
||||||
|
/* <private> */
|
||||||
|
GtkGLArea parent;
|
||||||
|
GtkClapperGLWidgetPrivate *priv;
|
||||||
|
|
||||||
|
/* properties */
|
||||||
|
gboolean force_aspect_ratio;
|
||||||
|
gint par_n, par_d;
|
||||||
|
gboolean ignore_textures;
|
||||||
|
|
||||||
|
gint display_width;
|
||||||
|
gint display_height;
|
||||||
|
|
||||||
|
/* Widget dimensions */
|
||||||
|
gint scaled_width;
|
||||||
|
gint scaled_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;
|
||||||
|
|
||||||
|
/* event controllers */
|
||||||
|
GtkEventController *key_controller;
|
||||||
|
GtkEventController *motion_controller;
|
||||||
|
GtkGesture *click_gesture;
|
||||||
|
|
||||||
|
/* Pending draw idles callback */
|
||||||
|
guint draw_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GtkClapperGLWidgetClass
|
||||||
|
{
|
||||||
|
GtkGLAreaClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* API */
|
||||||
|
gboolean gtk_clapper_gl_widget_set_format (GtkClapperGLWidget * widget, GstVideoInfo * v_info);
|
||||||
|
void gtk_clapper_gl_widget_set_buffer (GtkClapperGLWidget * widget, GstBuffer * buffer);
|
||||||
|
void gtk_clapper_gl_widget_set_element (GtkClapperGLWidget * widget, GstElement * element);
|
||||||
|
|
||||||
|
GtkWidget * gtk_clapper_gl_widget_new (void);
|
||||||
|
|
||||||
|
gboolean gtk_clapper_gl_widget_init_winsys (GtkClapperGLWidget * widget);
|
||||||
|
GstGLDisplay * gtk_clapper_gl_widget_get_display (GtkClapperGLWidget * widget);
|
||||||
|
GstGLContext * gtk_clapper_gl_widget_get_context (GtkClapperGLWidget * widget);
|
||||||
|
GstGLContext * gtk_clapper_gl_widget_get_gtk_context (GtkClapperGLWidget * widget);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GTK_CLAPPER_GL_WIDGET_H__ */
|
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
#define GTKCONFIG_PLUGIN gtk4
|
|
||||||
#define GTKCONFIG_NAME "GTK4"
|
|
||||||
#define GTKCONFIG_SINK "gtk4sink"
|
|
||||||
#define GTKCONFIG_GLSINK "gtk4glsink"
|
|
||||||
#else
|
|
||||||
#define GTKCONFIG_PLUGIN gtk
|
|
||||||
#define GTKCONFIG_NAME "GTK"
|
|
||||||
#define GTKCONFIG_SINK "gtksink"
|
|
||||||
#define GTKCONFIG_GLSINK "gtkglsink"
|
|
||||||
#endif
|
|
@@ -1,619 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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
|
|
||||||
#define DEFAULT_IGNORE_TEXTURES FALSE
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
PROP_FORCE_ASPECT_RATIO,
|
|
||||||
PROP_PIXEL_ASPECT_RATIO,
|
|
||||||
PROP_IGNORE_ALPHA,
|
|
||||||
PROP_IGNORE_TEXTURES,
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
static void
|
|
||||||
gtk_gst_base_widget_measure (GtkWidget * widget, GtkOrientation orientation,
|
|
||||||
gint for_size, gint * min, gint * natural,
|
|
||||||
gint * minimum_baseline, gint * natural_baseline)
|
|
||||||
{
|
|
||||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
|
||||||
gtk_gst_base_widget_get_preferred_width (widget, min, natural);
|
|
||||||
else
|
|
||||||
gtk_gst_base_widget_get_preferred_height (widget, min, natural);
|
|
||||||
|
|
||||||
*minimum_baseline = -1;
|
|
||||||
*natural_baseline = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
case PROP_IGNORE_TEXTURES:
|
|
||||||
gtk_widget->ignore_textures = 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;
|
|
||||||
case PROP_IGNORE_TEXTURES:
|
|
||||||
g_value_set_boolean (value, gtk_widget->ignore_textures);
|
|
||||||
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 GdkEvent *
|
|
||||||
_get_current_event (GtkEventController * controller)
|
|
||||||
{
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
return gtk_event_controller_get_current_event (controller);
|
|
||||||
#else
|
|
||||||
return gtk_get_current_event ();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_gdk_event_free (GdkEvent * event)
|
|
||||||
{
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
if (event)
|
|
||||||
gdk_event_free (event);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_base_widget_key_event (GtkEventControllerKey * key_controller,
|
|
||||||
guint keyval, guint keycode, GdkModifierType state)
|
|
||||||
{
|
|
||||||
GtkEventController *controller = GTK_EVENT_CONTROLLER (key_controller);
|
|
||||||
GtkWidget *widget = gtk_event_controller_get_widget (controller);
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
||||||
if (GST_IS_NAVIGATION (element)) {
|
|
||||||
GdkEvent *event = _get_current_event (controller);
|
|
||||||
const gchar *str = _gdk_key_to_navigation_string (keyval);
|
|
||||||
|
|
||||||
if (str) {
|
|
||||||
const gchar *key_type =
|
|
||||||
gdk_event_get_event_type (event) ==
|
|
||||||
GDK_KEY_PRESS ? "key-press" : "key-release";
|
|
||||||
gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
|
|
||||||
}
|
|
||||||
_gdk_event_free (event);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_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 (
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
#else
|
|
||||||
GtkGestureMultiPress * gesture,
|
|
||||||
#endif
|
|
||||||
gint n_press, gdouble x, gdouble y)
|
|
||||||
{
|
|
||||||
GtkEventController *controller = GTK_EVENT_CONTROLLER (gesture);
|
|
||||||
GtkWidget *widget = gtk_event_controller_get_widget (controller);
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
GstElement *element;
|
|
||||||
|
|
||||||
if ((element = g_weak_ref_get (&base_widget->element))) {
|
|
||||||
if (GST_IS_NAVIGATION (element)) {
|
|
||||||
GdkEvent *event = _get_current_event (controller);
|
|
||||||
const gchar *key_type =
|
|
||||||
gdk_event_get_event_type (event) == GDK_BUTTON_PRESS
|
|
||||||
? "mouse-button-press" : "mouse-button-release";
|
|
||||||
gdouble stream_x, stream_y;
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
guint button;
|
|
||||||
gdk_event_get_button (event, &button);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y);
|
|
||||||
|
|
||||||
gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
/* Gesture is set to ignore other buttons so we do not have to check */
|
|
||||||
GDK_BUTTON_PRIMARY,
|
|
||||||
#else
|
|
||||||
button,
|
|
||||||
#endif
|
|
||||||
stream_x, stream_y);
|
|
||||||
|
|
||||||
_gdk_event_free (event);
|
|
||||||
}
|
|
||||||
g_object_unref (element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_base_widget_motion_event (GtkEventControllerMotion * motion_controller,
|
|
||||||
gdouble x, gdouble y)
|
|
||||||
{
|
|
||||||
GtkEventController *controller = GTK_EVENT_CONTROLLER (motion_controller);
|
|
||||||
GtkWidget *widget = gtk_event_controller_get_widget (controller);
|
|
||||||
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 stream_x, stream_y;
|
|
||||||
|
|
||||||
_display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y);
|
|
||||||
|
|
||||||
gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
|
|
||||||
0, stream_x, stream_y);
|
|
||||||
}
|
|
||||||
g_object_unref (element);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_klass, PROP_IGNORE_TEXTURES,
|
|
||||||
g_param_spec_boolean ("ignore-textures", "Ignore Textures",
|
|
||||||
"When enabled, textures will be ignored and not drawn",
|
|
||||||
DEFAULT_IGNORE_TEXTURES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
||||||
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
widget_klass->measure = gtk_gst_base_widget_measure;
|
|
||||||
#else
|
|
||||||
widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
|
|
||||||
widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0,
|
|
||||||
"GTK Video Base Widget");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
widget->ignore_textures = DEFAULT_IGNORE_TEXTURES;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
widget->key_controller = gtk_event_controller_key_new (
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
GTK_WIDGET (widget)
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
g_signal_connect (widget->key_controller, "key-pressed",
|
|
||||||
G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
|
|
||||||
g_signal_connect (widget->key_controller, "key-released",
|
|
||||||
G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
|
|
||||||
|
|
||||||
widget->motion_controller = gtk_event_controller_motion_new (
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
GTK_WIDGET (widget)
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
g_signal_connect (widget->motion_controller, "motion",
|
|
||||||
G_CALLBACK (gtk_gst_base_widget_motion_event), NULL);
|
|
||||||
|
|
||||||
widget->click_gesture =
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_gesture_click_new ();
|
|
||||||
#else
|
|
||||||
gtk_gesture_multi_press_new (GTK_WIDGET (widget));
|
|
||||||
#endif
|
|
||||||
g_signal_connect (widget->click_gesture, "pressed",
|
|
||||||
G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
|
|
||||||
g_signal_connect (widget->click_gesture, "released",
|
|
||||||
G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
|
|
||||||
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
/* Otherwise widget in grid will appear as a 1x1px
|
|
||||||
* video which might be misleading for users */
|
|
||||||
gtk_widget_set_hexpand (GTK_WIDGET (widget), TRUE);
|
|
||||||
gtk_widget_set_vexpand (GTK_WIDGET (widget), TRUE);
|
|
||||||
|
|
||||||
gtk_widget_set_focusable (GTK_WIDGET (widget), TRUE);
|
|
||||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (widget->click_gesture),
|
|
||||||
GDK_BUTTON_PRIMARY);
|
|
||||||
|
|
||||||
gtk_widget_add_controller (GTK_WIDGET (widget), widget->key_controller);
|
|
||||||
gtk_widget_add_controller (GTK_WIDGET (widget), widget->motion_controller);
|
|
||||||
gtk_widget_add_controller (GTK_WIDGET (widget),
|
|
||||||
GTK_EVENT_CONTROLLER (widget->click_gesture));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gtk_gst_base_widget_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
/* GTK4 takes ownership of EventControllers
|
|
||||||
* while GTK3 still needs manual unref */
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
g_object_unref (widget->key_controller);
|
|
||||||
g_object_unref (widget->motion_controller);
|
|
||||||
g_object_unref (widget->click_gesture);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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>
|
|
||||||
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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 {
|
|
||||||
GtkGLArea gl_area;
|
|
||||||
} parent;
|
|
||||||
|
|
||||||
/* properties */
|
|
||||||
gboolean force_aspect_ratio;
|
|
||||||
gint par_n, par_d;
|
|
||||||
gboolean ignore_alpha;
|
|
||||||
gboolean ignore_textures;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* event controllers */
|
|
||||||
GtkEventController *key_controller;
|
|
||||||
GtkEventController *motion_controller;
|
|
||||||
GtkGesture *click_gesture;
|
|
||||||
|
|
||||||
/* Pending draw idles callback */
|
|
||||||
guint draw_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _GtkGstBaseWidgetClass
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
GtkGLAreaClass gl_area_class;
|
|
||||||
} 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);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GTK_GST_BASE_WIDGET_H__ */
|
|
@@ -1,604 +0,0 @@
|
|||||||
/*
|
|
||||||
* GStreamer
|
|
||||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
|
||||||
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.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 "gtkgstglwidget.h"
|
|
||||||
#include "gstgtkutils.h"
|
|
||||||
#include <gst/gl/gstglfuncs.h>
|
|
||||||
#include <gst/video/video.h>
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
#include <gdk/x11/gdkx.h>
|
|
||||||
#else
|
|
||||||
#include <gdk/gdkx.h>
|
|
||||||
#endif
|
|
||||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
#if defined(BUILD_FOR_GTK4)
|
|
||||||
#include <gdk/wayland/gdkwayland.h>
|
|
||||||
#else
|
|
||||||
#include <gdk/gdkwayland.h>
|
|
||||||
#endif
|
|
||||||
#include <gst/gl/wayland/gstgldisplay_wayland.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:gtkgstglwidget
|
|
||||||
* @title: GtkGstGlWidget
|
|
||||||
* @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers
|
|
||||||
* @see_also: #GtkGLArea, #GstBuffer
|
|
||||||
*
|
|
||||||
* #GtkGstGLWidget is an #GtkWidget that renders GStreamer video buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define GST_CAT_DEFAULT gtk_gst_gl_widget_debug
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
|
||||||
|
|
||||||
struct _GtkGstGLWidgetPrivate
|
|
||||||
{
|
|
||||||
gboolean initiated;
|
|
||||||
GstGLDisplay *display;
|
|
||||||
GdkGLContext *gdk_context;
|
|
||||||
GstGLContext *other_context;
|
|
||||||
GstGLContext *context;
|
|
||||||
GstGLUpload *upload;
|
|
||||||
GstGLShader *shader;
|
|
||||||
GLuint vao;
|
|
||||||
GLuint vertex_buffer;
|
|
||||||
GLint attr_position;
|
|
||||||
GLint attr_texture;
|
|
||||||
GLuint current_tex;
|
|
||||||
GstGLOverlayCompositor *overlay_compositor;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const GLfloat vertices[] = {
|
|
||||||
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
|
||||||
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
|
||||||
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA,
|
|
||||||
G_ADD_PRIVATE (GtkGstGLWidget)
|
|
||||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkgstglwidget", 0,
|
|
||||||
"GTK Gst GL Widget"));
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_bind_buffer (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
|
|
||||||
|
|
||||||
/* Load the vertex position */
|
|
||||||
gl->VertexAttribPointer (priv->attr_position, 3, GL_FLOAT, GL_FALSE,
|
|
||||||
5 * sizeof (GLfloat), (void *) 0);
|
|
||||||
|
|
||||||
/* Load the texture coordinate */
|
|
||||||
gl->VertexAttribPointer (priv->attr_texture, 2, GL_FLOAT, GL_FALSE,
|
|
||||||
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
|
|
||||||
|
|
||||||
gl->EnableVertexAttribArray (priv->attr_position);
|
|
||||||
gl->EnableVertexAttribArray (priv->attr_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_unbind_buffer (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
gl->DisableVertexAttribArray (priv->attr_position);
|
|
||||||
gl->DisableVertexAttribArray (priv->attr_texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_init_redisplay (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "initializing redisplay");
|
|
||||||
if (!(priv->shader = gst_gl_shader_new_default (priv->context, &error))) {
|
|
||||||
GST_ERROR ("Failed to initialize shader: %s", error->message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->attr_position =
|
|
||||||
gst_gl_shader_get_attribute_location (priv->shader, "a_position");
|
|
||||||
priv->attr_texture =
|
|
||||||
gst_gl_shader_get_attribute_location (priv->shader, "a_texcoord");
|
|
||||||
|
|
||||||
if (gl->GenVertexArrays) {
|
|
||||||
gl->GenVertexArrays (1, &priv->vao);
|
|
||||||
gl->BindVertexArray (priv->vao);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl->GenBuffers (1, &priv->vertex_buffer);
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
|
|
||||||
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
|
|
||||||
GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
if (gl->GenVertexArrays) {
|
|
||||||
gtk_gst_gl_widget_bind_buffer (gst_widget);
|
|
||||||
gl->BindVertexArray (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
|
||||||
|
|
||||||
priv->overlay_compositor =
|
|
||||||
gst_gl_overlay_compositor_new (priv->other_context);
|
|
||||||
|
|
||||||
priv->initiated = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_redraw_texture (GtkGstGLWidget * gst_widget, guint tex)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
|
||||||
const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
|
|
||||||
|
|
||||||
if (gst_widget->base.force_aspect_ratio) {
|
|
||||||
GstVideoRectangle src, dst, result;
|
|
||||||
gint widget_width, widget_height, widget_scale;
|
|
||||||
|
|
||||||
gl->ClearColor (0.0, 0.0, 0.0, 1.0);
|
|
||||||
gl->Clear (GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
widget_scale = gtk_widget_get_scale_factor ((GtkWidget *) gst_widget);
|
|
||||||
widget_width = gtk_widget_get_allocated_width ((GtkWidget *) gst_widget);
|
|
||||||
widget_height = gtk_widget_get_allocated_height ((GtkWidget *) gst_widget);
|
|
||||||
|
|
||||||
src.x = 0;
|
|
||||||
src.y = 0;
|
|
||||||
src.w = gst_widget->base.display_width;
|
|
||||||
src.h = gst_widget->base.display_height;
|
|
||||||
|
|
||||||
dst.x = 0;
|
|
||||||
dst.y = 0;
|
|
||||||
dst.w = widget_width * widget_scale;
|
|
||||||
dst.h = widget_height * widget_scale;
|
|
||||||
|
|
||||||
gst_video_sink_center_rect (src, dst, &result, TRUE);
|
|
||||||
|
|
||||||
gl->Viewport (result.x, result.y, result.w, result.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_gl_shader_use (priv->shader);
|
|
||||||
|
|
||||||
if (gl->BindVertexArray)
|
|
||||||
gl->BindVertexArray (priv->vao);
|
|
||||||
gtk_gst_gl_widget_bind_buffer (gst_widget);
|
|
||||||
|
|
||||||
gl->ActiveTexture (GL_TEXTURE0);
|
|
||||||
gl->BindTexture (GL_TEXTURE_2D, tex);
|
|
||||||
gst_gl_shader_set_uniform_1i (priv->shader, "tex", 0);
|
|
||||||
|
|
||||||
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
|
||||||
|
|
||||||
if (gl->BindVertexArray)
|
|
||||||
gl->BindVertexArray (0);
|
|
||||||
else
|
|
||||||
gtk_gst_gl_widget_unbind_buffer (gst_widget);
|
|
||||||
|
|
||||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
_draw_black (GstGLContext * context)
|
|
||||||
{
|
|
||||||
const GstGLFuncs *gl = context->gl_vtable;
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (context, "rendering black");
|
|
||||||
gl->ClearColor (0.0, 0.0, 0.0, 1.0);
|
|
||||||
gl->Clear (GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
_draw_black_with_gdk (GdkGLContext * gdk_context)
|
|
||||||
{
|
|
||||||
GST_DEBUG ("rendering empty frame with gdk context %p", gdk_context);
|
|
||||||
glClearColor (0.0, 0.0, 0.0, 1.0);
|
|
||||||
glClear (GL_COLOR_BUFFER_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_gst_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (widget)->priv;
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (widget);
|
|
||||||
|
|
||||||
/* Draw black with GDK context when priv is not available yet.
|
|
||||||
GTK calls render with GDK context already active. */
|
|
||||||
if (!priv->context || !priv->other_context || base_widget->ignore_textures) {
|
|
||||||
_draw_black_with_gdk (context);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
|
|
||||||
if (!priv->initiated || !base_widget->negotiated) {
|
|
||||||
if (!priv->initiated)
|
|
||||||
gtk_gst_gl_widget_init_redisplay (GTK_GST_GL_WIDGET (widget));
|
|
||||||
|
|
||||||
_draw_black (priv->other_context);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Upload latest buffer */
|
|
||||||
if (base_widget->pending_buffer) {
|
|
||||||
GstBuffer *buffer = base_widget->pending_buffer;
|
|
||||||
GstVideoFrame gl_frame;
|
|
||||||
GstGLSyncMeta *sync_meta;
|
|
||||||
|
|
||||||
if (!gst_video_frame_map (&gl_frame, &base_widget->v_info, buffer,
|
|
||||||
GST_MAP_READ | GST_MAP_GL)) {
|
|
||||||
_draw_black (priv->other_context);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->current_tex = *(guint *) gl_frame.data[0];
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "redrawing texture %u",
|
|
||||||
priv->current_tex);
|
|
||||||
|
|
||||||
gst_gl_overlay_compositor_upload_overlays (priv->overlay_compositor,
|
|
||||||
buffer);
|
|
||||||
|
|
||||||
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
|
|
||||||
if (sync_meta) {
|
|
||||||
/* XXX: the set_sync() seems to be needed for resizing */
|
|
||||||
gst_gl_sync_meta_set_sync_point (sync_meta, priv->context);
|
|
||||||
gst_gl_sync_meta_wait (sync_meta, priv->other_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_video_frame_unmap (&gl_frame);
|
|
||||||
|
|
||||||
if (base_widget->buffer)
|
|
||||||
gst_buffer_unref (base_widget->buffer);
|
|
||||||
|
|
||||||
/* Keep the buffer to ensure current_tex stay valid */
|
|
||||||
base_widget->buffer = buffer;
|
|
||||||
base_widget->pending_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG ("rendering buffer %p with gdk context %p",
|
|
||||||
base_widget->buffer, context);
|
|
||||||
|
|
||||||
_redraw_texture (GTK_GST_GL_WIDGET (widget), priv->current_tex);
|
|
||||||
gst_gl_overlay_compositor_draw_overlays (priv->overlay_compositor);
|
|
||||||
|
|
||||||
gst_gl_insert_debug_marker (priv->other_context, "texture %u redrawn",
|
|
||||||
priv->current_tex);
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_reset_gl (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
const GstGLFuncs *gl = priv->other_context->gl_vtable;
|
|
||||||
|
|
||||||
if (!priv->gdk_context)
|
|
||||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (gst_widget));
|
|
||||||
|
|
||||||
if (priv->gdk_context == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gdk_gl_context_make_current (priv->gdk_context);
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
|
|
||||||
if (priv->vao) {
|
|
||||||
gl->DeleteVertexArrays (1, &priv->vao);
|
|
||||||
priv->vao = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->vertex_buffer) {
|
|
||||||
gl->DeleteBuffers (1, &priv->vertex_buffer);
|
|
||||||
priv->vertex_buffer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->upload) {
|
|
||||||
gst_object_unref (priv->upload);
|
|
||||||
priv->upload = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->shader) {
|
|
||||||
gst_object_unref (priv->shader);
|
|
||||||
priv->shader = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->overlay_compositor)
|
|
||||||
gst_object_unref (priv->overlay_compositor);
|
|
||||||
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
|
|
||||||
gst_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
|
|
||||||
gdk_gl_context_clear_current ();
|
|
||||||
|
|
||||||
g_object_unref (priv->gdk_context);
|
|
||||||
priv->gdk_context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_finalize (GObject * object)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (object)->priv;
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (object);
|
|
||||||
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) _reset_gl, base_widget);
|
|
||||||
|
|
||||||
if (priv->context)
|
|
||||||
gst_object_unref (priv->context);
|
|
||||||
|
|
||||||
if (priv->display)
|
|
||||||
gst_object_unref (priv->display);
|
|
||||||
|
|
||||||
gtk_gst_base_widget_finalize (object);
|
|
||||||
G_OBJECT_CLASS (gtk_gst_gl_widget_parent_class)->finalize (object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_class_init (GtkGstGLWidgetClass * klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_klass = (GObjectClass *) klass;
|
|
||||||
GtkGLAreaClass *gl_widget_klass = (GtkGLAreaClass *) klass;
|
|
||||||
|
|
||||||
gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
|
|
||||||
|
|
||||||
gobject_klass->finalize = gtk_gst_gl_widget_finalize;
|
|
||||||
gl_widget_klass->render = gtk_gst_gl_widget_render;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
gtk_gst_gl_widget_init (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (gst_widget);
|
|
||||||
GdkDisplay *display;
|
|
||||||
GtkGstGLWidgetPrivate *priv;
|
|
||||||
|
|
||||||
gtk_gst_base_widget_init (base_widget);
|
|
||||||
|
|
||||||
gst_widget->priv = priv = gtk_gst_gl_widget_get_instance_private (gst_widget);
|
|
||||||
|
|
||||||
display = gdk_display_get_default ();
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
|
||||||
if (GDK_IS_X11_DISPLAY (display)) {
|
|
||||||
priv->display = (GstGLDisplay *)
|
|
||||||
gst_gl_display_x11_new_with_display (gdk_x11_display_get_xdisplay
|
|
||||||
(display));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
if (GDK_IS_WAYLAND_DISPLAY (display)) {
|
|
||||||
struct wl_display *wayland_display =
|
|
||||||
gdk_wayland_display_get_wl_display (display);
|
|
||||||
priv->display = (GstGLDisplay *)
|
|
||||||
gst_gl_display_wayland_new_with_display (wayland_display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
(void) display;
|
|
||||||
|
|
||||||
if (!priv->display)
|
|
||||||
priv->display = gst_gl_display_new ();
|
|
||||||
|
|
||||||
GST_INFO ("Created %" GST_PTR_FORMAT, priv->display);
|
|
||||||
|
|
||||||
/* GTK4 always has alpha */
|
|
||||||
#if !defined(BUILD_FOR_GTK4)
|
|
||||||
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gst_widget),
|
|
||||||
!base_widget->ignore_alpha);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_get_gl_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
|
|
||||||
GstGLAPI gl_api = GST_GL_API_NONE;
|
|
||||||
guintptr gl_handle = 0;
|
|
||||||
|
|
||||||
gtk_widget_realize (GTK_WIDGET (gst_widget));
|
|
||||||
|
|
||||||
if (priv->other_context)
|
|
||||||
gst_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
|
|
||||||
if (priv->gdk_context)
|
|
||||||
g_object_unref (priv->gdk_context);
|
|
||||||
|
|
||||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (gst_widget));
|
|
||||||
if (priv->gdk_context == NULL) {
|
|
||||||
GError *error = gtk_gl_area_get_error (GTK_GL_AREA (gst_widget));
|
|
||||||
|
|
||||||
GST_ERROR_OBJECT (gst_widget, "Error creating GdkGLContext : %s",
|
|
||||||
error ? error->message : "No error set by Gdk");
|
|
||||||
g_clear_error (&error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_ref (priv->gdk_context);
|
|
||||||
|
|
||||||
gdk_gl_context_make_current (priv->gdk_context);
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
|
||||||
if (GST_IS_GL_DISPLAY_X11 (priv->display)) {
|
|
||||||
#if GST_GL_HAVE_PLATFORM_GLX
|
|
||||||
if (!gl_handle) {
|
|
||||||
platform = GST_GL_PLATFORM_GLX;
|
|
||||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if GST_GL_HAVE_PLATFORM_EGL
|
|
||||||
if (!gl_handle) {
|
|
||||||
platform = GST_GL_PLATFORM_EGL;
|
|
||||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (gl_handle) {
|
|
||||||
gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
|
|
||||||
priv->other_context =
|
|
||||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
|
||||||
platform, gl_api);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
|
||||||
if (GST_IS_GL_DISPLAY_WAYLAND (priv->display)) {
|
|
||||||
platform = GST_GL_PLATFORM_EGL;
|
|
||||||
gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
|
|
||||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
|
||||||
if (gl_handle)
|
|
||||||
priv->other_context =
|
|
||||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
|
||||||
platform, gl_api);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
(void) platform;
|
|
||||||
(void) gl_api;
|
|
||||||
(void) gl_handle;
|
|
||||||
|
|
||||||
if (priv->other_context) {
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
GST_INFO ("Retrieved Gdk OpenGL context %" GST_PTR_FORMAT,
|
|
||||||
priv->other_context);
|
|
||||||
gst_gl_context_activate (priv->other_context, TRUE);
|
|
||||||
if (!gst_gl_context_fill_info (priv->other_context, &error)) {
|
|
||||||
GST_ERROR ("failed to retrieve gdk context info: %s", error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
g_object_unref (priv->other_context);
|
|
||||||
priv->other_context = NULL;
|
|
||||||
} else {
|
|
||||||
gst_gl_context_activate (priv->other_context, FALSE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GST_WARNING ("Could not retrieve Gdk OpenGL context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *
|
|
||||||
gtk_gst_gl_widget_new (void)
|
|
||||||
{
|
|
||||||
return (GtkWidget *) g_object_new (GTK_TYPE_GST_GL_WIDGET, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
g_return_val_if_fail (GTK_IS_GST_GL_WIDGET (gst_widget), FALSE);
|
|
||||||
g_return_val_if_fail (priv->display != NULL, FALSE);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
|
|
||||||
|
|
||||||
if (priv->display && priv->gdk_context && priv->other_context) {
|
|
||||||
GST_TRACE ("have already initialized contexts");
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!priv->other_context) {
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) _get_gl_context, gst_widget);
|
|
||||||
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GST_IS_GL_CONTEXT (priv->other_context)) {
|
|
||||||
GST_FIXME ("Could not retrieve Gdk OpenGL context");
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_OBJECT_LOCK (priv->display);
|
|
||||||
if (!gst_gl_display_create_context (priv->display, priv->other_context,
|
|
||||||
&priv->context, &error)) {
|
|
||||||
GST_WARNING ("Could not create OpenGL context: %s",
|
|
||||||
error ? error->message : "Unknown");
|
|
||||||
g_clear_error (&error);
|
|
||||||
GST_OBJECT_UNLOCK (priv->display);
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
gst_gl_display_add_context (priv->display, priv->context);
|
|
||||||
GST_OBJECT_UNLOCK (priv->display);
|
|
||||||
|
|
||||||
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLContext *
|
|
||||||
gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->other_context)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->other_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLContext *
|
|
||||||
gtk_gst_gl_widget_get_context (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->context)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
GstGLDisplay *
|
|
||||||
gtk_gst_gl_widget_get_display (GtkGstGLWidget * gst_widget)
|
|
||||||
{
|
|
||||||
if (!gst_widget->priv->display)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return gst_object_ref (gst_widget->priv->display);
|
|
||||||
}
|
|
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_GL_WIDGET_H__
|
|
||||||
#define __GTK_GST_GL_WIDGET_H__
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gst/gst.h>
|
|
||||||
#include <gst/gl/gl.h>
|
|
||||||
|
|
||||||
#include "gtkgstbasewidget.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
GType gtk_gst_gl_widget_get_type (void);
|
|
||||||
#define GTK_TYPE_GST_GL_WIDGET (gtk_gst_gl_widget_get_type())
|
|
||||||
#define GTK_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidget))
|
|
||||||
#define GTK_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidgetClass))
|
|
||||||
#define GTK_IS_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_GL_WIDGET))
|
|
||||||
#define GTK_IS_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_GL_WIDGET))
|
|
||||||
#define GTK_GST_GL_WIDGET_CAST(obj) ((GtkGstGLWidget*)(obj))
|
|
||||||
|
|
||||||
typedef struct _GtkGstGLWidget GtkGstGLWidget;
|
|
||||||
typedef struct _GtkGstGLWidgetClass GtkGstGLWidgetClass;
|
|
||||||
typedef struct _GtkGstGLWidgetPrivate GtkGstGLWidgetPrivate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstGLWidget:
|
|
||||||
*
|
|
||||||
* Opaque #GtkGstGLWidget object
|
|
||||||
*/
|
|
||||||
struct _GtkGstGLWidget
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidget base;
|
|
||||||
|
|
||||||
GtkGstGLWidgetPrivate *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GtkGstGLWidgetClass:
|
|
||||||
*
|
|
||||||
* The #GtkGstGLWidgetClass struct only contains private data
|
|
||||||
*/
|
|
||||||
struct _GtkGstGLWidgetClass
|
|
||||||
{
|
|
||||||
/* <private> */
|
|
||||||
GtkGstBaseWidgetClass base_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
GtkWidget * gtk_gst_gl_widget_new (void);
|
|
||||||
|
|
||||||
gboolean gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * widget);
|
|
||||||
GstGLDisplay * gtk_gst_gl_widget_get_display (GtkGstGLWidget * widget);
|
|
||||||
GstGLContext * gtk_gst_gl_widget_get_context (GtkGstGLWidget * widget);
|
|
||||||
GstGLContext * gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * widget);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __GTK_GST_GL_WIDGET_H__ */
|
|
@@ -8,11 +8,9 @@ gstclapper_sources = [
|
|||||||
'gstclapper-visualization.c',
|
'gstclapper-visualization.c',
|
||||||
'gstclapper-gtk4-plugin.c',
|
'gstclapper-gtk4-plugin.c',
|
||||||
|
|
||||||
'gtk4/gstgtkbasesink.c',
|
'gtk4/gstclapperglsink.c',
|
||||||
'gtk4/gstgtkutils.c',
|
'gtk4/gstgtkutils.c',
|
||||||
'gtk4/gtkgstbasewidget.c',
|
'gtk4/gtkclapperglwidget.c',
|
||||||
'gtk4/gstgtkglsink.c',
|
|
||||||
'gtk4/gtkgstglwidget.c',
|
|
||||||
]
|
]
|
||||||
gstclapper_headers = [
|
gstclapper_headers = [
|
||||||
'clapper.h',
|
'clapper.h',
|
||||||
@@ -32,7 +30,6 @@ gstclapper_defines = [
|
|||||||
'-DBUILDING_GST_CLAPPER',
|
'-DBUILDING_GST_CLAPPER',
|
||||||
'-DGST_USE_UNSTABLE_API',
|
'-DGST_USE_UNSTABLE_API',
|
||||||
'-DHAVE_GTK_GL',
|
'-DHAVE_GTK_GL',
|
||||||
'-DBUILD_FOR_GTK4',
|
|
||||||
]
|
]
|
||||||
gtk_deps = [gstgl_dep, gstglproto_dep]
|
gtk_deps = [gstgl_dep, gstglproto_dep]
|
||||||
have_gtk_gl_windowing = false
|
have_gtk_gl_windowing = false
|
||||||
|
Reference in New Issue
Block a user