1 /*
2  * GStreamer
3  * Copyright (C) 2015 Jan Schmidt <jan@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-glstereosplit
23  * @title: glstereosplit
24  *
25  * Receive a stereoscopic video stream and split into left/right
26  *
27  * ## Examples
28  * |[
29  * gst-launch-1.0 videotestsrc ! glstereosplit name=s ! queue ! glimagesink s. ! queue ! glimagesink
30  * ]|
31  * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required.
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include "gstglstereosplit.h"
40 
41 #define GST_CAT_DEFAULT gst_gl_stereosplit_debug
42 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
43 
44 #define SUPPORTED_GL_APIS GST_GL_API_GLES2 | GST_GL_API_OPENGL | GST_GL_API_OPENGL3
45 #define DEBUG_INIT \
46   GST_DEBUG_CATEGORY_INIT (gst_gl_stereosplit_debug, "glstereosplit", 0, "glstereosplit element");
47 
48 G_DEFINE_TYPE_WITH_CODE (GstGLStereoSplit, gst_gl_stereosplit,
49     GST_TYPE_ELEMENT, DEBUG_INIT);
50 
51 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
52     GST_PAD_SINK, GST_PAD_ALWAYS,
53     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
54         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
55     );
56 
57 static GstStaticPadTemplate src_left_template = GST_STATIC_PAD_TEMPLATE ("left",
58     GST_PAD_SRC, GST_PAD_ALWAYS,
59     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
60         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
61     );
62 
63 static GstStaticPadTemplate src_right_template =
64 GST_STATIC_PAD_TEMPLATE ("right",
65     GST_PAD_SRC, GST_PAD_ALWAYS,
66     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
67         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
68             "RGBA"))
69     );
70 
71 static void stereosplit_reset (GstGLStereoSplit * self);
72 static void stereosplit_finalize (GstGLStereoSplit * self);
73 static void stereosplit_set_context (GstElement * element,
74     GstContext * context);
75 static GstFlowReturn stereosplit_chain (GstPad * pad, GstGLStereoSplit * split,
76     GstBuffer * buf);
77 static GstStateChangeReturn stereosplit_change_state (GstElement * element,
78     GstStateChange transition);
79 static gboolean stereosplit_sink_query (GstPad * pad, GstObject * parent,
80     GstQuery * query);
81 static gboolean stereosplit_sink_event (GstPad * pad, GstObject * parent,
82     GstEvent * event);
83 static gboolean stereosplit_src_query (GstPad * pad, GstObject * parent,
84     GstQuery * query);
85 static gboolean stereosplit_src_event (GstPad * pad, GstObject * parent,
86     GstEvent * event);
87 static gboolean ensure_context (GstGLStereoSplit * self);
88 
89 static void
gst_gl_stereosplit_class_init(GstGLStereoSplitClass * klass)90 gst_gl_stereosplit_class_init (GstGLStereoSplitClass * klass)
91 {
92   GObjectClass *gobject_class = (GObjectClass *) klass;
93   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
94 
95   gst_element_class_set_static_metadata (element_class,
96       "GLStereoSplit", "Codec/Converter",
97       "Splits a stereoscopic stream into separate left/right streams",
98       "Jan Schmidt <jan@centricular.com>\n"
99       "Matthew Waters <matthew@centricular.com>");
100 
101   gobject_class->finalize = (GObjectFinalizeFunc) (stereosplit_finalize);
102 
103   element_class->change_state = stereosplit_change_state;
104   element_class->set_context = stereosplit_set_context;
105 
106   gst_element_class_add_static_pad_template (element_class, &sink_template);
107   gst_element_class_add_static_pad_template (element_class, &src_left_template);
108   gst_element_class_add_static_pad_template (element_class,
109       &src_right_template);
110 }
111 
112 static void
gst_gl_stereosplit_init(GstGLStereoSplit * self)113 gst_gl_stereosplit_init (GstGLStereoSplit * self)
114 {
115   GstPad *pad;
116 
117   pad = self->sink_pad =
118       gst_pad_new_from_static_template (&sink_template, "sink");
119 
120   gst_pad_set_chain_function (pad, (GstPadChainFunction) (stereosplit_chain));
121   gst_pad_set_query_function (pad, stereosplit_sink_query);
122   gst_pad_set_event_function (pad, stereosplit_sink_event);
123 
124   gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
125 
126   pad = self->left_pad =
127       gst_pad_new_from_static_template (&src_left_template, "left");
128   gst_pad_set_query_function (pad, stereosplit_src_query);
129   gst_pad_set_event_function (pad, stereosplit_src_event);
130   gst_element_add_pad (GST_ELEMENT (self), self->left_pad);
131 
132   pad = self->right_pad =
133       gst_pad_new_from_static_template (&src_right_template, "right");
134   gst_pad_set_query_function (pad, stereosplit_src_query);
135   gst_pad_set_event_function (pad, stereosplit_src_event);
136   gst_element_add_pad (GST_ELEMENT (self), self->right_pad);
137 
138   self->viewconvert = gst_gl_view_convert_new ();
139 }
140 
141 static void
stereosplit_reset(GstGLStereoSplit * self)142 stereosplit_reset (GstGLStereoSplit * self)
143 {
144   if (self->context)
145     gst_object_replace ((GstObject **) & self->context, NULL);
146   if (self->display)
147     gst_object_replace ((GstObject **) & self->display, NULL);
148 }
149 
150 static void
stereosplit_finalize(GstGLStereoSplit * self)151 stereosplit_finalize (GstGLStereoSplit * self)
152 {
153   GObjectClass *klass = G_OBJECT_CLASS (gst_gl_stereosplit_parent_class);
154 
155   if (self->viewconvert)
156     gst_object_replace ((GstObject **) & self->viewconvert, NULL);
157 
158   klass->finalize ((GObject *) (self));
159 }
160 
161 static void
stereosplit_set_context(GstElement * element,GstContext * context)162 stereosplit_set_context (GstElement * element, GstContext * context)
163 {
164   GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
165 
166   gst_gl_handle_set_context (element, context, &stereosplit->display,
167       &stereosplit->other_context);
168 
169   if (stereosplit->display)
170     gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
171 
172   GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->set_context (element,
173       context);
174 }
175 
176 static GstStateChangeReturn
stereosplit_change_state(GstElement * element,GstStateChange transition)177 stereosplit_change_state (GstElement * element, GstStateChange transition)
178 {
179   GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
180   GstStateChangeReturn result;
181 
182   switch (transition) {
183     case GST_STATE_CHANGE_NULL_TO_READY:
184       if (!gst_gl_ensure_element_data (element, &stereosplit->display,
185               &stereosplit->other_context))
186         return GST_STATE_CHANGE_FAILURE;
187 
188       gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
189       break;
190     default:
191       break;
192   }
193 
194   result =
195       GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->change_state
196       (element, transition);
197 
198   switch (transition) {
199     case GST_STATE_CHANGE_READY_TO_NULL:
200       if (stereosplit->other_context) {
201         gst_object_unref (stereosplit->other_context);
202         stereosplit->other_context = NULL;
203       }
204 
205       if (stereosplit->display) {
206         gst_object_unref (stereosplit->display);
207         stereosplit->display = NULL;
208       }
209       break;
210     case GST_STATE_CHANGE_PAUSED_TO_READY:
211       stereosplit_reset (stereosplit);
212       break;
213     default:
214       break;
215   }
216 
217   return result;
218 }
219 
220 static GstCaps *
stereosplit_transform_caps(GstGLStereoSplit * self,GstPadDirection direction,GstCaps * caps,GstCaps * filter)221 stereosplit_transform_caps (GstGLStereoSplit * self, GstPadDirection direction,
222     GstCaps * caps, GstCaps * filter)
223 {
224   GstCaps *next_caps;
225 
226   /* FIXME: Is this the right way to ensure a context here ? */
227   if (!ensure_context (self))
228     return NULL;
229 
230   next_caps =
231       gst_gl_view_convert_transform_caps (self->viewconvert, direction, caps,
232       NULL);
233 
234   return next_caps;
235 }
236 
237 static GstCaps *
strip_mview_fields(GstCaps * incaps,GstVideoMultiviewFlags keep_flags)238 strip_mview_fields (GstCaps * incaps, GstVideoMultiviewFlags keep_flags)
239 {
240   GstCaps *outcaps = gst_caps_make_writable (incaps);
241 
242   gint i, n;
243 
244   n = gst_caps_get_size (outcaps);
245   for (i = 0; i < n; i++) {
246     GstStructure *st = gst_caps_get_structure (outcaps, i);
247     GstVideoMultiviewFlags flags, mask;
248 
249     gst_structure_remove_field (st, "multiview-mode");
250     if (gst_structure_get_flagset (st, "multiview-flags", (guint *) & flags,
251             (guint *) & mask)) {
252       flags &= keep_flags;
253       mask = keep_flags;
254       gst_structure_set (st, "multiview-flags",
255           GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags, mask, NULL);
256     }
257   }
258 
259   return outcaps;
260 }
261 
262 static gboolean stereosplit_do_bufferpool (GstGLStereoSplit * self,
263     GstCaps * caps);
264 
265 static GstCaps *
stereosplit_get_src_caps(GstGLStereoSplit * split,GstPad * pad,GstVideoMultiviewMode preferred_mode)266 stereosplit_get_src_caps (GstGLStereoSplit * split,
267     GstPad * pad, GstVideoMultiviewMode preferred_mode)
268 {
269   GstCaps *outcaps, *tmp, *templ_caps;
270   GValue item = G_VALUE_INIT, list = G_VALUE_INIT;
271 
272   /* Get the template format */
273   templ_caps = gst_pad_get_pad_template_caps (pad);
274 
275   /* And limit down to the preferred mode or mono */
276   templ_caps = gst_caps_make_writable (templ_caps);
277 
278   g_value_init (&item, G_TYPE_STRING);
279   g_value_init (&list, GST_TYPE_LIST);
280   g_value_set_static_string (&item,
281       gst_video_multiview_mode_to_caps_string (preferred_mode));
282   gst_value_list_append_value (&list, &item);
283   g_value_set_static_string (&item,
284       gst_video_multiview_mode_to_caps_string (GST_VIDEO_MULTIVIEW_MODE_MONO));
285   gst_value_list_append_value (&list, &item);
286 
287   gst_caps_set_value (templ_caps, "multiview-mode", &list);
288 
289   g_value_unset (&list);
290   g_value_unset (&item);
291 
292   /* And intersect with the peer */
293   if ((tmp = gst_pad_peer_query_caps (pad, NULL)) == NULL) {
294     gst_caps_unref (templ_caps);
295     return NULL;
296   }
297 
298   outcaps = gst_caps_intersect_full (tmp, templ_caps, GST_CAPS_INTERSECT_FIRST);
299   gst_caps_unref (tmp);
300   gst_caps_unref (templ_caps);
301 
302   GST_DEBUG_OBJECT (split, "Src pad %" GST_PTR_FORMAT " caps %" GST_PTR_FORMAT,
303       pad, outcaps);
304   return outcaps;
305 }
306 
307 static gboolean
stereosplit_set_output_caps(GstGLStereoSplit * split,GstCaps * sinkcaps)308 stereosplit_set_output_caps (GstGLStereoSplit * split, GstCaps * sinkcaps)
309 {
310   GstCaps *left = NULL, *right = NULL, *tridcaps = NULL;
311   GstCaps *tmp, *combined;
312   gboolean res = FALSE;
313 
314   /* Choose some preferred output caps.
315    * Keep input width/height and PAR, preserve preferred output
316    * multiview flags for flipping/flopping if any, and set each
317    * left right pad to either left/mono and right/mono, as they prefer
318    */
319 
320   /* Calculate what downstream can collectively support */
321   left =
322       stereosplit_get_src_caps (split, split->left_pad,
323       GST_VIDEO_MULTIVIEW_MODE_LEFT);
324   if (left == NULL)
325     goto fail;
326   right =
327       stereosplit_get_src_caps (split, split->right_pad,
328       GST_VIDEO_MULTIVIEW_MODE_RIGHT);
329   if (right == NULL)
330     goto fail;
331 
332   tridcaps = stereosplit_transform_caps (split, GST_PAD_SINK, sinkcaps, NULL);
333 
334   if (!tridcaps || gst_caps_is_empty (tridcaps)) {
335     GST_ERROR_OBJECT (split,
336         "Failed to transform input caps %" GST_PTR_FORMAT, sinkcaps);
337     goto fail;
338   }
339 
340   /* Preserve downstream preferred flipping/flopping */
341   tmp =
342       strip_mview_fields (gst_caps_ref (left),
343       GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED |
344       GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED);
345   combined = gst_caps_intersect (tridcaps, tmp);
346   gst_caps_unref (tridcaps);
347   gst_caps_unref (tmp);
348   tridcaps = combined;
349 
350   tmp =
351       strip_mview_fields (gst_caps_ref (right),
352       GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED |
353       GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED);
354   combined = gst_caps_intersect (tridcaps, tmp);
355   gst_caps_unref (tridcaps);
356   gst_caps_unref (tmp);
357   tridcaps = combined;
358 
359   if (G_UNLIKELY (gst_caps_is_empty (tridcaps))) {
360     gst_caps_unref (tridcaps);
361     goto fail;
362   }
363 
364   /* Now generate the version for each output pad */
365   GST_DEBUG_OBJECT (split, "Attempting to set output caps %" GST_PTR_FORMAT,
366       tridcaps);
367   tmp = gst_caps_intersect (tridcaps, left);
368   gst_caps_unref (left);
369   left = tmp;
370   left = gst_caps_fixate (left);
371   if (!gst_pad_set_caps (split->left_pad, left)) {
372     GST_ERROR_OBJECT (split,
373         "Failed to set left output caps %" GST_PTR_FORMAT, left);
374     goto fail;
375   }
376 
377   tmp = gst_caps_intersect (tridcaps, right);
378   gst_caps_unref (right);
379   right = tmp;
380   right = gst_caps_fixate (right);
381   if (!gst_pad_set_caps (split->right_pad, right)) {
382     GST_ERROR_OBJECT (split,
383         "Failed to set right output caps %" GST_PTR_FORMAT, right);
384     goto fail;
385   }
386 
387   gst_gl_view_convert_set_context (split->viewconvert, split->context);
388 
389   tridcaps = gst_caps_make_writable (tridcaps);
390   gst_caps_set_simple (tridcaps, "multiview-mode", G_TYPE_STRING,
391       "separated", "views", G_TYPE_INT, 2, NULL);
392   tridcaps = gst_caps_fixate (tridcaps);
393 
394   if (!gst_gl_view_convert_set_caps (split->viewconvert, sinkcaps, tridcaps)) {
395     GST_ERROR_OBJECT (split, "Failed to set caps on converter");
396     goto fail;
397   }
398 
399   /* FIXME: Provide left and right caps to do_bufferpool */
400   stereosplit_do_bufferpool (split, left);
401 
402   res = TRUE;
403 
404 fail:
405   if (left)
406     gst_caps_unref (left);
407   if (right)
408     gst_caps_unref (right);
409   if (tridcaps)
410     gst_caps_unref (tridcaps);
411   return res;
412 }
413 
414 static gboolean
_find_local_gl_context(GstGLStereoSplit * split)415 _find_local_gl_context (GstGLStereoSplit * split)
416 {
417   if (gst_gl_query_local_gl_context (GST_ELEMENT (split), GST_PAD_SRC,
418           &split->context))
419     return TRUE;
420   if (gst_gl_query_local_gl_context (GST_ELEMENT (split), GST_PAD_SINK,
421           &split->context))
422     return TRUE;
423   return FALSE;
424 }
425 
426 static gboolean
ensure_context(GstGLStereoSplit * self)427 ensure_context (GstGLStereoSplit * self)
428 {
429   GError *error = NULL;
430 
431   if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
432     return FALSE;
433 
434   gst_gl_display_filter_gl_api (self->display, SUPPORTED_GL_APIS);
435 
436   _find_local_gl_context (self);
437 
438   if (!self->context) {
439     GST_OBJECT_LOCK (self->display);
440     do {
441       if (self->context)
442         gst_object_unref (self->context);
443       /* just get a GL context.  we don't care */
444       self->context =
445           gst_gl_display_get_gl_context_for_thread (self->display, NULL);
446       if (!self->context) {
447         if (!gst_gl_display_create_context (self->display, self->other_context,
448                 &self->context, &error)) {
449           GST_OBJECT_UNLOCK (self->display);
450           goto context_error;
451         }
452       }
453     } while (!gst_gl_display_add_context (self->display, self->context));
454     GST_OBJECT_UNLOCK (self->display);
455   }
456 
457   {
458     GstGLAPI current_gl_api = gst_gl_context_get_gl_api (self->context);
459     if ((current_gl_api & (SUPPORTED_GL_APIS)) == 0)
460       goto unsupported_gl_api;
461   }
462 
463   return TRUE;
464 
465 unsupported_gl_api:
466   {
467     GstGLAPI gl_api = gst_gl_context_get_gl_api (self->context);
468     gchar *gl_api_str = gst_gl_api_to_string (gl_api);
469     gchar *supported_gl_api_str = gst_gl_api_to_string (SUPPORTED_GL_APIS);
470     GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
471         ("GL API's not compatible context: %s supported: %s", gl_api_str,
472             supported_gl_api_str), (NULL));
473 
474     g_free (supported_gl_api_str);
475     g_free (gl_api_str);
476     return FALSE;
477   }
478 context_error:
479   {
480     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("%s", error->message),
481         (NULL));
482     g_clear_error (&error);
483     return FALSE;
484   }
485 }
486 
487 static gboolean
stereosplit_decide_allocation(GstGLStereoSplit * self,GstQuery * query)488 stereosplit_decide_allocation (GstGLStereoSplit * self, GstQuery * query)
489 {
490   if (!ensure_context (self))
491     return FALSE;
492 
493   return TRUE;
494 
495 }
496 
497 static gboolean
stereosplit_propose_allocation(GstGLStereoSplit * self,GstQuery * query)498 stereosplit_propose_allocation (GstGLStereoSplit * self, GstQuery * query)
499 {
500 
501   if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
502     return FALSE;
503 
504   return TRUE;
505 }
506 
507 static gboolean
stereosplit_do_bufferpool(GstGLStereoSplit * self,GstCaps * caps)508 stereosplit_do_bufferpool (GstGLStereoSplit * self, GstCaps * caps)
509 {
510   GstQuery *query;
511 
512   query = gst_query_new_allocation (caps, TRUE);
513   if (!gst_pad_peer_query (self->left_pad, query)) {
514     if (!gst_pad_peer_query (self->right_pad, query)) {
515       GST_DEBUG_OBJECT (self, "peer ALLOCATION query failed on both src pads");
516     }
517   }
518 
519   if (!stereosplit_decide_allocation (self, query)) {
520     gst_query_unref (query);
521     return FALSE;
522   }
523 
524   gst_query_unref (query);
525   return TRUE;
526 }
527 
528 static GstFlowReturn
stereosplit_chain(GstPad * pad,GstGLStereoSplit * split,GstBuffer * buf)529 stereosplit_chain (GstPad * pad, GstGLStereoSplit * split, GstBuffer * buf)
530 {
531   GstBuffer *left, *right;
532   GstBuffer *split_buffer = NULL;
533   GstFlowReturn ret;
534   gint i, n_planes;
535 
536   n_planes = GST_VIDEO_INFO_N_PLANES (&split->viewconvert->out_info);
537 
538   GST_LOG_OBJECT (split, "chaining buffer %" GST_PTR_FORMAT, buf);
539 
540   if (gst_gl_view_convert_submit_input_buffer (split->viewconvert,
541           GST_BUFFER_IS_DISCONT (buf), buf) != GST_FLOW_OK) {
542     GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
543             "Failed to 3d convert buffer"),
544         ("Could not get submit input buffer"));
545     return GST_FLOW_ERROR;
546   }
547 
548   ret = gst_gl_view_convert_get_output (split->viewconvert, &split_buffer);
549   if (ret != GST_FLOW_OK) {
550     GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
551             "Failed to 3d convert buffer"), ("Could not get output buffer"));
552     return GST_FLOW_ERROR;
553   }
554   if (split_buffer == NULL)
555     return GST_FLOW_OK;         /* Need another input buffer */
556 
557   left = gst_buffer_new ();
558   gst_buffer_copy_into (left, buf,
559       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
560   GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
561 
562   gst_buffer_add_parent_buffer_meta (left, split_buffer);
563 
564   for (i = 0; i < n_planes; i++) {
565     GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
566     gst_buffer_append_memory (left, mem);
567   }
568 
569   ret = gst_pad_push (split->left_pad, gst_buffer_ref (left));
570   /* Allow unlinked on the first pad - as long as the 2nd isn't unlinked */
571   gst_buffer_unref (left);
572   if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)) {
573     gst_buffer_unref (split_buffer);
574     return ret;
575   }
576 
577   right = gst_buffer_new ();
578   gst_buffer_copy_into (right, buf,
579       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
580   GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
581   gst_buffer_add_parent_buffer_meta (right, split_buffer);
582   for (i = n_planes; i < n_planes * 2; i++) {
583     GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
584     gst_buffer_append_memory (right, mem);
585   }
586 
587   ret = gst_pad_push (split->right_pad, gst_buffer_ref (right));
588   gst_buffer_unref (right);
589   gst_buffer_unref (split_buffer);
590   return ret;
591 }
592 
593 static gboolean
stereosplit_src_query(GstPad * pad,GstObject * parent,GstQuery * query)594 stereosplit_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
595 {
596   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
597 
598   switch (GST_QUERY_TYPE (query)) {
599     case GST_QUERY_CONTEXT:
600     {
601       if (gst_gl_handle_context_query ((GstElement *) split, query,
602               split->display, split->context, split->other_context))
603         return TRUE;
604 
605       return gst_pad_query_default (pad, parent, query);
606     }
607       /* FIXME: Handle caps query */
608     default:
609       return gst_pad_query_default (pad, parent, query);
610   }
611 }
612 
613 static gboolean
stereosplit_src_event(GstPad * pad,GstObject * parent,GstEvent * event)614 stereosplit_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
615 {
616   return gst_pad_event_default (pad, parent, event);
617 }
618 
619 static gboolean
stereosplit_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)620 stereosplit_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
621 {
622   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
623 
624   GST_DEBUG_OBJECT (split, "sink query %s",
625       gst_query_type_get_name (GST_QUERY_TYPE (query)));
626 
627   switch (GST_QUERY_TYPE (query)) {
628     case GST_QUERY_CONTEXT:
629     {
630       if (gst_gl_handle_context_query ((GstElement *) split, query,
631               split->display, split->context, split->other_context))
632         return TRUE;
633 
634       return gst_pad_query_default (pad, parent, query);
635     }
636     case GST_QUERY_ALLOCATION:
637     {
638       return stereosplit_propose_allocation (split, query);
639     }
640     case GST_QUERY_ACCEPT_CAPS:
641     {
642       GstCaps *possible, *caps;
643       gboolean allowed;
644 
645       gst_query_parse_accept_caps (query, &caps);
646 
647       if (!(possible = gst_pad_query_caps (split->sink_pad, caps)))
648         return FALSE;
649 
650       allowed = gst_caps_is_subset (caps, possible);
651       gst_caps_unref (possible);
652 
653       gst_query_set_accept_caps_result (query, allowed);
654       return allowed;
655     }
656     case GST_QUERY_CAPS:
657     {
658       GstCaps *filter, *left, *right, *combined, *ret, *templ_caps;
659 
660       gst_query_parse_caps (query, &filter);
661 
662       /* Calculate what downstream can collectively support */
663       if (!(left = gst_pad_peer_query_caps (split->left_pad, NULL)))
664         return FALSE;
665       if (!(right = gst_pad_peer_query_caps (split->right_pad, NULL)))
666         return FALSE;
667 
668       /* Strip out multiview mode and flags that might break the
669        * intersection, since we can convert.
670        * We could keep downstream preferred flip/flopping and list
671        * separated as preferred in the future which might
672        * theoretically allow us an easier conversion, but it's not essential
673        */
674       left = strip_mview_fields (left, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
675       right = strip_mview_fields (right, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
676 
677       combined = gst_caps_intersect (left, right);
678       gst_caps_unref (left);
679       gst_caps_unref (right);
680 
681       /* Intersect peer caps with our template formats */
682       templ_caps = gst_pad_get_pad_template_caps (split->left_pad);
683       ret =
684           gst_caps_intersect_full (combined, templ_caps,
685           GST_CAPS_INTERSECT_FIRST);
686       gst_caps_unref (templ_caps);
687 
688       gst_caps_unref (combined);
689       combined = ret;
690 
691       if (!combined || gst_caps_is_empty (combined)) {
692         gst_caps_unref (combined);
693         return FALSE;
694       }
695 
696       /* Convert from the src pad caps to input formats we support */
697       ret = stereosplit_transform_caps (split, GST_PAD_SRC, combined, filter);
698       gst_caps_unref (combined);
699       combined = ret;
700 
701       /* Intersect with the sink pad template then */
702       templ_caps = gst_pad_get_pad_template_caps (split->sink_pad);
703       ret =
704           gst_caps_intersect_full (combined, templ_caps,
705           GST_CAPS_INTERSECT_FIRST);
706       gst_caps_unref (templ_caps);
707 
708       GST_LOG_OBJECT (split, "Returning sink pad caps %" GST_PTR_FORMAT, ret);
709 
710       gst_query_set_caps_result (query, ret);
711       return !gst_caps_is_empty (ret);
712     }
713     default:
714       return gst_pad_query_default (pad, parent, query);
715   }
716 }
717 
718 static gboolean
stereosplit_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)719 stereosplit_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
720 {
721   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
722 
723   switch (GST_EVENT_TYPE (event)) {
724     case GST_EVENT_CAPS:
725     {
726       GstCaps *caps;
727 
728       gst_event_parse_caps (event, &caps);
729 
730       return stereosplit_set_output_caps (split, caps);
731     }
732     default:
733       return gst_pad_event_default (pad, parent, event);
734   }
735 }
736