3 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:gstgtkglsink
30 #include "gstgtkglsink.h"
31 #include "gtkgstglwidget.h"
33 GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
34 #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
36 static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
37 static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
38 static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
39 static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
41 static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
44 static GstStaticPadTemplate gst_gtk_gl_sink_template =
45 GST_STATIC_PAD_TEMPLATE ("sink",
48 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
49 (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
50 GST_VIDEO_CAPS_MAKE_WITH_FEATURES
51 (GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
52 GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
54 #define gst_gtk_gl_sink_parent_class parent_class
55 G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
56 GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
57 "gtkglsink", 0, "Gtk GL Video Sink"));
60 gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
62 GstElementClass *gstelement_class;
63 GstBaseSinkClass *gstbasesink_class;
64 GstGtkBaseSinkClass *gstgtkbasesink_class;
66 gstelement_class = (GstElementClass *) klass;
67 gstbasesink_class = (GstBaseSinkClass *) klass;
68 gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
70 gstbasesink_class->query = gst_gtk_gl_sink_query;
71 gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
72 gstbasesink_class->start = gst_gtk_gl_sink_start;
73 gstbasesink_class->stop = gst_gtk_gl_sink_stop;
74 gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
76 gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
77 gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
79 gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink",
80 "Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
81 "Matthew Waters <matthew@centricular.com>");
83 gst_element_class_add_static_pad_template (gstelement_class,
84 &gst_gtk_gl_sink_template);
88 gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
93 gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
95 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
98 switch (GST_QUERY_TYPE (query)) {
99 case GST_QUERY_CONTEXT:
101 const gchar *context_type;
102 GstContext *context, *old_context;
104 res = gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
105 >k_sink->display, >k_sink->gtk_context);
107 if (gtk_sink->display)
108 gst_gl_display_filter_gl_api (gtk_sink->display, GST_GL_API_OPENGL3);
110 gst_query_parse_context_type (query, &context_type);
112 if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
115 gst_query_parse_context (query, &old_context);
118 context = gst_context_copy (old_context);
120 context = gst_context_new ("gst.gl.local_context", FALSE);
122 s = gst_context_writable_structure (context);
123 gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, gtk_sink->context,
125 gst_query_set_context (query, context);
126 gst_context_unref (context);
128 res = gtk_sink->context != NULL;
130 GST_LOG_OBJECT (gtk_sink, "context query of type %s %i", context_type,
135 res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
143 _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
144 GstGtkGLSink * gtk_sink)
146 gint scale_factor, width, height;
147 gboolean reconfigure;
149 scale_factor = gtk_widget_get_scale_factor (widget);
150 width = scale_factor * gtk_widget_get_allocated_width (widget);
151 height = scale_factor * gtk_widget_get_allocated_height (widget);
153 GST_OBJECT_LOCK (gtk_sink);
155 (width != gtk_sink->display_width || height != gtk_sink->display_height);
156 gtk_sink->display_width = width;
157 gtk_sink->display_height = height;
158 GST_OBJECT_UNLOCK (gtk_sink);
161 GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad.");
162 gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad,
163 gst_event_new_reconfigure ());
168 gst_gtk_gl_sink_start (GstBaseSink * bsink)
170 GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
171 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
172 GtkGstGLWidget *gst_widget;
174 if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
177 /* After this point, gtk_sink->widget will always be set */
178 gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
180 /* Track the allocation size */
181 g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb),
183 _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
185 if (!gtk_gst_gl_widget_init_winsys (gst_widget))
188 gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
189 gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
190 gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
192 if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
199 gst_gtk_gl_sink_stop (GstBaseSink * bsink)
201 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
203 if (gtk_sink->display) {
204 gst_object_unref (gtk_sink->display);
205 gtk_sink->display = NULL;
208 if (gtk_sink->context) {
209 gst_object_unref (gtk_sink->context);
210 gtk_sink->context = NULL;
213 if (gtk_sink->gtk_context) {
214 gst_object_unref (gtk_sink->gtk_context);
215 gtk_sink->gtk_context = NULL;
218 return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
222 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
224 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
225 GstBufferPool *pool = NULL;
226 GstStructure *config;
230 GstStructure *allocation_meta = NULL;
231 gint display_width, display_height;
233 if (!gtk_sink->display || !gtk_sink->context)
236 gst_query_parse_allocation (query, &caps, &need_pool);
244 if (!gst_video_info_from_caps (&info, caps))
247 GST_DEBUG_OBJECT (gtk_sink, "create new pool");
248 pool = gst_gl_buffer_pool_new (gtk_sink->context);
250 /* the normal size of a frame */
253 config = gst_buffer_pool_get_config (pool);
254 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
255 gst_buffer_pool_config_add_option (config,
256 GST_BUFFER_POOL_OPTION_GL_SYNC_META);
258 if (!gst_buffer_pool_set_config (pool, config))
261 /* we need at least 2 buffer because we hold on to the last one */
262 gst_query_add_allocation_pool (query, pool, size, 2, 0);
263 gst_object_unref (pool);
266 GST_OBJECT_LOCK (gtk_sink);
267 display_width = gtk_sink->display_width;
268 display_height = gtk_sink->display_height;
269 GST_OBJECT_UNLOCK (gtk_sink);
271 if (display_width != 0 && display_height != 0) {
272 GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
273 display_width, display_height);
274 allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
275 "width", G_TYPE_UINT, display_width,
276 "height", G_TYPE_UINT, display_height, NULL);
279 gst_query_add_allocation_meta (query,
280 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
283 gst_structure_free (allocation_meta);
285 /* we also support various metadata */
286 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
288 if (gtk_sink->context->gl_vtable->FenceSync)
289 gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
296 GST_DEBUG_OBJECT (bsink, "no caps specified");
301 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
306 GST_DEBUG_OBJECT (bsink, "failed setting config");
312 gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
315 GstCaps *result = NULL;
317 tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
320 GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
323 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
324 gst_caps_unref (tmp);
329 result = gst_gl_overlay_compositor_add_caps (result);
331 GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);