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