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")));
51 #define gst_gtk_gl_sink_parent_class parent_class
52 G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
53 GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
54 "gtkglsink", 0, "Gtk Video Sink"));
57 gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
59 GstElementClass *gstelement_class;
60 GstBaseSinkClass *gstbasesink_class;
61 GstGtkBaseSinkClass *gstgtkbasesink_class;
63 gstelement_class = (GstElementClass *) klass;
64 gstbasesink_class = (GstBaseSinkClass *) klass;
65 gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
67 gstbasesink_class->query = gst_gtk_gl_sink_query;
68 gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
69 gstbasesink_class->start = gst_gtk_gl_sink_start;
70 gstbasesink_class->stop = gst_gtk_gl_sink_stop;
71 gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
73 gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
74 gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
76 gst_element_class_set_metadata (gstelement_class, "Gtk Video Sink",
77 "Sink/Video", "A video sink that renders to a GtkWidget",
78 "Matthew Waters <matthew@centricular.com>");
80 gst_element_class_add_pad_template (gstelement_class,
81 gst_static_pad_template_get (&gst_gtk_gl_sink_template));
85 gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
90 gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
92 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
95 switch (GST_QUERY_TYPE (query)) {
96 case GST_QUERY_CONTEXT:
98 const gchar *context_type;
99 GstContext *context, *old_context;
101 res = gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
102 >k_sink->display, >k_sink->gtk_context);
104 if (gtk_sink->display)
105 gst_gl_display_filter_gl_api (gtk_sink->display, GST_GL_API_OPENGL3);
107 gst_query_parse_context_type (query, &context_type);
109 if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
112 gst_query_parse_context (query, &old_context);
115 context = gst_context_copy (old_context);
117 context = gst_context_new ("gst.gl.local_context", FALSE);
119 s = gst_context_writable_structure (context);
120 gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, gtk_sink->context,
122 gst_query_set_context (query, context);
123 gst_context_unref (context);
125 res = gtk_sink->context != NULL;
127 GST_LOG_OBJECT (gtk_sink, "context query of type %s %i", context_type,
132 res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
140 _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
141 GstGtkGLSink * gtk_sink)
143 gint scale_factor, width, height;
144 gboolean reconfigure;
146 scale_factor = gtk_widget_get_scale_factor (widget);
147 width = scale_factor * gtk_widget_get_allocated_width (widget);
148 height = scale_factor * gtk_widget_get_allocated_height (widget);
150 GST_OBJECT_LOCK (gtk_sink);
152 (width != gtk_sink->display_width || height != gtk_sink->display_height);
153 gtk_sink->display_width = width;
154 gtk_sink->display_height = height;
155 GST_OBJECT_UNLOCK (gtk_sink);
158 GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad.");
159 gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad,
160 gst_event_new_reconfigure ());
165 gst_gtk_gl_sink_start (GstBaseSink * bsink)
167 GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
168 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
169 GtkGstGLWidget *gst_widget;
171 if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
174 /* After this point, gtk_sink->widget will always be set */
175 gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
177 /* Track the allocation size */
178 g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb),
180 _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
182 if (!gtk_gst_gl_widget_init_winsys (gst_widget))
185 gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
186 gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
187 gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
189 if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
196 gst_gtk_gl_sink_stop (GstBaseSink * bsink)
198 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
200 if (gtk_sink->display) {
201 gst_object_unref (gtk_sink->display);
202 gtk_sink->display = NULL;
205 if (gtk_sink->context) {
206 gst_object_unref (gtk_sink->context);
207 gtk_sink->context = NULL;
210 if (gtk_sink->gtk_context) {
211 gst_object_unref (gtk_sink->gtk_context);
212 gtk_sink->gtk_context = NULL;
215 return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
219 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
221 GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
222 GstBufferPool *pool = NULL;
223 GstStructure *config;
227 GstStructure *allocation_meta = NULL;
228 gint display_width, display_height;
230 if (!gtk_sink->display || !gtk_sink->context)
233 gst_query_parse_allocation (query, &caps, &need_pool);
241 if (!gst_video_info_from_caps (&info, caps))
244 GST_DEBUG_OBJECT (gtk_sink, "create new pool");
245 pool = gst_gl_buffer_pool_new (gtk_sink->context);
247 /* the normal size of a frame */
250 config = gst_buffer_pool_get_config (pool);
251 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
252 gst_buffer_pool_config_add_option (config,
253 GST_BUFFER_POOL_OPTION_GL_SYNC_META);
255 if (!gst_buffer_pool_set_config (pool, config))
258 /* we need at least 2 buffer because we hold on to the last one */
259 gst_query_add_allocation_pool (query, pool, size, 2, 0);
260 gst_object_unref (pool);
263 GST_OBJECT_LOCK (gtk_sink);
264 display_width = gtk_sink->display_width;
265 display_height = gtk_sink->display_height;
266 GST_OBJECT_UNLOCK (gtk_sink);
268 if (display_width != 0 && display_height != 0) {
269 GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
270 display_width, display_height);
271 allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
272 "width", G_TYPE_UINT, display_width,
273 "height", G_TYPE_UINT, display_height, NULL);
276 gst_query_add_allocation_meta (query,
277 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
280 gst_structure_free (allocation_meta);
282 /* we also support various metadata */
283 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
285 if (gtk_sink->context->gl_vtable->FenceSync)
286 gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
293 GST_DEBUG_OBJECT (bsink, "no caps specified");
298 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
303 GST_DEBUG_OBJECT (bsink, "failed setting config");
309 gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
312 GstCaps *result = NULL;
314 tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
317 GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
320 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
321 gst_caps_unref (tmp);
326 result = gst_gl_overlay_compositor_add_caps (result);
328 GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);