glmixer: override the caps query in order to 'convert' capsfeatures
[platform/upstream/gstreamer.git] / ext / gl / gstglmixer.c
1 /* Generic video mixer plugin
2  *
3  * GStreamer
4  * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <gst/gst.h>
27 #include <gst/base/gstcollectpads.h>
28 #include <gst/video/video.h>
29
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #endif
36
37 #include "gstglmixer.h"
38
39 #if GST_GL_HAVE_PLATFORM_EGL
40 #include <gst/gl/egl/gsteglimagememory.h>
41 #endif
42
43 #define gst_gl_mixer_parent_class parent_class
44 G_DEFINE_ABSTRACT_TYPE (GstGLMixer, gst_gl_mixer, GST_TYPE_VIDEO_AGGREGATOR);
45 static gboolean gst_gl_mixer_do_bufferpool (GstGLMixer * mix,
46     GstCaps * outcaps);
47
48
49 #define GST_CAT_DEFAULT gst_gl_mixer_debug
50 GST_DEBUG_CATEGORY (gst_gl_mixer_debug);
51
52 static void gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
53     GValue * value, GParamSpec * pspec);
54 static void gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
55     const GValue * value, GParamSpec * pspec);
56 static void gst_gl_mixer_pad_finalize (GObject * object);
57
58 static void gst_gl_mixer_set_context (GstElement * element,
59     GstContext * context);
60
61 enum
62 {
63   PROP_PAD_0
64 };
65
66 #define GST_GL_MIXER_GET_PRIVATE(obj)  \
67     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_GL_MIXER, GstGLMixerPrivate))
68
69 struct _GstGLMixerPrivate
70 {
71   gboolean negotiated;
72
73   GstBufferPool *pool;
74   gboolean pool_active;
75   GstAllocator *allocator;
76   GstAllocationParams params;
77   GstQuery *query;
78
79   gboolean gl_resource_ready;
80   GMutex gl_resource_lock;
81   GCond gl_resource_cond;
82 };
83
84 G_DEFINE_TYPE (GstGLMixerPad, gst_gl_mixer_pad, GST_TYPE_VIDEO_AGGREGATOR_PAD);
85
86 static void
87 gst_gl_mixer_pad_class_init (GstGLMixerPadClass * klass)
88 {
89   GObjectClass *gobject_class = (GObjectClass *) klass;
90
91   gobject_class->set_property = gst_gl_mixer_pad_set_property;
92   gobject_class->get_property = gst_gl_mixer_pad_get_property;
93
94   gobject_class->finalize = gst_gl_mixer_pad_finalize;
95 }
96
97 static void
98 gst_gl_mixer_pad_finalize (GObject * object)
99 {
100   GstGLMixerPad *pad = GST_GL_MIXER_PAD (object);
101
102   if (pad->upload) {
103     gst_object_unref (pad->upload);
104     pad->upload = NULL;
105   }
106
107   G_OBJECT_CLASS (gst_gl_mixer_pad_parent_class)->finalize (object);
108 }
109
110 static void
111 gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
112     GValue * value, GParamSpec * pspec)
113 {
114   switch (prop_id) {
115     default:
116       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117       break;
118   }
119 }
120
121 static void
122 gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
123     const GValue * value, GParamSpec * pspec)
124 {
125   switch (prop_id) {
126     default:
127       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128       break;
129   }
130 }
131
132 static gboolean
133 _negotiated_caps (GstVideoAggregator * vagg, GstCaps * caps)
134 {
135   GstGLMixer *mix = GST_GL_MIXER (vagg);
136   gboolean ret = gst_gl_mixer_do_bufferpool (mix, caps);
137
138   mix->priv->negotiated = ret;
139
140   gst_caps_replace (&mix->out_caps, caps);
141
142   return ret;
143 }
144
145 static gboolean
146 gst_gl_mixer_propose_allocation (GstGLMixer * mix,
147     GstQuery * decide_query, GstQuery * query)
148 {
149   GstBufferPool *pool;
150   GstStructure *config;
151   GstCaps *caps;
152   guint size = 0;
153   gboolean need_pool;
154   GError *error = NULL;
155   GstStructure *gl_context;
156   gchar *platform, *gl_apis;
157   gpointer handle;
158   GstAllocator *allocator = NULL;
159   GstAllocationParams params;
160
161   gst_query_parse_allocation (query, &caps, &need_pool);
162
163   if (caps == NULL)
164     goto no_caps;
165
166   if ((pool = mix->priv->pool))
167     gst_object_ref (pool);
168
169   if (pool != NULL) {
170     GstCaps *pcaps;
171
172     /* we had a pool, check caps */
173     GST_DEBUG_OBJECT (mix, "check existing pool caps");
174     config = gst_buffer_pool_get_config (pool);
175     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
176
177     if (!gst_caps_is_equal (caps, pcaps)) {
178       GST_DEBUG_OBJECT (mix, "pool has different caps");
179       /* different caps, we can't use this pool */
180       gst_object_unref (pool);
181       pool = NULL;
182     }
183     gst_structure_free (config);
184   }
185
186   if (!gst_gl_ensure_element_data (mix, &mix->display, &mix->other_context))
187     return FALSE;
188
189   if (!mix->context) {
190     mix->context = gst_gl_context_new (mix->display);
191     if (!gst_gl_context_create (mix->context, mix->other_context, &error))
192       goto context_error;
193   }
194
195   if (pool == NULL && need_pool) {
196     GstVideoInfo info;
197
198     if (!gst_video_info_from_caps (&info, caps))
199       goto invalid_caps;
200
201     GST_DEBUG_OBJECT (mix, "create new pool");
202     pool = gst_gl_buffer_pool_new (mix->context);
203
204     /* the normal size of a frame */
205     size = info.size;
206
207     config = gst_buffer_pool_get_config (pool);
208     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
209     if (!gst_buffer_pool_set_config (pool, config))
210       goto config_failed;
211   }
212
213   if (pool) {
214     gst_query_add_allocation_pool (query, pool, size, 1, 0);
215     gst_object_unref (pool);
216   }
217
218   /* we also support various metadata */
219   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
220
221   gl_apis = gst_gl_api_to_string (gst_gl_context_get_gl_api (mix->context));
222   platform =
223       gst_gl_platform_to_string (gst_gl_context_get_gl_platform (mix->context));
224   handle = (gpointer) gst_gl_context_get_gl_context (mix->context);
225
226   gl_context =
227       gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext",
228       GST_GL_TYPE_CONTEXT, mix->context, "gst.gl.context.handle",
229       G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform,
230       "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
231   gst_query_add_allocation_meta (query,
232       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context);
233
234   g_free (gl_apis);
235   g_free (platform);
236   gst_structure_free (gl_context);
237
238   gst_allocation_params_init (&params);
239
240   allocator = gst_allocator_find (GST_GL_MEMORY_ALLOCATOR);
241   gst_query_add_allocation_param (query, allocator, &params);
242   gst_object_unref (allocator);
243
244   return TRUE;
245
246   /* ERRORS */
247 no_caps:
248   {
249     GST_DEBUG_OBJECT (mix, "no caps specified");
250     return FALSE;
251   }
252 invalid_caps:
253   {
254     GST_DEBUG_OBJECT (mix, "invalid caps specified");
255     return FALSE;
256   }
257 config_failed:
258   {
259     GST_DEBUG_OBJECT (mix, "failed setting config");
260     return FALSE;
261   }
262 context_error:
263   {
264     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
265         (NULL));
266     return FALSE;
267   }
268 }
269
270 static gboolean
271 gst_gl_mixer_pad_sink_acceptcaps (GstPad * pad, GstGLMixer * mix,
272     GstCaps * caps)
273 {
274   gboolean ret;
275   GstCaps *template_caps;
276
277   GST_DEBUG_OBJECT (pad, "try accept caps of %" GST_PTR_FORMAT, caps);
278
279   template_caps = gst_pad_get_pad_template_caps (pad);
280   template_caps = gst_caps_make_writable (template_caps);
281
282   ret = gst_caps_can_intersect (caps, template_caps);
283   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
284       (ret ? "" : "not "), caps);
285   gst_caps_unref (template_caps);
286
287   return ret;
288 }
289
290 static GstCaps *
291 gst_gl_mixer_pad_sink_getcaps (GstPad * pad, GstGLMixer * mix, GstCaps * filter)
292 {
293   GstCaps *srccaps;
294   GstCaps *template_caps;
295   GstCaps *filtered_caps;
296   GstCaps *returned_caps;
297   gboolean had_current_caps = TRUE;
298
299   template_caps = gst_pad_get_pad_template_caps (pad);
300
301   srccaps = gst_pad_get_current_caps (pad);
302   if (srccaps == NULL) {
303     had_current_caps = FALSE;
304     srccaps = template_caps;
305   }
306
307   filtered_caps = srccaps;
308   if (filter)
309     filtered_caps = gst_caps_intersect (srccaps, filter);
310   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
311
312   gst_caps_unref (srccaps);
313   if (filter)
314     gst_caps_unref (filtered_caps);
315   if (had_current_caps)
316     gst_caps_unref (template_caps);
317
318   return returned_caps;
319 }
320
321 static gboolean
322 gst_gl_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
323     GstQuery * query)
324 {
325   gboolean ret = FALSE;
326   GstGLMixer *mix = GST_GL_MIXER (agg);
327
328   GST_TRACE ("QUERY %" GST_PTR_FORMAT, query);
329
330   switch (GST_QUERY_TYPE (query)) {
331     case GST_QUERY_CAPS:
332     {
333       GstCaps *filter, *caps;
334
335       gst_query_parse_caps (query, &filter);
336       caps = gst_gl_mixer_pad_sink_getcaps (GST_PAD (bpad), mix, filter);
337       gst_query_set_caps_result (query, caps);
338       gst_caps_unref (caps);
339       ret = TRUE;
340       break;
341     }
342     case GST_QUERY_ACCEPT_CAPS:
343     {
344       GstCaps *caps;
345
346       gst_query_parse_accept_caps (query, &caps);
347       ret = gst_gl_mixer_pad_sink_acceptcaps (GST_PAD (bpad), mix, caps);
348       gst_query_set_accept_caps_result (query, ret);
349       ret = TRUE;
350       break;
351     }
352     case GST_QUERY_ALLOCATION:
353     {
354       GstQuery *decide_query = NULL;
355
356       GST_OBJECT_LOCK (mix);
357       if (G_UNLIKELY (!mix->priv->negotiated)) {
358         GST_DEBUG_OBJECT (mix,
359             "not negotiated yet, can't answer ALLOCATION query");
360         GST_OBJECT_UNLOCK (mix);
361         return FALSE;
362       }
363       if ((decide_query = mix->priv->query))
364         gst_query_ref (decide_query);
365       GST_OBJECT_UNLOCK (mix);
366
367       GST_DEBUG_OBJECT (mix,
368           "calling propose allocation with query %" GST_PTR_FORMAT,
369           decide_query);
370
371       /* pass the query to the propose_allocation vmethod if any */
372       ret = gst_gl_mixer_propose_allocation (mix, decide_query, query);
373
374       if (decide_query)
375         gst_query_unref (decide_query);
376
377       GST_DEBUG_OBJECT (mix, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, query);
378       break;
379     }
380     case GST_QUERY_CONTEXT:
381     {
382       ret = gst_gl_handle_context_query ((GstElement *) mix, query,
383           &mix->display, &mix->other_context);
384       break;
385     }
386     default:
387       ret = GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
388       break;
389   }
390
391   return ret;
392 }
393
394 static void
395 gst_gl_mixer_pad_init (GstGLMixerPad * mixerpad)
396 {
397 }
398
399 /* GLMixer signals and args */
400 enum
401 {
402   /* FILL ME */
403   LAST_SIGNAL
404 };
405
406 enum
407 {
408   PROP_0
409 };
410
411 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
412     GST_PAD_SRC,
413     GST_PAD_ALWAYS,
414     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
415         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
416             "RGBA") "; "
417         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
418         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
419             "RGBA")
420         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
421     );
422
423 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
424     GST_PAD_SINK,
425     GST_PAD_REQUEST,
426     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
427         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
428             "RGBA") "; "
429         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
430         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
431             "RGBA")
432         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
433     );
434
435 static gboolean gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query);
436 static GstFlowReturn
437 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
438     GstBuffer ** outbuf);
439 static gboolean
440 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
441     gboolean active);
442 static gboolean gst_gl_mixer_stop (GstAggregator * agg);
443 static gboolean gst_gl_mixer_start (GstAggregator * agg);
444
445 static GstFlowReturn
446 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg,
447     GstBuffer * outbuffer);
448
449 static void gst_gl_mixer_set_property (GObject * object, guint prop_id,
450     const GValue * value, GParamSpec * pspec);
451 static void gst_gl_mixer_get_property (GObject * object, guint prop_id,
452     GValue * value, GParamSpec * pspec);
453
454 static gboolean gst_gl_mixer_decide_allocation (GstGLMixer * mix,
455     GstQuery * query);
456 static gboolean gst_gl_mixer_set_allocation (GstGLMixer * mix,
457     GstBufferPool * pool, GstAllocator * allocator,
458     GstAllocationParams * params, GstQuery * query);
459
460 static void gst_gl_mixer_finalize (GObject * object);
461
462 static void
463 gst_gl_mixer_class_init (GstGLMixerClass * klass)
464 {
465   GObjectClass *gobject_class;
466   GstElementClass *element_class;
467
468   GstVideoAggregatorClass *videoaggregator_class =
469       (GstVideoAggregatorClass *) klass;
470   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
471
472   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "opengl mixer");
473
474   gobject_class = (GObjectClass *) klass;
475   element_class = GST_ELEMENT_CLASS (klass);
476
477   g_type_class_add_private (klass, sizeof (GstGLMixerPrivate));
478
479   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_finalize);
480
481   gobject_class->get_property = gst_gl_mixer_get_property;
482   gobject_class->set_property = gst_gl_mixer_set_property;
483
484   gst_element_class_add_pad_template (element_class,
485       gst_static_pad_template_get (&src_factory));
486   gst_element_class_add_pad_template (element_class,
487       gst_static_pad_template_get (&sink_factory));
488
489   element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_mixer_set_context);
490
491   agg_class->sinkpads_type = GST_TYPE_GL_MIXER_PAD;
492   agg_class->sink_query = gst_gl_mixer_sink_query;
493   agg_class->src_query = gst_gl_mixer_src_query;
494   agg_class->src_activate = gst_gl_mixer_src_activate_mode;
495   agg_class->stop = gst_gl_mixer_stop;
496   agg_class->start = gst_gl_mixer_start;
497
498   videoaggregator_class->disable_frame_conversion = TRUE;
499   videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
500   videoaggregator_class->get_output_buffer = gst_gl_mixer_get_output_buffer;
501   videoaggregator_class->negotiated_caps = _negotiated_caps;
502
503
504   /* Register the pad class */
505   g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
506
507   klass->set_caps = NULL;
508
509 }
510
511 static void
512 gst_gl_mixer_reset (GstGLMixer * mix)
513 {
514   /* clean up collect data */
515   mix->priv->negotiated = FALSE;
516 }
517
518 static void
519 gst_gl_mixer_init (GstGLMixer * mix)
520 {
521   mix->priv = GST_GL_MIXER_GET_PRIVATE (mix);
522   mix->array_buffers = 0;
523   mix->display = NULL;
524   mix->fbo = 0;
525   mix->depthbuffer = 0;
526
527   mix->priv->gl_resource_ready = FALSE;
528   g_mutex_init (&mix->priv->gl_resource_lock);
529   g_cond_init (&mix->priv->gl_resource_cond);
530   /* initialize variables */
531   gst_gl_mixer_reset (mix);
532 }
533
534 static void
535 gst_gl_mixer_finalize (GObject * object)
536 {
537   GstGLMixer *mix = GST_GL_MIXER (object);
538   GstGLMixerPrivate *priv = mix->priv;
539
540   if (mix->other_context) {
541     gst_object_unref (mix->other_context);
542     mix->other_context = NULL;
543   }
544
545   g_mutex_clear (&priv->gl_resource_lock);
546   g_cond_clear (&priv->gl_resource_cond);
547   G_OBJECT_CLASS (parent_class)->finalize (object);
548 }
549
550 static void
551 gst_gl_mixer_set_context (GstElement * element, GstContext * context)
552 {
553   GstGLMixer *mix = GST_GL_MIXER (element);
554
555   gst_gl_handle_set_context (element, context, &mix->display, &mix->context);
556 }
557
558 static gboolean
559 gst_gl_mixer_activate (GstGLMixer * mix, gboolean active)
560 {
561   gboolean result = TRUE;
562
563   if (active) {
564     if (!gst_gl_ensure_element_data (mix, &mix->display, &mix->other_context))
565       result = FALSE;
566   }
567
568   return result;
569 }
570
571 static gboolean
572 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
573     gboolean active)
574 {
575   GstGLMixer *mix;
576   gboolean result = FALSE;
577
578   mix = GST_GL_MIXER (aggregator);
579
580   switch (mode) {
581     case GST_PAD_MODE_PUSH:
582     case GST_PAD_MODE_PULL:
583       result = gst_gl_mixer_activate (mix, active);
584       break;
585     default:
586       result = TRUE;
587       break;
588   }
589   return result;
590 }
591
592 static gboolean
593 gst_gl_mixer_query_caps (GstPad * pad, GstAggregator * agg, GstQuery * query)
594 {
595   GstCaps *filter, *caps;
596   GstStructure *s;
597   gint n;
598
599   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
600
601   gst_query_parse_caps (query, &filter);
602
603   if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
604     caps = gst_video_info_to_caps (&vagg->info);
605   } else {
606     caps = gst_pad_get_pad_template_caps (agg->srcpad);
607   }
608
609   caps = gst_caps_make_writable (caps);
610
611   n = gst_caps_get_size (caps) - 1;
612   for (; n >= 0; n--) {
613     s = gst_caps_get_structure (caps, n);
614     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
615         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
616     if (GST_VIDEO_INFO_FPS_D (&vagg->info) != 0) {
617       gst_structure_set (s,
618           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
619     }
620   }
621
622   if (filter)
623     caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
624
625   gst_query_set_caps_result (query, caps);
626   gst_caps_unref (caps);
627
628   return TRUE;
629 }
630
631 static GstCaps *
632 gst_gl_mixer_set_caps_features (const GstCaps * caps,
633     const gchar * feature_name)
634 {
635   GstCaps *tmp = gst_caps_copy (caps);
636   guint n = gst_caps_get_size (tmp);
637   guint i = 0;
638
639   for (i = 0; i < n; i++) {
640     GstCapsFeatures *features = gst_caps_get_features (tmp, i);
641     if (features) {
642       guint n_f = gst_caps_features_get_size (features);
643       guint j = 0;
644       for (j = 0; j < n_f; j++) {
645         gst_caps_features_remove_id (features,
646             gst_caps_features_get_nth_id (features, j));
647       }
648     }
649
650     gst_caps_features_add (features, feature_name);
651     gst_caps_set_simple (tmp, "format", G_TYPE_STRING, "RGBA", NULL);
652   }
653
654   return tmp;
655 }
656
657 /* copies the given caps */
658 static GstCaps *
659 gst_gl_mixer_caps_remove_format_info (GstCaps * caps)
660 {
661   GstStructure *st;
662   GstCapsFeatures *f;
663   gint i, n;
664   GstCaps *res;
665
666   res = gst_caps_new_empty ();
667
668   n = gst_caps_get_size (caps);
669   for (i = 0; i < n; i++) {
670     st = gst_caps_get_structure (caps, i);
671     f = gst_caps_get_features (caps, i);
672
673     /* If this is already expressed by the existing caps
674      * skip this structure */
675     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
676       continue;
677
678     st = gst_structure_copy (st);
679     /* Only remove format info for the cases when we can actually convert */
680     if (!gst_caps_features_is_any (f)
681         && gst_caps_features_is_equal (f,
682             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
683       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
684           "width", "height", NULL);
685
686     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
687   }
688
689   return res;
690 }
691
692 GstCaps *
693 gst_gl_mixer_update_caps (GstGLMixer * mix, GstCaps * caps)
694 {
695   GstCaps *result = NULL;
696   GstCaps *glcaps = gst_gl_mixer_set_caps_features (caps,
697       GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
698 #if GST_GL_HAVE_PLATFORM_EGL
699   GstCaps *eglcaps = gst_gl_mixer_set_caps_features (caps,
700       GST_CAPS_FEATURE_MEMORY_EGL_IMAGE);
701 #endif
702   GstCaps *uploadcaps = gst_gl_mixer_set_caps_features (caps,
703       GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META);
704   GstCaps *raw_caps =
705       gst_caps_from_string (GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS));
706
707   result = gst_caps_new_empty ();
708
709   result = gst_caps_merge (result, glcaps);
710 #if GST_GL_HAVE_PLATFORM_EGL
711   result = gst_caps_merge (result, eglcaps);
712 #endif
713   result = gst_caps_merge (result, uploadcaps);
714   result = gst_caps_merge (result, raw_caps);
715
716   result = gst_caps_merge (result, gst_gl_mixer_caps_remove_format_info (caps));
717
718   GST_DEBUG_OBJECT (mix, "returning %" GST_PTR_FORMAT, result);
719
720   return result;
721 }
722
723 static gboolean
724 gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query)
725 {
726   gboolean res = FALSE;
727   GstGLMixer *mix = GST_GL_MIXER (agg);
728
729   switch (GST_QUERY_TYPE (query)) {
730     case GST_QUERY_CONTEXT:
731     {
732       res = gst_gl_handle_context_query ((GstElement *) mix, query,
733           &mix->display, &mix->other_context);
734       break;
735     }
736     case GST_QUERY_CAPS:
737       res = gst_gl_mixer_query_caps (agg->srcpad, agg, query);
738       break;
739     default:
740       res = GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
741       break;
742   }
743
744   return res;
745 }
746
747 static GstFlowReturn
748 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
749     GstBuffer ** outbuf)
750 {
751   GstGLMixer *mix = GST_GL_MIXER (videoaggregator);
752
753   if (!mix->priv->pool_active) {
754     if (!gst_buffer_pool_set_active (mix->priv->pool, TRUE)) {
755       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
756           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
757       return GST_FLOW_ERROR;
758     }
759     mix->priv->pool_active = TRUE;
760   }
761
762   return gst_buffer_pool_acquire_buffer (mix->priv->pool, outbuf, NULL);
763 }
764
765 static gboolean
766 gst_gl_mixer_decide_allocation (GstGLMixer * mix, GstQuery * query)
767 {
768   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
769   GstBufferPool *pool = NULL;
770   GstStructure *config;
771   GstCaps *caps;
772   guint min, max, size;
773   gboolean update_pool;
774   GError *error = NULL;
775   guint idx;
776   guint out_width, out_height;
777   GstGLContext *other_context = NULL;
778   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
779
780   if (!gst_gl_ensure_element_data (mix, &mix->display, &mix->other_context))
781     return FALSE;
782
783   if (gst_query_find_allocation_meta (query,
784           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
785     GstGLContext *context;
786     const GstStructure *upload_meta_params;
787     gpointer handle;
788     gchar *type;
789     gchar *apis;
790
791     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
792     if (upload_meta_params) {
793       if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
794               GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
795         GstGLContext *old = mix->context;
796
797         mix->context = context;
798         if (old)
799           gst_object_unref (old);
800       } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle",
801               G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING,
802               &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL)
803           && handle) {
804         GstGLPlatform platform;
805         GstGLAPI gl_apis;
806
807         GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s",
808             handle, type, apis);
809
810         platform = gst_gl_platform_from_string (type);
811         gl_apis = gst_gl_api_from_string (apis);
812
813         if (gl_apis && platform)
814           other_context =
815               gst_gl_context_new_wrapped (mix->display, (guintptr) handle,
816               platform, gl_apis);
817       }
818     }
819   }
820
821   if (mix->other_context) {
822     if (!other_context) {
823       other_context = mix->other_context;
824     } else {
825       GST_ELEMENT_WARNING (mix, LIBRARY, SETTINGS,
826           ("%s", "Cannot share with more than one GL context"),
827           ("%s", "Cannot share with more than one GL context"));
828     }
829   }
830
831   if (!mix->context) {
832     mix->context = gst_gl_context_new (mix->display);
833     if (!gst_gl_context_create (mix->context, other_context, &error))
834       goto context_error;
835   }
836
837   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
838   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
839
840   g_mutex_lock (&mix->priv->gl_resource_lock);
841   mix->priv->gl_resource_ready = FALSE;
842   if (mix->fbo) {
843     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
844     mix->fbo = 0;
845     mix->depthbuffer = 0;
846   }
847
848   if (!gst_gl_context_gen_fbo (mix->context, out_width, out_height,
849           &mix->fbo, &mix->depthbuffer)) {
850     g_cond_signal (&mix->priv->gl_resource_cond);
851     g_mutex_unlock (&mix->priv->gl_resource_lock);
852     goto context_error;
853   }
854
855   if (mix->out_tex_id)
856     gst_gl_context_del_texture (mix->context, &mix->out_tex_id);
857   gst_gl_context_gen_texture (mix->context, &mix->out_tex_id,
858       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
859
860   gst_query_parse_allocation (query, &caps, NULL);
861
862   if (mixer_class->set_caps)
863     mixer_class->set_caps (mix, caps);
864
865   mix->priv->gl_resource_ready = TRUE;
866   g_cond_signal (&mix->priv->gl_resource_cond);
867   g_mutex_unlock (&mix->priv->gl_resource_lock);
868
869   if (gst_query_get_n_allocation_pools (query) > 0) {
870     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
871
872     update_pool = TRUE;
873   } else {
874     GstVideoInfo vinfo;
875
876     gst_video_info_init (&vinfo);
877     gst_video_info_from_caps (&vinfo, caps);
878     size = vinfo.size;
879     min = max = 0;
880     update_pool = FALSE;
881   }
882
883   if (!pool)
884     pool = gst_gl_buffer_pool_new (mix->context);
885
886   config = gst_buffer_pool_get_config (pool);
887   gst_buffer_pool_config_set_params (config, caps, size, min, max);
888
889   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
890   gst_buffer_pool_config_add_option (config,
891       GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
892
893   gst_buffer_pool_set_config (pool, config);
894
895   if (update_pool)
896     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
897   else
898     gst_query_add_allocation_pool (query, pool, size, min, max);
899
900   gst_object_unref (pool);
901
902   return TRUE;
903
904 context_error:
905   {
906     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
907         (NULL));
908     return FALSE;
909   }
910 }
911
912 /* takes ownership of the pool, allocator and query */
913 static gboolean
914 gst_gl_mixer_set_allocation (GstGLMixer * mix,
915     GstBufferPool * pool, GstAllocator * allocator,
916     GstAllocationParams * params, GstQuery * query)
917 {
918   GstAllocator *oldalloc;
919   GstBufferPool *oldpool;
920   GstQuery *oldquery;
921   GstGLMixerPrivate *priv = mix->priv;
922
923   GST_DEBUG ("storing allocation query");
924
925   GST_OBJECT_LOCK (mix);
926   oldpool = priv->pool;
927   priv->pool = pool;
928   priv->pool_active = FALSE;
929
930   oldalloc = priv->allocator;
931   priv->allocator = allocator;
932
933   oldquery = priv->query;
934   priv->query = query;
935
936   if (params)
937     priv->params = *params;
938   else
939     gst_allocation_params_init (&priv->params);
940   GST_OBJECT_UNLOCK (mix);
941
942   if (oldpool) {
943     GST_DEBUG_OBJECT (mix, "deactivating old pool %p", oldpool);
944     gst_buffer_pool_set_active (oldpool, FALSE);
945     gst_object_unref (oldpool);
946   }
947   if (oldalloc) {
948     gst_object_unref (oldalloc);
949   }
950   if (oldquery) {
951     gst_query_unref (oldquery);
952   }
953   return TRUE;
954 }
955
956 static gboolean
957 gst_gl_mixer_do_bufferpool (GstGLMixer * mix, GstCaps * outcaps)
958 {
959   GstQuery *query;
960   gboolean result = TRUE;
961   GstBufferPool *pool = NULL;
962   GstAllocator *allocator;
963   GstAllocationParams params;
964   GstAggregator *agg = GST_AGGREGATOR (mix);
965
966   /* find a pool for the negotiated caps now */
967   GST_DEBUG_OBJECT (mix, "doing allocation query");
968   query = gst_query_new_allocation (outcaps, TRUE);
969   if (!gst_pad_peer_query (agg->srcpad, query)) {
970     /* not a problem, just debug a little */
971     GST_DEBUG_OBJECT (mix, "peer ALLOCATION query failed");
972   }
973
974   GST_DEBUG_OBJECT (mix, "calling decide_allocation");
975   result = gst_gl_mixer_decide_allocation (mix, query);
976
977   GST_DEBUG_OBJECT (mix, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
978       query);
979
980   if (!result)
981     goto no_decide_allocation;
982
983   /* we got configuration from our peer or the decide_allocation method,
984    * parse them */
985   if (gst_query_get_n_allocation_params (query) > 0) {
986     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
987   } else {
988     allocator = NULL;
989     gst_allocation_params_init (&params);
990   }
991
992   if (gst_query_get_n_allocation_pools (query) > 0)
993     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
994
995   /* now store */
996   result = gst_gl_mixer_set_allocation (mix, pool, allocator, &params, query);
997
998   return result;
999
1000   /* Errors */
1001 no_decide_allocation:
1002   {
1003     GST_WARNING_OBJECT (mix, "Failed to decide allocation");
1004     gst_query_unref (query);
1005
1006     return result;
1007   }
1008 }
1009
1010 gboolean
1011 gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
1012 {
1013   guint i;
1014   GList *walk;
1015   guint out_tex;
1016   gboolean res = TRUE;
1017   guint array_index = 0;
1018   GstVideoFrame out_frame;
1019   GstElement *element = GST_ELEMENT (mix);
1020   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
1021   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
1022   GstGLMixerPrivate *priv = mix->priv;
1023   gboolean to_download =
1024       gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY,
1025       gst_caps_get_features (mix->out_caps, 0));
1026   GstMapFlags out_map_flags = GST_MAP_WRITE;
1027
1028   GST_TRACE ("Processing buffers");
1029
1030   to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0));
1031
1032   if (!to_download)
1033     out_map_flags |= GST_MAP_GL;
1034
1035   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, out_map_flags)) {
1036     return FALSE;
1037   }
1038
1039   if (!to_download) {
1040     out_tex = *(guint *) out_frame.data[0];
1041   } else {
1042     GST_INFO ("Output Buffer does not contain correct memory, "
1043         "attempting to wrap for download");
1044
1045     if (!mix->download)
1046       mix->download = gst_gl_download_new (mix->context);
1047
1048     gst_gl_download_set_format (mix->download, &out_frame.info);
1049     out_tex = mix->out_tex_id;
1050   }
1051
1052   GST_OBJECT_LOCK (mix);
1053   walk = element->sinkpads;
1054
1055   i = mix->frames->len;
1056   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1057   for (; i < element->numsinkpads; i++)
1058     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1059   while (walk) {
1060     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
1061     GstVideoAggregatorPad *vaggpad = walk->data;
1062     GstGLMixerFrameData *frame;
1063
1064     frame = g_ptr_array_index (mix->frames, array_index);
1065     frame->pad = pad;
1066     frame->texture = 0;
1067
1068     walk = g_list_next (walk);
1069
1070     if (vaggpad->buffer != NULL) {
1071       guint in_tex;
1072
1073       if (!pad->upload) {
1074         pad->upload = gst_gl_upload_new (mix->context);
1075
1076         gst_gl_upload_set_format (pad->upload, &vaggpad->info);
1077       }
1078
1079       if (!gst_gl_upload_perform_with_buffer (pad->upload,
1080               vaggpad->buffer, &in_tex, NULL)) {
1081         ++array_index;
1082         pad->mapped = FALSE;
1083         continue;
1084       }
1085       pad->mapped = TRUE;
1086
1087       frame->texture = in_tex;
1088     }
1089     ++array_index;
1090   }
1091
1092   g_mutex_lock (&priv->gl_resource_lock);
1093   if (!priv->gl_resource_ready)
1094     g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
1095
1096   if (!priv->gl_resource_ready) {
1097     g_mutex_unlock (&priv->gl_resource_lock);
1098     GST_ERROR_OBJECT (mix,
1099         "fbo used to render can't be created, do not run process_textures");
1100     res = FALSE;
1101     goto out;
1102   }
1103
1104   mix_class->process_textures (mix, mix->frames, out_tex);
1105
1106   g_mutex_unlock (&priv->gl_resource_lock);
1107
1108   if (to_download) {
1109     if (!gst_gl_download_perform_with_data (mix->download, out_tex,
1110             out_frame.data)) {
1111       GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s",
1112               "Failed to download video frame"), (NULL));
1113       res = FALSE;
1114       goto out;
1115     }
1116   }
1117
1118 out:
1119   i = 0;
1120   walk = GST_ELEMENT (mix)->sinkpads;
1121   while (walk) {
1122     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
1123
1124     if (pad->mapped)
1125       gst_gl_upload_release_buffer (pad->upload);
1126
1127     pad->mapped = FALSE;
1128     walk = g_list_next (walk);
1129     i++;
1130   }
1131   GST_OBJECT_UNLOCK (mix);
1132
1133   gst_video_frame_unmap (&out_frame);
1134
1135   return res;
1136 }
1137
1138 static gboolean
1139 gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
1140 {
1141   GList *walk;
1142   guint i, array_index = 0;
1143   GstElement *element = GST_ELEMENT (mix);
1144   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
1145
1146   GST_OBJECT_LOCK (mix);
1147   walk = GST_ELEMENT (mix)->sinkpads;
1148   i = mix->frames->len;
1149   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1150   for (; i < element->numsinkpads; i++)
1151     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1152   while (walk) {                /* We walk with this list because it's ordered */
1153     GstVideoAggregatorPad *vaggpad = walk->data;
1154
1155     walk = g_list_next (walk);
1156
1157     if (vaggpad->buffer != NULL) {
1158       /* put buffer into array */
1159       mix->array_buffers->pdata[array_index] = vaggpad->buffer;
1160     }
1161     ++array_index;
1162   }
1163   GST_OBJECT_UNLOCK (mix);
1164
1165   return mix_class->process_buffers (mix, mix->array_buffers, outbuf);
1166 }
1167
1168
1169
1170 static GstFlowReturn
1171 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1172 {
1173   gboolean res = FALSE;
1174   GstGLMixer *mix = GST_GL_MIXER (vagg);
1175   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
1176
1177   if (mix_class->process_buffers)
1178     res = gst_gl_mixer_process_buffers (mix, outbuf);
1179   else if (mix_class->process_textures)
1180     res = gst_gl_mixer_process_textures (mix, outbuf);
1181
1182   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
1183 }
1184
1185 static void
1186 gst_gl_mixer_get_property (GObject * object,
1187     guint prop_id, GValue * value, GParamSpec * pspec)
1188 {
1189   switch (prop_id) {
1190     default:
1191       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1192       break;
1193   }
1194 }
1195
1196 static void
1197 gst_gl_mixer_set_property (GObject * object,
1198     guint prop_id, const GValue * value, GParamSpec * pspec)
1199 {
1200   switch (prop_id) {
1201     default:
1202       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1203       break;
1204   }
1205 }
1206
1207 static gboolean
1208 _clean_upload (GstAggregator * agg, GstPad * aggpad, gpointer udata)
1209 {
1210   GstGLMixerPad *pad = GST_GL_MIXER_PAD (aggpad);
1211
1212   if (pad->upload) {
1213     gst_object_unref (pad->upload);
1214     pad->upload = NULL;
1215   }
1216
1217   return TRUE;
1218 }
1219
1220 static void
1221 _free_glmixer_frame_data (GstGLMixerFrameData * frame)
1222 {
1223   g_slice_free1 (sizeof (GstGLMixerFrameData), frame);
1224 }
1225
1226 static gboolean
1227 gst_gl_mixer_start (GstAggregator * agg)
1228 {
1229   guint i;
1230   GstGLMixer *mix = GST_GL_MIXER (agg);
1231   GstElement *element = GST_ELEMENT (agg);
1232
1233   if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg))
1234     return FALSE;
1235
1236   GST_OBJECT_LOCK (mix);
1237   mix->array_buffers = g_ptr_array_new_full (element->numsinkpads,
1238       (GDestroyNotify) _free_glmixer_frame_data);
1239   mix->frames = g_ptr_array_new_full (element->numsinkpads, NULL);
1240
1241   g_ptr_array_set_size (mix->array_buffers, element->numsinkpads);
1242   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1243
1244   for (i = 0; i < element->numsinkpads; i++)
1245     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1246
1247   GST_OBJECT_UNLOCK (mix);
1248
1249   return TRUE;
1250 }
1251
1252 static gboolean
1253 gst_gl_mixer_stop (GstAggregator * agg)
1254 {
1255   GstGLMixer *mix = GST_GL_MIXER (agg);
1256   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
1257
1258   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
1259     return FALSE;
1260
1261   GST_OBJECT_LOCK (agg);
1262   g_ptr_array_free (mix->frames, TRUE);
1263   mix->frames = NULL;
1264   g_ptr_array_free (mix->array_buffers, TRUE);
1265   mix->array_buffers = NULL;
1266   GST_OBJECT_UNLOCK (agg);
1267
1268   if (mixer_class->reset)
1269     mixer_class->reset (mix);
1270   if (mix->fbo) {
1271     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
1272     mix->fbo = 0;
1273     mix->depthbuffer = 0;
1274   }
1275   if (mix->download) {
1276     gst_object_unref (mix->download);
1277     mix->download = NULL;
1278   }
1279
1280   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (mix), _clean_upload, NULL);
1281
1282   if (mix->priv->query) {
1283     gst_query_unref (mix->priv->query);
1284     mix->priv->query = NULL;
1285   }
1286
1287   if (mix->priv->pool) {
1288     gst_object_unref (mix->priv->pool);
1289     mix->priv->pool = NULL;
1290   }
1291
1292   if (mix->display) {
1293     gst_object_unref (mix->display);
1294     mix->display = NULL;
1295   }
1296
1297   if (mix->context) {
1298     gst_object_unref (mix->context);
1299     mix->context = NULL;
1300   }
1301
1302   gst_gl_mixer_reset (mix);
1303
1304   return TRUE;
1305 }