glmixer: Add other-context property
[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   GstGLMixerPrivate *priv = GST_GL_MIXER (object)->priv;
467
468   g_mutex_clear (&priv->gl_resource_lock);
469   g_cond_clear (&priv->gl_resource_cond);
470   G_OBJECT_CLASS (parent_class)->finalize (object);
471 }
472
473 static void
474 gst_gl_mixer_set_context (GstElement * element, GstContext * context)
475 {
476   GstGLMixer *mix = GST_GL_MIXER (element);
477
478   gst_gl_handle_set_context (element, context, &mix->display);
479 }
480
481 static gboolean
482 gst_gl_mixer_activate (GstGLMixer * mix, gboolean active)
483 {
484   gboolean result = TRUE;
485
486   if (active) {
487     if (!gst_gl_ensure_display (mix, &mix->display))
488       result = FALSE;
489   }
490
491   return result;
492 }
493
494 static gboolean
495 gst_gl_mixer_src_activate_mode (GstAggregator * aggregator, GstPadMode mode,
496     gboolean active)
497 {
498   GstGLMixer *mix;
499   gboolean result = FALSE;
500
501   mix = GST_GL_MIXER (aggregator);
502
503   switch (mode) {
504     case GST_PAD_MODE_PUSH:
505     case GST_PAD_MODE_PULL:
506       result = gst_gl_mixer_activate (mix, active);
507       break;
508     default:
509       result = TRUE;
510       break;
511   }
512   return result;
513 }
514
515 static gboolean
516 gst_gl_mixer_query_caps (GstPad * pad, GstAggregator * agg, GstQuery * query)
517 {
518   GstCaps *filter, *caps;
519   GstStructure *s;
520   gint n;
521
522   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
523
524   gst_query_parse_caps (query, &filter);
525
526   if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) {
527     caps = gst_video_info_to_caps (&vagg->info);
528   } else {
529     caps = gst_pad_get_pad_template_caps (agg->srcpad);
530   }
531
532   caps = gst_caps_make_writable (caps);
533
534   n = gst_caps_get_size (caps) - 1;
535   for (; n >= 0; n--) {
536     s = gst_caps_get_structure (caps, n);
537     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
538         "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
539     if (GST_VIDEO_INFO_FPS_D (&vagg->info) != 0) {
540       gst_structure_set (s,
541           "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
542     }
543   }
544
545   if (filter)
546     caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
547
548   gst_query_set_caps_result (query, caps);
549   gst_caps_unref (caps);
550
551   return TRUE;
552 }
553
554 static gboolean
555 gst_gl_mixer_src_query (GstAggregator * agg, GstQuery * query)
556 {
557   gboolean res = FALSE;
558   GstGLMixer *mix = GST_GL_MIXER (agg);
559
560   switch (GST_QUERY_TYPE (query)) {
561     case GST_QUERY_CONTEXT:
562     {
563       res = gst_gl_handle_context_query ((GstElement *) mix, query,
564           &mix->display);
565       break;
566     }
567     case GST_QUERY_CAPS:
568       res = gst_gl_mixer_query_caps (agg->srcpad, agg, query);
569       break;
570     default:
571       res = GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
572       break;
573   }
574
575   return res;
576 }
577
578 static GstFlowReturn
579 gst_gl_mixer_get_output_buffer (GstVideoAggregator * videoaggregator,
580     GstBuffer ** outbuf)
581 {
582   GstGLMixer *mix = GST_GL_MIXER (videoaggregator);
583
584   if (!mix->priv->pool_active) {
585     if (!gst_buffer_pool_set_active (mix->priv->pool, TRUE)) {
586       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
587           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
588       return GST_FLOW_ERROR;
589     }
590     mix->priv->pool_active = TRUE;
591   }
592
593   return gst_buffer_pool_acquire_buffer (mix->priv->pool, outbuf, NULL);
594 }
595
596 static gboolean
597 gst_gl_mixer_decide_allocation (GstGLMixer * mix, GstQuery * query)
598 {
599   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
600   GstBufferPool *pool = NULL;
601   GstStructure *config;
602   GstCaps *caps;
603   guint min, max, size;
604   gboolean update_pool;
605   GError *error = NULL;
606   guint idx;
607   guint out_width, out_height;
608   GstGLContext *other_context = NULL;
609   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
610
611   if (!gst_gl_ensure_display (mix, &mix->display))
612     return FALSE;
613
614   if (gst_query_find_allocation_meta (query,
615           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx)) {
616     GstGLContext *context;
617     const GstStructure *upload_meta_params;
618     gpointer handle;
619     gchar *type;
620     gchar *apis;
621
622     gst_query_parse_nth_allocation_meta (query, idx, &upload_meta_params);
623     if (upload_meta_params) {
624       if (gst_structure_get (upload_meta_params, "gst.gl.GstGLContext",
625               GST_GL_TYPE_CONTEXT, &context, NULL) && context) {
626         GstGLContext *old = mix->context;
627
628         mix->context = context;
629         if (old)
630           gst_object_unref (old);
631       } else if (gst_structure_get (upload_meta_params, "gst.gl.context.handle",
632               G_TYPE_POINTER, &handle, "gst.gl.context.type", G_TYPE_STRING,
633               &type, "gst.gl.context.apis", G_TYPE_STRING, &apis, NULL)
634           && handle) {
635         GstGLPlatform platform;
636         GstGLAPI gl_apis;
637
638         GST_DEBUG ("got GL context handle 0x%p with type %s and apis %s",
639             handle, type, apis);
640
641         platform = gst_gl_platform_from_string (type);
642         gl_apis = gst_gl_api_from_string (apis);
643
644         if (gl_apis && platform)
645           other_context =
646               gst_gl_context_new_wrapped (mix->display, (guintptr) handle,
647               platform, gl_apis);
648       }
649     }
650   }
651
652   if (mix->other_context) {
653     if (!other_context) {
654       other_context = mix->other_context;
655     } else {
656       GST_ELEMENT_WARNING (mix, LIBRARY, SETTINGS,
657           ("%s", "Cannot share with more than one GL context"),
658           ("%s", "Cannot share with more than one GL context"));
659     }
660   }
661
662   if (!mix->context) {
663     mix->context = gst_gl_context_new (mix->display);
664     if (!gst_gl_context_create (mix->context, other_context, &error))
665       goto context_error;
666   }
667
668   out_width = GST_VIDEO_INFO_WIDTH (&vagg->info);
669   out_height = GST_VIDEO_INFO_HEIGHT (&vagg->info);
670
671   g_mutex_lock (&mix->priv->gl_resource_lock);
672   mix->priv->gl_resource_ready = FALSE;
673   if (mix->fbo) {
674     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
675     mix->fbo = 0;
676     mix->depthbuffer = 0;
677   }
678
679   if (!gst_gl_context_gen_fbo (mix->context, out_width, out_height,
680           &mix->fbo, &mix->depthbuffer)) {
681     g_cond_signal (&mix->priv->gl_resource_cond);
682     g_mutex_unlock (&mix->priv->gl_resource_lock);
683     goto context_error;
684   }
685
686   if (mix->out_tex_id)
687     gst_gl_context_del_texture (mix->context, &mix->out_tex_id);
688   gst_gl_context_gen_texture (mix->context, &mix->out_tex_id,
689       GST_VIDEO_FORMAT_RGBA, out_width, out_height);
690
691   gst_query_parse_allocation (query, &caps, NULL);
692
693   if (mixer_class->set_caps)
694     mixer_class->set_caps (mix, caps);
695
696   mix->priv->gl_resource_ready = TRUE;
697   g_cond_signal (&mix->priv->gl_resource_cond);
698   g_mutex_unlock (&mix->priv->gl_resource_lock);
699
700   if (gst_query_get_n_allocation_pools (query) > 0) {
701     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
702
703     update_pool = TRUE;
704   } else {
705     GstVideoInfo vinfo;
706
707     gst_video_info_init (&vinfo);
708     gst_video_info_from_caps (&vinfo, caps);
709     size = vinfo.size;
710     min = max = 0;
711     update_pool = FALSE;
712   }
713
714   if (!pool)
715     pool = gst_gl_buffer_pool_new (mix->context);
716
717   config = gst_buffer_pool_get_config (pool);
718   gst_buffer_pool_config_set_params (config, caps, size, min, max);
719
720   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
721
722   gst_buffer_pool_set_config (pool, config);
723
724   if (update_pool)
725     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
726   else
727     gst_query_add_allocation_pool (query, pool, size, min, max);
728
729   gst_object_unref (pool);
730
731   return TRUE;
732
733 context_error:
734   {
735     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
736         (NULL));
737     return FALSE;
738   }
739 }
740
741 /* takes ownership of the pool, allocator and query */
742 static gboolean
743 gst_gl_mixer_set_allocation (GstGLMixer * mix,
744     GstBufferPool * pool, GstAllocator * allocator,
745     GstAllocationParams * params, GstQuery * query)
746 {
747   GstAllocator *oldalloc;
748   GstBufferPool *oldpool;
749   GstQuery *oldquery;
750   GstGLMixerPrivate *priv = mix->priv;
751
752   GST_DEBUG ("storing allocation query");
753
754   GST_OBJECT_LOCK (mix);
755   oldpool = priv->pool;
756   priv->pool = pool;
757   priv->pool_active = FALSE;
758
759   oldalloc = priv->allocator;
760   priv->allocator = allocator;
761
762   oldquery = priv->query;
763   priv->query = query;
764
765   if (params)
766     priv->params = *params;
767   else
768     gst_allocation_params_init (&priv->params);
769   GST_OBJECT_UNLOCK (mix);
770
771   if (oldpool) {
772     GST_DEBUG_OBJECT (mix, "deactivating old pool %p", oldpool);
773     gst_buffer_pool_set_active (oldpool, FALSE);
774     gst_object_unref (oldpool);
775   }
776   if (oldalloc) {
777     gst_object_unref (oldalloc);
778   }
779   if (oldquery) {
780     gst_query_unref (oldquery);
781   }
782   return TRUE;
783 }
784
785 static gboolean
786 gst_gl_mixer_do_bufferpool (GstGLMixer * mix, GstCaps * outcaps)
787 {
788   GstQuery *query;
789   gboolean result = TRUE;
790   GstBufferPool *pool = NULL;
791   GstAllocator *allocator;
792   GstAllocationParams params;
793   GstAggregator *agg = GST_AGGREGATOR (mix);
794
795   /* find a pool for the negotiated caps now */
796   GST_DEBUG_OBJECT (mix, "doing allocation query");
797   query = gst_query_new_allocation (outcaps, TRUE);
798   if (!gst_pad_peer_query (agg->srcpad, query)) {
799     /* not a problem, just debug a little */
800     GST_DEBUG_OBJECT (mix, "peer ALLOCATION query failed");
801   }
802
803   GST_DEBUG_OBJECT (mix, "calling decide_allocation");
804   result = gst_gl_mixer_decide_allocation (mix, query);
805
806   GST_DEBUG_OBJECT (mix, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
807       query);
808
809   if (!result)
810     goto no_decide_allocation;
811
812   /* we got configuration from our peer or the decide_allocation method,
813    * parse them */
814   if (gst_query_get_n_allocation_params (query) > 0) {
815     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
816   } else {
817     allocator = NULL;
818     gst_allocation_params_init (&params);
819   }
820
821   if (gst_query_get_n_allocation_pools (query) > 0)
822     gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
823
824   /* now store */
825   result = gst_gl_mixer_set_allocation (mix, pool, allocator, &params, query);
826
827   return result;
828
829   /* Errors */
830 no_decide_allocation:
831   {
832     GST_WARNING_OBJECT (mix, "Failed to decide allocation");
833     gst_query_unref (query);
834
835     return result;
836   }
837 }
838
839 gboolean
840 gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
841 {
842   guint i;
843   GList *walk;
844   guint out_tex;
845   gboolean res = TRUE;
846   guint array_index = 0;
847   GstVideoFrame out_frame;
848   GstElement *element = GST_ELEMENT (mix);
849   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
850   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
851   GstGLMixerPrivate *priv = mix->priv;
852   gboolean to_download =
853       gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY,
854       gst_caps_get_features (mix->out_caps, 0));
855   GstMapFlags out_map_flags = GST_MAP_WRITE;
856
857   GST_TRACE ("Processing buffers");
858
859   to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0));
860
861   if (!to_download)
862     out_map_flags |= GST_MAP_GL;
863
864   if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, out_map_flags)) {
865     return FALSE;
866   }
867
868   if (!to_download) {
869     out_tex = *(guint *) out_frame.data[0];
870   } else {
871     GST_INFO ("Output Buffer does not contain correct memory, "
872         "attempting to wrap for download");
873
874     if (!mix->download)
875       mix->download = gst_gl_download_new (mix->context);
876
877     gst_gl_download_set_format (mix->download, &out_frame.info);
878     out_tex = mix->out_tex_id;
879   }
880
881   GST_OBJECT_LOCK (mix);
882   walk = element->sinkpads;
883
884   i = mix->frames->len;
885   g_ptr_array_set_size (mix->frames, element->numsinkpads);
886   for (; i < element->numsinkpads; i++)
887     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
888   while (walk) {
889     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
890     GstVideoAggregatorPad *vaggpad = walk->data;
891     GstGLMixerFrameData *frame;
892
893     frame = g_ptr_array_index (mix->frames, array_index);
894     frame->pad = pad;
895     frame->texture = 0;
896
897     walk = g_list_next (walk);
898
899     if (vaggpad->buffer != NULL) {
900       guint in_tex;
901
902       if (!pad->upload) {
903         pad->upload = gst_gl_upload_new (mix->context);
904
905         gst_gl_upload_set_format (pad->upload, &vaggpad->info);
906       }
907
908       if (!gst_gl_upload_perform_with_buffer (pad->upload,
909               vaggpad->buffer, &in_tex, NULL)) {
910         ++array_index;
911         pad->mapped = FALSE;
912         continue;
913       }
914       pad->mapped = TRUE;
915
916       frame->texture = in_tex;
917     }
918     ++array_index;
919   }
920
921   g_mutex_lock (&priv->gl_resource_lock);
922   if (!priv->gl_resource_ready)
923     g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
924
925   if (!priv->gl_resource_ready) {
926     g_mutex_unlock (&priv->gl_resource_lock);
927     GST_ERROR_OBJECT (mix,
928         "fbo used to render can't be created, do not run process_textures");
929     res = FALSE;
930     goto out;
931   }
932
933   mix_class->process_textures (mix, mix->frames, out_tex);
934
935   g_mutex_unlock (&priv->gl_resource_lock);
936
937   if (to_download) {
938     if (!gst_gl_download_perform_with_data (mix->download, out_tex,
939             out_frame.data)) {
940       GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s",
941               "Failed to download video frame"), (NULL));
942       res = FALSE;
943       goto out;
944     }
945   }
946
947 out:
948   i = 0;
949   walk = GST_ELEMENT (mix)->sinkpads;
950   while (walk) {
951     GstGLMixerPad *pad = GST_GL_MIXER_PAD (walk->data);
952
953     if (pad->mapped)
954       gst_gl_upload_release_buffer (pad->upload);
955
956     pad->mapped = FALSE;
957     walk = g_list_next (walk);
958     i++;
959   }
960   GST_OBJECT_UNLOCK (mix);
961
962   gst_video_frame_unmap (&out_frame);
963
964   return res;
965 }
966
967 static gboolean
968 gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
969 {
970   GList *walk;
971   guint i, array_index = 0;
972   GstElement *element = GST_ELEMENT (mix);
973   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
974
975   GST_OBJECT_LOCK (mix);
976   walk = GST_ELEMENT (mix)->sinkpads;
977   i = mix->frames->len;
978   g_ptr_array_set_size (mix->frames, element->numsinkpads);
979   for (; i < element->numsinkpads; i++)
980     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
981   while (walk) {                /* We walk with this list because it's ordered */
982     GstVideoAggregatorPad *vaggpad = walk->data;
983
984     walk = g_list_next (walk);
985
986     if (vaggpad->buffer != NULL) {
987       /* put buffer into array */
988       mix->array_buffers->pdata[array_index] = vaggpad->buffer;
989     }
990     ++array_index;
991   }
992   GST_OBJECT_UNLOCK (mix);
993
994   return mix_class->process_buffers (mix, mix->array_buffers, outbuf);
995 }
996
997
998
999 static GstFlowReturn
1000 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
1001 {
1002   gboolean res = FALSE;
1003   GstGLMixer *mix = GST_GL_MIXER (vagg);
1004   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
1005
1006   if (mix_class->process_buffers)
1007     res = gst_gl_mixer_process_buffers (mix, outbuf);
1008   else if (mix_class->process_textures)
1009     res = gst_gl_mixer_process_textures (mix, outbuf);
1010
1011   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
1012 }
1013
1014 static void
1015 gst_gl_mixer_get_property (GObject * object,
1016     guint prop_id, GValue * value, GParamSpec * pspec)
1017 {
1018   GstGLMixer *mix = GST_GL_MIXER (object);
1019
1020   switch (prop_id) {
1021     case PROP_OTHER_CONTEXT:
1022       g_value_set_object (value, mix->other_context);
1023       break;
1024     default:
1025       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1026       break;
1027   }
1028 }
1029
1030 static void
1031 gst_gl_mixer_set_property (GObject * object,
1032     guint prop_id, const GValue * value, GParamSpec * pspec)
1033 {
1034   GstGLMixer *mix = GST_GL_MIXER (object);
1035
1036   switch (prop_id) {
1037     case PROP_OTHER_CONTEXT:
1038     {
1039       if (mix->other_context)
1040         gst_object_unref (mix->other_context);
1041       mix->other_context = g_value_dup_object (value);
1042       break;
1043     }
1044     default:
1045       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046       break;
1047   }
1048 }
1049
1050 static gboolean
1051 _clean_upload (GstAggregator * agg, GstPad * aggpad, gpointer udata)
1052 {
1053   GstGLMixerPad *pad = GST_GL_MIXER_PAD (aggpad);
1054
1055   if (pad->upload) {
1056     gst_object_unref (pad->upload);
1057     pad->upload = NULL;
1058   }
1059
1060   return TRUE;
1061 }
1062
1063 static void
1064 _free_glmixer_frame_data (GstGLMixerFrameData * frame)
1065 {
1066   g_slice_free1 (sizeof (GstGLMixerFrameData), frame);
1067 }
1068
1069 static gboolean
1070 gst_gl_mixer_start (GstAggregator * agg)
1071 {
1072   guint i;
1073   GstGLMixer *mix = GST_GL_MIXER (agg);
1074   GstElement *element = GST_ELEMENT (agg);
1075
1076   if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg))
1077     return FALSE;
1078
1079   GST_OBJECT_LOCK (mix);
1080   mix->array_buffers = g_ptr_array_new_full (element->numsinkpads,
1081       (GDestroyNotify) _free_glmixer_frame_data);
1082   mix->frames = g_ptr_array_new_full (element->numsinkpads, NULL);
1083
1084   g_ptr_array_set_size (mix->array_buffers, element->numsinkpads);
1085   g_ptr_array_set_size (mix->frames, element->numsinkpads);
1086
1087   for (i = 0; i < element->numsinkpads; i++)
1088     mix->frames->pdata[i] = g_slice_new0 (GstGLMixerFrameData);
1089
1090   GST_OBJECT_UNLOCK (mix);
1091
1092   return TRUE;
1093 }
1094
1095 static gboolean
1096 gst_gl_mixer_stop (GstAggregator * agg)
1097 {
1098   GstGLMixer *mix = GST_GL_MIXER (agg);
1099   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
1100
1101   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
1102     return FALSE;
1103
1104   GST_OBJECT_LOCK (agg);
1105   g_ptr_array_free (mix->frames, TRUE);
1106   mix->frames = NULL;
1107   g_ptr_array_free (mix->array_buffers, TRUE);
1108   mix->array_buffers = NULL;
1109   GST_OBJECT_UNLOCK (agg);
1110
1111   if (mixer_class->reset)
1112     mixer_class->reset (mix);
1113   if (mix->fbo) {
1114     gst_gl_context_del_fbo (mix->context, mix->fbo, mix->depthbuffer);
1115     mix->fbo = 0;
1116     mix->depthbuffer = 0;
1117   }
1118   if (mix->download) {
1119     gst_object_unref (mix->download);
1120     mix->download = NULL;
1121   }
1122
1123   gst_aggregator_iterate_sinkpads (GST_AGGREGATOR (mix), _clean_upload, NULL);
1124
1125   if (mix->priv->query) {
1126     gst_query_unref (mix->priv->query);
1127     mix->priv->query = NULL;
1128   }
1129
1130   if (mix->priv->pool) {
1131     gst_object_unref (mix->priv->pool);
1132     mix->priv->pool = NULL;
1133   }
1134
1135   if (mix->display) {
1136     gst_object_unref (mix->display);
1137     mix->display = NULL;
1138   }
1139
1140   if (mix->context) {
1141     gst_object_unref (mix->context);
1142     mix->context = NULL;
1143   }
1144
1145   if (mix->other_context) {
1146     gst_object_unref (mix->other_context);
1147     mix->other_context = NULL;
1148   }
1149
1150   gst_gl_mixer_reset (mix);
1151
1152   return TRUE;
1153 }