gl: add a new glbasemixer class below glmixer
[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/video/video.h>
28
29 #include "gstglmixer.h"
30
31 #if GST_GL_HAVE_PLATFORM_EGL
32 #include <gst/gl/egl/gsteglimagememory.h>
33 #endif
34
35 #define gst_gl_mixer_parent_class parent_class
36 G_DEFINE_ABSTRACT_TYPE (GstGLMixer, gst_gl_mixer, GST_TYPE_GL_BASE_MIXER);
37
38 #define GST_CAT_DEFAULT gst_gl_mixer_debug
39 GST_DEBUG_CATEGORY (gst_gl_mixer_debug);
40
41 static void gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
42     GValue * value, GParamSpec * pspec);
43 static void gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
44     const GValue * value, GParamSpec * pspec);
45 static void gst_gl_mixer_pad_finalize (GObject * object);
46 static GstBuffer *_default_pad_upload_buffer (GstGLMixer * mix,
47     GstGLMixerFrameData * frame, GstBuffer * buffer);
48
49 enum
50 {
51   PROP_PAD_0
52 };
53
54 #define GST_GL_MIXER_GET_PRIVATE(obj)  \
55     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_GL_MIXER, GstGLMixerPrivate))
56
57 struct _GstGLMixerPrivate
58 {
59   gboolean negotiated;
60
61   gboolean gl_resource_ready;
62   GMutex gl_resource_lock;
63   GCond gl_resource_cond;
64 };
65
66 G_DEFINE_TYPE (GstGLMixerPad, gst_gl_mixer_pad, GST_TYPE_GL_BASE_MIXER_PAD);
67
68 static void
69 gst_gl_mixer_pad_class_init (GstGLMixerPadClass * klass)
70 {
71   GObjectClass *gobject_class = (GObjectClass *) klass;
72   GstVideoAggregatorPadClass *vaggpad_class =
73       (GstVideoAggregatorPadClass *) klass;
74
75   gobject_class->set_property = gst_gl_mixer_pad_set_property;
76   gobject_class->get_property = gst_gl_mixer_pad_get_property;
77
78   gobject_class->finalize = gst_gl_mixer_pad_finalize;
79
80   vaggpad_class->set_info = NULL;
81   vaggpad_class->prepare_frame = NULL;
82   vaggpad_class->clean_frame = NULL;
83
84   klass->upload_buffer = _default_pad_upload_buffer;
85 }
86
87 static void
88 gst_gl_mixer_pad_finalize (GObject * object)
89 {
90   GstGLMixerPad *pad = GST_GL_MIXER_PAD (object);
91
92   if (pad->upload) {
93     gst_object_unref (pad->upload);
94     pad->upload = NULL;
95   }
96
97   G_OBJECT_CLASS (gst_gl_mixer_pad_parent_class)->finalize (object);
98 }
99
100 static void
101 _init_upload (GstGLMixer * mix, GstGLMixerPad * pad)
102 {
103   GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad);
104   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
105
106   if (!pad->upload) {
107     GstCaps *in_caps = gst_pad_get_current_caps (GST_PAD (pad));
108     GstCaps *upload_caps = gst_caps_copy (in_caps);
109     GstCapsFeatures *gl_features =
110         gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
111
112     pad->upload = gst_gl_upload_new (context);
113
114     gst_caps_set_features (upload_caps, 0,
115         gst_caps_features_copy (gl_features));
116     gst_gl_upload_set_caps (pad->upload, in_caps, upload_caps);
117     gst_caps_unref (in_caps);
118
119     if (!pad->convert) {
120       GstVideoInfo gl_info;
121       GstCaps *gl_caps;
122
123       gst_video_info_set_format (&gl_info,
124           GST_VIDEO_FORMAT_RGBA,
125           GST_VIDEO_INFO_WIDTH (&vaggpad->info),
126           GST_VIDEO_INFO_HEIGHT (&vaggpad->info));
127       gl_caps = gst_video_info_to_caps (&gl_info);
128       gst_caps_set_features (gl_caps, 0, gst_caps_features_copy (gl_features));
129
130       pad->convert = gst_gl_color_convert_new (context);
131
132       gst_gl_color_convert_set_caps (pad->convert, upload_caps, gl_caps);
133       gst_caps_unref (gl_caps);
134     }
135
136     gst_caps_unref (upload_caps);
137     gst_caps_features_free (gl_features);
138   }
139 }
140
141 static void
142 gst_gl_mixer_pad_get_property (GObject * object, guint prop_id,
143     GValue * value, GParamSpec * pspec)
144 {
145   switch (prop_id) {
146     default:
147       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
148       break;
149   }
150 }
151
152 static void
153 gst_gl_mixer_pad_set_property (GObject * object, guint prop_id,
154     const GValue * value, GParamSpec * pspec)
155 {
156   switch (prop_id) {
157     default:
158       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
159       break;
160   }
161 }
162
163 static gboolean
164 _negotiated_caps (GstVideoAggregator * vagg, GstCaps * caps)
165 {
166   GstGLMixer *mix = GST_GL_MIXER (vagg);
167   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
168   GstGLBaseMixerClass *base_mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
169   gboolean ret;
170
171   mix->priv->negotiated = TRUE;
172   base_mix_class->supported_gl_api = mix_class->supported_gl_api;
173
174   gst_caps_replace (&mix->out_caps, caps);
175
176   ret = GST_VIDEO_AGGREGATOR_CLASS (parent_class)->negotiated_caps (vagg, caps);
177
178   mix->context = GST_GL_BASE_MIXER (mix)->context;
179
180   return ret;
181 }
182
183 static gboolean
184 gst_gl_mixer_propose_allocation (GstGLBaseMixer * base_mix,
185     GstGLBaseMixerPad * base_pad, GstQuery * decide_query, GstQuery * query)
186 {
187   GstGLMixer *mix = GST_GL_MIXER (base_mix);
188   GstGLMixerPad *pad = GST_GL_MIXER_PAD (base_pad);
189   GstGLContext *context = base_mix->context;
190   GstBufferPool *pool = NULL;
191   GstStructure *config;
192   GstCaps *caps;
193   guint size = 0;
194   gboolean need_pool;
195
196   gst_query_parse_allocation (query, &caps, &need_pool);
197
198   if (caps == NULL)
199     goto no_caps;
200
201   if (need_pool) {
202     GstVideoInfo info;
203
204     if (!gst_video_info_from_caps (&info, caps))
205       goto invalid_caps;
206
207     GST_DEBUG_OBJECT (mix, "create new pool");
208     pool = gst_gl_buffer_pool_new (context);
209
210     /* the normal size of a frame */
211     size = info.size;
212
213     config = gst_buffer_pool_get_config (pool);
214     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
215     if (!gst_buffer_pool_set_config (pool, config))
216       goto config_failed;
217   }
218
219   if (pool) {
220     gst_query_add_allocation_pool (query, pool, size, 1, 0);
221     gst_object_unref (pool);
222   }
223
224   /* we also support various metadata */
225   if (context->gl_vtable->FenceSync)
226     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
227
228   _init_upload (mix, pad);
229
230   gst_gl_upload_propose_allocation (pad->upload, decide_query, query);
231
232   return TRUE;
233
234   /* ERRORS */
235 no_caps:
236   {
237     GST_DEBUG_OBJECT (mix, "no caps specified");
238     return FALSE;
239   }
240 invalid_caps:
241   {
242     GST_DEBUG_OBJECT (mix, "invalid caps specified");
243     return FALSE;
244   }
245 config_failed:
246   {
247     GST_DEBUG_OBJECT (mix, "failed setting config");
248     return FALSE;
249   }
250 }
251
252 static gboolean
253 gst_gl_mixer_pad_sink_acceptcaps (GstPad * pad, GstGLMixer * mix,
254     GstCaps * caps)
255 {
256   gboolean ret;
257   GstCaps *template_caps;
258
259   GST_DEBUG_OBJECT (pad, "try accept caps of %" GST_PTR_FORMAT, caps);
260
261   template_caps = gst_pad_get_pad_template_caps (pad);
262   template_caps = gst_caps_make_writable (template_caps);
263
264   ret = gst_caps_can_intersect (caps, template_caps);
265   GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT,
266       (ret ? "" : "not "), caps);
267   gst_caps_unref (template_caps);
268
269   return ret;
270 }
271
272 static GstCaps *
273 gst_gl_mixer_set_caps_features (const GstCaps * caps,
274     const gchar * feature_name)
275 {
276   GstCaps *ret = gst_gl_caps_replace_all_caps_features (caps, feature_name);
277   gst_caps_set_simple (ret, "format", G_TYPE_STRING, "RGBA", NULL);
278   return ret;
279 }
280
281 GstCaps *
282 gst_gl_mixer_update_caps (GstGLMixer * mix, GstCaps * caps)
283 {
284   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
285   GstCaps *result, *tmp, *gl_caps;
286
287   gl_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory),format=RGBA");
288
289   result =
290       gst_gl_color_convert_transform_caps (context, GST_PAD_SRC, gl_caps, NULL);
291   tmp = result;
292   GST_DEBUG_OBJECT (mix, "convert returned caps %" GST_PTR_FORMAT, tmp);
293
294   result = gst_gl_upload_transform_caps (context, GST_PAD_SRC, tmp, NULL);
295   gst_caps_unref (tmp);
296   tmp = result;
297   GST_DEBUG_OBJECT (mix, "transfer returned caps %" GST_PTR_FORMAT, tmp);
298
299   return result;
300 }
301
302 static GstCaps *
303 gst_gl_mixer_pad_sink_getcaps (GstPad * pad, GstGLMixer * mix, GstCaps * filter)
304 {
305   GstCaps *sinkcaps;
306   GstCaps *template_caps;
307   GstCaps *filtered_caps;
308   GstCaps *returned_caps;
309   gboolean had_current_caps = TRUE;
310
311   template_caps = gst_pad_get_pad_template_caps (pad);
312
313   sinkcaps = gst_pad_get_current_caps (pad);
314   if (sinkcaps == NULL) {
315     had_current_caps = FALSE;
316     sinkcaps = template_caps;
317   } else {
318     sinkcaps =
319         gst_caps_merge (sinkcaps, gst_gl_mixer_update_caps (mix, sinkcaps));
320   }
321
322   filtered_caps = sinkcaps;
323   if (filter)
324     filtered_caps = gst_caps_intersect (sinkcaps, filter);
325   returned_caps = gst_caps_intersect (filtered_caps, template_caps);
326
327   if (filter)
328     gst_caps_unref (filtered_caps);
329   if (had_current_caps)
330     gst_caps_unref (template_caps);
331
332   GST_DEBUG_OBJECT (pad, "returning %" GST_PTR_FORMAT, returned_caps);
333
334   return returned_caps;
335 }
336
337 static gboolean
338 gst_gl_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
339     GstQuery * query)
340 {
341   gboolean ret = FALSE;
342   GstGLMixer *mix = GST_GL_MIXER (agg);
343
344   GST_TRACE ("QUERY %" GST_PTR_FORMAT, query);
345
346   switch (GST_QUERY_TYPE (query)) {
347     case GST_QUERY_CAPS:
348     {
349       GstCaps *filter, *caps;
350
351       gst_query_parse_caps (query, &filter);
352       caps = gst_gl_mixer_pad_sink_getcaps (GST_PAD (bpad), mix, filter);
353       gst_query_set_caps_result (query, caps);
354       gst_caps_unref (caps);
355       ret = TRUE;
356       break;
357     }
358     case GST_QUERY_ACCEPT_CAPS:
359     {
360       GstCaps *caps;
361
362       gst_query_parse_accept_caps (query, &caps);
363       ret = gst_gl_mixer_pad_sink_acceptcaps (GST_PAD (bpad), mix, caps);
364       gst_query_set_accept_caps_result (query, ret);
365       ret = TRUE;
366       break;
367     }
368     default:
369       ret = GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);
370       break;
371   }
372
373   return ret;
374 }
375
376 static void
377 gst_gl_mixer_pad_init (GstGLMixerPad * mixerpad)
378 {
379 }
380
381 /* GLMixer signals and args */
382 enum
383 {
384   /* FILL ME */
385   LAST_SIGNAL
386 };
387
388 enum
389 {
390   PROP_0,
391 };
392
393 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
394     GST_PAD_SRC,
395     GST_PAD_ALWAYS,
396     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
397         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
398             "RGBA") "; "
399         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
400         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
401             "RGBA")
402         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
403     );
404
405 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
406     GST_PAD_SINK,
407     GST_PAD_REQUEST,
408     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
409         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
410             "RGBA") "; "
411 #if GST_GL_HAVE_PLATFORM_EGL
412         GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE,
413             "RGBA") "; "
414 #endif
415         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
416         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
417             "RGBA")
418         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
419     );
420
421 static gboolean gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query);
422 static GstFlowReturn gst_gl_mixer_get_output_buffer (GstVideoAggregator *
423     videoaggregator, GstBuffer ** outbuf);
424 static gboolean gst_gl_mixer_stop (GstAggregator * agg);
425 static gboolean gst_gl_mixer_start (GstAggregator * agg);
426
427 static GstFlowReturn
428 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg,
429     GstBuffer * outbuffer);
430
431 static void gst_gl_mixer_set_property (GObject * object, guint prop_id,
432     const GValue * value, GParamSpec * pspec);
433 static void gst_gl_mixer_get_property (GObject * object, guint prop_id,
434     GValue * value, GParamSpec * pspec);
435
436 static gboolean gst_gl_mixer_decide_allocation (GstGLBaseMixer * mix,
437     GstQuery * query);
438
439 static void gst_gl_mixer_finalize (GObject * object);
440
441 static void
442 gst_gl_mixer_class_init (GstGLMixerClass * klass)
443 {
444   GObjectClass *gobject_class = (GObjectClass *) klass;
445   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
446   GstVideoAggregatorClass *videoaggregator_class =
447       (GstVideoAggregatorClass *) klass;
448   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
449   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_CLASS (klass);;
450
451   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "OpenGL mixer");
452
453   g_type_class_add_private (klass, sizeof (GstGLMixerPrivate));
454
455   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_finalize);
456
457   gobject_class->get_property = gst_gl_mixer_get_property;
458   gobject_class->set_property = gst_gl_mixer_set_property;
459
460   gst_element_class_add_pad_template (element_class,
461       gst_static_pad_template_get (&src_factory));
462   gst_element_class_add_pad_template (element_class,
463       gst_static_pad_template_get (&sink_factory));
464
465   agg_class->sinkpads_type = GST_TYPE_GL_MIXER_PAD;
466   agg_class->sink_query = gst_gl_mixer_sink_query;
467   agg_class->src_query = gst_gl_mixer_src_query;
468   agg_class->stop = gst_gl_mixer_stop;
469   agg_class->start = gst_gl_mixer_start;
470
471   videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
472   videoaggregator_class->get_output_buffer = gst_gl_mixer_get_output_buffer;
473   videoaggregator_class->negotiated_caps = _negotiated_caps;
474   videoaggregator_class->find_best_format = NULL;
475
476   mix_class->propose_allocation = gst_gl_mixer_propose_allocation;
477   mix_class->decide_allocation = gst_gl_mixer_decide_allocation;
478
479   /* Register the pad class */
480   g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
481
482   klass->set_caps = NULL;
483   klass->supported_gl_api = GST_GL_API_ANY;
484 }
485
486 static void
487 gst_gl_mixer_reset (GstGLMixer * mix)
488 {
489   mix->priv->negotiated = FALSE;
490 }
491
492 static void
493 gst_gl_mixer_init (GstGLMixer * mix)
494 {
495   mix->priv = GST_GL_MIXER_GET_PRIVATE (mix);
496   mix->array_buffers = 0;
497   mix->fbo = 0;
498   mix->depthbuffer = 0;
499
500   mix->priv->gl_resource_ready = FALSE;
501   g_mutex_init (&mix->priv->gl_resource_lock);
502   g_cond_init (&mix->priv->gl_resource_cond);
503   /* initialize variables */
504   gst_gl_mixer_reset (mix);
505 }
506
507 static void
508 gst_gl_mixer_finalize (GObject * object)
509 {
510   GstGLMixer *mix = GST_GL_MIXER (object);
511   GstGLMixerPrivate *priv = mix->priv;
512
513   g_mutex_clear (&priv->gl_resource_lock);
514   g_cond_clear (&priv->gl_resource_cond);
515   G_OBJECT_CLASS (parent_class)->finalize (object);
516 }
517
518 static gboolean
519 gst_gl_mixer_query_caps (GstPad * pad, GstAggregator * agg, GstQuery * query)
520 {
521   GstGLMixer *mix = GST_GL_MIXER (agg);
522   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
523   GstCaps *filter, *current_caps, *retcaps, *gl_caps;
524
525   gst_query_parse_caps (query, &filter);
526
527   current_caps = gst_pad_get_current_caps (pad);
528   if (current_caps == NULL)
529     current_caps = gst_pad_get_pad_template_caps (agg->srcpad);
530
531   /* convert from current caps to GLMemory caps */
532   gl_caps =
533       gst_caps_merge (gst_gl_mixer_set_caps_features
534       (current_caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY),
535       gst_gl_mixer_set_caps_features (current_caps,
536           GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META));
537   retcaps =
538       gst_gl_download_transform_caps (context, GST_PAD_SINK, current_caps,
539       NULL);
540   retcaps = gst_caps_merge (gl_caps, retcaps);
541   gst_caps_unref (current_caps);
542
543   if (filter) {
544     current_caps =
545         gst_caps_intersect_full (filter, retcaps, GST_CAPS_INTERSECT_FIRST);
546     gst_caps_unref (retcaps);
547     retcaps = current_caps;
548   }
549
550   gst_query_set_caps_result (query, retcaps);
551   gst_caps_unref (retcaps);
552
553   return TRUE;
554 }
555
556 static gboolean
557 gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query)
558 {
559   gboolean res = FALSE;
560
561   switch (GST_QUERY_TYPE (query)) {
562     case GST_QUERY_CAPS:
563       res = gst_gl_mixer_query_caps (agg->srcpad, agg, query);
564       break;
565     default:
566       res = GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
567       break;
568   }
569
570   return res;
571 }
572
573 static GstFlowReturn
574 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
575     GstBuffer ** outbuf)
576 {
577   GstGLMixer *mix = GST_GL_MIXER (videoaggregator);
578   GstBufferPool *pool;
579   GstFlowReturn ret;
580
581   pool =
582       gst_gl_base_mixer_get_buffer_pool (GST_GL_BASE_MIXER (videoaggregator));
583
584   if (!pool)
585     return GST_FLOW_NOT_NEGOTIATED;
586
587   if (!gst_buffer_pool_is_active (pool)) {
588     if (!gst_buffer_pool_set_active (pool, TRUE)) {
589       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
590           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
591       return GST_FLOW_ERROR;
592     }
593   }
594
595   ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL);
596   gst_object_unref (pool);
597
598   return ret;
599 }
600
601 static gboolean
602 gst_gl_mixer_decide_allocation (GstGLBaseMixer * base_mix, GstQuery * query)
603 {
604   GstGLMixer *mix = GST_GL_MIXER (base_mix);
605   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
606   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
607   GstGLContext *context = base_mix->context;
608   GstBufferPool *pool = NULL;
609   GstStructure *config;
610   GstCaps *caps;
611   guint min, max, size;
612   gboolean update_pool;
613   GError *error = NULL;
614   guint out_width, out_height;
615
616   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
617   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
618
619   g_mutex_lock (&mix->priv->gl_resource_lock);
620   mix->priv->gl_resource_ready = FALSE;
621   if (mix->fbo) {
622     gst_gl_context_del_fbo (context, mix->fbo, mix->depthbuffer);
623     mix->fbo = 0;
624     mix->depthbuffer = 0;
625   }
626
627   if (!gst_gl_context_gen_fbo (context, out_width, out_height,
628           &mix->fbo, &mix->depthbuffer)) {
629     g_cond_signal (&mix->priv->gl_resource_cond);
630     g_mutex_unlock (&mix->priv->gl_resource_lock);
631     goto context_error;
632   }
633
634   if (mix->out_tex_id)
635     gst_gl_context_del_texture (context, &mix->out_tex_id);
636   gst_gl_context_gen_texture (context, &mix->out_tex_id,
637       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
638
639   gst_query_parse_allocation (query, &caps, NULL);
640
641   mix->context = context;
642   if (mixer_class->set_caps)
643     mixer_class->set_caps (mix, caps);
644
645   mix->priv->gl_resource_ready = TRUE;
646   g_cond_signal (&mix->priv->gl_resource_cond);
647   g_mutex_unlock (&mix->priv->gl_resource_lock);
648
649   if (gst_query_get_n_allocation_pools (query) > 0) {
650     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
651
652     update_pool = TRUE;
653   } else {
654     GstVideoInfo vinfo;
655
656     gst_video_info_init (&vinfo);
657     gst_video_info_from_caps (&vinfo, caps);
658     size = vinfo.size;
659     min = max = 0;
660     update_pool = FALSE;
661   }
662
663   if (!pool)
664     pool = gst_gl_buffer_pool_new (context);
665   config = gst_buffer_pool_get_config (pool);
666
667   gst_buffer_pool_config_set_params (config, caps, size, min, max);
668   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
669   if (gst_query_find_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL))
670     gst_buffer_pool_config_add_option (config,
671         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
672   gst_buffer_pool_config_add_option (config,
673       GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
674
675   gst_buffer_pool_set_config (pool, config);
676
677   if (update_pool)
678     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
679   else
680     gst_query_add_allocation_pool (query, pool, size, min, max);
681
682   gst_object_unref (pool);
683
684   return TRUE;
685
686 context_error:
687   {
688     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
689         (NULL));
690     return FALSE;
691   }
692 }
693
694 static GstBuffer *
695 _default_pad_upload_buffer (GstGLMixer * mix, GstGLMixerFrameData * frame,
696     GstBuffer * buffer)
697 {
698   GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (frame->pad);
699   GstGLMixerPad *pad = frame->pad;
700   GstBuffer *uploaded_buf, *gl_buffer;
701   GstVideoInfo gl_info;
702   GstVideoFrame gl_frame;
703   GstGLSyncMeta *sync_meta;
704
705   gst_video_info_set_format (&gl_info,
706       GST_VIDEO_FORMAT_RGBA,
707       GST_VIDEO_INFO_WIDTH (&vaggpad->info),
708       GST_VIDEO_INFO_HEIGHT (&vaggpad->info));
709
710   _init_upload (mix, pad);
711
712   sync_meta = gst_buffer_get_gl_sync_meta (vaggpad->buffer);
713   if (sync_meta)
714     gst_gl_sync_meta_wait (sync_meta);
715
716   if (gst_gl_upload_perform_with_buffer (pad->upload,
717           vaggpad->buffer, &uploaded_buf) != GST_GL_UPLOAD_DONE) {
718     return NULL;
719   }
720
721   if (!(gl_buffer = gst_gl_color_convert_perform (pad->convert, uploaded_buf))) {
722     gst_buffer_unref (uploaded_buf);
723     return NULL;
724   }
725
726   if (!gst_video_frame_map (&gl_frame, &gl_info, gl_buffer,
727           GST_MAP_READ | GST_MAP_GL)) {
728     gst_buffer_unref (uploaded_buf);
729     gst_buffer_unref (gl_buffer);
730     return NULL;
731   }
732
733   frame->texture = *(guint *) gl_frame.data[0];
734
735   gst_buffer_unref (uploaded_buf);
736   gst_video_frame_unmap (&gl_frame);
737
738   return gl_buffer;
739 }
740
741 gboolean
742 gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
743 {
744   guint i;
745   GList *walk;
746   guint out_tex, out_tex_target;
747   gboolean res = TRUE;
748   guint array_index = 0;
749   GstVideoFrame out_frame;
750   GstElement *element = GST_ELEMENT (mix);
751   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
752   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
753   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
754   GstGLMixerPrivate *priv = mix->priv;
755   gboolean to_download =
756       gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY,
757       gst_caps_get_features (mix->out_caps, 0));
758   GstMapFlags out_map_flags = GST_MAP_WRITE;
759
760   GST_TRACE ("Processing buffers");
761
762   to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0));
763
764   if (!to_download)
765     out_map_flags |= GST_MAP_GL;
766
767   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, out_map_flags)) {
768     return FALSE;
769   }
770
771   if (!to_download) {
772     out_tex = *(guint *) out_frame.data[0];
773     out_tex_target =
774         ((GstGLMemory *) gst_buffer_peek_memory (outbuf, 0))->tex_target;
775   } else {
776     GST_INFO ("Output Buffer does not contain correct memory, "
777         "attempting to wrap for download");
778
779     if (!mix->download)
780       mix->download = gst_gl_download_new (context);
781
782     gst_gl_download_set_format (mix->download, &out_frame.info);
783     out_tex = mix->out_tex_id;
784     out_tex_target = GL_TEXTURE_2D;
785   }
786
787   GST_OBJECT_LOCK (mix);
788   walk = element->sinkpads;
789
790   i = mix->frames->len;
791   g_ptr_array_set_size (mix->frames, element->numsinkpads);
792   for (; i < element->numsinkpads; i++)
793     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
794   while (walk) {
795     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
796     GstGLMixerPadClass *pad_class = GST_GL_MIXER_PAD_GET_CLASS (pad);
797     GstVideoAggregatorPad *vaggpad = walk->data;
798     GstGLMixerFrameData *frame;
799
800     frame = g_ptr_array_index (mix->frames, array_index);
801     frame->pad = pad;
802     frame->texture = 0;
803
804     walk = g_list_next (walk);
805
806     if (vaggpad->buffer != NULL) {
807       g_assert (pad_class->upload_buffer);
808
809       if (pad->gl_buffer)
810         gst_buffer_unref (pad->gl_buffer);
811       pad->gl_buffer = pad_class->upload_buffer (mix, frame, vaggpad->buffer);
812
813       GST_DEBUG_OBJECT (pad,
814           "uploaded buffer %" GST_PTR_FORMAT " from buffer %" GST_PTR_FORMAT,
815           pad->gl_buffer, vaggpad->buffer);
816     }
817
818     ++array_index;
819   }
820
821   g_mutex_lock (&priv->gl_resource_lock);
822   if (!priv->gl_resource_ready)
823     g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
824
825   if (!priv->gl_resource_ready) {
826     g_mutex_unlock (&priv->gl_resource_lock);
827     GST_ERROR_OBJECT (mix,
828         "fbo used to render can't be created, do not run process_textures");
829     res = FALSE;
830     goto out;
831   }
832
833   mix_class->process_textures (mix, mix->frames, out_tex);
834
835   g_mutex_unlock (&priv->gl_resource_lock);
836
837   if (to_download) {
838     if (!gst_gl_download_perform_with_data (mix->download,
839             out_tex, out_tex_target, out_frame.data)) {
840       GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s",
841               "Failed to download video frame"), (NULL));
842       res = FALSE;
843       goto out;
844     }
845   }
846
847 out:
848   i = 0;
849   walk = GST_ELEMENT (mix)->sinkpads;
850   while (walk) {
851     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
852
853     if (pad->upload)
854       gst_gl_upload_release_buffer (pad->upload);
855
856     walk = g_list_next (walk);
857     i++;
858   }
859   GST_OBJECT_UNLOCK (mix);
860
861   gst_video_frame_unmap (&out_frame);
862
863   return res;
864 }
865
866 static gboolean
867 gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
868 {
869   GList *walk;
870   guint i, array_index = 0;
871   GstElement *element = GST_ELEMENT (mix);
872   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
873
874   GST_OBJECT_LOCK (mix);
875   walk = GST_ELEMENT (mix)->sinkpads;
876   i = mix->frames->len;
877   g_ptr_array_set_size (mix->frames, element->numsinkpads);
878   for (; i < element->numsinkpads; i++)
879     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
880   while (walk) {                /* We walk with this list because it's ordered */
881     GstVideoAggregatorPad *vaggpad = walk->data;
882
883     walk = g_list_next (walk);
884
885     if (vaggpad->buffer != NULL) {
886       /* put buffer into array */
887       mix->array_buffers->pdata[array_index] = vaggpad->buffer;
888     }
889     ++array_index;
890   }
891   GST_OBJECT_UNLOCK (mix);
892
893   return mix_class->process_buffers (mix, mix->array_buffers, outbuf);
894 }
895
896 static GstFlowReturn
897 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
898 {
899   gboolean res = FALSE;
900   GstGLMixer *mix = GST_GL_MIXER (vagg);
901   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
902   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
903   GstGLSyncMeta *sync_meta;
904
905   if (mix_class->process_buffers)
906     res = gst_gl_mixer_process_buffers (mix, outbuf);
907   else if (mix_class->process_textures)
908     res = gst_gl_mixer_process_textures (mix, outbuf);
909
910   sync_meta = gst_buffer_get_gl_sync_meta (outbuf);
911   if (sync_meta)
912     gst_gl_sync_meta_set_sync_point (sync_meta, context);
913
914   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
915 }
916
917 static void
918 gst_gl_mixer_get_property (GObject * object,
919     guint prop_id, GValue * value, GParamSpec * pspec)
920 {
921   switch (prop_id) {
922     default:
923       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
924       break;
925   }
926 }
927
928 static void
929 gst_gl_mixer_set_property (GObject * object,
930     guint prop_id, const GValue * value, GParamSpec * pspec)
931 {
932   switch (prop_id) {
933     default:
934       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
935       break;
936   }
937 }
938
939 static gboolean
940 _clean_upload (GstAggregator * agg, GstAggregatorPad * aggpad, gpointer udata)
941 {
942   GstGLMixerPad *pad = GST_GL_MIXER_PAD (aggpad);
943
944   if (pad->gl_buffer) {
945     gst_buffer_unref (pad->gl_buffer);
946     pad->gl_buffer = NULL;
947   }
948
949   if (pad->upload) {
950     gst_object_unref (pad->upload);
951     pad->upload = NULL;
952   }
953
954   if (pad->convert) {
955     gst_object_unref (pad->convert);
956     pad->convert = NULL;
957   }
958
959   return TRUE;
960 }
961
962 static void
963 _free_glmixer_frame_data (GstGLMixerFrameData * frame)
964 {
965   g_slice_free1 (sizeof (GstGLMixerFrameData), frame);
966 }
967
968 static gboolean
969 gst_gl_mixer_start (GstAggregator * agg)
970 {
971   guint i;
972   GstGLMixer *mix = GST_GL_MIXER (agg);
973   GstElement *element = GST_ELEMENT (agg);
974
975   GST_OBJECT_LOCK (mix);
976   mix->array_buffers = g_ptr_array_new_full (element->numsinkpads,
977       (GDestroyNotify) _free_glmixer_frame_data);
978   mix->frames = g_ptr_array_new_full (element->numsinkpads, NULL);
979
980   g_ptr_array_set_size (mix->array_buffers, element->numsinkpads);
981   g_ptr_array_set_size (mix->frames, element->numsinkpads);
982
983   for (i = 0; i < element->numsinkpads; i++)
984     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
985
986   GST_OBJECT_UNLOCK (mix);
987
988   return GST_AGGREGATOR_CLASS (parent_class)->start (agg);
989 }
990
991 static gboolean
992 gst_gl_mixer_stop (GstAggregator * agg)
993 {
994   GstGLMixer *mix = GST_GL_MIXER (agg);
995   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
996   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
997
998   GST_OBJECT_LOCK (agg);
999   g_ptr_array_free (mix->frames, TRUE);
1000   mix->frames = NULL;
1001   g_ptr_array_free (mix->array_buffers, TRUE);
1002   mix->array_buffers = NULL;
1003   GST_OBJECT_UNLOCK (agg);
1004
1005   if (mixer_class->reset)
1006     mixer_class->reset (mix);
1007   if (mix->fbo) {
1008     gst_gl_context_del_fbo (context, mix->fbo, mix->depthbuffer);
1009     mix->fbo = 0;
1010     mix->depthbuffer = 0;
1011   }
1012   if (mix->download) {
1013     gst_object_unref (mix->download);
1014     mix->download = NULL;
1015   }
1016
1017   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (mix), _clean_upload, NULL);
1018
1019   gst_gl_mixer_reset (mix);
1020
1021   return GST_AGGREGATOR_CLASS (parent_class)->stop (agg);
1022 }