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