glmixer: override the accept 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 gboolean
291 gst_gl_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
292     GstQuery * query)
293 {
294   gboolean ret = FALSE;
295   GstGLMixer *mix = GST_GL_MIXER (agg);
296
297   GST_TRACE ("QUERY %" GST_PTR_FORMAT, query);
298
299   switch (GST_QUERY_TYPE (query)) {
300     case GST_QUERY_ACCEPT_CAPS:
301     {
302       GstCaps *caps;
303
304       gst_query_parse_accept_caps (query, &caps);
305       ret = gst_gl_mixer_pad_sink_acceptcaps (GST_PAD (bpad), mix, caps);
306       gst_query_set_accept_caps_result (query, ret);
307       ret = TRUE;
308       break;
309     }
310     case GST_QUERY_ALLOCATION:
311     {
312       GstQuery *decide_query = NULL;
313
314       GST_OBJECT_LOCK (mix);
315       if (G_UNLIKELY (!mix->priv->negotiated)) {
316         GST_DEBUG_OBJECT (mix,
317             "not negotiated yet, can't answer ALLOCATION query");
318         GST_OBJECT_UNLOCK (mix);
319         return FALSE;
320       }
321       if ((decide_query = mix->priv->query))
322         gst_query_ref (decide_query);
323       GST_OBJECT_UNLOCK (mix);
324
325       GST_DEBUG_OBJECT (mix,
326           "calling propose allocation with query %" GST_PTR_FORMAT,
327           decide_query);
328
329       /* pass the query to the propose_allocation vmethod if any */
330       ret = gst_gl_mixer_propose_allocation (mix, decide_query, query);
331
332       if (decide_query)
333         gst_query_unref (decide_query);
334
335       GST_DEBUG_OBJECT (mix, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, query);
336       break;
337     }
338     case GST_QUERY_CONTEXT:
339     {
340       ret = gst_gl_handle_context_query ((GstElement *) mix, query,
341           &mix->display, &mix->other_context);
342       break;
343     }
344     default:
345       ret = GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
346       break;
347   }
348
349   return ret;
350 }
351
352 static void
353 gst_gl_mixer_pad_init (GstGLMixerPad * mixerpad)
354 {
355 }
356
357 /* GLMixer signals and args */
358 enum
359 {
360   /* FILL ME */
361   LAST_SIGNAL
362 };
363
364 enum
365 {
366   PROP_0
367 };
368
369 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
370     GST_PAD_SRC,
371     GST_PAD_ALWAYS,
372     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
373         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
374             "RGBA") "; "
375         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
376         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
377             "RGBA")
378         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
379     );
380
381 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
382     GST_PAD_SINK,
383     GST_PAD_REQUEST,
384     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
385         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
386             "RGBA") "; "
387         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
388         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
389             "RGBA")
390         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
391     );
392
393 static gboolean gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query);
394 static GstFlowReturn
395 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
396     GstBuffer ** outbuf);
397 static gboolean
398 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
399     gboolean active);
400 static gboolean gst_gl_mixer_stop (GstAggregator * agg);
401 static gboolean gst_gl_mixer_start (GstAggregator * agg);
402
403 static GstFlowReturn
404 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg,
405     GstBuffer * outbuffer);
406
407 static void gst_gl_mixer_set_property (GObject * object, guint prop_id,
408     const GValue * value, GParamSpec * pspec);
409 static void gst_gl_mixer_get_property (GObject * object, guint prop_id,
410     GValue * value, GParamSpec * pspec);
411
412 static gboolean gst_gl_mixer_decide_allocation (GstGLMixer * mix,
413     GstQuery * query);
414 static gboolean gst_gl_mixer_set_allocation (GstGLMixer * mix,
415     GstBufferPool * pool, GstAllocator * allocator,
416     GstAllocationParams * params, GstQuery * query);
417
418 static void gst_gl_mixer_finalize (GObject * object);
419
420 static void
421 gst_gl_mixer_class_init (GstGLMixerClass * klass)
422 {
423   GObjectClass *gobject_class;
424   GstElementClass *element_class;
425
426   GstVideoAggregatorClass *videoaggregator_class =
427       (GstVideoAggregatorClass *) klass;
428   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
429
430   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "opengl mixer");
431
432   gobject_class = (GObjectClass *) klass;
433   element_class = GST_ELEMENT_CLASS (klass);
434
435   g_type_class_add_private (klass, sizeof (GstGLMixerPrivate));
436
437   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_finalize);
438
439   gobject_class->get_property = gst_gl_mixer_get_property;
440   gobject_class->set_property = gst_gl_mixer_set_property;
441
442   gst_element_class_add_pad_template (element_class,
443       gst_static_pad_template_get (&src_factory));
444   gst_element_class_add_pad_template (element_class,
445       gst_static_pad_template_get (&sink_factory));
446
447   element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_mixer_set_context);
448
449   agg_class->sinkpads_type = GST_TYPE_GL_MIXER_PAD;
450   agg_class->sink_query = gst_gl_mixer_sink_query;
451   agg_class->src_query = gst_gl_mixer_src_query;
452   agg_class->src_activate = gst_gl_mixer_src_activate_mode;
453   agg_class->stop = gst_gl_mixer_stop;
454   agg_class->start = gst_gl_mixer_start;
455
456   videoaggregator_class->disable_frame_conversion = TRUE;
457   videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
458   videoaggregator_class->get_output_buffer = gst_gl_mixer_get_output_buffer;
459   videoaggregator_class->negotiated_caps = _negotiated_caps;
460
461
462   /* Register the pad class */
463   g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
464
465   klass->set_caps = NULL;
466
467 }
468
469 static void
470 gst_gl_mixer_reset (GstGLMixer * mix)
471 {
472   /* clean up collect data */
473   mix->priv->negotiated = FALSE;
474 }
475
476 static void
477 gst_gl_mixer_init (GstGLMixer * mix)
478 {
479   mix->priv = GST_GL_MIXER_GET_PRIVATE (mix);
480   mix->array_buffers = 0;
481   mix->display = NULL;
482   mix->fbo = 0;
483   mix->depthbuffer = 0;
484
485   mix->priv->gl_resource_ready = FALSE;
486   g_mutex_init (&mix->priv->gl_resource_lock);
487   g_cond_init (&mix->priv->gl_resource_cond);
488   /* initialize variables */
489   gst_gl_mixer_reset (mix);
490 }
491
492 static void
493 gst_gl_mixer_finalize (GObject * object)
494 {
495   GstGLMixer *mix = GST_GL_MIXER (object);
496   GstGLMixerPrivate *priv = mix->priv;
497
498   if (mix->other_context) {
499     gst_object_unref (mix->other_context);
500     mix->other_context = NULL;
501   }
502
503   g_mutex_clear (&priv->gl_resource_lock);
504   g_cond_clear (&priv->gl_resource_cond);
505   G_OBJECT_CLASS (parent_class)->finalize (object);
506 }
507
508 static void
509 gst_gl_mixer_set_context (GstElement * element, GstContext * context)
510 {
511   GstGLMixer *mix = GST_GL_MIXER (element);
512
513   gst_gl_handle_set_context (element, context, &mix->display, &mix->context);
514 }
515
516 static gboolean
517 gst_gl_mixer_activate (GstGLMixer * mix, gboolean active)
518 {
519   gboolean result = TRUE;
520
521   if (active) {
522     if (!gst_gl_ensure_element_data (mix, &mix->display, &mix->other_context))
523       result = FALSE;
524   }
525
526   return result;
527 }
528
529 static gboolean
530 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
531     gboolean active)
532 {
533   GstGLMixer *mix;
534   gboolean result = FALSE;
535
536   mix = GST_GL_MIXER (aggregator);
537
538   switch (mode) {
539     case GST_PAD_MODE_PUSH:
540     case GST_PAD_MODE_PULL:
541       result = gst_gl_mixer_activate (mix, active);
542       break;
543     default:
544       result = TRUE;
545       break;
546   }
547   return result;
548 }
549
550 static gboolean
551 gst_gl_mixer_query_caps (GstPad * pad, GstAggregator * agg, GstQuery * query)
552 {
553   GstCaps *filter, *caps;
554   GstStructure *s;
555   gint n;
556
557   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
558
559   gst_query_parse_caps (query, &filter);
560
561   if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
562     caps = gst_video_info_to_caps (&vagg->info);
563   } else {
564     caps = gst_pad_get_pad_template_caps (agg->srcpad);
565   }
566
567   caps = gst_caps_make_writable (caps);
568
569   n = gst_caps_get_size (caps) - 1;
570   for (; n >= 0; n--) {
571     s = gst_caps_get_structure (caps, n);
572     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
573         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
574     if (GST_VIDEO_INFO_FPS_D (&vagg->info) != 0) {
575       gst_structure_set (s,
576           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
577     }
578   }
579
580   if (filter)
581     caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
582
583   gst_query_set_caps_result (query, caps);
584   gst_caps_unref (caps);
585
586   return TRUE;
587 }
588
589 static GstCaps *
590 gst_gl_mixer_set_caps_features (const GstCaps * caps,
591     const gchar * feature_name)
592 {
593   GstCaps *tmp = gst_caps_copy (caps);
594   guint n = gst_caps_get_size (tmp);
595   guint i = 0;
596
597   for (i = 0; i < n; i++) {
598     GstCapsFeatures *features = gst_caps_get_features (tmp, i);
599     if (features) {
600       guint n_f = gst_caps_features_get_size (features);
601       guint j = 0;
602       for (j = 0; j < n_f; j++) {
603         gst_caps_features_remove_id (features,
604             gst_caps_features_get_nth_id (features, j));
605       }
606     }
607
608     gst_caps_features_add (features, feature_name);
609     gst_caps_set_simple (tmp, "format", G_TYPE_STRING, "RGBA", NULL);
610   }
611
612   return tmp;
613 }
614
615 /* copies the given caps */
616 static GstCaps *
617 gst_gl_mixer_caps_remove_format_info (GstCaps * caps)
618 {
619   GstStructure *st;
620   GstCapsFeatures *f;
621   gint i, n;
622   GstCaps *res;
623
624   res = gst_caps_new_empty ();
625
626   n = gst_caps_get_size (caps);
627   for (i = 0; i < n; i++) {
628     st = gst_caps_get_structure (caps, i);
629     f = gst_caps_get_features (caps, i);
630
631     /* If this is already expressed by the existing caps
632      * skip this structure */
633     if (i > 0 && gst_caps_is_subset_structure_full (res, st, f))
634       continue;
635
636     st = gst_structure_copy (st);
637     /* Only remove format info for the cases when we can actually convert */
638     if (!gst_caps_features_is_any (f)
639         && gst_caps_features_is_equal (f,
640             GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))
641       gst_structure_remove_fields (st, "format", "colorimetry", "chroma-site",
642           "width", "height", NULL);
643
644     gst_caps_append_structure_full (res, st, gst_caps_features_copy (f));
645   }
646
647   return res;
648 }
649
650 GstCaps *
651 gst_gl_mixer_update_caps (GstGLMixer * mix, GstCaps * caps)
652 {
653   GstCaps *result = NULL;
654   GstCaps *glcaps = gst_gl_mixer_set_caps_features (caps,
655       GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
656 #if GST_GL_HAVE_PLATFORM_EGL
657   GstCaps *eglcaps = gst_gl_mixer_set_caps_features (caps,
658       GST_CAPS_FEATURE_MEMORY_EGL_IMAGE);
659 #endif
660   GstCaps *uploadcaps = gst_gl_mixer_set_caps_features (caps,
661       GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META);
662   GstCaps *raw_caps =
663       gst_caps_from_string (GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS));
664
665   result = gst_caps_new_empty ();
666
667   result = gst_caps_merge (result, glcaps);
668 #if GST_GL_HAVE_PLATFORM_EGL
669   result = gst_caps_merge (result, eglcaps);
670 #endif
671   result = gst_caps_merge (result, uploadcaps);
672   result = gst_caps_merge (result, raw_caps);
673
674   result = gst_caps_merge (result, gst_gl_mixer_caps_remove_format_info (caps));
675
676   GST_DEBUG_OBJECT (mix, "returning %" GST_PTR_FORMAT, result);
677
678   return result;
679 }
680
681 static gboolean
682 gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query)
683 {
684   gboolean res = FALSE;
685   GstGLMixer *mix = GST_GL_MIXER (agg);
686
687   switch (GST_QUERY_TYPE (query)) {
688     case GST_QUERY_CONTEXT:
689     {
690       res = gst_gl_handle_context_query ((GstElement *) mix, query,
691           &mix->display, &mix->other_context);
692       break;
693     }
694     case GST_QUERY_CAPS:
695       res = gst_gl_mixer_query_caps (agg->srcpad, agg, query);
696       break;
697     default:
698       res = GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
699       break;
700   }
701
702   return res;
703 }
704
705 static GstFlowReturn
706 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
707     GstBuffer ** outbuf)
708 {
709   GstGLMixer *mix = GST_GL_MIXER (videoaggregator);
710
711   if (!mix->priv->pool_active) {
712     if (!gst_buffer_pool_set_active (mix->priv->pool, TRUE)) {
713       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
714           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
715       return GST_FLOW_ERROR;
716     }
717     mix->priv->pool_active = TRUE;
718   }
719
720   return gst_buffer_pool_acquire_buffer (mix->priv->pool, outbuf, NULL);
721 }
722
723 static gboolean
724 gst_gl_mixer_decide_allocation (GstGLMixer * mix, GstQuery * query)
725 {
726   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
727   GstBufferPool *pool = NULL;
728   GstStructure *config;
729   GstCaps *caps;
730   guint min, max, size;
731   gboolean update_pool;
732   GError *error = NULL;
733   guint idx;
734   guint out_width, out_height;
735   GstGLContext *other_context = NULL;
736   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
737
738   if (!gst_gl_ensure_element_data (mix, &mix->display, &mix->other_context))
739     return FALSE;
740
741   if (gst_query_find_allocation_meta (query,
742           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
743     GstGLContext *context;
744     const GstStructure *upload_meta_params;
745     gpointer handle;
746     gchar *type;
747     gchar *apis;
748
749     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
750     if (upload_meta_params) {
751       if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
752               GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
753         GstGLContext *old = mix->context;
754
755         mix->context = context;
756         if (old)
757           gst_object_unref (old);
758       } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle",
759               G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING,
760               &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL)
761           && handle) {
762         GstGLPlatform platform;
763         GstGLAPI gl_apis;
764
765         GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s",
766             handle, type, apis);
767
768         platform = gst_gl_platform_from_string (type);
769         gl_apis = gst_gl_api_from_string (apis);
770
771         if (gl_apis && platform)
772           other_context =
773               gst_gl_context_new_wrapped (mix->display, (guintptr) handle,
774               platform, gl_apis);
775       }
776     }
777   }
778
779   if (mix->other_context) {
780     if (!other_context) {
781       other_context = mix->other_context;
782     } else {
783       GST_ELEMENT_WARNING (mix, LIBRARY, SETTINGS,
784           ("%s", "Cannot share with more than one GL context"),
785           ("%s", "Cannot share with more than one GL context"));
786     }
787   }
788
789   if (!mix->context) {
790     mix->context = gst_gl_context_new (mix->display);
791     if (!gst_gl_context_create (mix->context, other_context, &error))
792       goto context_error;
793   }
794
795   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
796   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
797
798   g_mutex_lock (&mix->priv->gl_resource_lock);
799   mix->priv->gl_resource_ready = FALSE;
800   if (mix->fbo) {
801     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
802     mix->fbo = 0;
803     mix->depthbuffer = 0;
804   }
805
806   if (!gst_gl_context_gen_fbo (mix->context, out_width, out_height,
807           &mix->fbo, &mix->depthbuffer)) {
808     g_cond_signal (&mix->priv->gl_resource_cond);
809     g_mutex_unlock (&mix->priv->gl_resource_lock);
810     goto context_error;
811   }
812
813   if (mix->out_tex_id)
814     gst_gl_context_del_texture (mix->context, &mix->out_tex_id);
815   gst_gl_context_gen_texture (mix->context, &mix->out_tex_id,
816       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
817
818   gst_query_parse_allocation (query, &caps, NULL);
819
820   if (mixer_class->set_caps)
821     mixer_class->set_caps (mix, caps);
822
823   mix->priv->gl_resource_ready = TRUE;
824   g_cond_signal (&mix->priv->gl_resource_cond);
825   g_mutex_unlock (&mix->priv->gl_resource_lock);
826
827   if (gst_query_get_n_allocation_pools (query) > 0) {
828     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
829
830     update_pool = TRUE;
831   } else {
832     GstVideoInfo vinfo;
833
834     gst_video_info_init (&vinfo);
835     gst_video_info_from_caps (&vinfo, caps);
836     size = vinfo.size;
837     min = max = 0;
838     update_pool = FALSE;
839   }
840
841   if (!pool)
842     pool = gst_gl_buffer_pool_new (mix->context);
843
844   config = gst_buffer_pool_get_config (pool);
845   gst_buffer_pool_config_set_params (config, caps, size, min, max);
846
847   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
848   gst_buffer_pool_config_add_option (config,
849       GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
850
851   gst_buffer_pool_set_config (pool, config);
852
853   if (update_pool)
854     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
855   else
856     gst_query_add_allocation_pool (query, pool, size, min, max);
857
858   gst_object_unref (pool);
859
860   return TRUE;
861
862 context_error:
863   {
864     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
865         (NULL));
866     return FALSE;
867   }
868 }
869
870 /* takes ownership of the pool, allocator and query */
871 static gboolean
872 gst_gl_mixer_set_allocation (GstGLMixer * mix,
873     GstBufferPool * pool, GstAllocator * allocator,
874     GstAllocationParams * params, GstQuery * query)
875 {
876   GstAllocator *oldalloc;
877   GstBufferPool *oldpool;
878   GstQuery *oldquery;
879   GstGLMixerPrivate *priv = mix->priv;
880
881   GST_DEBUG ("storing allocation query");
882
883   GST_OBJECT_LOCK (mix);
884   oldpool = priv->pool;
885   priv->pool = pool;
886   priv->pool_active = FALSE;
887
888   oldalloc = priv->allocator;
889   priv->allocator = allocator;
890
891   oldquery = priv->query;
892   priv->query = query;
893
894   if (params)
895     priv->params = *params;
896   else
897     gst_allocation_params_init (&priv->params);
898   GST_OBJECT_UNLOCK (mix);
899
900   if (oldpool) {
901     GST_DEBUG_OBJECT (mix, "deactivating old pool %p", oldpool);
902     gst_buffer_pool_set_active (oldpool, FALSE);
903     gst_object_unref (oldpool);
904   }
905   if (oldalloc) {
906     gst_object_unref (oldalloc);
907   }
908   if (oldquery) {
909     gst_query_unref (oldquery);
910   }
911   return TRUE;
912 }
913
914 static gboolean
915 gst_gl_mixer_do_bufferpool (GstGLMixer * mix, GstCaps * outcaps)
916 {
917   GstQuery *query;
918   gboolean result = TRUE;
919   GstBufferPool *pool = NULL;
920   GstAllocator *allocator;
921   GstAllocationParams params;
922   GstAggregator *agg = GST_AGGREGATOR (mix);
923
924   /* find a pool for the negotiated caps now */
925   GST_DEBUG_OBJECT (mix, "doing allocation query");
926   query = gst_query_new_allocation (outcaps, TRUE);
927   if (!gst_pad_peer_query (agg->srcpad, query)) {
928     /* not a problem, just debug a little */
929     GST_DEBUG_OBJECT (mix, "peer ALLOCATION query failed");
930   }
931
932   GST_DEBUG_OBJECT (mix, "calling decide_allocation");
933   result = gst_gl_mixer_decide_allocation (mix, query);
934
935   GST_DEBUG_OBJECT (mix, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
936       query);
937
938   if (!result)
939     goto no_decide_allocation;
940
941   /* we got configuration from our peer or the decide_allocation method,
942    * parse them */
943   if (gst_query_get_n_allocation_params (query) > 0) {
944     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
945   } else {
946     allocator = NULL;
947     gst_allocation_params_init (&params);
948   }
949
950   if (gst_query_get_n_allocation_pools (query) > 0)
951     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
952
953   /* now store */
954   result = gst_gl_mixer_set_allocation (mix, pool, allocator, &params, query);
955
956   return result;
957
958   /* Errors */
959 no_decide_allocation:
960   {
961     GST_WARNING_OBJECT (mix, "Failed to decide allocation");
962     gst_query_unref (query);
963
964     return result;
965   }
966 }
967
968 gboolean
969 gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
970 {
971   guint i;
972   GList *walk;
973   guint out_tex;
974   gboolean res = TRUE;
975   guint array_index = 0;
976   GstVideoFrame out_frame;
977   GstElement *element = GST_ELEMENT (mix);
978   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
979   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
980   GstGLMixerPrivate *priv = mix->priv;
981   gboolean to_download =
982       gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY,
983       gst_caps_get_features (mix->out_caps, 0));
984   GstMapFlags out_map_flags = GST_MAP_WRITE;
985
986   GST_TRACE ("Processing buffers");
987
988   to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0));
989
990   if (!to_download)
991     out_map_flags |= GST_MAP_GL;
992
993   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, out_map_flags)) {
994     return FALSE;
995   }
996
997   if (!to_download) {
998     out_tex = *(guint *) out_frame.data[0];
999   } else {
1000     GST_INFO ("Output Buffer does not contain correct memory, "
1001         "attempting to wrap for download");
1002
1003     if (!mix->download)
1004       mix->download = gst_gl_download_new (mix->context);
1005
1006     gst_gl_download_set_format (mix->download, &out_frame.info);
1007     out_tex = mix->out_tex_id;
1008   }
1009
1010   GST_OBJECT_LOCK (mix);
1011   walk = element->sinkpads;
1012
1013   i = mix->frames->len;
1014   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1015   for (; i < element->numsinkpads; i++)
1016     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1017   while (walk) {
1018     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
1019     GstVideoAggregatorPad *vaggpad = walk->data;
1020     GstGLMixerFrameData *frame;
1021
1022     frame = g_ptr_array_index (mix->frames, array_index);
1023     frame->pad = pad;
1024     frame->texture = 0;
1025
1026     walk = g_list_next (walk);
1027
1028     if (vaggpad->buffer != NULL) {
1029       guint in_tex;
1030
1031       if (!pad->upload) {
1032         pad->upload = gst_gl_upload_new (mix->context);
1033
1034         gst_gl_upload_set_format (pad->upload, &vaggpad->info);
1035       }
1036
1037       if (!gst_gl_upload_perform_with_buffer (pad->upload,
1038               vaggpad->buffer, &in_tex, NULL)) {
1039         ++array_index;
1040         pad->mapped = FALSE;
1041         continue;
1042       }
1043       pad->mapped = TRUE;
1044
1045       frame->texture = in_tex;
1046     }
1047     ++array_index;
1048   }
1049
1050   g_mutex_lock (&priv->gl_resource_lock);
1051   if (!priv->gl_resource_ready)
1052     g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
1053
1054   if (!priv->gl_resource_ready) {
1055     g_mutex_unlock (&priv->gl_resource_lock);
1056     GST_ERROR_OBJECT (mix,
1057         "fbo used to render can't be created, do not run process_textures");
1058     res = FALSE;
1059     goto out;
1060   }
1061
1062   mix_class->process_textures (mix, mix->frames, out_tex);
1063
1064   g_mutex_unlock (&priv->gl_resource_lock);
1065
1066   if (to_download) {
1067     if (!gst_gl_download_perform_with_data (mix->download, out_tex,
1068             out_frame.data)) {
1069       GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s",
1070               "Failed to download video frame"), (NULL));
1071       res = FALSE;
1072       goto out;
1073     }
1074   }
1075
1076 out:
1077   i = 0;
1078   walk = GST_ELEMENT (mix)->sinkpads;
1079   while (walk) {
1080     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
1081
1082     if (pad->mapped)
1083       gst_gl_upload_release_buffer (pad->upload);
1084
1085     pad->mapped = FALSE;
1086     walk = g_list_next (walk);
1087     i++;
1088   }
1089   GST_OBJECT_UNLOCK (mix);
1090
1091   gst_video_frame_unmap (&out_frame);
1092
1093   return res;
1094 }
1095
1096 static gboolean
1097 gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
1098 {
1099   GList *walk;
1100   guint i, array_index = 0;
1101   GstElement *element = GST_ELEMENT (mix);
1102   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
1103
1104   GST_OBJECT_LOCK (mix);
1105   walk = GST_ELEMENT (mix)->sinkpads;
1106   i = mix->frames->len;
1107   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1108   for (; i < element->numsinkpads; i++)
1109     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1110   while (walk) {                /* We walk with this list because it's ordered */
1111     GstVideoAggregatorPad *vaggpad = walk->data;
1112
1113     walk = g_list_next (walk);
1114
1115     if (vaggpad->buffer != NULL) {
1116       /* put buffer into array */
1117       mix->array_buffers->pdata[array_index] = vaggpad->buffer;
1118     }
1119     ++array_index;
1120   }
1121   GST_OBJECT_UNLOCK (mix);
1122
1123   return mix_class->process_buffers (mix, mix->array_buffers, outbuf);
1124 }
1125
1126
1127
1128 static GstFlowReturn
1129 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1130 {
1131   gboolean res = FALSE;
1132   GstGLMixer *mix = GST_GL_MIXER (vagg);
1133   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
1134
1135   if (mix_class->process_buffers)
1136     res = gst_gl_mixer_process_buffers (mix, outbuf);
1137   else if (mix_class->process_textures)
1138     res = gst_gl_mixer_process_textures (mix, outbuf);
1139
1140   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
1141 }
1142
1143 static void
1144 gst_gl_mixer_get_property (GObject * object,
1145     guint prop_id, GValue * value, GParamSpec * pspec)
1146 {
1147   switch (prop_id) {
1148     default:
1149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1150       break;
1151   }
1152 }
1153
1154 static void
1155 gst_gl_mixer_set_property (GObject * object,
1156     guint prop_id, const GValue * value, GParamSpec * pspec)
1157 {
1158   switch (prop_id) {
1159     default:
1160       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1161       break;
1162   }
1163 }
1164
1165 static gboolean
1166 _clean_upload (GstAggregator * agg, GstPad * aggpad, gpointer udata)
1167 {
1168   GstGLMixerPad *pad = GST_GL_MIXER_PAD (aggpad);
1169
1170   if (pad->upload) {
1171     gst_object_unref (pad->upload);
1172     pad->upload = NULL;
1173   }
1174
1175   return TRUE;
1176 }
1177
1178 static void
1179 _free_glmixer_frame_data (GstGLMixerFrameData * frame)
1180 {
1181   g_slice_free1 (sizeof (GstGLMixerFrameData), frame);
1182 }
1183
1184 static gboolean
1185 gst_gl_mixer_start (GstAggregator * agg)
1186 {
1187   guint i;
1188   GstGLMixer *mix = GST_GL_MIXER (agg);
1189   GstElement *element = GST_ELEMENT (agg);
1190
1191   if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg))
1192     return FALSE;
1193
1194   GST_OBJECT_LOCK (mix);
1195   mix->array_buffers = g_ptr_array_new_full (element->numsinkpads,
1196       (GDestroyNotify) _free_glmixer_frame_data);
1197   mix->frames = g_ptr_array_new_full (element->numsinkpads, NULL);
1198
1199   g_ptr_array_set_size (mix->array_buffers, element->numsinkpads);
1200   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1201
1202   for (i = 0; i < element->numsinkpads; i++)
1203     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1204
1205   GST_OBJECT_UNLOCK (mix);
1206
1207   return TRUE;
1208 }
1209
1210 static gboolean
1211 gst_gl_mixer_stop (GstAggregator * agg)
1212 {
1213   GstGLMixer *mix = GST_GL_MIXER (agg);
1214   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
1215
1216   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
1217     return FALSE;
1218
1219   GST_OBJECT_LOCK (agg);
1220   g_ptr_array_free (mix->frames, TRUE);
1221   mix->frames = NULL;
1222   g_ptr_array_free (mix->array_buffers, TRUE);
1223   mix->array_buffers = NULL;
1224   GST_OBJECT_UNLOCK (agg);
1225
1226   if (mixer_class->reset)
1227     mixer_class->reset (mix);
1228   if (mix->fbo) {
1229     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
1230     mix->fbo = 0;
1231     mix->depthbuffer = 0;
1232   }
1233   if (mix->download) {
1234     gst_object_unref (mix->download);
1235     mix->download = NULL;
1236   }
1237
1238   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (mix), _clean_upload, NULL);
1239
1240   if (mix->priv->query) {
1241     gst_query_unref (mix->priv->query);
1242     mix->priv->query = NULL;
1243   }
1244
1245   if (mix->priv->pool) {
1246     gst_object_unref (mix->priv->pool);
1247     mix->priv->pool = NULL;
1248   }
1249
1250   if (mix->display) {
1251     gst_object_unref (mix->display);
1252     mix->display = NULL;
1253   }
1254
1255   if (mix->context) {
1256     gst_object_unref (mix->context);
1257     mix->context = NULL;
1258   }
1259
1260   gst_gl_mixer_reset (mix);
1261
1262   return TRUE;
1263 }