gl: fix multi gl object leaks
[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 #define gst_gl_mixer_parent_class parent_class
40 G_DEFINE_ABSTRACT_TYPE (GstGLMixer, gst_gl_mixer, GST_TYPE_VIDEO_AGGREGATOR);
41 static gboolean gst_gl_mixer_do_bufferpool (GstGLMixer * mix,
42     GstCaps * outcaps);
43
44
45 #define GST_CAT_DEFAULT gst_gl_mixer_debug
46 GST_DEBUG_CATEGORY (gst_gl_mixer_debug);
47
48 static void gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
49     GValue * value, GParamSpec * pspec);
50 static void gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
51     const GValue * value, GParamSpec * pspec);
52
53 static void gst_gl_mixer_set_context (GstElement * element,
54     GstContext * context);
55
56 enum
57 {
58   PROP_PAD_0
59 };
60
61 #define GST_GL_MIXER_GET_PRIVATE(obj)  \
62     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_GL_MIXER, GstGLMixerPrivate))
63
64 struct _GstGLMixerPrivate
65 {
66   gboolean negotiated;
67
68   GstBufferPool *pool;
69   gboolean pool_active;
70   GstAllocator *allocator;
71   GstAllocationParams params;
72   GstQuery *query;
73 };
74
75 G_DEFINE_TYPE (GstGLMixerPad, gst_gl_mixer_pad, GST_TYPE_VIDEO_AGGREGATOR_PAD);
76
77 static void
78 gst_gl_mixer_pad_class_init (GstGLMixerPadClass * klass)
79 {
80   GObjectClass *gobject_class = (GObjectClass *) klass;
81
82   gobject_class->set_property = gst_gl_mixer_pad_set_property;
83   gobject_class->get_property = gst_gl_mixer_pad_get_property;
84 }
85
86 static void
87 gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
88     GValue * value, GParamSpec * pspec)
89 {
90   switch (prop_id) {
91     default:
92       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
93       break;
94   }
95 }
96
97 static void
98 gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
99     const GValue * value, GParamSpec * pspec)
100 {
101   switch (prop_id) {
102     default:
103       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104       break;
105   }
106 }
107
108 static gboolean
109 _negotiated_caps (GstVideoAggregator * vagg, GstCaps * caps)
110 {
111   GstGLMixer *mix = GST_GL_MIXER (vagg);
112   gboolean ret = gst_gl_mixer_do_bufferpool (mix, caps);
113
114   mix->priv->negotiated = ret;
115
116   return ret;
117 }
118
119 static gboolean
120 gst_gl_mixer_propose_allocation (GstGLMixer * mix,
121     GstQuery * decide_query, GstQuery * query)
122 {
123   GstBufferPool *pool;
124   GstStructure *config;
125   GstCaps *caps;
126   guint size = 0;
127   gboolean need_pool;
128   GError *error = NULL;
129   GstStructure *gl_context;
130   gchar *platform, *gl_apis;
131   gpointer handle;
132   GstAllocator *allocator = NULL;
133   GstAllocationParams params;
134
135   gst_query_parse_allocation (query, &caps, &need_pool);
136
137   if (caps == NULL)
138     goto no_caps;
139
140   if ((pool = mix->priv->pool))
141     gst_object_ref (pool);
142
143   if (pool != NULL) {
144     GstCaps *pcaps;
145
146     /* we had a pool, check caps */
147     GST_DEBUG_OBJECT (mix, "check existing pool caps");
148     config = gst_buffer_pool_get_config (pool);
149     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
150
151     if (!gst_caps_is_equal (caps, pcaps)) {
152       GST_DEBUG_OBJECT (mix, "pool has different caps");
153       /* different caps, we can't use this pool */
154       gst_object_unref (pool);
155       pool = NULL;
156     }
157     gst_structure_free (config);
158   }
159
160   if (!gst_gl_ensure_display (mix, &mix->display))
161     return FALSE;
162
163   if (!mix->context) {
164     mix->context = gst_gl_context_new (mix->display);
165     if (!gst_gl_context_create (mix->context, NULL, &error))
166       goto context_error;
167   }
168
169   if (pool == NULL && need_pool) {
170     GstVideoInfo info;
171
172     if (!gst_video_info_from_caps (&info, caps))
173       goto invalid_caps;
174
175     GST_DEBUG_OBJECT (mix, "create new pool");
176     pool = gst_gl_buffer_pool_new (mix->context);
177
178     /* the normal size of a frame */
179     size = info.size;
180
181     config = gst_buffer_pool_get_config (pool);
182     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
183     if (!gst_buffer_pool_set_config (pool, config))
184       goto config_failed;
185   }
186
187   if (pool) {
188     gst_query_add_allocation_pool (query, pool, size, 1, 0);
189     gst_object_unref (pool);
190   }
191
192   /* we also support various metadata */
193   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
194
195   gl_apis = gst_gl_api_to_string (gst_gl_context_get_gl_api (mix->context));
196   platform =
197       gst_gl_platform_to_string (gst_gl_context_get_gl_platform (mix->context));
198   handle = (gpointer) gst_gl_context_get_gl_context (mix->context);
199
200   gl_context =
201       gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext",
202       GST_GL_TYPE_CONTEXT, mix->context, "gst.gl.context.handle",
203       G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform,
204       "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
205   gst_query_add_allocation_meta (query,
206       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context);
207
208   g_free (gl_apis);
209   g_free (platform);
210   gst_structure_free (gl_context);
211
212   gst_allocation_params_init (&params);
213
214   allocator = gst_allocator_find (GST_GL_MEMORY_ALLOCATOR);
215   gst_query_add_allocation_param (query, allocator, &params);
216   gst_object_unref (allocator);
217
218   return TRUE;
219
220   /* ERRORS */
221 no_caps:
222   {
223     GST_DEBUG_OBJECT (mix, "no caps specified");
224     return FALSE;
225   }
226 invalid_caps:
227   {
228     GST_DEBUG_OBJECT (mix, "invalid caps specified");
229     return FALSE;
230   }
231 config_failed:
232   {
233     GST_DEBUG_OBJECT (mix, "failed setting config");
234     return FALSE;
235   }
236 context_error:
237   {
238     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
239         (NULL));
240     return FALSE;
241   }
242 }
243
244 static gboolean
245 gst_gl_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
246     GstQuery * query)
247 {
248   gboolean ret = FALSE;
249   GstGLMixer *mix = GST_GL_MIXER (agg);
250
251   GST_TRACE ("QUERY %" GST_PTR_FORMAT, query);
252
253   switch (GST_QUERY_TYPE (query)) {
254     case GST_QUERY_ALLOCATION:
255     {
256       GstQuery *decide_query = NULL;
257       gboolean negotiated;
258
259       GST_OBJECT_LOCK (mix);
260       if (G_UNLIKELY (!(negotiated = mix->priv->negotiated))) {
261         GST_DEBUG_OBJECT (mix,
262             "not negotiated yet, can't answer ALLOCATION query");
263         GST_OBJECT_UNLOCK (mix);
264         return FALSE;
265       }
266       if ((decide_query = mix->priv->query))
267         gst_query_ref (decide_query);
268       GST_OBJECT_UNLOCK (mix);
269
270       GST_DEBUG_OBJECT (mix,
271           "calling propose allocation with query %" GST_PTR_FORMAT,
272           decide_query);
273
274       /* pass the query to the propose_allocation vmethod if any */
275       ret = gst_gl_mixer_propose_allocation (mix, decide_query, query);
276
277       if (decide_query)
278         gst_query_unref (decide_query);
279
280       GST_DEBUG_OBJECT (mix, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, query);
281       break;
282     }
283     case GST_QUERY_CONTEXT:
284     {
285       ret = gst_gl_handle_context_query ((GstElement *) mix, query,
286           &mix->display);
287       break;
288     }
289     default:
290       ret = GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
291       break;
292   }
293
294   return ret;
295 }
296
297 static void
298 gst_gl_mixer_pad_init (GstGLMixerPad * mixerpad)
299 {
300 }
301
302 /* GLMixer signals and args */
303 enum
304 {
305   /* FILL ME */
306   LAST_SIGNAL
307 };
308
309 enum
310 {
311   PROP_0
312 };
313
314 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
315     GST_PAD_SRC,
316     GST_PAD_ALWAYS,
317     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
318         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
319             "RGBA") "; "
320         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
321         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
322             "RGBA")
323         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
324     );
325
326 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
327     GST_PAD_SINK,
328     GST_PAD_REQUEST,
329     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
330         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
331             "RGBA") "; "
332         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
333         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
334             "RGBA")
335         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
336     );
337
338 static gboolean gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query);
339 static GstFlowReturn
340 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
341     GstBuffer ** outbuf);
342 static gboolean
343 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
344     gboolean active);
345 static gboolean gst_gl_mixer_stop (GstAggregator * agg);
346 static gboolean gst_gl_mixer_start (GstAggregator * agg);
347
348 static GstFlowReturn
349 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg,
350     GstBuffer * outbuffer);
351
352 static void gst_gl_mixer_set_property (GObject * object, guint prop_id,
353     const GValue * value, GParamSpec * pspec);
354 static void gst_gl_mixer_get_property (GObject * object, guint prop_id,
355     GValue * value, GParamSpec * pspec);
356
357 static gboolean gst_gl_mixer_decide_allocation (GstGLMixer * mix,
358     GstQuery * query);
359 static gboolean gst_gl_mixer_set_allocation (GstGLMixer * mix,
360     GstBufferPool * pool, GstAllocator * allocator,
361     GstAllocationParams * params, GstQuery * query);
362
363 static void gst_gl_mixer_finalize (GObject * object);
364
365 static void
366 gst_gl_mixer_class_init (GstGLMixerClass * klass)
367 {
368   GObjectClass *gobject_class;
369   GstElementClass *element_class;
370
371   GstVideoAggregatorClass *videoaggregator_class =
372       (GstVideoAggregatorClass *) klass;
373   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
374
375   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "opengl mixer");
376
377   gobject_class = (GObjectClass *) klass;
378   element_class = GST_ELEMENT_CLASS (klass);
379
380   g_type_class_add_private (klass, sizeof (GstGLMixerPrivate));
381
382   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_finalize);
383
384   gobject_class->get_property = gst_gl_mixer_get_property;
385   gobject_class->set_property = gst_gl_mixer_set_property;
386
387   gst_element_class_add_pad_template (element_class,
388       gst_static_pad_template_get (&src_factory));
389   gst_element_class_add_pad_template (element_class,
390       gst_static_pad_template_get (&sink_factory));
391
392   element_class->set_context = GST_DEBUG_FUNCPTR (gst_gl_mixer_set_context);
393
394   agg_class->sinkpads_type = GST_TYPE_GL_MIXER_PAD;
395   agg_class->sink_query = gst_gl_mixer_sink_query;
396   agg_class->src_query = gst_gl_mixer_src_query;
397   agg_class->src_activate = gst_gl_mixer_src_activate_mode;
398   agg_class->stop = gst_gl_mixer_stop;
399   agg_class->start = gst_gl_mixer_start;
400
401   videoaggregator_class->disable_frame_conversion = TRUE;
402   videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
403   videoaggregator_class->get_output_buffer = gst_gl_mixer_get_output_buffer;
404   videoaggregator_class->negotiated_caps = _negotiated_caps;
405
406
407   /* Register the pad class */
408   g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
409
410   klass->set_caps = NULL;
411
412 }
413
414 static void
415 gst_gl_mixer_reset (GstGLMixer * mix)
416 {
417   /* clean up collect data */
418   mix->priv->negotiated = FALSE;
419 }
420
421 static void
422 gst_gl_mixer_init (GstGLMixer * mix)
423 {
424   mix->priv = GST_GL_MIXER_GET_PRIVATE (mix);
425   mix->array_buffers = 0;
426   mix->display = NULL;
427   mix->fbo = 0;
428   mix->depthbuffer = 0;
429
430   /* initialize variables */
431   gst_gl_mixer_reset (mix);
432 }
433
434 static void
435 gst_gl_mixer_finalize (GObject * object)
436 {
437   G_OBJECT_CLASS (parent_class)->finalize (object);
438 }
439
440 static void
441 gst_gl_mixer_set_context (GstElement * element, GstContext * context)
442 {
443   GstGLMixer *mix = GST_GL_MIXER (element);
444
445   gst_gl_handle_set_context (element, context, &mix->display);
446 }
447
448 static gboolean
449 gst_gl_mixer_activate (GstGLMixer * mix, gboolean active)
450 {
451   gboolean result = TRUE;
452
453   if (active) {
454     if (!gst_gl_ensure_display (mix, &mix->display))
455       result = FALSE;
456   }
457
458   return result;
459 }
460
461 static gboolean
462 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
463     gboolean active)
464 {
465   GstGLMixer *mix;
466   gboolean result = FALSE;
467
468   mix = GST_GL_MIXER (aggregator);
469
470   switch (mode) {
471     case GST_PAD_MODE_PUSH:
472     case GST_PAD_MODE_PULL:
473       result = gst_gl_mixer_activate (mix, active);
474       break;
475     default:
476       result = TRUE;
477       break;
478   }
479   return result;
480 }
481
482 static gboolean
483 gst_gl_mixer_query_caps (GstPad * pad, GstAggregator * agg, GstQuery * query)
484 {
485   GstCaps *filter, *caps;
486   GstStructure *s;
487   gint n;
488
489   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
490
491   gst_query_parse_caps (query, &filter);
492
493   if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
494     caps = gst_video_info_to_caps (&vagg->info);
495   } else {
496     caps = gst_pad_get_pad_template_caps (agg->srcpad);
497   }
498
499   caps = gst_caps_make_writable (caps);
500
501   n = gst_caps_get_size (caps) - 1;
502   for (; n >= 0; n--) {
503     s = gst_caps_get_structure (caps, n);
504     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
505         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
506     if (GST_VIDEO_INFO_FPS_D (&vagg->info) != 0) {
507       gst_structure_set (s,
508           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
509     }
510   }
511
512   if (filter)
513     caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
514
515   gst_query_set_caps_result (query, caps);
516   gst_caps_unref (caps);
517
518   return TRUE;
519 }
520
521 static gboolean
522 gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query)
523 {
524   gboolean res = FALSE;
525   GstGLMixer *mix = GST_GL_MIXER (agg);
526
527   switch (GST_QUERY_TYPE (query)) {
528     case GST_QUERY_CONTEXT:
529     {
530       res = gst_gl_handle_context_query ((GstElement *) mix, query,
531           &mix->display);
532       break;
533     }
534     case GST_QUERY_CAPS:
535       res = gst_gl_mixer_query_caps (agg->srcpad, agg, query);
536       break;
537     default:
538       res = GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
539       break;
540   }
541
542   return res;
543 }
544
545 static GstFlowReturn
546 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
547     GstBuffer ** outbuf)
548 {
549   GstGLMixer *mix = GST_GL_MIXER (videoaggregator);
550
551   if (!mix->priv->pool_active) {
552     if (!gst_buffer_pool_set_active (mix->priv->pool, TRUE)) {
553       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
554           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
555       return GST_FLOW_ERROR;
556     }
557     mix->priv->pool_active = TRUE;
558   }
559
560   return gst_buffer_pool_acquire_buffer (mix->priv->pool, outbuf, NULL);
561 }
562
563 static gboolean
564 gst_gl_mixer_decide_allocation (GstGLMixer * mix, GstQuery * query)
565 {
566   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
567   GstBufferPool *pool = NULL;
568   GstStructure *config;
569   GstCaps *caps;
570   guint min, max, size;
571   gboolean update_pool;
572   GError *error = NULL;
573   guint idx;
574   guint out_width, out_height;
575   GstGLContext *other_context = NULL;
576   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
577
578   gst_query_parse_allocation (query, &caps, NULL);
579
580   if (gst_query_get_n_allocation_pools (query) > 0) {
581     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
582
583     update_pool = TRUE;
584   } else {
585     GstVideoInfo vinfo;
586
587     gst_video_info_init (&vinfo);
588     gst_video_info_from_caps (&vinfo, caps);
589     size = vinfo.size;
590     min = max = 0;
591     update_pool = FALSE;
592   }
593
594   if (!gst_gl_ensure_display (mix, &mix->display))
595     return FALSE;
596
597   if (gst_query_find_allocation_meta (query,
598           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
599     GstGLContext *context;
600     const GstStructure *upload_meta_params;
601     gpointer handle;
602     gchar *type;
603     gchar *apis;
604
605     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
606     if (upload_meta_params) {
607       if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
608               GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
609         GstGLContext *old = mix->context;
610
611         mix->context = context;
612         if (old)
613           gst_object_unref (old);
614       } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle",
615               G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING,
616               &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL)
617           && handle) {
618         GstGLPlatform platform = GST_GL_PLATFORM_NONE;
619         GstGLAPI gl_apis;
620
621         GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s",
622             handle, type, apis);
623
624         platform = gst_gl_platform_from_string (type);
625         gl_apis = gst_gl_api_from_string (apis);
626
627         if (gl_apis && platform)
628           other_context =
629               gst_gl_context_new_wrapped (mix->display, (guintptr) handle,
630               platform, gl_apis);
631       }
632     }
633   }
634
635   if (!mix->context) {
636     mix->context = gst_gl_context_new (mix->display);
637     if (!gst_gl_context_create (mix->context, other_context, &error))
638       goto context_error;
639   }
640
641   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
642   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
643
644   if (mix->fbo) {
645     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
646     mix->fbo = 0;
647     mix->depthbuffer = 0;
648   }
649
650   if (!gst_gl_context_gen_fbo (mix->context, out_width, out_height,
651           &mix->fbo, &mix->depthbuffer))
652     goto context_error;
653
654   if (mix->out_tex_id)
655     gst_gl_context_del_texture (mix->context, &mix->out_tex_id);
656   gst_gl_context_gen_texture (mix->context, &mix->out_tex_id,
657       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
658
659   if (mixer_class->set_caps)
660     mixer_class->set_caps (mix, caps);
661
662   if (!pool)
663     pool = gst_gl_buffer_pool_new (mix->context);
664
665   config = gst_buffer_pool_get_config (pool);
666   gst_buffer_pool_config_set_params (config, caps, size, min, max);
667
668   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
669
670   gst_buffer_pool_set_config (pool, config);
671
672   if (update_pool)
673     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
674   else
675     gst_query_add_allocation_pool (query, pool, size, min, max);
676
677   gst_object_unref (pool);
678
679   return TRUE;
680
681 context_error:
682   {
683     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
684         (NULL));
685     return FALSE;
686   }
687 }
688
689 /* takes ownership of the pool, allocator and query */
690 static gboolean
691 gst_gl_mixer_set_allocation (GstGLMixer * mix,
692     GstBufferPool * pool, GstAllocator * allocator,
693     GstAllocationParams * params, GstQuery * query)
694 {
695   GstAllocator *oldalloc;
696   GstBufferPool *oldpool;
697   GstQuery *oldquery;
698   GstGLMixerPrivate *priv = mix->priv;
699
700   GST_DEBUG ("storing allocation query");
701
702   GST_OBJECT_LOCK (mix);
703   oldpool = priv->pool;
704   priv->pool = pool;
705   priv->pool_active = FALSE;
706
707   oldalloc = priv->allocator;
708   priv->allocator = allocator;
709
710   oldquery = priv->query;
711   priv->query = query;
712
713   if (params)
714     priv->params = *params;
715   else
716     gst_allocation_params_init (&priv->params);
717   GST_OBJECT_UNLOCK (mix);
718
719   if (oldpool) {
720     GST_DEBUG_OBJECT (mix, "deactivating old pool %p", oldpool);
721     gst_buffer_pool_set_active (oldpool, FALSE);
722     gst_object_unref (oldpool);
723   }
724   if (oldalloc) {
725     gst_object_unref (oldalloc);
726   }
727   if (oldquery) {
728     gst_query_unref (oldquery);
729   }
730   return TRUE;
731 }
732
733 static gboolean
734 gst_gl_mixer_do_bufferpool (GstGLMixer * mix, GstCaps * outcaps)
735 {
736   GstQuery *query;
737   gboolean result = TRUE;
738   GstBufferPool *pool = NULL;
739   GstAllocator *allocator;
740   GstAllocationParams params;
741   GstAggregator *agg = GST_AGGREGATOR (mix);
742
743   /* find a pool for the negotiated caps now */
744   GST_DEBUG_OBJECT (mix, "doing allocation query");
745   query = gst_query_new_allocation (outcaps, TRUE);
746   if (!gst_pad_peer_query (agg->srcpad, query)) {
747     /* not a problem, just debug a little */
748     GST_DEBUG_OBJECT (mix, "peer ALLOCATION query failed");
749   }
750
751   GST_DEBUG_OBJECT (mix, "calling decide_allocation");
752   result = gst_gl_mixer_decide_allocation (mix, query);
753
754   GST_DEBUG_OBJECT (mix, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
755       query);
756
757   if (!result)
758     goto no_decide_allocation;
759
760   /* we got configuration from our peer or the decide_allocation method,
761    * parse them */
762   if (gst_query_get_n_allocation_params (query) > 0) {
763     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
764   } else {
765     allocator = NULL;
766     gst_allocation_params_init (&params);
767   }
768
769   if (gst_query_get_n_allocation_pools (query) > 0)
770     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
771
772   /* now store */
773   result = gst_gl_mixer_set_allocation (mix, pool, allocator, &params, query);
774
775   return result;
776
777   /* Errors */
778 no_decide_allocation:
779   {
780     GST_WARNING_OBJECT (mix, "Failed to decide allocation");
781     gst_query_unref (query);
782
783     return result;
784   }
785 }
786
787 gboolean
788 gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
789 {
790   guint i;
791   GList *walk;
792   guint out_tex;
793   gboolean res = TRUE;
794   guint array_index = 0;
795   GstVideoFrame out_frame;
796   gboolean out_gl_wrapped = FALSE;
797   GstElement *element = GST_ELEMENT (mix);
798   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
799   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
800
801   GST_TRACE ("Processing buffers");
802
803   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf,
804           GST_MAP_WRITE | GST_MAP_GL)) {
805     return FALSE;
806   }
807
808   if (gst_is_gl_memory (out_frame.map[0].memory)) {
809     out_tex = *(guint *) out_frame.data[0];
810   } else {
811     GST_INFO ("Output Buffer does not contain correct memory, "
812         "attempting to wrap for download");
813
814     out_tex = mix->out_tex_id;;
815
816     if (!mix->download)
817       mix->download = gst_gl_download_new (mix->context);
818
819     gst_gl_download_set_format (mix->download, &out_frame.info);
820     out_gl_wrapped = TRUE;
821   }
822
823   GST_OBJECT_LOCK (mix);
824   walk = element->sinkpads;
825
826   i = mix->frames->len;
827   g_ptr_array_set_size (mix->frames, element->numsinkpads);
828   for (; i < element->numsinkpads; i++)
829     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
830   while (walk) {
831     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
832     GstVideoAggregatorPad *vaggpad = walk->data;
833     GstGLMixerFrameData *frame;
834
835     frame = g_ptr_array_index (mix->frames, array_index);
836     frame->pad = pad;
837     frame->texture = 0;
838
839     walk = g_list_next (walk);
840
841     if (vaggpad->buffer != NULL) {
842       guint in_tex;
843
844       if (!pad->upload) {
845         pad->upload = gst_gl_upload_new (mix->context);
846
847         gst_gl_upload_set_format (pad->upload, &vaggpad->info);
848       }
849
850       if (!gst_gl_upload_perform_with_buffer (pad->upload,
851               vaggpad->buffer, &in_tex)) {
852         ++array_index;
853         pad->mapped = FALSE;
854         continue;
855       }
856       pad->mapped = TRUE;
857
858       frame->texture = in_tex;
859     }
860     ++array_index;
861   }
862
863   mix_class->process_textures (mix, mix->frames, out_tex);
864
865   if (out_gl_wrapped) {
866     if (!gst_gl_download_perform_with_data (mix->download, out_tex,
867             out_frame.data)) {
868       GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s",
869               "Failed to download video frame"), (NULL));
870       res = FALSE;
871       goto out;
872     }
873   }
874
875 out:
876   i = 0;
877   walk = GST_ELEMENT (mix)->sinkpads;
878   while (walk) {
879     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
880
881     if (pad->mapped)
882       gst_gl_upload_release_buffer (pad->upload);
883
884     pad->mapped = FALSE;
885     walk = g_list_next (walk);
886     i++;
887   }
888   GST_OBJECT_UNLOCK (mix);
889
890   gst_video_frame_unmap (&out_frame);
891
892   return res;
893 }
894
895 static gboolean
896 gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
897 {
898   GList *walk;
899   guint i, array_index = 0;
900   GstElement *element = GST_ELEMENT (mix);
901   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
902
903   GST_OBJECT_LOCK (mix);
904   walk = GST_ELEMENT (mix)->sinkpads;
905   i = mix->frames->len;
906   g_ptr_array_set_size (mix->frames, element->numsinkpads);
907   for (; i < element->numsinkpads; i++)
908     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
909   while (walk) {                /* We walk with this list because it's ordered */
910     GstVideoAggregatorPad *vaggpad = walk->data;
911
912     walk = g_list_next (walk);
913
914     if (vaggpad->buffer != NULL) {
915       /* put buffer into array */
916       mix->array_buffers->pdata[array_index] = vaggpad->buffer;
917     }
918     ++array_index;
919   }
920   GST_OBJECT_UNLOCK (mix);
921
922   return mix_class->process_buffers (mix, mix->array_buffers, outbuf);
923 }
924
925
926
927 static GstFlowReturn
928 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
929 {
930   gboolean res = FALSE;
931   GstGLMixer *mix = GST_GL_MIXER (vagg);
932   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
933
934   if (mix_class->process_buffers)
935     res = gst_gl_mixer_process_buffers (mix, outbuf);
936   else if (mix_class->process_textures)
937     res = gst_gl_mixer_process_textures (mix, outbuf);
938
939   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
940 }
941
942 static void
943 gst_gl_mixer_get_property (GObject * object,
944     guint prop_id, GValue * value, GParamSpec * pspec)
945 {
946   switch (prop_id) {
947     default:
948       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
949       break;
950   }
951 }
952
953 static void
954 gst_gl_mixer_set_property (GObject * object,
955     guint prop_id, const GValue * value, GParamSpec * pspec)
956 {
957   switch (prop_id) {
958     default:
959       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
960       break;
961   }
962 }
963
964 static gboolean
965 _clean_upload (GstAggregator * agg, GstPad * aggpad, gpointer udata)
966 {
967   GstGLMixerPad *pad = GST_GL_MIXER_PAD (aggpad);
968
969   if (pad->upload) {
970     gst_object_unref (pad->upload);
971     pad->upload = NULL;
972   }
973
974   return TRUE;
975 }
976
977 static void
978 _free_glmixer_frame_data (GstGLMixerFrameData * frame)
979 {
980   g_slice_free1 (sizeof (GstGLMixerFrameData), frame);
981 }
982
983 static gboolean
984 gst_gl_mixer_start (GstAggregator * agg)
985 {
986   guint i;
987   GstGLMixer *mix = GST_GL_MIXER (agg);
988   GstElement *element = GST_ELEMENT (agg);
989
990   if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg))
991     return FALSE;
992
993   GST_OBJECT_LOCK (mix);
994   mix->array_buffers = g_ptr_array_new_full (element->numsinkpads,
995       (GDestroyNotify) _free_glmixer_frame_data);
996   mix->frames = g_ptr_array_new_full (element->numsinkpads, NULL);
997
998   g_ptr_array_set_size (mix->array_buffers, element->numsinkpads);
999   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1000
1001   for (i = 0; i < element->numsinkpads; i++)
1002     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1003
1004   GST_OBJECT_UNLOCK (mix);
1005
1006   return TRUE;
1007 }
1008
1009 static gboolean
1010 gst_gl_mixer_stop (GstAggregator * agg)
1011 {
1012   GstGLMixer *mix = GST_GL_MIXER (agg);
1013   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
1014
1015   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
1016     return FALSE;
1017
1018   GST_OBJECT_LOCK (agg);
1019   g_ptr_array_free (mix->frames, TRUE);
1020   mix->frames = NULL;
1021   g_ptr_array_free (mix->array_buffers, TRUE);
1022   mix->array_buffers = NULL;
1023   GST_OBJECT_UNLOCK (agg);
1024
1025   if (mixer_class->reset)
1026     mixer_class->reset (mix);
1027   if (mix->fbo) {
1028     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
1029     mix->fbo = 0;
1030     mix->depthbuffer = 0;
1031   }
1032   if (mix->download) {
1033     gst_object_unref (mix->download);
1034     mix->download = NULL;
1035   }
1036
1037   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (mix), _clean_upload, NULL);
1038
1039   if (mix->priv->query) {
1040     gst_query_unref (mix->priv->query);
1041     mix->priv->query = NULL;
1042   }
1043
1044   if (mix->priv->pool) {
1045     gst_object_unref (mix->priv->pool);
1046     mix->priv->pool = NULL;
1047   }
1048
1049   if (mix->display) {
1050     gst_object_unref (mix->display);
1051     mix->display = NULL;
1052   }
1053
1054   if (mix->context) {
1055     gst_object_unref (mix->context);
1056     mix->context = NULL;
1057   }
1058   gst_gl_mixer_reset (mix);
1059
1060   return TRUE;
1061 }