• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gstglelements.h"
40 #include "gstglstereosplit.h"
41 
42 #define GST_CAT_DEFAULT gst_gl_stereosplit_debug
43 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
44 
45 #define SUPPORTED_GL_APIS GST_GL_API_GLES2 | GST_GL_API_OPENGL | GST_GL_API_OPENGL3
46 #define DEBUG_INIT \
47   GST_DEBUG_CATEGORY_INIT (gst_gl_stereosplit_debug, "glstereosplit", 0, "glstereosplit element");
48 
49 G_DEFINE_TYPE_WITH_CODE (GstGLStereoSplit, gst_gl_stereosplit,
50     GST_TYPE_ELEMENT, DEBUG_INIT);
51 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glstereosplit, "glstereosplit",
52     GST_RANK_NONE, GST_TYPE_GL_STEREOSPLIT, gl_element_init (plugin));
53 
54 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
55     GST_PAD_SINK, GST_PAD_ALWAYS,
56     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
57         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
58     );
59 
60 static GstStaticPadTemplate src_left_template = GST_STATIC_PAD_TEMPLATE ("left",
61     GST_PAD_SRC, GST_PAD_ALWAYS,
62     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
63         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
64     );
65 
66 static GstStaticPadTemplate src_right_template =
67 GST_STATIC_PAD_TEMPLATE ("right",
68     GST_PAD_SRC, GST_PAD_ALWAYS,
69     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
70         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
71             "RGBA"))
72     );
73 
74 static void stereosplit_reset (GstGLStereoSplit * self);
75 static void stereosplit_finalize (GstGLStereoSplit * self);
76 static void stereosplit_set_context (GstElement * element,
77     GstContext * context);
78 static GstFlowReturn stereosplit_chain (GstPad * pad, GstGLStereoSplit * split,
79     GstBuffer * buf);
80 static GstStateChangeReturn stereosplit_change_state (GstElement * element,
81     GstStateChange transition);
82 static gboolean stereosplit_sink_query (GstPad * pad, GstObject * parent,
83     GstQuery * query);
84 static gboolean stereosplit_sink_event (GstPad * pad, GstObject * parent,
85     GstEvent * event);
86 static gboolean stereosplit_src_query (GstPad * pad, GstObject * parent,
87     GstQuery * query);
88 static gboolean stereosplit_src_event (GstPad * pad, GstObject * parent,
89     GstEvent * event);
90 static gboolean ensure_context (GstGLStereoSplit * self);
91 static gboolean ensure_context_unlocked (GstGLStereoSplit * self);
92 
93 static void
gst_gl_stereosplit_class_init(GstGLStereoSplitClass * klass)94 gst_gl_stereosplit_class_init (GstGLStereoSplitClass * klass)
95 {
96   GObjectClass *gobject_class = (GObjectClass *) klass;
97   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
98 
99   gst_element_class_set_static_metadata (element_class,
100       "GLStereoSplit", "Codec/Converter",
101       "Splits a stereoscopic stream into separate left/right streams",
102       "Jan Schmidt <jan@centricular.com>\n"
103       "Matthew Waters <matthew@centricular.com>");
104 
105   gobject_class->finalize = (GObjectFinalizeFunc) (stereosplit_finalize);
106 
107   element_class->change_state = stereosplit_change_state;
108   element_class->set_context = stereosplit_set_context;
109 
110   gst_element_class_add_static_pad_template (element_class, &sink_template);
111   gst_element_class_add_static_pad_template (element_class, &src_left_template);
112   gst_element_class_add_static_pad_template (element_class,
113       &src_right_template);
114 }
115 
116 static void
gst_gl_stereosplit_init(GstGLStereoSplit * self)117 gst_gl_stereosplit_init (GstGLStereoSplit * self)
118 {
119   GstPad *pad;
120 
121   pad = self->sink_pad =
122       gst_pad_new_from_static_template (&sink_template, "sink");
123 
124   gst_pad_set_chain_function (pad, (GstPadChainFunction) (stereosplit_chain));
125   gst_pad_set_query_function (pad, stereosplit_sink_query);
126   gst_pad_set_event_function (pad, stereosplit_sink_event);
127 
128   gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
129 
130   pad = self->left_pad =
131       gst_pad_new_from_static_template (&src_left_template, "left");
132   gst_pad_set_query_function (pad, stereosplit_src_query);
133   gst_pad_set_event_function (pad, stereosplit_src_event);
134   gst_element_add_pad (GST_ELEMENT (self), self->left_pad);
135 
136   pad = self->right_pad =
137       gst_pad_new_from_static_template (&src_right_template, "right");
138   gst_pad_set_query_function (pad, stereosplit_src_query);
139   gst_pad_set_event_function (pad, stereosplit_src_event);
140   gst_element_add_pad (GST_ELEMENT (self), self->right_pad);
141 
142   self->viewconvert = gst_gl_view_convert_new ();
143 
144   g_rec_mutex_init (&self->context_lock);
145 }
146 
147 static void
stereosplit_reset(GstGLStereoSplit * self)148 stereosplit_reset (GstGLStereoSplit * self)
149 {
150   if (self->context)
151     gst_object_replace ((GstObject **) & self->context, NULL);
152   if (self->display)
153     gst_object_replace ((GstObject **) & self->display, NULL);
154 }
155 
156 static void
stereosplit_finalize(GstGLStereoSplit * self)157 stereosplit_finalize (GstGLStereoSplit * self)
158 {
159   GObjectClass *klass = G_OBJECT_CLASS (gst_gl_stereosplit_parent_class);
160 
161   if (self->viewconvert)
162     gst_object_replace ((GstObject **) & self->viewconvert, NULL);
163 
164   g_rec_mutex_clear (&self->context_lock);
165 
166   klass->finalize ((GObject *) (self));
167 }
168 
169 static void
stereosplit_set_context(GstElement * element,GstContext * context)170 stereosplit_set_context (GstElement * element, GstContext * context)
171 {
172   GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
173   GstGLDisplay *old_display, *new_display;
174 
175   g_rec_mutex_lock (&stereosplit->context_lock);
176   GST_DEBUG_OBJECT (element, "set context of %" GST_PTR_FORMAT, context);
177   old_display =
178       stereosplit->display ? gst_object_ref (stereosplit->display) : NULL;
179   gst_gl_handle_set_context (element, context, &stereosplit->display,
180       &stereosplit->other_context);
181 
182   if (stereosplit->display)
183     gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
184 
185   new_display =
186       stereosplit->display ? gst_object_ref (stereosplit->display) : NULL;
187 
188   if (old_display && new_display) {
189     if (old_display != new_display) {
190       gst_clear_object (&stereosplit->context);
191       gst_gl_view_convert_set_context (stereosplit->viewconvert, NULL);
192       GST_INFO_OBJECT (stereosplit, "display changed to %" GST_PTR_FORMAT,
193           new_display);
194       if (ensure_context_unlocked (stereosplit)) {
195         gst_gl_view_convert_set_context (stereosplit->viewconvert,
196             stereosplit->context);
197       }
198     }
199   }
200   gst_clear_object (&old_display);
201   gst_clear_object (&new_display);
202   g_rec_mutex_unlock (&stereosplit->context_lock);
203 
204   GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->set_context (element,
205       context);
206 }
207 
208 static GstStateChangeReturn
stereosplit_change_state(GstElement * element,GstStateChange transition)209 stereosplit_change_state (GstElement * element, GstStateChange transition)
210 {
211   GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
212   GstStateChangeReturn result;
213 
214   switch (transition) {
215     case GST_STATE_CHANGE_NULL_TO_READY:
216       g_rec_mutex_lock (&stereosplit->context_lock);
217       if (!gst_gl_ensure_element_data (element, &stereosplit->display,
218               &stereosplit->other_context))
219         return GST_STATE_CHANGE_FAILURE;
220 
221       gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
222       g_rec_mutex_unlock (&stereosplit->context_lock);
223       break;
224     default:
225       break;
226   }
227 
228   result =
229       GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->change_state
230       (element, transition);
231 
232   switch (transition) {
233     case GST_STATE_CHANGE_READY_TO_NULL:
234       g_rec_mutex_lock (&stereosplit->context_lock);
235       gst_clear_object (&stereosplit->other_context);
236       gst_clear_object (&stereosplit->display);
237       g_rec_mutex_unlock (&stereosplit->context_lock);
238       break;
239     case GST_STATE_CHANGE_PAUSED_TO_READY:
240       stereosplit_reset (stereosplit);
241       break;
242     default:
243       break;
244   }
245 
246   return result;
247 }
248 
249 static GstCaps *
stereosplit_transform_caps(GstGLStereoSplit * self,GstPadDirection direction,GstCaps * caps,GstCaps * filter)250 stereosplit_transform_caps (GstGLStereoSplit * self, GstPadDirection direction,
251     GstCaps * caps, GstCaps * filter)
252 {
253   GstCaps *next_caps;
254 
255   next_caps =
256       gst_gl_view_convert_transform_caps (self->viewconvert, direction, caps,
257       NULL);
258 
259   return next_caps;
260 }
261 
262 static GstCaps *
strip_mview_fields(GstCaps * incaps,GstVideoMultiviewFlags keep_flags)263 strip_mview_fields (GstCaps * incaps, GstVideoMultiviewFlags keep_flags)
264 {
265   GstCaps *outcaps = gst_caps_make_writable (incaps);
266 
267   gint i, n;
268 
269   n = gst_caps_get_size (outcaps);
270   for (i = 0; i < n; i++) {
271     GstStructure *st = gst_caps_get_structure (outcaps, i);
272     GstVideoMultiviewFlags flags, mask;
273 
274     gst_structure_remove_field (st, "multiview-mode");
275     if (gst_structure_get_flagset (st, "multiview-flags", (guint *) & flags,
276             (guint *) & mask)) {
277       flags &= keep_flags;
278       mask = keep_flags;
279       gst_structure_set (st, "multiview-flags",
280           GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags, mask, NULL);
281     }
282   }
283 
284   return outcaps;
285 }
286 
287 static gboolean stereosplit_do_bufferpool (GstGLStereoSplit * self,
288     GstCaps * caps);
289 
290 static GstCaps *
stereosplit_get_src_caps(GstGLStereoSplit * split,GstPad * pad,GstVideoMultiviewMode preferred_mode)291 stereosplit_get_src_caps (GstGLStereoSplit * split,
292     GstPad * pad, GstVideoMultiviewMode preferred_mode)
293 {
294   GstCaps *outcaps, *tmp, *templ_caps;
295   GValue item = G_VALUE_INIT, list = G_VALUE_INIT;
296 
297   /* Get the template format */
298   templ_caps = gst_pad_get_pad_template_caps (pad);
299 
300   /* And limit down to the preferred mode or mono */
301   templ_caps = gst_caps_make_writable (templ_caps);
302 
303   g_value_init (&item, G_TYPE_STRING);
304   g_value_init (&list, GST_TYPE_LIST);
305   g_value_set_static_string (&item,
306       gst_video_multiview_mode_to_caps_string (preferred_mode));
307   gst_value_list_append_value (&list, &item);
308   g_value_set_static_string (&item,
309       gst_video_multiview_mode_to_caps_string (GST_VIDEO_MULTIVIEW_MODE_MONO));
310   gst_value_list_append_value (&list, &item);
311 
312   gst_caps_set_value (templ_caps, "multiview-mode", &list);
313 
314   g_value_unset (&list);
315   g_value_unset (&item);
316 
317   /* And intersect with the peer */
318   if ((tmp = gst_pad_peer_query_caps (pad, NULL)) == NULL) {
319     gst_caps_unref (templ_caps);
320     return NULL;
321   }
322 
323   outcaps = gst_caps_intersect_full (tmp, templ_caps, GST_CAPS_INTERSECT_FIRST);
324   gst_caps_unref (tmp);
325   gst_caps_unref (templ_caps);
326 
327   GST_DEBUG_OBJECT (split, "Src pad %" GST_PTR_FORMAT " caps %" GST_PTR_FORMAT,
328       pad, outcaps);
329   return outcaps;
330 }
331 
332 static gboolean
stereosplit_set_output_caps(GstGLStereoSplit * split,GstCaps * sinkcaps)333 stereosplit_set_output_caps (GstGLStereoSplit * split, GstCaps * sinkcaps)
334 {
335   GstCaps *left = NULL, *right = NULL, *tridcaps = NULL;
336   GstCaps *tmp, *combined;
337   gboolean res = FALSE;
338 
339   /* Choose some preferred output caps.
340    * Keep input width/height and PAR, preserve preferred output
341    * multiview flags for flipping/flopping if any, and set each
342    * left right pad to either left/mono and right/mono, as they prefer
343    */
344 
345   if (!ensure_context (split)) {
346     res = FALSE;
347     goto fail;
348   }
349 
350   /* Calculate what downstream can collectively support */
351   left =
352       stereosplit_get_src_caps (split, split->left_pad,
353       GST_VIDEO_MULTIVIEW_MODE_LEFT);
354   if (left == NULL)
355     goto fail;
356   right =
357       stereosplit_get_src_caps (split, split->right_pad,
358       GST_VIDEO_MULTIVIEW_MODE_RIGHT);
359   if (right == NULL)
360     goto fail;
361 
362   tridcaps = stereosplit_transform_caps (split, GST_PAD_SINK, sinkcaps, NULL);
363 
364   if (!tridcaps || gst_caps_is_empty (tridcaps)) {
365     GST_ERROR_OBJECT (split,
366         "Failed to transform input caps %" GST_PTR_FORMAT, sinkcaps);
367     goto fail;
368   }
369 
370   /* Preserve downstream preferred flipping/flopping */
371   tmp =
372       strip_mview_fields (gst_caps_ref (left),
373       GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED |
374       GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED);
375   combined = gst_caps_intersect (tridcaps, tmp);
376   gst_caps_unref (tridcaps);
377   gst_caps_unref (tmp);
378   tridcaps = combined;
379 
380   tmp =
381       strip_mview_fields (gst_caps_ref (right),
382       GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED |
383       GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED);
384   combined = gst_caps_intersect (tridcaps, tmp);
385   gst_caps_unref (tridcaps);
386   gst_caps_unref (tmp);
387   tridcaps = combined;
388 
389   if (G_UNLIKELY (gst_caps_is_empty (tridcaps))) {
390     gst_caps_unref (tridcaps);
391     goto fail;
392   }
393 
394   /* Now generate the version for each output pad */
395   GST_DEBUG_OBJECT (split, "Attempting to set output caps %" GST_PTR_FORMAT,
396       tridcaps);
397   tmp = gst_caps_intersect (tridcaps, left);
398   gst_caps_unref (left);
399   left = tmp;
400   left = gst_caps_fixate (left);
401   if (!gst_pad_set_caps (split->left_pad, left)) {
402     GST_ERROR_OBJECT (split,
403         "Failed to set left output caps %" GST_PTR_FORMAT, left);
404     goto fail;
405   }
406 
407   tmp = gst_caps_intersect (tridcaps, right);
408   gst_caps_unref (right);
409   right = tmp;
410   right = gst_caps_fixate (right);
411   if (!gst_pad_set_caps (split->right_pad, right)) {
412     GST_ERROR_OBJECT (split,
413         "Failed to set right output caps %" GST_PTR_FORMAT, right);
414     goto fail;
415   }
416 
417   /* FIXME: Provide left and right caps to do_bufferpool */
418   stereosplit_do_bufferpool (split, left);
419 
420   g_rec_mutex_lock (&split->context_lock);
421   gst_gl_view_convert_set_context (split->viewconvert, split->context);
422 
423   tridcaps = gst_caps_make_writable (tridcaps);
424   gst_caps_set_simple (tridcaps, "multiview-mode", G_TYPE_STRING,
425       "separated", "views", G_TYPE_INT, 2, NULL);
426   tridcaps = gst_caps_fixate (tridcaps);
427 
428   if (!gst_gl_view_convert_set_caps (split->viewconvert, sinkcaps, tridcaps)) {
429     g_rec_mutex_unlock (&split->context_lock);
430     GST_ERROR_OBJECT (split, "Failed to set caps on converter");
431     goto fail;
432   }
433   g_rec_mutex_unlock (&split->context_lock);
434 
435   res = TRUE;
436 
437 fail:
438   if (left)
439     gst_caps_unref (left);
440   if (right)
441     gst_caps_unref (right);
442   if (tridcaps)
443     gst_caps_unref (tridcaps);
444   return res;
445 }
446 
447 static gboolean
_find_local_gl_context_unlocked(GstGLStereoSplit * split)448 _find_local_gl_context_unlocked (GstGLStereoSplit * split)
449 {
450   GstGLContext *context, *prev_context;
451   gboolean ret;
452 
453   if (split->context && split->context->display == split->display)
454     return TRUE;
455 
456   context = prev_context = split->context;
457   g_rec_mutex_unlock (&split->context_lock);
458   /* we need to drop the lock to query as another element may also be
459    * performing a context query on us which would also attempt to take the
460    * context_lock. Our query could block on the same lock in the other element.
461    */
462   ret =
463       gst_gl_query_local_gl_context (GST_ELEMENT (split), GST_PAD_SRC,
464       &context);
465   g_rec_mutex_lock (&split->context_lock);
466   if (ret) {
467     if (split->context != prev_context) {
468       /* we need to recheck everything since we dropped the lock and the
469        * context has changed */
470       if (split->context && split->context->display == split->display) {
471         if (context != split->context)
472           gst_clear_object (&context);
473         return TRUE;
474       }
475     }
476 
477     if (context->display == split->display) {
478       split->context = context;
479       return TRUE;
480     }
481     if (context != split->context)
482       gst_clear_object (&context);
483   }
484 
485   context = prev_context = split->context;
486   g_rec_mutex_unlock (&split->context_lock);
487   /* we need to drop the lock to query as another element may also be
488    * performing a context query on us which would also attempt to take the
489    * context_lock. Our query could block on the same lock in the other element.
490    */
491   ret =
492       gst_gl_query_local_gl_context (GST_ELEMENT (split), GST_PAD_SINK,
493       &context);
494   g_rec_mutex_lock (&split->context_lock);
495   if (ret) {
496     if (split->context != prev_context) {
497       /* we need to recheck everything now that we dropped the lock */
498       if (split->context && split->context->display == split->display) {
499         if (context != split->context)
500           gst_clear_object (&context);
501         return TRUE;
502       }
503     }
504 
505     if (context->display == split->display) {
506       split->context = context;
507       return TRUE;
508     }
509     if (context != split->context)
510       gst_clear_object (&context);
511   }
512 
513   return FALSE;
514 }
515 
516 static gboolean
ensure_context_unlocked(GstGLStereoSplit * self)517 ensure_context_unlocked (GstGLStereoSplit * self)
518 {
519   GError *error = NULL;
520 
521   GST_DEBUG_OBJECT (self, "attempting to find an OpenGL context, existing %"
522       GST_PTR_FORMAT, self->context);
523 
524   if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
525     return FALSE;
526 
527   gst_gl_display_filter_gl_api (self->display, SUPPORTED_GL_APIS);
528 
529   _find_local_gl_context_unlocked (self);
530 
531   if (!self->context) {
532     GST_OBJECT_LOCK (self->display);
533     do {
534       if (self->context)
535         gst_object_unref (self->context);
536       /* just get a GL context.  we don't care */
537       self->context =
538           gst_gl_display_get_gl_context_for_thread (self->display, NULL);
539       if (!self->context) {
540         if (!gst_gl_display_create_context (self->display, self->other_context,
541                 &self->context, &error)) {
542           GST_OBJECT_UNLOCK (self->display);
543           goto context_error;
544         }
545       }
546     } while (!gst_gl_display_add_context (self->display, self->context));
547     GST_OBJECT_UNLOCK (self->display);
548   }
549 
550   {
551     GstGLAPI current_gl_api = gst_gl_context_get_gl_api (self->context);
552     if ((current_gl_api & (SUPPORTED_GL_APIS)) == 0)
553       goto unsupported_gl_api;
554   }
555 
556   GST_INFO_OBJECT (self, "found OpenGL context %" GST_PTR_FORMAT,
557       self->context);
558 
559   return TRUE;
560 
561 unsupported_gl_api:
562   {
563     GstGLAPI gl_api = gst_gl_context_get_gl_api (self->context);
564     gchar *gl_api_str = gst_gl_api_to_string (gl_api);
565     gchar *supported_gl_api_str = gst_gl_api_to_string (SUPPORTED_GL_APIS);
566     GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
567         ("GL API's not compatible context: %s supported: %s", gl_api_str,
568             supported_gl_api_str), (NULL));
569 
570     g_free (supported_gl_api_str);
571     g_free (gl_api_str);
572     return FALSE;
573   }
574 context_error:
575   {
576     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("%s", error->message),
577         (NULL));
578     g_clear_error (&error);
579     return FALSE;
580   }
581 }
582 
583 static gboolean
ensure_context(GstGLStereoSplit * self)584 ensure_context (GstGLStereoSplit * self)
585 {
586   gboolean ret;
587   g_rec_mutex_lock (&self->context_lock);
588   ret = ensure_context_unlocked (self);
589   g_rec_mutex_unlock (&self->context_lock);
590   return ret;
591 }
592 
593 static gboolean
stereosplit_decide_allocation(GstGLStereoSplit * self,GstQuery * query)594 stereosplit_decide_allocation (GstGLStereoSplit * self, GstQuery * query)
595 {
596   if (!ensure_context_unlocked (self))
597     return FALSE;
598 
599   return TRUE;
600 }
601 
602 static gboolean
stereosplit_propose_allocation(GstGLStereoSplit * self,GstQuery * query)603 stereosplit_propose_allocation (GstGLStereoSplit * self, GstQuery * query)
604 {
605   if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
606     return FALSE;
607 
608   return TRUE;
609 }
610 
611 static gboolean
stereosplit_do_bufferpool(GstGLStereoSplit * self,GstCaps * caps)612 stereosplit_do_bufferpool (GstGLStereoSplit * self, GstCaps * caps)
613 {
614   GstQuery *query;
615 
616   query = gst_query_new_allocation (caps, TRUE);
617   if (!gst_pad_peer_query (self->left_pad, query)) {
618     if (!gst_pad_peer_query (self->right_pad, query)) {
619       GST_DEBUG_OBJECT (self, "peer ALLOCATION query failed on both src pads");
620     }
621   }
622 
623   if (!stereosplit_decide_allocation (self, query)) {
624     gst_query_unref (query);
625     return FALSE;
626   }
627 
628   gst_query_unref (query);
629   return TRUE;
630 }
631 
632 static GstFlowReturn
stereosplit_chain(GstPad * pad,GstGLStereoSplit * split,GstBuffer * buf)633 stereosplit_chain (GstPad * pad, GstGLStereoSplit * split, GstBuffer * buf)
634 {
635   GstBuffer *left, *right;
636   GstBuffer *split_buffer = NULL;
637   GstFlowReturn ret;
638   gint i, n_planes;
639 
640   n_planes = GST_VIDEO_INFO_N_PLANES (&split->viewconvert->out_info);
641 
642   GST_LOG_OBJECT (split, "chaining buffer %" GST_PTR_FORMAT, buf);
643 
644   gst_buffer_ref (buf);
645 
646   g_rec_mutex_lock (&split->context_lock);
647 
648   if (gst_gl_view_convert_submit_input_buffer (split->viewconvert,
649           GST_BUFFER_IS_DISCONT (buf), buf) != GST_FLOW_OK) {
650     g_rec_mutex_unlock (&split->context_lock);
651     GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
652             "Failed to 3d convert buffer"),
653         ("Could not get submit input buffer"));
654     gst_buffer_unref (buf);
655     return GST_FLOW_ERROR;
656   }
657 
658   ret = gst_gl_view_convert_get_output (split->viewconvert, &split_buffer);
659   g_rec_mutex_unlock (&split->context_lock);
660   if (ret != GST_FLOW_OK) {
661     GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
662             "Failed to 3d convert buffer"), ("Could not get output buffer"));
663     gst_buffer_unref (buf);
664     return GST_FLOW_ERROR;
665   }
666   if (split_buffer == NULL) {
667     gst_buffer_unref (buf);
668     return GST_FLOW_OK;         /* Need another input buffer */
669   }
670 
671   left = gst_buffer_new ();
672   gst_buffer_copy_into (left, buf,
673       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
674   GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
675 
676   gst_buffer_add_parent_buffer_meta (left, split_buffer);
677 
678   for (i = 0; i < n_planes; i++) {
679     GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
680     gst_buffer_append_memory (left, mem);
681   }
682 
683   ret = gst_pad_push (split->left_pad, gst_buffer_ref (left));
684   /* Allow unlinked on the first pad - as long as the 2nd isn't unlinked */
685   gst_buffer_unref (left);
686   if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)) {
687     gst_buffer_unref (split_buffer);
688     gst_buffer_unref (buf);
689     return ret;
690   }
691 
692   right = gst_buffer_new ();
693   gst_buffer_copy_into (right, buf,
694       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
695   GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
696   gst_buffer_add_parent_buffer_meta (right, split_buffer);
697   for (i = n_planes; i < n_planes * 2; i++) {
698     GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
699     gst_buffer_append_memory (right, mem);
700   }
701 
702   ret = gst_pad_push (split->right_pad, gst_buffer_ref (right));
703   gst_buffer_unref (right);
704   gst_buffer_unref (split_buffer);
705   gst_buffer_unref (buf);
706   return ret;
707 }
708 
709 static gboolean
stereosplit_src_query(GstPad * pad,GstObject * parent,GstQuery * query)710 stereosplit_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
711 {
712   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
713 
714   switch (GST_QUERY_TYPE (query)) {
715     case GST_QUERY_CONTEXT:
716     {
717       GstGLDisplay *display = NULL;
718       GstGLContext *other = NULL, *local = NULL;
719       gboolean ret;
720 
721       g_rec_mutex_lock (&split->context_lock);
722       if (split->display)
723         display = gst_object_ref (split->display);
724       if (split->context)
725         local = gst_object_ref (split->context);
726       if (split->other_context)
727         other = gst_object_ref (split->other_context);
728       g_rec_mutex_unlock (&split->context_lock);
729 
730       ret = gst_gl_handle_context_query ((GstElement *) split, query,
731           display, local, other);
732 
733       gst_clear_object (&display);
734       gst_clear_object (&other);
735       gst_clear_object (&local);
736       if (ret)
737         return TRUE;
738 
739       return gst_pad_query_default (pad, parent, query);
740     }
741       /* FIXME: Handle caps query */
742     default:
743       return gst_pad_query_default (pad, parent, query);
744   }
745 }
746 
747 static gboolean
stereosplit_src_event(GstPad * pad,GstObject * parent,GstEvent * event)748 stereosplit_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
749 {
750   return gst_pad_event_default (pad, parent, event);
751 }
752 
753 static gboolean
stereosplit_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)754 stereosplit_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
755 {
756   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
757 
758   GST_DEBUG_OBJECT (split, "sink query %s",
759       gst_query_type_get_name (GST_QUERY_TYPE (query)));
760 
761   switch (GST_QUERY_TYPE (query)) {
762     case GST_QUERY_CONTEXT:
763     {
764       GstGLDisplay *display = NULL;
765       GstGLContext *other = NULL, *local = NULL;
766       gboolean ret;
767 
768       g_rec_mutex_lock (&split->context_lock);
769       if (split->display)
770         display = gst_object_ref (split->display);
771       if (split->context)
772         local = gst_object_ref (split->context);
773       if (split->other_context)
774         other = gst_object_ref (split->other_context);
775       g_rec_mutex_unlock (&split->context_lock);
776 
777       ret = gst_gl_handle_context_query ((GstElement *) split, query,
778           display, local, other);
779 
780       gst_clear_object (&display);
781       gst_clear_object (&other);
782       gst_clear_object (&local);
783       if (ret)
784         return TRUE;
785 
786       return gst_pad_query_default (pad, parent, query);
787     }
788     case GST_QUERY_ALLOCATION:
789     {
790       return stereosplit_propose_allocation (split, query);
791     }
792     case GST_QUERY_ACCEPT_CAPS:
793     {
794       GstCaps *possible, *caps;
795       gboolean allowed;
796 
797       gst_query_parse_accept_caps (query, &caps);
798 
799       if (!(possible = gst_pad_query_caps (split->sink_pad, caps)))
800         return FALSE;
801 
802       allowed = gst_caps_is_subset (caps, possible);
803       gst_caps_unref (possible);
804 
805       gst_query_set_accept_caps_result (query, allowed);
806       return allowed;
807     }
808     case GST_QUERY_CAPS:
809     {
810       GstCaps *filter, *left, *right, *combined, *ret, *templ_caps;
811       gboolean result;
812 
813       gst_query_parse_caps (query, &filter);
814 
815       /* Calculate what downstream can collectively support */
816       if (!(left = gst_pad_peer_query_caps (split->left_pad, NULL)))
817         return FALSE;
818       if (!(right = gst_pad_peer_query_caps (split->right_pad, NULL)))
819         return FALSE;
820 
821       /* Strip out multiview mode and flags that might break the
822        * intersection, since we can convert.
823        * We could keep downstream preferred flip/flopping and list
824        * separated as preferred in the future which might
825        * theoretically allow us an easier conversion, but it's not essential
826        */
827       left = strip_mview_fields (left, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
828       right = strip_mview_fields (right, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
829 
830       combined = gst_caps_intersect (left, right);
831       gst_caps_unref (left);
832       gst_caps_unref (right);
833 
834       /* Intersect peer caps with our template formats */
835       templ_caps = gst_pad_get_pad_template_caps (split->left_pad);
836       ret =
837           gst_caps_intersect_full (combined, templ_caps,
838           GST_CAPS_INTERSECT_FIRST);
839       gst_caps_unref (templ_caps);
840 
841       gst_caps_unref (combined);
842       combined = ret;
843 
844       if (!combined || gst_caps_is_empty (combined)) {
845         gst_caps_unref (combined);
846         return FALSE;
847       }
848 
849       /* Convert from the src pad caps to input formats we support */
850       ret = stereosplit_transform_caps (split, GST_PAD_SRC, combined, filter);
851       gst_caps_unref (combined);
852       combined = ret;
853 
854       /* Intersect with the sink pad template then */
855       templ_caps = gst_pad_get_pad_template_caps (split->sink_pad);
856       ret =
857           gst_caps_intersect_full (combined, templ_caps,
858           GST_CAPS_INTERSECT_FIRST);
859       gst_caps_unref (templ_caps);
860       gst_caps_unref (combined);
861 
862       GST_LOG_OBJECT (split, "Returning sink pad caps %" GST_PTR_FORMAT, ret);
863 
864       gst_query_set_caps_result (query, ret);
865       result = !gst_caps_is_empty (ret);
866       gst_caps_unref (ret);
867       return result;
868     }
869     default:
870       return gst_pad_query_default (pad, parent, query);
871   }
872 }
873 
874 static gboolean
stereosplit_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)875 stereosplit_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
876 {
877   GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
878 
879   switch (GST_EVENT_TYPE (event)) {
880     case GST_EVENT_CAPS:
881     {
882       GstCaps *caps;
883 
884       gst_event_parse_caps (event, &caps);
885 
886       return stereosplit_set_output_caps (split, caps);
887     }
888     default:
889       return gst_pad_event_default (pad, parent, event);
890   }
891 }
892