gtk: Fix logging in base widget and fix desc of GL sink
[platform/upstream/gst-plugins-good.git] / ext / gtk / gstgtkglsink.c
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:gstgtkglsink
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gstgtkglsink.h"
31 #include "gtkgstglwidget.h"
32
33 GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
34 #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
35
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,
40     GstQuery * query);
41 static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
42     GstCaps * filter);
43
44 static GstStaticPadTemplate gst_gtk_gl_sink_template =
45     GST_STATIC_PAD_TEMPLATE ("sink",
46     GST_PAD_SINK,
47     GST_PAD_ALWAYS,
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")));
53
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"));
58
59 static void
60 gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
61 {
62   GstElementClass *gstelement_class;
63   GstBaseSinkClass *gstbasesink_class;
64   GstGtkBaseSinkClass *gstgtkbasesink_class;
65
66   gstelement_class = (GstElementClass *) klass;
67   gstbasesink_class = (GstBaseSinkClass *) klass;
68   gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
69
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;
75
76   gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
77   gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
78
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>");
82
83   gst_element_class_add_static_pad_template (gstelement_class,
84       &gst_gtk_gl_sink_template);
85 }
86
87 static void
88 gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
89 {
90 }
91
92 static gboolean
93 gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
94 {
95   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
96   gboolean res = FALSE;
97
98   switch (GST_QUERY_TYPE (query)) {
99     case GST_QUERY_CONTEXT:
100     {
101       const gchar *context_type;
102       GstContext *context, *old_context;
103
104       res = gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
105           &gtk_sink->display, &gtk_sink->gtk_context);
106
107       if (gtk_sink->display)
108         gst_gl_display_filter_gl_api (gtk_sink->display, GST_GL_API_OPENGL3);
109
110       gst_query_parse_context_type (query, &context_type);
111
112       if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
113         GstStructure *s;
114
115         gst_query_parse_context (query, &old_context);
116
117         if (old_context)
118           context = gst_context_copy (old_context);
119         else
120           context = gst_context_new ("gst.gl.local_context", FALSE);
121
122         s = gst_context_writable_structure (context);
123         gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, gtk_sink->context,
124             NULL);
125         gst_query_set_context (query, context);
126         gst_context_unref (context);
127
128         res = gtk_sink->context != NULL;
129       }
130       GST_LOG_OBJECT (gtk_sink, "context query of type %s %i", context_type,
131           res);
132       break;
133     }
134     default:
135       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
136       break;
137   }
138
139   return res;
140 }
141
142 static void
143 _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
144     GstGtkGLSink * gtk_sink)
145 {
146   gint scale_factor, width, height;
147   gboolean reconfigure;
148
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);
152
153   GST_OBJECT_LOCK (gtk_sink);
154   reconfigure =
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);
159
160   if (reconfigure) {
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 ());
164   }
165 }
166
167 static gboolean
168 gst_gtk_gl_sink_start (GstBaseSink * bsink)
169 {
170   GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
171   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
172   GtkGstGLWidget *gst_widget;
173
174   if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
175     return FALSE;
176
177   /* After this point, gtk_sink->widget will always be set */
178   gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
179
180   /* Track the allocation size */
181   g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb),
182       gtk_sink);
183   _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
184
185   if (!gtk_gst_gl_widget_init_winsys (gst_widget))
186     return FALSE;
187
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);
191
192   if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
193     return FALSE;
194
195   return TRUE;
196 }
197
198 static gboolean
199 gst_gtk_gl_sink_stop (GstBaseSink * bsink)
200 {
201   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
202
203   if (gtk_sink->display) {
204     gst_object_unref (gtk_sink->display);
205     gtk_sink->display = NULL;
206   }
207
208   if (gtk_sink->context) {
209     gst_object_unref (gtk_sink->context);
210     gtk_sink->context = NULL;
211   }
212
213   if (gtk_sink->gtk_context) {
214     gst_object_unref (gtk_sink->gtk_context);
215     gtk_sink->gtk_context = NULL;
216   }
217
218   return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
219 }
220
221 static gboolean
222 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
223 {
224   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
225   GstBufferPool *pool = NULL;
226   GstStructure *config;
227   GstCaps *caps;
228   guint size;
229   gboolean need_pool;
230   GstStructure *allocation_meta = NULL;
231   gint display_width, display_height;
232
233   if (!gtk_sink->display || !gtk_sink->context)
234     return FALSE;
235
236   gst_query_parse_allocation (query, &caps, &need_pool);
237
238   if (caps == NULL)
239     goto no_caps;
240
241   if (need_pool) {
242     GstVideoInfo info;
243
244     if (!gst_video_info_from_caps (&info, caps))
245       goto invalid_caps;
246
247     GST_DEBUG_OBJECT (gtk_sink, "create new pool");
248     pool = gst_gl_buffer_pool_new (gtk_sink->context);
249
250     /* the normal size of a frame */
251     size = info.size;
252
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);
257
258     if (!gst_buffer_pool_set_config (pool, config))
259       goto config_failed;
260
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);
264   }
265
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);
270
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);
277   }
278
279   gst_query_add_allocation_meta (query,
280       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
281
282   if (allocation_meta)
283     gst_structure_free (allocation_meta);
284
285   /* we also support various metadata */
286   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
287
288   if (gtk_sink->context->gl_vtable->FenceSync)
289     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
290
291   return TRUE;
292
293   /* ERRORS */
294 no_caps:
295   {
296     GST_DEBUG_OBJECT (bsink, "no caps specified");
297     return FALSE;
298   }
299 invalid_caps:
300   {
301     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
302     return FALSE;
303   }
304 config_failed:
305   {
306     GST_DEBUG_OBJECT (bsink, "failed setting config");
307     return FALSE;
308   }
309 }
310
311 static GstCaps *
312 gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
313 {
314   GstCaps *tmp = NULL;
315   GstCaps *result = NULL;
316
317   tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
318
319   if (filter) {
320     GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
321         filter);
322
323     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
324     gst_caps_unref (tmp);
325   } else {
326     result = tmp;
327   }
328
329   result = gst_gl_overlay_compositor_add_caps (result);
330
331   GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
332
333   return result;
334 }