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