v4l2src: Try to avoid TRY_FMT when camera is streaming
[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     return FALSE;
161
162   gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
163   gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
164   gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
165
166   if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
167     return FALSE;
168
169   gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
170       gtk_sink->display);
171
172   return TRUE;
173 }
174
175 static gboolean
176 gst_gtk_gl_sink_stop (GstBaseSink * bsink)
177 {
178   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
179
180   if (gtk_sink->display) {
181     gst_object_unref (gtk_sink->display);
182     gtk_sink->display = NULL;
183   }
184
185   if (gtk_sink->context) {
186     gst_object_unref (gtk_sink->context);
187     gtk_sink->context = NULL;
188   }
189
190   if (gtk_sink->gtk_context) {
191     gst_object_unref (gtk_sink->gtk_context);
192     gtk_sink->gtk_context = NULL;
193   }
194
195   return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
196 }
197
198 static gboolean
199 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
200 {
201   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
202   GstBufferPool *pool = NULL;
203   GstStructure *config;
204   GstCaps *caps;
205   GstVideoInfo info;
206   guint size;
207   gboolean need_pool;
208   GstStructure *allocation_meta = NULL;
209   gint display_width, display_height;
210
211   if (!gtk_sink->display || !gtk_sink->context)
212     return FALSE;
213
214   gst_query_parse_allocation (query, &caps, &need_pool);
215
216   if (caps == NULL)
217     goto no_caps;
218
219   if (!gst_video_info_from_caps (&info, caps))
220     goto invalid_caps;
221
222   /* the normal size of a frame */
223   size = info.size;
224
225   if (need_pool) {
226     GST_DEBUG_OBJECT (gtk_sink, "create new pool");
227     pool = gst_gl_buffer_pool_new (gtk_sink->context);
228
229     config = gst_buffer_pool_get_config (pool);
230     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
231     gst_buffer_pool_config_add_option (config,
232         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
233
234     if (!gst_buffer_pool_set_config (pool, config))
235       goto config_failed;
236   }
237
238   /* we need at least 2 buffer because we hold on to the last one */
239   gst_query_add_allocation_pool (query, pool, size, 2, 0);
240   if (pool)
241     gst_object_unref (pool);
242
243   GST_OBJECT_LOCK (gtk_sink);
244   display_width = gtk_sink->display_width;
245   display_height = gtk_sink->display_height;
246   GST_OBJECT_UNLOCK (gtk_sink);
247
248   if (display_width != 0 && display_height != 0) {
249     GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
250         display_width, display_height);
251     allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
252         "width", G_TYPE_UINT, display_width,
253         "height", G_TYPE_UINT, display_height, NULL);
254   }
255
256   gst_query_add_allocation_meta (query,
257       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
258
259   if (allocation_meta)
260     gst_structure_free (allocation_meta);
261
262   /* we also support various metadata */
263   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
264
265   if (gtk_sink->context->gl_vtable->FenceSync)
266     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
267
268   return TRUE;
269
270   /* ERRORS */
271 no_caps:
272   {
273     GST_DEBUG_OBJECT (bsink, "no caps specified");
274     return FALSE;
275   }
276 invalid_caps:
277   {
278     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
279     return FALSE;
280   }
281 config_failed:
282   {
283     GST_DEBUG_OBJECT (bsink, "failed setting config");
284     return FALSE;
285   }
286 }
287
288 static GstCaps *
289 gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
290 {
291   GstCaps *tmp = NULL;
292   GstCaps *result = NULL;
293
294   tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
295
296   if (filter) {
297     GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
298         filter);
299
300     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
301     gst_caps_unref (tmp);
302   } else {
303     result = tmp;
304   }
305
306   result = gst_gl_overlay_compositor_add_caps (result);
307
308   GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
309
310   return result;
311 }