plugins: uddate gst_type_mark_as_plugin_api() calls
[platform/upstream/gstreamer.git] / ext / gl / gstglstereomix.c
1 /*
2  * Combine video streams to 3D stereo
3  *
4  * GStreamer
5  * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
6  * Copyright (C) 2014 Jan Schmidt <jan@noraisin.net>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:element-glstereomix
26  * @title: glstereomix
27  *
28  * Combine 2 input streams to produce a stereoscopic output
29  * stream. Input views are taken from the left pad and right pad
30  * respectively, and mixed according to their timelines.
31  *
32  * If either input stream is stereoscopic, the approproriate view
33  * (left or right) is taken from each stream and placed into the output.
34  *
35  * The multiview representation on the output is chosen according to
36  * the downstream caps.
37  *
38  * ## Examples
39  * |[
40  * gst-launch-1.0 -v videotestsrc pattern=ball name=left \
41  *     videotestsrc name=right glstereomix name=mix \
42  *     left. ! vid/x-raw,width=640,height=480! glupload ! mix.  \
43  *     right. ! video/x-raw,width=640,height=480! glupload ! mix.  \
44  *     mix. ! video/x-raw'(memory:GLMemory)',multiview-mode=side-by-side ! \
45  *     queue ! glimagesink output-multiview-mode=side-by-side
46  * ]| Mix 2 different videotestsrc patterns into a side-by-side stereo image and display it.
47  * |[
48  * gst-launch-1.0 -ev v4l2src name=left \
49  *     videotestsrc name=right \
50  *     glstereomix name=mix \
51  *     left. ! video/x-raw,width=640,height=480 ! glupload ! glcolorconvert ! mix.  \
52  *     right. ! video/x-raw,width=640,height=480 ! glupload ! mix.  \
53  *     mix. ! video/x-raw'(memory:GLMemory)',multiview-mode=top-bottom ! \
54  *     glcolorconvert ! gldownload ! queue ! x264enc ! h264parse ! \
55  *     mp4mux ! progressreport ! filesink location=output.mp4
56  * ]| Mix the input from a camera to the left view, and videotestsrc to the right view,
57  *    and encode as a top-bottom frame packed H.264 video.
58  *
59  */
60 #ifdef HAVE_CONFIG_H
61 #include "config.h"
62 #endif
63
64 #include "gstglstereomix.h"
65
66 #define GST_CAT_DEFAULT gst_gl_stereo_mix_debug
67 GST_DEBUG_CATEGORY (gst_gl_stereo_mix_debug);
68
69 G_DEFINE_TYPE (GstGLStereoMixPad, gst_gl_stereo_mix_pad, GST_TYPE_GL_MIXER_PAD);
70
71 static void
72 gst_gl_stereo_mix_pad_class_init (GstGLStereoMixPadClass * klass)
73 {
74 }
75
76 static void
77 gst_gl_stereo_mix_pad_init (GstGLStereoMixPad * pad)
78 {
79 }
80
81 static void gst_gl_stereo_mix_child_proxy_init (gpointer g_iface,
82     gpointer iface_data);
83
84 #define gst_gl_stereo_mix_parent_class parent_class
85 G_DEFINE_TYPE_WITH_CODE (GstGLStereoMix, gst_gl_stereo_mix, GST_TYPE_GL_MIXER,
86     G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
87         gst_gl_stereo_mix_child_proxy_init));
88
89 static GstCaps *_update_caps (GstVideoAggregator * vagg, GstCaps * caps);
90 static gboolean _negotiated_caps (GstAggregator * aggregator, GstCaps * caps);
91 static gboolean gst_gl_stereo_mix_make_output (GstGLStereoMix * mix);
92 static gboolean gst_gl_stereo_mix_process_frames (GstGLStereoMix * mixer);
93
94 #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
95
96 /* GLStereoMix signals and args */
97 enum
98 {
99   /* FILL ME */
100   LAST_SIGNAL
101 };
102
103 enum
104 {
105   PROP_0,
106   PROP_DOWNMIX_MODE
107 };
108
109 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
110     GST_PAD_SRC,
111     GST_PAD_ALWAYS,
112     GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
113         "format = (string) RGBA, "
114         "width = " GST_VIDEO_SIZE_RANGE ", "
115         "height = " GST_VIDEO_SIZE_RANGE ", "
116         "framerate = " GST_VIDEO_FPS_RANGE ","
117         "texture-target = (string) 2D"
118         "; "
119         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
120         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
121             "RGBA")
122         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
123     );
124
125 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u",
126     GST_PAD_SINK,
127     GST_PAD_REQUEST,
128     GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
129         "format = (string) RGBA, "
130         "width = " GST_VIDEO_SIZE_RANGE ", "
131         "height = " GST_VIDEO_SIZE_RANGE ", "
132         "framerate = " GST_VIDEO_FPS_RANGE ","
133         "texture-target = (string) 2D"
134         "; "
135         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
136         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
137             "RGBA")
138         "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
139     );
140
141 static GstPad *gst_gl_stereo_mix_request_new_pad (GstElement * element,
142     GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps);
143 static void gst_gl_stereo_mix_release_pad (GstElement * element, GstPad * pad);
144
145 static GstFlowReturn gst_gl_stereo_mix_create_output_buffer (GstVideoAggregator
146     * videoaggregator, GstBuffer ** outbuf);
147 static gboolean gst_gl_stereo_mix_stop (GstAggregator * agg);
148 static gboolean gst_gl_stereo_mix_start (GstAggregator * agg);
149 static gboolean gst_gl_stereo_mix_src_query (GstAggregator * agg,
150     GstQuery * query);
151
152 static void gst_gl_stereo_mix_set_property (GObject * object, guint prop_id,
153     const GValue * value, GParamSpec * pspec);
154 static void gst_gl_stereo_mix_get_property (GObject * object, guint prop_id,
155     GValue * value, GParamSpec * pspec);
156
157 static void gst_gl_stereo_mix_finalize (GObject * object);
158
159 static GstFlowReturn
160 gst_gl_stereo_mix_aggregate_frames (GstVideoAggregator * vagg,
161     GstBuffer * outbuffer);
162
163 static void
164 gst_gl_stereo_mix_class_init (GstGLStereoMixClass * klass)
165 {
166   GObjectClass *gobject_class = (GObjectClass *) klass;
167   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
168   GstVideoAggregatorClass *videoaggregator_class =
169       (GstVideoAggregatorClass *) klass;
170   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
171   GstGLBaseMixerClass *base_mix_class = (GstGLBaseMixerClass *) klass;
172
173   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glstereomixer", 0,
174       "opengl stereoscopic mixer");
175
176   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_stereo_mix_finalize);
177
178   gobject_class->get_property = gst_gl_stereo_mix_get_property;
179   gobject_class->set_property = gst_gl_stereo_mix_set_property;
180
181   gst_element_class_set_metadata (element_class, "OpenGL stereo video combiner",
182       "Filter/Effect/Video", "OpenGL stereo video combiner",
183       "Jan Schmidt <jan@centricular.com>");
184
185   g_object_class_install_property (gobject_class, PROP_DOWNMIX_MODE,
186       g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output",
187           "Output anaglyph type to generate when downmixing to mono",
188           GST_TYPE_GL_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
189           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
190
191   gst_element_class_add_static_pad_template_with_gtype (element_class,
192       &src_factory, GST_TYPE_AGGREGATOR_PAD);
193   gst_element_class_add_static_pad_template_with_gtype (element_class,
194       &sink_factory, GST_TYPE_GL_STEREO_MIX_PAD);
195
196   element_class->request_new_pad =
197       GST_DEBUG_FUNCPTR (gst_gl_stereo_mix_request_new_pad);
198   element_class->release_pad =
199       GST_DEBUG_FUNCPTR (gst_gl_stereo_mix_release_pad);
200
201   agg_class->stop = gst_gl_stereo_mix_stop;
202   agg_class->start = gst_gl_stereo_mix_start;
203   agg_class->src_query = gst_gl_stereo_mix_src_query;
204   agg_class->negotiated_src_caps = _negotiated_caps;
205
206   videoaggregator_class->aggregate_frames = gst_gl_stereo_mix_aggregate_frames;
207   videoaggregator_class->update_caps = _update_caps;
208   videoaggregator_class->create_output_buffer =
209       gst_gl_stereo_mix_create_output_buffer;
210
211   base_mix_class->supported_gl_api =
212       GST_GL_API_GLES2 | GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
213
214   gst_type_mark_as_plugin_api (GST_TYPE_GL_STEREO_DOWNMIX, 0);
215   gst_type_mark_as_plugin_api (GST_TYPE_GL_STEREO_MIX_PAD, 0);
216 }
217
218 static void
219 gst_gl_stereo_mix_init (GstGLStereoMix * mix)
220 {
221 }
222
223 static void
224 gst_gl_stereo_mix_finalize (GObject * object)
225 {
226   //GstGLStereoMix *mix = GST_GL_STEREO_MIX (object);
227
228   G_OBJECT_CLASS (parent_class)->finalize (object);
229 }
230
231 static gboolean
232 gst_gl_stereo_mix_query_caps (GstPad * pad, GstAggregator * agg,
233     GstQuery * query)
234 {
235   GstCaps *filter, *caps;
236
237   gst_query_parse_caps (query, &filter);
238
239   caps = gst_pad_get_current_caps (agg->srcpad);
240   if (caps == NULL) {
241     caps = gst_pad_get_pad_template_caps (agg->srcpad);
242   }
243
244   if (filter)
245     caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
246
247   gst_query_set_caps_result (query, caps);
248   gst_caps_unref (caps);
249
250   return TRUE;
251 }
252
253 static gboolean
254 gst_gl_stereo_mix_src_query (GstAggregator * agg, GstQuery * query)
255 {
256   switch (GST_QUERY_TYPE (query)) {
257     case GST_QUERY_CAPS:
258       return gst_gl_stereo_mix_query_caps (agg->srcpad, agg, query);
259       break;
260     default:
261       break;
262   }
263
264   return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
265 }
266
267
268 static GstFlowReturn
269 gst_gl_stereo_mix_create_output_buffer (GstVideoAggregator * videoaggregator,
270     GstBuffer ** outbuf)
271 {
272   GstGLStereoMix *mix = GST_GL_STEREO_MIX (videoaggregator);
273   GstFlowReturn ret = GST_FLOW_OK;
274
275 #if 0
276
277   if (!mix->priv->pool_active) {
278     if (!gst_buffer_pool_set_active (mix->priv->pool, TRUE)) {
279       GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
280           ("failed to activate bufferpool"), ("failed to activate bufferpool"));
281       return GST_FLOW_ERROR;
282     }
283     mix->priv->pool_active = TRUE;
284   }
285
286   return gst_buffer_pool_acquire_buffer (mix->priv->pool, outbuf, NULL);
287 #endif
288
289   if (!gst_gl_stereo_mix_make_output (mix)) {
290     gst_buffer_replace (&mix->primary_out, NULL);
291     gst_buffer_replace (&mix->auxilliary_out, NULL);
292     GST_ELEMENT_ERROR (mix, RESOURCE, SETTINGS,
293         ("Failed to generate output"), ("failed to generate output"));
294     ret = GST_FLOW_ERROR;
295   }
296
297   if (mix->auxilliary_out) {
298     *outbuf = mix->auxilliary_out;
299     mix->auxilliary_out = NULL;
300   } else {
301     *outbuf = mix->primary_out;
302     mix->primary_out = NULL;
303   }
304   return ret;
305 }
306
307 static gboolean
308 gst_gl_stereo_mix_make_output (GstGLStereoMix * mix)
309 {
310   GList *walk;
311   gboolean res = FALSE;
312   GstElement *element = GST_ELEMENT (mix);
313   gboolean missing_buffer = FALSE;
314
315   GST_LOG_OBJECT (mix, "Processing buffers");
316
317   GST_OBJECT_LOCK (mix);
318   walk = element->sinkpads;
319   while (walk) {
320     GstVideoAggregatorPad *vaggpad = walk->data;
321     GstGLStereoMixPad *pad = walk->data;
322     GstBuffer *buffer = gst_video_aggregator_pad_get_current_buffer (vaggpad);
323
324     GST_LOG_OBJECT (mix, "Checking pad %" GST_PTR_FORMAT, vaggpad);
325
326     if (buffer != NULL) {
327       pad->current_buffer = buffer;
328
329       GST_DEBUG_OBJECT (pad, "Got buffer %" GST_PTR_FORMAT,
330           pad->current_buffer);
331     } else {
332       GST_LOG_OBJECT (mix, "No buffer on pad %" GST_PTR_FORMAT, vaggpad);
333       pad->current_buffer = NULL;
334       missing_buffer = TRUE;
335     }
336     walk = g_list_next (walk);
337   }
338   if (missing_buffer) {
339     /* We're still waiting for a buffer to turn up on at least one input */
340     GST_WARNING_OBJECT (mix, "Not generating output - need more input buffers");
341     res = TRUE;
342     goto out;
343   }
344
345   /* Copy GL memory from each input frame to the output */
346   if (!gst_gl_stereo_mix_process_frames (mix)) {
347     GST_LOG_OBJECT (mix, "Failed to process frames to output");
348     goto out;
349   }
350
351   if (mix->primary_out == NULL)
352     goto out;
353
354   res = TRUE;
355
356 out:
357   GST_OBJECT_UNLOCK (mix);
358
359   return res;
360 }
361
362 static GstFlowReturn
363 gst_gl_stereo_mix_aggregate_frames (GstVideoAggregator * vagg,
364     GstBuffer * outbuf)
365 {
366   GstGLStereoMix *mix = GST_GL_STEREO_MIX (vagg);
367   /* If we're operating in frame-by-frame mode, push
368    * the primary view now, and let the parent class
369    * push the remaining auxiliary view */
370   if (GST_VIDEO_INFO_MULTIVIEW_MODE (&vagg->info) ==
371       GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME) {
372     /* Transfer the timestamps video-agg put on the aux buffer */
373     gst_buffer_copy_into (mix->primary_out, outbuf,
374         GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
375     gst_aggregator_finish_buffer (GST_AGGREGATOR (vagg), mix->primary_out);
376     mix->primary_out = NULL;
377
378     /* And actually, we don't want timestamps on the aux buffer */
379     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
380     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
381   }
382   return GST_FLOW_OK;
383 }
384
385 static void
386 gst_gl_stereo_mix_get_property (GObject * object,
387     guint prop_id, GValue * value, GParamSpec * pspec)
388 {
389   GstGLStereoMix *mix = GST_GL_STEREO_MIX (object);
390
391   switch (prop_id) {
392     case PROP_DOWNMIX_MODE:
393       g_value_set_enum (value, mix->downmix_mode);
394       break;
395     default:
396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397       break;
398   }
399 }
400
401 static void
402 gst_gl_stereo_mix_set_property (GObject * object,
403     guint prop_id, const GValue * value, GParamSpec * pspec)
404 {
405   GstGLStereoMix *mix = GST_GL_STEREO_MIX (object);
406
407   switch (prop_id) {
408     case PROP_DOWNMIX_MODE:
409       mix->downmix_mode = g_value_get_enum (value);
410       if (mix->viewconvert)
411         g_object_set_property (G_OBJECT (mix->viewconvert), "downmix-mode",
412             value);
413       break;
414     default:
415       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
416       break;
417   }
418 }
419
420 static GstPad *
421 gst_gl_stereo_mix_request_new_pad (GstElement * element, GstPadTemplate * templ,
422     const gchar * req_name, const GstCaps * caps)
423 {
424   GstPad *newpad;
425
426   newpad = (GstPad *)
427       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
428       templ, req_name, caps);
429
430   if (newpad == NULL)
431     goto could_not_create;
432
433   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
434       GST_OBJECT_NAME (newpad));
435
436   return GST_PAD_CAST (newpad);
437
438 could_not_create:
439   {
440     GST_DEBUG_OBJECT (element, "could not create/add pad");
441     return NULL;
442   }
443 }
444
445 static void
446 gst_gl_stereo_mix_release_pad (GstElement * element, GstPad * pad)
447 {
448   GST_DEBUG_OBJECT (element, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
449
450   gst_child_proxy_child_removed (GST_CHILD_PROXY (element), G_OBJECT (pad),
451       GST_OBJECT_NAME (pad));
452
453   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
454 }
455
456 static gboolean
457 gst_gl_stereo_mix_start (GstAggregator * agg)
458 {
459   GstGLStereoMix *mix = GST_GL_STEREO_MIX (agg);
460
461   if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg))
462     return FALSE;
463
464   GST_OBJECT_LOCK (mix);
465   mix->viewconvert = gst_gl_view_convert_new ();
466   g_object_set (G_OBJECT (mix->viewconvert), "downmix-mode",
467       mix->downmix_mode, NULL);
468   GST_OBJECT_UNLOCK (mix);
469
470   return TRUE;
471 }
472
473 static gboolean
474 gst_gl_stereo_mix_stop (GstAggregator * agg)
475 {
476   GstGLStereoMix *mix = GST_GL_STEREO_MIX (agg);
477
478   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
479     return FALSE;
480
481   if (mix->viewconvert) {
482     gst_object_unref (mix->viewconvert);
483     mix->viewconvert = NULL;
484   }
485
486   return TRUE;
487 }
488
489 /* Convert to caps that can be accepted by this element... */
490 static GstCaps *
491 get_converted_caps (GstGLStereoMix * mix, GstCaps * caps)
492 {
493 #if 0
494   GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
495   GstCaps *result, *tmp;
496
497   GST_LOG_OBJECT (mix, "Converting caps %" GST_PTR_FORMAT, caps);
498   result = gst_gl_upload_transform_caps (context, GST_PAD_SINK, caps, NULL);
499   tmp = result;
500   GST_TRACE_OBJECT (mix, "transfer returned caps %" GST_PTR_FORMAT, tmp);
501
502   result =
503       gst_gl_color_convert_transform_caps (context, GST_PAD_SINK, tmp, NULL);
504   gst_caps_unref (tmp);
505   GST_TRACE_OBJECT (mix, "convert returned caps %" GST_PTR_FORMAT, tmp);
506
507   tmp = result;
508   result = gst_gl_view_convert_transform_caps (mix->viewconvert,
509       GST_PAD_SINK, tmp, NULL);
510   gst_caps_unref (tmp);
511 #else
512   GstCaps *result;
513
514   GST_LOG_OBJECT (mix, "Converting caps %" GST_PTR_FORMAT, caps);
515   result = gst_gl_view_convert_transform_caps (mix->viewconvert,
516       GST_PAD_SINK, caps, NULL);
517 #endif
518
519   GST_LOG_OBJECT (mix, "returning caps %" GST_PTR_FORMAT, result);
520
521   return result;
522 }
523
524 /* Return the possible output caps based on inputs and downstream prefs */
525 static GstCaps *
526 _update_caps (GstVideoAggregator * vagg, GstCaps * caps)
527 {
528   GstGLStereoMix *mix = GST_GL_STEREO_MIX (vagg);
529   GList *l;
530   gint best_width = -1, best_height = -1;
531   gdouble best_fps = -1, cur_fps;
532   gint best_fps_n = 0, best_fps_d = 1;
533   GstVideoInfo *mix_info;
534   GstCaps *blend_caps, *tmp_caps;
535   GstCaps *out_caps;
536
537   GST_OBJECT_LOCK (vagg);
538
539   for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) {
540     GstVideoAggregatorPad *pad = l->data;
541     GstVideoInfo tmp = pad->info;
542     gint this_width, this_height;
543     gint fps_n, fps_d;
544
545     if (!pad->info.finfo)
546       continue;
547
548     /* This can happen if we release a pad and another pad hasn't been negotiated_caps yet */
549     if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN)
550       continue;
551
552     /* Convert to per-view width/height for unpacked forms */
553     gst_video_multiview_video_info_change_mode (&tmp,
554         GST_VIDEO_MULTIVIEW_MODE_SEPARATED, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
555
556     this_width = GST_VIDEO_INFO_WIDTH (&tmp);
557     this_height = GST_VIDEO_INFO_HEIGHT (&tmp);
558     fps_n = GST_VIDEO_INFO_FPS_N (&tmp);
559     fps_d = GST_VIDEO_INFO_FPS_D (&tmp);
560
561     GST_INFO_OBJECT (vagg, "Input pad %" GST_PTR_FORMAT
562         " w %u h %u", pad, this_width, this_height);
563
564     if (this_width == 0 || this_height == 0)
565       continue;
566
567     if (best_width < this_width)
568       best_width = this_width;
569     if (best_height < this_height)
570       best_height = this_height;
571
572     if (fps_d == 0)
573       cur_fps = 0.0;
574     else
575       gst_util_fraction_to_double (fps_n, fps_d, &cur_fps);
576
577     if (best_fps < cur_fps) {
578       best_fps = cur_fps;
579       best_fps_n = fps_n;
580       best_fps_d = fps_d;
581     }
582
583     /* FIXME: Preserve PAR for at least one input when different sized inputs */
584   }
585   GST_OBJECT_UNLOCK (vagg);
586
587   mix_info = &mix->mix_info;
588   gst_video_info_set_format (mix_info, GST_VIDEO_FORMAT_RGBA, best_width,
589       best_height);
590
591   GST_VIDEO_INFO_FPS_N (mix_info) = best_fps_n;
592   GST_VIDEO_INFO_FPS_D (mix_info) = best_fps_d;
593
594   GST_VIDEO_INFO_MULTIVIEW_MODE (mix_info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED;
595   GST_VIDEO_INFO_VIEWS (mix_info) = 2;
596
597   /* FIXME: If input is marked as flipped or flopped, preserve those flags */
598   GST_VIDEO_INFO_MULTIVIEW_FLAGS (mix_info) = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
599
600   /* Choose our output format based on downstream preferences */
601   blend_caps = gst_video_info_to_caps (mix_info);
602
603   gst_caps_set_features (blend_caps, 0,
604       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));
605
606   tmp_caps = get_converted_caps (GST_GL_STEREO_MIX (vagg), blend_caps);
607   gst_caps_unref (blend_caps);
608
609   out_caps = gst_caps_intersect (caps, tmp_caps);
610   gst_caps_unref (tmp_caps);
611
612   GST_DEBUG_OBJECT (vagg, "Possible output caps %" GST_PTR_FORMAT, out_caps);
613
614   return out_caps;
615 }
616
617 /* Called after videoaggregator fixates our caps */
618 static gboolean
619 _negotiated_caps (GstAggregator * agg, GstCaps * caps)
620 {
621   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg);
622   GstGLStereoMix *mix = GST_GL_STEREO_MIX (vagg);
623   GstCaps *in_caps;
624
625   GST_LOG_OBJECT (mix, "Configured output caps %" GST_PTR_FORMAT, caps);
626
627   if (GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps)
628     if (!GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps))
629       return FALSE;
630
631   /* Update the glview_convert output */
632
633   /* We can configure the view_converter now */
634   gst_gl_view_convert_set_context (mix->viewconvert,
635       GST_GL_BASE_MIXER (mix)->context);
636
637   in_caps = gst_video_info_to_caps (&mix->mix_info);
638   gst_caps_set_features (in_caps, 0,
639       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));
640   gst_caps_set_simple (in_caps, "texture-target", G_TYPE_STRING,
641       GST_GL_TEXTURE_TARGET_2D_STR, NULL);
642
643   gst_gl_view_convert_set_caps (mix->viewconvert, in_caps, caps);
644
645   return TRUE;
646 }
647
648 /* called with the object lock held */
649 static gboolean
650 gst_gl_stereo_mix_process_frames (GstGLStereoMix * mixer)
651 {
652   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mixer);
653   GstBuffer *converted_buffer, *inbuf;
654   GstVideoInfo *out_info = &vagg->info;
655 #ifndef G_DISABLE_ASSERT
656   gint n;
657 #endif
658   gint v, views;
659   gint valid_views = 0;
660   GList *walk;
661
662   inbuf = gst_buffer_new ();
663   walk = GST_ELEMENT (mixer)->sinkpads;
664   while (walk) {
665     GstGLStereoMixPad *pad = walk->data;
666     GstMemory *in_mem;
667
668     GST_LOG_OBJECT (mixer, "Handling frame %d", valid_views);
669
670     if (!pad || !pad->current_buffer) {
671       GST_DEBUG ("skipping texture, null frame");
672       walk = g_list_next (walk);
673       continue;
674     }
675
676     in_mem = gst_buffer_get_memory (pad->current_buffer, 0);
677
678     GST_LOG_OBJECT (mixer,
679         "Appending memory %" GST_PTR_FORMAT " to intermediate buffer", in_mem);
680     /* Appending the memory to a 2nd buffer locks it
681      * exclusive a 2nd time, which will mark it for
682      * copy-on-write. The ref will keep the memory
683      * alive but we add a parent_buffer_meta to also
684      * prevent the input buffer from returning to any buffer
685      * pool it might belong to
686      */
687     gst_buffer_append_memory (inbuf, in_mem);
688     /* Use parent buffer meta to keep input buffer alive */
689     gst_buffer_add_parent_buffer_meta (inbuf, pad->current_buffer);
690
691     valid_views++;
692     walk = g_list_next (walk);
693   }
694
695   if (mixer->mix_info.views != valid_views) {
696     GST_WARNING_OBJECT (mixer, "Not enough input views to process");
697     return FALSE;
698   }
699
700   if (GST_VIDEO_INFO_MULTIVIEW_MODE (out_info) ==
701       GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
702     views = out_info->views;
703   else
704     views = 1;
705
706   /* We can configure the view_converter now */
707   gst_gl_view_convert_set_context (mixer->viewconvert,
708       GST_GL_BASE_MIXER (mixer)->context);
709
710   if (gst_gl_view_convert_submit_input_buffer (mixer->viewconvert,
711           FALSE, inbuf) != GST_FLOW_OK)
712     return FALSE;
713
714   /* Clear any existing buffers, just in case */
715   gst_buffer_replace (&mixer->primary_out, NULL);
716   gst_buffer_replace (&mixer->auxilliary_out, NULL);
717
718   if (gst_gl_view_convert_get_output (mixer->viewconvert,
719           &mixer->primary_out) != GST_FLOW_OK)
720     return FALSE;
721
722   if (GST_VIDEO_INFO_MULTIVIEW_MODE (out_info) ==
723       GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME) {
724     if (gst_gl_view_convert_get_output (mixer->viewconvert,
725             &mixer->auxilliary_out) != GST_FLOW_OK)
726       return FALSE;
727   }
728
729   if (mixer->primary_out == NULL)
730     return FALSE;
731
732   converted_buffer = mixer->primary_out;
733
734 #ifndef G_DISABLE_ASSERT
735   n = gst_buffer_n_memory (converted_buffer);
736   g_assert (n == GST_VIDEO_INFO_N_PLANES (out_info) * views);
737 #endif
738
739   for (v = 0; v < views; v++) {
740     gst_buffer_add_video_meta_full (converted_buffer, v,
741         GST_VIDEO_INFO_FORMAT (out_info),
742         GST_VIDEO_INFO_WIDTH (out_info),
743         GST_VIDEO_INFO_HEIGHT (out_info),
744         GST_VIDEO_INFO_N_PLANES (out_info), out_info->offset, out_info->stride);
745     if (mixer->auxilliary_out) {
746       gst_buffer_add_video_meta_full (mixer->auxilliary_out, v,
747           GST_VIDEO_INFO_FORMAT (out_info),
748           GST_VIDEO_INFO_WIDTH (out_info),
749           GST_VIDEO_INFO_HEIGHT (out_info),
750           GST_VIDEO_INFO_N_PLANES (out_info), out_info->offset,
751           out_info->stride);
752     }
753   }
754
755   return TRUE;
756 }
757
758 /* GstChildProxy implementation */
759 static GObject *
760 gst_gl_stereo_mix_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
761     guint index)
762 {
763   GstGLStereoMix *gl_stereo_mix = GST_GL_STEREO_MIX (child_proxy);
764   GObject *obj = NULL;
765
766   GST_OBJECT_LOCK (gl_stereo_mix);
767   obj = g_list_nth_data (GST_ELEMENT_CAST (gl_stereo_mix)->sinkpads, index);
768   if (obj)
769     gst_object_ref (obj);
770   GST_OBJECT_UNLOCK (gl_stereo_mix);
771
772   return obj;
773 }
774
775 static guint
776 gst_gl_stereo_mix_child_proxy_get_children_count (GstChildProxy * child_proxy)
777 {
778   guint count = 0;
779   GstGLStereoMix *gl_stereo_mix = GST_GL_STEREO_MIX (child_proxy);
780
781   GST_OBJECT_LOCK (gl_stereo_mix);
782   count = GST_ELEMENT_CAST (gl_stereo_mix)->numsinkpads;
783   GST_OBJECT_UNLOCK (gl_stereo_mix);
784   GST_INFO_OBJECT (gl_stereo_mix, "Children Count: %d", count);
785
786   return count;
787 }
788
789 static void
790 gst_gl_stereo_mix_child_proxy_init (gpointer g_iface, gpointer iface_data)
791 {
792   GstChildProxyInterface *iface = g_iface;
793
794   iface->get_child_by_index = gst_gl_stereo_mix_child_proxy_get_child_by_index;
795   iface->get_children_count = gst_gl_stereo_mix_child_proxy_get_children_count;
796 }