22410f72ab73792a36440aedcafc6c221a0105fe
[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:element-gtkglsink
23  * @title: gtkglsink
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <gst/gl/gstglfuncs.h>
31
32 #include "gstgtkglsink.h"
33 #include "gtkgstglwidget.h"
34
35 GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
36 #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
37
38 static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
39 static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
40 static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
41 static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
42     GstQuery * query);
43 static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
44     GstCaps * filter);
45
46 static GstStaticPadTemplate gst_gtk_gl_sink_template =
47     GST_STATIC_PAD_TEMPLATE ("sink",
48     GST_PAD_SINK,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
51         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
52         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
53         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
54             GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
55
56 #define gst_gtk_gl_sink_parent_class parent_class
57 G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
58     GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
59         "gtkglsink", 0, "Gtk GL Video Sink"));
60
61 static void
62 gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
63 {
64   GstElementClass *gstelement_class;
65   GstBaseSinkClass *gstbasesink_class;
66   GstGtkBaseSinkClass *gstgtkbasesink_class;
67
68   gstelement_class = (GstElementClass *) klass;
69   gstbasesink_class = (GstBaseSinkClass *) klass;
70   gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
71
72   gstbasesink_class->query = gst_gtk_gl_sink_query;
73   gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
74   gstbasesink_class->start = gst_gtk_gl_sink_start;
75   gstbasesink_class->stop = gst_gtk_gl_sink_stop;
76   gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
77
78   gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
79   gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
80
81   gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink",
82       "Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
83       "Matthew Waters <matthew@centricular.com>");
84
85   gst_element_class_add_static_pad_template (gstelement_class,
86       &gst_gtk_gl_sink_template);
87 }
88
89 static void
90 gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
91 {
92 }
93
94 static gboolean
95 gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
96 {
97   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
98   gboolean res = FALSE;
99
100   switch (GST_QUERY_TYPE (query)) {
101     case GST_QUERY_CONTEXT:
102     {
103       if (gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
104               gtk_sink->display, gtk_sink->context, gtk_sink->gtk_context))
105         return TRUE;
106       break;
107     }
108     default:
109       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
110       break;
111   }
112
113   return res;
114 }
115
116 static void
117 _size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
118     GstGtkGLSink * gtk_sink)
119 {
120   gint scale_factor, width, height;
121   gboolean reconfigure;
122
123   scale_factor = gtk_widget_get_scale_factor (widget);
124   width = scale_factor * gtk_widget_get_allocated_width (widget);
125   height = scale_factor * gtk_widget_get_allocated_height (widget);
126
127   GST_OBJECT_LOCK (gtk_sink);
128   reconfigure =
129       (width != gtk_sink->display_width || height != gtk_sink->display_height);
130   gtk_sink->display_width = width;
131   gtk_sink->display_height = height;
132   GST_OBJECT_UNLOCK (gtk_sink);
133
134   if (reconfigure) {
135     GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad.");
136     gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad,
137         gst_event_new_reconfigure ());
138   }
139 }
140
141 static gboolean
142 gst_gtk_gl_sink_start (GstBaseSink * bsink)
143 {
144   GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
145   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
146   GtkGstGLWidget *gst_widget;
147
148   if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
149     return FALSE;
150
151   /* After this point, gtk_sink->widget will always be set */
152   gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
153
154   /* Track the allocation size */
155   g_signal_connect (gst_widget, "size-allocate", G_CALLBACK (_size_changed_cb),
156       gtk_sink);
157   _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
158
159   if (!gtk_gst_gl_widget_init_winsys (gst_widget)) {
160     GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
161             "Failed to initialize OpenGL with Gtk"), (NULL));
162     return FALSE;
163   }
164
165   gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
166   gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
167   gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
168
169   if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context) {
170     GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
171             "Failed to retrieve OpenGL context from Gtk"), (NULL));
172     return FALSE;
173   }
174
175   gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
176       gtk_sink->display);
177
178   return TRUE;
179 }
180
181 static gboolean
182 gst_gtk_gl_sink_stop (GstBaseSink * bsink)
183 {
184   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
185
186   if (gtk_sink->display) {
187     gst_object_unref (gtk_sink->display);
188     gtk_sink->display = NULL;
189   }
190
191   if (gtk_sink->context) {
192     gst_object_unref (gtk_sink->context);
193     gtk_sink->context = NULL;
194   }
195
196   if (gtk_sink->gtk_context) {
197     gst_object_unref (gtk_sink->gtk_context);
198     gtk_sink->gtk_context = NULL;
199   }
200
201   return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
202 }
203
204 static gboolean
205 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
206 {
207   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
208   GstBufferPool *pool = NULL;
209   GstStructure *config;
210   GstCaps *caps;
211   GstVideoInfo info;
212   guint size;
213   gboolean need_pool;
214   GstStructure *allocation_meta = NULL;
215   gint display_width, display_height;
216
217   if (!gtk_sink->display || !gtk_sink->context)
218     return FALSE;
219
220   gst_query_parse_allocation (query, &caps, &need_pool);
221
222   if (caps == NULL)
223     goto no_caps;
224
225   if (!gst_video_info_from_caps (&info, caps))
226     goto invalid_caps;
227
228   /* the normal size of a frame */
229   size = info.size;
230
231   if (need_pool) {
232     GST_DEBUG_OBJECT (gtk_sink, "create new pool");
233     pool = gst_gl_buffer_pool_new (gtk_sink->context);
234
235     config = gst_buffer_pool_get_config (pool);
236     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
237     gst_buffer_pool_config_add_option (config,
238         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
239
240     if (!gst_buffer_pool_set_config (pool, config))
241       goto config_failed;
242   }
243
244   /* we need at least 2 buffer because we hold on to the last one */
245   gst_query_add_allocation_pool (query, pool, size, 2, 0);
246   if (pool)
247     gst_object_unref (pool);
248
249   GST_OBJECT_LOCK (gtk_sink);
250   display_width = gtk_sink->display_width;
251   display_height = gtk_sink->display_height;
252   GST_OBJECT_UNLOCK (gtk_sink);
253
254   if (display_width != 0 && display_height != 0) {
255     GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
256         display_width, display_height);
257     allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
258         "width", G_TYPE_UINT, display_width,
259         "height", G_TYPE_UINT, display_height, NULL);
260   }
261
262   gst_query_add_allocation_meta (query,
263       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
264
265   if (allocation_meta)
266     gst_structure_free (allocation_meta);
267
268   /* we also support various metadata */
269   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
270
271   if (gtk_sink->context->gl_vtable->FenceSync)
272     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
273
274   return TRUE;
275
276   /* ERRORS */
277 no_caps:
278   {
279     GST_DEBUG_OBJECT (bsink, "no caps specified");
280     return FALSE;
281   }
282 invalid_caps:
283   {
284     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
285     return FALSE;
286   }
287 config_failed:
288   {
289     GST_DEBUG_OBJECT (bsink, "failed setting config");
290     return FALSE;
291   }
292 }
293
294 static GstCaps *
295 gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
296 {
297   GstCaps *tmp = NULL;
298   GstCaps *result = NULL;
299
300   tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
301
302   if (filter) {
303     GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
304         filter);
305
306     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
307     gst_caps_unref (tmp);
308   } else {
309     result = tmp;
310   }
311
312   result = gst_gl_overlay_compositor_add_caps (result);
313
314   GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
315
316   return result;
317 }