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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/video/gstvideometa.h>
26
27 #include <gst/gl/gl.h>
28 #include <gst/gl/gstglutils_private.h>
29
30 /**
31 * SECTION:gstglbasefilter
32 * @short_description: #GstBaseTransform subclass for transforming OpenGL resources
33 * @title: GstGLBaseFilter
34 * @see_also: #GstBaseTransform
35 *
36 * #GstGLBaseFilter handles the nitty gritty details of retrieving an OpenGL
37 * context. It also provided some wrappers around #GstBaseTransform's
38 * start(), stop() and set_caps() virtual methods that ensure an OpenGL context
39 * is available and current in the calling thread.
40 */
41
42 #define GST_CAT_DEFAULT gst_gl_base_filter_debug
43 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
44
45 struct _GstGLBaseFilterPrivate
46 {
47 GstGLContext *other_context;
48
49 gboolean gl_result;
50 gboolean gl_started;
51 };
52
53 /* Properties */
54 enum
55 {
56 PROP_0,
57 PROP_CONTEXT
58 };
59
60 #define gst_gl_base_filter_parent_class parent_class
61 G_DEFINE_TYPE_WITH_CODE (GstGLBaseFilter, gst_gl_base_filter,
62 GST_TYPE_BASE_TRANSFORM, G_ADD_PRIVATE (GstGLBaseFilter)
63 GST_DEBUG_CATEGORY_INIT (gst_gl_base_filter_debug,
64 "glbasefilter", 0, "glbasefilter element");
65 );
66
67 static void gst_gl_base_filter_finalize (GObject * object);
68 static void gst_gl_base_filter_set_property (GObject * object, guint prop_id,
69 const GValue * value, GParamSpec * pspec);
70 static void gst_gl_base_filter_get_property (GObject * object, guint prop_id,
71 GValue * value, GParamSpec * pspec);
72
73 static void gst_gl_base_filter_set_context (GstElement * element,
74 GstContext * context);
75 static GstStateChangeReturn gst_gl_base_filter_change_state (GstElement *
76 element, GstStateChange transition);
77 static gboolean gst_gl_base_filter_query (GstBaseTransform * trans,
78 GstPadDirection direction, GstQuery * query);
79 static void gst_gl_base_filter_reset (GstGLBaseFilter * filter);
80 static gboolean gst_gl_base_filter_start (GstBaseTransform * bt);
81 static gboolean gst_gl_base_filter_stop (GstBaseTransform * bt);
82 static gboolean gst_gl_base_filter_set_caps (GstBaseTransform * bt,
83 GstCaps * incaps, GstCaps * outcaps);
84 static gboolean gst_gl_base_filter_decide_allocation (GstBaseTransform * trans,
85 GstQuery * query);
86
87 /* GstGLContextThreadFunc */
88 static void gst_gl_base_filter_gl_start (GstGLContext * context, gpointer data);
89 static void gst_gl_base_filter_gl_stop (GstGLContext * context, gpointer data);
90
91 static gboolean gst_gl_base_filter_default_gl_start (GstGLBaseFilter * filter);
92 static void gst_gl_base_filter_default_gl_stop (GstGLBaseFilter * filter);
93
94 static void
gst_gl_base_filter_class_init(GstGLBaseFilterClass * klass)95 gst_gl_base_filter_class_init (GstGLBaseFilterClass * klass)
96 {
97 GObjectClass *gobject_class;
98 GstElementClass *element_class;
99
100 gobject_class = (GObjectClass *) klass;
101 element_class = GST_ELEMENT_CLASS (klass);
102
103 gobject_class->finalize = gst_gl_base_filter_finalize;
104 gobject_class->set_property = gst_gl_base_filter_set_property;
105 gobject_class->get_property = gst_gl_base_filter_get_property;
106
107 GST_BASE_TRANSFORM_CLASS (klass)->query = gst_gl_base_filter_query;
108 GST_BASE_TRANSFORM_CLASS (klass)->set_caps = gst_gl_base_filter_set_caps;
109 GST_BASE_TRANSFORM_CLASS (klass)->start = gst_gl_base_filter_start;
110 GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_base_filter_stop;
111 GST_BASE_TRANSFORM_CLASS (klass)->decide_allocation =
112 gst_gl_base_filter_decide_allocation;
113
114 element_class->set_context = gst_gl_base_filter_set_context;
115 element_class->change_state = gst_gl_base_filter_change_state;
116
117 g_object_class_install_property (gobject_class, PROP_CONTEXT,
118 g_param_spec_object ("context",
119 "OpenGL context",
120 "Get OpenGL context",
121 GST_TYPE_GL_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
122
123 klass->supported_gl_api = GST_GL_API_ANY;
124 klass->gl_start = gst_gl_base_filter_default_gl_start;
125 klass->gl_stop = gst_gl_base_filter_default_gl_stop;
126 }
127
128 static void
gst_gl_base_filter_init(GstGLBaseFilter * filter)129 gst_gl_base_filter_init (GstGLBaseFilter * filter)
130 {
131 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (filter), TRUE);
132
133 filter->priv = gst_gl_base_filter_get_instance_private (filter);
134 }
135
136 static void
gst_gl_base_filter_finalize(GObject * object)137 gst_gl_base_filter_finalize (GObject * object)
138 {
139 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (object);
140
141 gst_caps_replace (&filter->in_caps, NULL);
142 gst_caps_replace (&filter->out_caps, NULL);
143
144 G_OBJECT_CLASS (parent_class)->finalize (object);
145 }
146
147 static void
gst_gl_base_filter_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)148 gst_gl_base_filter_set_property (GObject * object, guint prop_id,
149 const GValue * value, GParamSpec * pspec)
150 {
151 switch (prop_id) {
152 default:
153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154 break;
155 }
156 }
157
158 static void
gst_gl_base_filter_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)159 gst_gl_base_filter_get_property (GObject * object, guint prop_id,
160 GValue * value, GParamSpec * pspec)
161 {
162 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (object);
163
164 switch (prop_id) {
165 case PROP_CONTEXT:
166 g_value_set_object (value, filter->context);
167 break;
168 default:
169 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
170 break;
171 }
172 }
173
174 static void
gst_gl_base_filter_set_context(GstElement * element,GstContext * context)175 gst_gl_base_filter_set_context (GstElement * element, GstContext * context)
176 {
177 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (element);
178 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
179
180 GST_OBJECT_LOCK (filter);
181 gst_gl_handle_set_context (element, context, &filter->display,
182 &filter->priv->other_context);
183 if (filter->display)
184 gst_gl_display_filter_gl_api (filter->display,
185 filter_class->supported_gl_api);
186 GST_OBJECT_UNLOCK (filter);
187
188 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
189 }
190
191 static gboolean
_find_local_gl_context(GstGLBaseFilter * filter)192 _find_local_gl_context (GstGLBaseFilter * filter)
193 {
194 if (gst_gl_query_local_gl_context (GST_ELEMENT (filter), GST_PAD_SRC,
195 &filter->context))
196 return TRUE;
197 if (gst_gl_query_local_gl_context (GST_ELEMENT (filter), GST_PAD_SINK,
198 &filter->context))
199 return TRUE;
200 return FALSE;
201 }
202
203 static gboolean
gst_gl_base_filter_query(GstBaseTransform * trans,GstPadDirection direction,GstQuery * query)204 gst_gl_base_filter_query (GstBaseTransform * trans, GstPadDirection direction,
205 GstQuery * query)
206 {
207 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (trans);
208
209 switch (GST_QUERY_TYPE (query)) {
210 case GST_QUERY_ALLOCATION:
211 {
212 if (direction == GST_PAD_SINK
213 && gst_base_transform_is_passthrough (trans)) {
214 _find_local_gl_context (filter);
215
216 return gst_pad_peer_query (GST_BASE_TRANSFORM_SRC_PAD (trans), query);
217 }
218 break;
219 }
220 case GST_QUERY_CONTEXT:
221 {
222 gboolean ret;
223 GST_OBJECT_LOCK (filter);
224 ret = gst_gl_handle_context_query ((GstElement *) filter, query,
225 filter->display, filter->context, filter->priv->other_context);
226 GST_OBJECT_UNLOCK (filter);
227 if (ret)
228 return TRUE;
229 break;
230 }
231 default:
232 break;
233 }
234
235 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
236 query);
237 }
238
239 static void
gst_gl_base_filter_reset(GstGLBaseFilter * filter)240 gst_gl_base_filter_reset (GstGLBaseFilter * filter)
241 {
242 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
243
244 if (filter->context) {
245 if (filter_class->gl_stop != NULL) {
246 gst_gl_context_thread_add (filter->context, gst_gl_base_filter_gl_stop,
247 filter);
248 }
249
250 gst_object_unref (filter->context);
251 filter->context = NULL;
252 }
253 }
254
255 static gboolean
gst_gl_base_filter_start(GstBaseTransform * bt)256 gst_gl_base_filter_start (GstBaseTransform * bt)
257 {
258 return TRUE;
259 }
260
261 static gboolean
gst_gl_base_filter_stop(GstBaseTransform * bt)262 gst_gl_base_filter_stop (GstBaseTransform * bt)
263 {
264 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (bt);
265
266 gst_gl_base_filter_reset (filter);
267
268 return TRUE;
269 }
270
271 static gboolean
gst_gl_base_filter_default_gl_start(GstGLBaseFilter * filter)272 gst_gl_base_filter_default_gl_start (GstGLBaseFilter * filter)
273 {
274 return TRUE;
275 }
276
277 static void
gst_gl_base_filter_gl_start(GstGLContext * context,gpointer data)278 gst_gl_base_filter_gl_start (GstGLContext * context, gpointer data)
279 {
280 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (data);
281 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
282
283 gst_gl_insert_debug_marker (filter->context,
284 "starting element %s", GST_OBJECT_NAME (filter));
285
286 filter->priv->gl_started = filter_class->gl_start (filter);
287 }
288
289 static void
gst_gl_base_filter_default_gl_stop(GstGLBaseFilter * filter)290 gst_gl_base_filter_default_gl_stop (GstGLBaseFilter * filter)
291 {
292 }
293
294 static void
gst_gl_base_filter_gl_stop(GstGLContext * context,gpointer data)295 gst_gl_base_filter_gl_stop (GstGLContext * context, gpointer data)
296 {
297 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (data);
298 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
299
300 gst_gl_insert_debug_marker (filter->context,
301 "stopping element %s", GST_OBJECT_NAME (filter));
302
303 if (filter->priv->gl_started)
304 filter_class->gl_stop (filter);
305
306 filter->priv->gl_started = FALSE;
307 }
308
309 static void
_gl_set_caps(GstGLContext * context,GstGLBaseFilter * filter)310 _gl_set_caps (GstGLContext * context, GstGLBaseFilter * filter)
311 {
312 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
313
314 if (filter_class->gl_set_caps)
315 filter->priv->gl_result =
316 filter_class->gl_set_caps (filter, filter->in_caps, filter->out_caps);
317 }
318
319 static gboolean
gst_gl_base_filter_decide_allocation(GstBaseTransform * trans,GstQuery * query)320 gst_gl_base_filter_decide_allocation (GstBaseTransform * trans,
321 GstQuery * query)
322 {
323 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (trans);
324 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
325
326 if (!gst_gl_base_filter_find_gl_context (filter))
327 return FALSE;
328
329 if (filter_class->gl_set_caps) {
330 gst_gl_context_thread_add (filter->context,
331 (GstGLContextThreadFunc) _gl_set_caps, filter);
332 if (!filter->priv->gl_result)
333 goto error;
334 }
335
336 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
337 query);
338
339 error:
340 {
341 GST_ELEMENT_ERROR (trans, LIBRARY, INIT,
342 ("Subclass failed to initialize."), (NULL));
343 return FALSE;
344 }
345 }
346
347 static gboolean
gst_gl_base_filter_set_caps(GstBaseTransform * bt,GstCaps * incaps,GstCaps * outcaps)348 gst_gl_base_filter_set_caps (GstBaseTransform * bt, GstCaps * incaps,
349 GstCaps * outcaps)
350 {
351 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (bt);
352
353 gst_caps_replace (&filter->in_caps, incaps);
354 gst_caps_replace (&filter->out_caps, outcaps);
355
356 return TRUE;
357 }
358
359 static GstStateChangeReturn
gst_gl_base_filter_change_state(GstElement * element,GstStateChange transition)360 gst_gl_base_filter_change_state (GstElement * element,
361 GstStateChange transition)
362 {
363 GstGLBaseFilter *filter = GST_GL_BASE_FILTER (element);
364 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
365 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
366
367 GST_DEBUG_OBJECT (filter, "changing state: %s => %s",
368 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
369 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
370
371 switch (transition) {
372 case GST_STATE_CHANGE_NULL_TO_READY:
373 if (!gst_gl_ensure_element_data (element, &filter->display,
374 &filter->priv->other_context)) {
375 GST_OBJECT_UNLOCK (filter);
376 return GST_STATE_CHANGE_FAILURE;
377 }
378
379 gst_gl_display_filter_gl_api (filter->display,
380 filter_class->supported_gl_api);
381 break;
382 default:
383 break;
384 }
385
386 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
387 if (ret == GST_STATE_CHANGE_FAILURE)
388 return ret;
389
390 switch (transition) {
391 case GST_STATE_CHANGE_READY_TO_NULL:
392 if (filter->priv->other_context) {
393 gst_object_unref (filter->priv->other_context);
394 filter->priv->other_context = NULL;
395 }
396
397 if (filter->display) {
398 gst_object_unref (filter->display);
399 filter->display = NULL;
400 }
401
402 if (filter->context) {
403 gst_object_unref (filter->context);
404 filter->context = NULL;
405 }
406 break;
407 default:
408 break;
409 }
410
411 return ret;
412 }
413
414 /**
415 * gst_gl_base_filter_find_gl_context:
416 * @filter: a #GstGLBaseFilter
417 *
418 * Returns: Whether an OpenGL context could be retrieved or created successfully
419 *
420 * Since: 1.16
421 */
422 gboolean
gst_gl_base_filter_find_gl_context(GstGLBaseFilter * filter)423 gst_gl_base_filter_find_gl_context (GstGLBaseFilter * filter)
424 {
425 GstGLBaseFilterClass *filter_class = GST_GL_BASE_FILTER_GET_CLASS (filter);
426 GError *error = NULL;
427 gboolean new_context = FALSE;
428
429 if (!filter->context)
430 new_context = TRUE;
431
432 _find_local_gl_context (filter);
433
434 if (!filter->context) {
435 GST_OBJECT_LOCK (filter->display);
436 do {
437 if (filter->context)
438 gst_object_unref (filter->context);
439 /* just get a GL context. we don't care */
440 filter->context =
441 gst_gl_display_get_gl_context_for_thread (filter->display, NULL);
442 if (!filter->context) {
443 if (!gst_gl_display_create_context (filter->display,
444 filter->priv->other_context, &filter->context, &error)) {
445 GST_OBJECT_UNLOCK (filter->display);
446 goto context_error;
447 }
448 }
449 } while (!gst_gl_display_add_context (filter->display, filter->context));
450 GST_OBJECT_UNLOCK (filter->display);
451 }
452
453 if (new_context || !filter->priv->gl_started) {
454 if (filter->priv->gl_started)
455 gst_gl_context_thread_add (filter->context, gst_gl_base_filter_gl_stop,
456 filter);
457
458 {
459 GstGLAPI current_gl_api = gst_gl_context_get_gl_api (filter->context);
460 if ((current_gl_api & filter_class->supported_gl_api) == 0)
461 goto unsupported_gl_api;
462 }
463
464 gst_gl_context_thread_add (filter->context, gst_gl_base_filter_gl_start,
465 filter);
466
467 if (!filter->priv->gl_started)
468 goto error;
469 }
470
471 return TRUE;
472
473 unsupported_gl_api:
474 {
475 GstGLAPI gl_api = gst_gl_context_get_gl_api (filter->context);
476 gchar *gl_api_str = gst_gl_api_to_string (gl_api);
477 gchar *supported_gl_api_str =
478 gst_gl_api_to_string (filter_class->supported_gl_api);
479
480 GST_ELEMENT_ERROR (filter, RESOURCE, BUSY,
481 ("GL API's not compatible context: %s supported: %s", gl_api_str,
482 supported_gl_api_str), (NULL));
483
484 g_free (supported_gl_api_str);
485 g_free (gl_api_str);
486 return FALSE;
487 }
488 context_error:
489 {
490 GST_ELEMENT_ERROR (filter, RESOURCE, NOT_FOUND, ("%s", error->message),
491 (NULL));
492 g_clear_error (&error);
493 return FALSE;
494 }
495 error:
496 {
497 GST_ELEMENT_ERROR (filter, LIBRARY, INIT,
498 ("Subclass failed to initialize."), (NULL));
499 return FALSE;
500 }
501 }
502