mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 21:51:09 +00:00
bb0a83b018
MSVC warns about this because it's a C++ compiler, and this actually results in useful things such as the incorrect 'gboolean' return value for functions that return GstFlowReturn, so let's do explicit conversions to reduce the noise and increase its efficacy.
821 lines
24 KiB
C
821 lines
24 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2015 Jan Schmidt <jan@centricular.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:element-glstereosplit
|
|
*
|
|
* Receive a stereoscopic video stream and split into left/right
|
|
*
|
|
* <refsect2>
|
|
* <title>Examples</title>
|
|
* |[
|
|
* gst-launch-1.0 videotestsrc ! glstereosplit name=s ! queue ! glimagesink s. ! queue ! glimagesink
|
|
* ]|
|
|
* FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required.
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstglstereosplit.h"
|
|
|
|
#define GST_CAT_DEFAULT gst_gl_stereosplit_debug
|
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
|
|
|
#define SUPPORTED_GL_APIS GST_GL_API_GLES2 | GST_GL_API_OPENGL | GST_GL_API_OPENGL3
|
|
#define DEBUG_INIT \
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_stereosplit_debug, "glstereosplit", 0, "glstereosplit element");
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstGLStereoSplit, gst_gl_stereosplit,
|
|
GST_TYPE_ELEMENT, DEBUG_INIT);
|
|
|
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK, GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
|
|
);
|
|
|
|
static GstStaticPadTemplate src_left_template = GST_STATIC_PAD_TEMPLATE ("left",
|
|
GST_PAD_SRC, GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA"))
|
|
);
|
|
|
|
static GstStaticPadTemplate src_right_template =
|
|
GST_STATIC_PAD_TEMPLATE ("right",
|
|
GST_PAD_SRC, GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
|
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
|
|
"RGBA"))
|
|
);
|
|
|
|
static void stereosplit_reset (GstGLStereoSplit * self);
|
|
static void stereosplit_finalize (GstGLStereoSplit * self);
|
|
static void stereosplit_set_context (GstElement * element,
|
|
GstContext * context);
|
|
static GstFlowReturn stereosplit_chain (GstPad * pad, GstGLStereoSplit * split,
|
|
GstBuffer * buf);
|
|
static GstStateChangeReturn stereosplit_change_state (GstElement * element,
|
|
GstStateChange transition);
|
|
static gboolean stereosplit_sink_query (GstPad * pad, GstObject * parent,
|
|
GstQuery * query);
|
|
static gboolean stereosplit_sink_event (GstPad * pad, GstObject * parent,
|
|
GstEvent * event);
|
|
static gboolean stereosplit_src_query (GstPad * pad, GstObject * parent,
|
|
GstQuery * query);
|
|
static gboolean stereosplit_src_event (GstPad * pad, GstObject * parent,
|
|
GstEvent * event);
|
|
static gboolean ensure_context (GstGLStereoSplit * self);
|
|
|
|
static void
|
|
gst_gl_stereosplit_class_init (GstGLStereoSplitClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"GLStereoSplit", "Codec/Converter",
|
|
"Splits a stereoscopic stream into separate left/right streams",
|
|
"Jan Schmidt <jan@centricular.com>\n"
|
|
"Matthew Waters <matthew@centricular.com>");
|
|
|
|
gobject_class->finalize = (GObjectFinalizeFunc) (stereosplit_finalize);
|
|
|
|
element_class->change_state = stereosplit_change_state;
|
|
element_class->set_context = stereosplit_set_context;
|
|
|
|
gst_element_class_add_static_pad_template (element_class, &sink_template);
|
|
gst_element_class_add_static_pad_template (element_class, &src_left_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&src_right_template);
|
|
}
|
|
|
|
static void
|
|
gst_gl_stereosplit_init (GstGLStereoSplit * self)
|
|
{
|
|
GstPad *pad;
|
|
|
|
pad = self->sink_pad =
|
|
gst_pad_new_from_static_template (&sink_template, "sink");
|
|
|
|
gst_pad_set_chain_function (pad, (GstPadChainFunction) (stereosplit_chain));
|
|
gst_pad_set_query_function (pad, stereosplit_sink_query);
|
|
gst_pad_set_event_function (pad, stereosplit_sink_event);
|
|
|
|
gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
|
|
|
|
pad = self->left_pad =
|
|
gst_pad_new_from_static_template (&src_left_template, "left");
|
|
gst_pad_set_query_function (pad, stereosplit_src_query);
|
|
gst_pad_set_event_function (pad, stereosplit_src_event);
|
|
gst_element_add_pad (GST_ELEMENT (self), self->left_pad);
|
|
|
|
pad = self->right_pad =
|
|
gst_pad_new_from_static_template (&src_right_template, "right");
|
|
gst_pad_set_query_function (pad, stereosplit_src_query);
|
|
gst_pad_set_event_function (pad, stereosplit_src_event);
|
|
gst_element_add_pad (GST_ELEMENT (self), self->right_pad);
|
|
|
|
self->viewconvert = gst_gl_view_convert_new ();
|
|
}
|
|
|
|
static void
|
|
stereosplit_reset (GstGLStereoSplit * self)
|
|
{
|
|
if (self->context)
|
|
gst_object_replace ((GstObject **) & self->context, NULL);
|
|
if (self->display)
|
|
gst_object_replace ((GstObject **) & self->display, NULL);
|
|
}
|
|
|
|
static void
|
|
stereosplit_finalize (GstGLStereoSplit * self)
|
|
{
|
|
GObjectClass *klass = G_OBJECT_CLASS (gst_gl_stereosplit_parent_class);
|
|
|
|
if (self->viewconvert)
|
|
gst_object_replace ((GstObject **) & self->viewconvert, NULL);
|
|
|
|
klass->finalize ((GObject *) (self));
|
|
}
|
|
|
|
static void
|
|
stereosplit_set_context (GstElement * element, GstContext * context)
|
|
{
|
|
GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
|
|
|
|
gst_gl_handle_set_context (element, context, &stereosplit->display,
|
|
&stereosplit->other_context);
|
|
|
|
if (stereosplit->display)
|
|
gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
|
|
|
|
GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->set_context (element,
|
|
context);
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
stereosplit_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstGLStereoSplit *stereosplit = GST_GL_STEREOSPLIT (element);
|
|
GstStateChangeReturn result;
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
if (!gst_gl_ensure_element_data (element, &stereosplit->display,
|
|
&stereosplit->other_context))
|
|
return GST_STATE_CHANGE_FAILURE;
|
|
|
|
gst_gl_display_filter_gl_api (stereosplit->display, SUPPORTED_GL_APIS);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
result =
|
|
GST_ELEMENT_CLASS (gst_gl_stereosplit_parent_class)->change_state
|
|
(element, transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
if (stereosplit->other_context) {
|
|
gst_object_unref (stereosplit->other_context);
|
|
stereosplit->other_context = NULL;
|
|
}
|
|
|
|
if (stereosplit->display) {
|
|
gst_object_unref (stereosplit->display);
|
|
stereosplit->display = NULL;
|
|
}
|
|
break;
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
stereosplit_reset (stereosplit);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static GstCaps *
|
|
stereosplit_transform_caps (GstGLStereoSplit * self, GstPadDirection direction,
|
|
GstCaps * caps, GstCaps * filter)
|
|
{
|
|
GstCaps *next_caps;
|
|
|
|
/* FIXME: Is this the right way to ensure a context here ? */
|
|
if (!ensure_context (self))
|
|
return NULL;
|
|
|
|
next_caps =
|
|
gst_gl_view_convert_transform_caps (self->viewconvert, direction, caps,
|
|
NULL);
|
|
|
|
return next_caps;
|
|
}
|
|
|
|
static GstCaps *
|
|
strip_mview_fields (GstCaps * incaps, GstVideoMultiviewFlags keep_flags)
|
|
{
|
|
GstCaps *outcaps = gst_caps_make_writable (incaps);
|
|
|
|
gint i, n;
|
|
|
|
n = gst_caps_get_size (outcaps);
|
|
for (i = 0; i < n; i++) {
|
|
GstStructure *st = gst_caps_get_structure (outcaps, i);
|
|
GstVideoMultiviewFlags flags, mask;
|
|
|
|
gst_structure_remove_field (st, "multiview-mode");
|
|
if (gst_structure_get_flagset (st, "multiview-flags", (guint*) &flags,
|
|
(guint*) &mask)) {
|
|
flags &= keep_flags;
|
|
mask = keep_flags;
|
|
gst_structure_set (st, "multiview-flags",
|
|
GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, flags, mask, NULL);
|
|
}
|
|
}
|
|
|
|
return outcaps;
|
|
}
|
|
|
|
static gboolean stereosplit_do_bufferpool (GstGLStereoSplit * self,
|
|
GstCaps * caps);
|
|
|
|
static GstCaps *
|
|
stereosplit_get_src_caps (GstGLStereoSplit * split,
|
|
GstPad * pad, GstVideoMultiviewMode preferred_mode)
|
|
{
|
|
GstCaps *outcaps, *tmp, *templ_caps;
|
|
GValue item = G_VALUE_INIT, list = G_VALUE_INIT;
|
|
|
|
/* Get the template format */
|
|
templ_caps = gst_pad_get_pad_template_caps (pad);
|
|
|
|
/* And limit down to the preferred mode or mono */
|
|
templ_caps = gst_caps_make_writable (templ_caps);
|
|
|
|
g_value_init (&item, G_TYPE_STRING);
|
|
g_value_init (&list, GST_TYPE_LIST);
|
|
g_value_set_static_string (&item,
|
|
gst_video_multiview_mode_to_caps_string (preferred_mode));
|
|
gst_value_list_append_value (&list, &item);
|
|
g_value_set_static_string (&item,
|
|
gst_video_multiview_mode_to_caps_string (GST_VIDEO_MULTIVIEW_MODE_MONO));
|
|
gst_value_list_append_value (&list, &item);
|
|
|
|
gst_caps_set_value (templ_caps, "multiview-mode", &list);
|
|
|
|
g_value_unset (&list);
|
|
g_value_unset (&item);
|
|
|
|
/* And intersect with the peer */
|
|
if ((tmp = gst_pad_peer_query_caps (pad, NULL)) == NULL) {
|
|
gst_caps_unref (templ_caps);
|
|
return NULL;
|
|
}
|
|
|
|
outcaps = gst_caps_intersect_full (tmp, templ_caps, GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (tmp);
|
|
gst_caps_unref (templ_caps);
|
|
|
|
GST_DEBUG_OBJECT (split, "Src pad %" GST_PTR_FORMAT " caps %" GST_PTR_FORMAT,
|
|
pad, outcaps);
|
|
return outcaps;
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_set_output_caps (GstGLStereoSplit * split, GstCaps * sinkcaps)
|
|
{
|
|
GstCaps *left = NULL, *right = NULL, *tridcaps = NULL;
|
|
GstCaps *tmp, *combined;
|
|
gboolean res = FALSE;
|
|
|
|
/* Choose some preferred output caps.
|
|
* Keep input width/height and PAR, preserve preferred output
|
|
* multiview flags for flipping/flopping if any, and set each
|
|
* left right pad to either left/mono and right/mono, as they prefer
|
|
*/
|
|
|
|
/* Calculate what downstream can collectively support */
|
|
left =
|
|
stereosplit_get_src_caps (split, split->left_pad,
|
|
GST_VIDEO_MULTIVIEW_MODE_LEFT);
|
|
if (left == NULL)
|
|
goto fail;
|
|
right =
|
|
stereosplit_get_src_caps (split, split->right_pad,
|
|
GST_VIDEO_MULTIVIEW_MODE_RIGHT);
|
|
if (right == NULL)
|
|
goto fail;
|
|
|
|
tridcaps = stereosplit_transform_caps (split, GST_PAD_SINK, sinkcaps, NULL);
|
|
|
|
if (!tridcaps || gst_caps_is_empty (tridcaps)) {
|
|
GST_ERROR_OBJECT (split,
|
|
"Failed to transform input caps %" GST_PTR_FORMAT, sinkcaps);
|
|
goto fail;
|
|
}
|
|
|
|
/* Preserve downstream preferred flipping/flopping */
|
|
tmp =
|
|
strip_mview_fields (gst_caps_ref (left),
|
|
GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED |
|
|
GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED);
|
|
combined = gst_caps_intersect (tridcaps, tmp);
|
|
gst_caps_unref (tridcaps);
|
|
gst_caps_unref (tmp);
|
|
tridcaps = combined;
|
|
|
|
tmp =
|
|
strip_mview_fields (gst_caps_ref (right),
|
|
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED |
|
|
GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED);
|
|
combined = gst_caps_intersect (tridcaps, tmp);
|
|
gst_caps_unref (tridcaps);
|
|
gst_caps_unref (tmp);
|
|
tridcaps = combined;
|
|
|
|
if (G_UNLIKELY (gst_caps_is_empty (tridcaps))) {
|
|
gst_caps_unref (tridcaps);
|
|
goto fail;
|
|
}
|
|
|
|
/* Now generate the version for each output pad */
|
|
GST_DEBUG_OBJECT (split, "Attempting to set output caps %" GST_PTR_FORMAT,
|
|
tridcaps);
|
|
tmp = gst_caps_intersect (tridcaps, left);
|
|
gst_caps_unref (left);
|
|
left = tmp;
|
|
left = gst_caps_fixate (left);
|
|
if (!gst_pad_set_caps (split->left_pad, left)) {
|
|
GST_ERROR_OBJECT (split,
|
|
"Failed to set left output caps %" GST_PTR_FORMAT, left);
|
|
goto fail;
|
|
}
|
|
|
|
tmp = gst_caps_intersect (tridcaps, right);
|
|
gst_caps_unref (right);
|
|
right = tmp;
|
|
right = gst_caps_fixate (right);
|
|
if (!gst_pad_set_caps (split->right_pad, right)) {
|
|
GST_ERROR_OBJECT (split,
|
|
"Failed to set right output caps %" GST_PTR_FORMAT, right);
|
|
goto fail;
|
|
}
|
|
|
|
gst_gl_view_convert_set_context (split->viewconvert, split->context);
|
|
|
|
tridcaps = gst_caps_make_writable (tridcaps);
|
|
gst_caps_set_simple (tridcaps, "multiview-mode", G_TYPE_STRING,
|
|
"separated", "views", G_TYPE_INT, 2, NULL);
|
|
tridcaps = gst_caps_fixate (tridcaps);
|
|
|
|
if (!gst_gl_view_convert_set_caps (split->viewconvert, sinkcaps, tridcaps)) {
|
|
GST_ERROR_OBJECT (split, "Failed to set caps on converter");
|
|
goto fail;
|
|
}
|
|
|
|
/* FIXME: Provide left and right caps to do_bufferpool */
|
|
stereosplit_do_bufferpool (split, left);
|
|
|
|
res = TRUE;
|
|
|
|
fail:
|
|
if (left)
|
|
gst_caps_unref (left);
|
|
if (right)
|
|
gst_caps_unref (right);
|
|
if (tridcaps)
|
|
gst_caps_unref (tridcaps);
|
|
return res;
|
|
}
|
|
|
|
static gboolean
|
|
_find_local_gl_context (GstGLStereoSplit * split)
|
|
{
|
|
GstQuery *query;
|
|
GstContext *context;
|
|
const GstStructure *s;
|
|
|
|
if (split->context)
|
|
return TRUE;
|
|
|
|
query = gst_query_new_context ("gst.gl.local_context");
|
|
if (!split->context
|
|
&& gst_gl_run_query (GST_ELEMENT (split), query, GST_PAD_SRC)) {
|
|
gst_query_parse_context (query, &context);
|
|
if (context) {
|
|
s = gst_context_get_structure (context);
|
|
gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &split->context,
|
|
NULL);
|
|
}
|
|
}
|
|
if (!split->context
|
|
&& gst_gl_run_query (GST_ELEMENT (split), query, GST_PAD_SINK)) {
|
|
gst_query_parse_context (query, &context);
|
|
if (context) {
|
|
s = gst_context_get_structure (context);
|
|
gst_structure_get (s, "context", GST_GL_TYPE_CONTEXT, &split->context,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (split, "found local context %p", split->context);
|
|
|
|
gst_query_unref (query);
|
|
|
|
if (split->context)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
ensure_context (GstGLStereoSplit * self)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
|
|
return FALSE;
|
|
|
|
gst_gl_display_filter_gl_api (self->display, SUPPORTED_GL_APIS);
|
|
|
|
_find_local_gl_context (self);
|
|
|
|
if (!self->context) {
|
|
GST_OBJECT_LOCK (self->display);
|
|
do {
|
|
if (self->context)
|
|
gst_object_unref (self->context);
|
|
/* just get a GL context. we don't care */
|
|
self->context =
|
|
gst_gl_display_get_gl_context_for_thread (self->display, NULL);
|
|
if (!self->context) {
|
|
if (!gst_gl_display_create_context (self->display, self->other_context,
|
|
&self->context, &error)) {
|
|
GST_OBJECT_UNLOCK (self->display);
|
|
goto context_error;
|
|
}
|
|
}
|
|
} while (!gst_gl_display_add_context (self->display, self->context));
|
|
GST_OBJECT_UNLOCK (self->display);
|
|
}
|
|
|
|
{
|
|
GstGLAPI current_gl_api = gst_gl_context_get_gl_api (self->context);
|
|
if ((current_gl_api & (SUPPORTED_GL_APIS)) == 0)
|
|
goto unsupported_gl_api;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
unsupported_gl_api:
|
|
{
|
|
GstGLAPI gl_api = gst_gl_context_get_gl_api (self->context);
|
|
gchar *gl_api_str = gst_gl_api_to_string (gl_api);
|
|
gchar *supported_gl_api_str = gst_gl_api_to_string (SUPPORTED_GL_APIS);
|
|
GST_ELEMENT_ERROR (self, RESOURCE, BUSY,
|
|
("GL API's not compatible context: %s supported: %s", gl_api_str,
|
|
supported_gl_api_str), (NULL));
|
|
|
|
g_free (supported_gl_api_str);
|
|
g_free (gl_api_str);
|
|
return FALSE;
|
|
}
|
|
context_error:
|
|
{
|
|
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND, ("%s", error->message),
|
|
(NULL));
|
|
g_clear_error (&error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_decide_allocation (GstGLStereoSplit * self, GstQuery * query)
|
|
{
|
|
if (!ensure_context (self))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_propose_allocation (GstGLStereoSplit * self, GstQuery * query)
|
|
{
|
|
|
|
if (!gst_gl_ensure_element_data (self, &self->display, &self->other_context))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_do_bufferpool (GstGLStereoSplit * self, GstCaps * caps)
|
|
{
|
|
GstQuery *query;
|
|
|
|
query = gst_query_new_allocation (caps, TRUE);
|
|
if (!gst_pad_peer_query (self->left_pad, query)) {
|
|
if (!gst_pad_peer_query (self->right_pad, query)) {
|
|
GST_DEBUG_OBJECT (self, "peer ALLOCATION query failed on both src pads");
|
|
}
|
|
}
|
|
|
|
if (!stereosplit_decide_allocation (self, query)) {
|
|
gst_query_unref (query);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_query_unref (query);
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
stereosplit_chain (GstPad * pad, GstGLStereoSplit * split, GstBuffer * buf)
|
|
{
|
|
GstBuffer *left, *right;
|
|
GstBuffer *split_buffer = NULL;
|
|
GstFlowReturn ret;
|
|
gint i, n_planes;
|
|
|
|
n_planes = GST_VIDEO_INFO_N_PLANES (&split->viewconvert->out_info);
|
|
|
|
GST_LOG_OBJECT (split, "chaining buffer %" GST_PTR_FORMAT, buf);
|
|
|
|
if (gst_gl_view_convert_submit_input_buffer (split->viewconvert,
|
|
GST_BUFFER_IS_DISCONT (buf), buf) != GST_FLOW_OK) {
|
|
GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
|
|
"Failed to 3d convert buffer"),
|
|
("Could not get submit input buffer"));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
ret = gst_gl_view_convert_get_output (split->viewconvert, &split_buffer);
|
|
if (ret != GST_FLOW_OK) {
|
|
GST_ELEMENT_ERROR (split, RESOURCE, NOT_FOUND, ("%s",
|
|
"Failed to 3d convert buffer"), ("Could not get output buffer"));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
if (split_buffer == NULL)
|
|
return GST_FLOW_OK; /* Need another input buffer */
|
|
|
|
left = gst_buffer_new ();
|
|
gst_buffer_copy_into (left, buf,
|
|
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
|
|
GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
|
|
|
|
gst_buffer_add_parent_buffer_meta (left, split_buffer);
|
|
|
|
for (i = 0; i < n_planes; i++) {
|
|
GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
|
|
gst_buffer_append_memory (left, mem);
|
|
}
|
|
|
|
ret = gst_pad_push (split->left_pad, gst_buffer_ref (left));
|
|
/* Allow unlinked on the first pad - as long as the 2nd isn't unlinked */
|
|
gst_buffer_unref (left);
|
|
if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)) {
|
|
gst_buffer_unref (split_buffer);
|
|
return ret;
|
|
}
|
|
|
|
right = gst_buffer_new ();
|
|
gst_buffer_copy_into (right, buf,
|
|
GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
|
|
GST_BUFFER_FLAG_UNSET (left, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE);
|
|
gst_buffer_add_parent_buffer_meta (right, split_buffer);
|
|
for (i = n_planes; i < n_planes * 2; i++) {
|
|
GstMemory *mem = gst_buffer_get_memory (split_buffer, i);
|
|
gst_buffer_append_memory (right, mem);
|
|
}
|
|
|
|
ret = gst_pad_push (split->right_pad, gst_buffer_ref (right));
|
|
gst_buffer_unref (right);
|
|
gst_buffer_unref (split_buffer);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
|
{
|
|
GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_CONTEXT:
|
|
{
|
|
const gchar *context_type;
|
|
GstContext *context, *old_context;
|
|
gboolean ret;
|
|
|
|
ret = gst_gl_handle_context_query ((GstElement *) split, query,
|
|
&split->display, &split->other_context);
|
|
if (split->display)
|
|
gst_gl_display_filter_gl_api (split->display, SUPPORTED_GL_APIS);
|
|
gst_query_parse_context_type (query, &context_type);
|
|
|
|
if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
|
|
GstStructure *s;
|
|
|
|
gst_query_parse_context (query, &old_context);
|
|
|
|
if (old_context)
|
|
context = gst_context_copy (old_context);
|
|
else
|
|
context = gst_context_new ("gst.gl.local_context", FALSE);
|
|
|
|
s = gst_context_writable_structure (context);
|
|
gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, split->context,
|
|
NULL);
|
|
gst_query_set_context (query, context);
|
|
gst_context_unref (context);
|
|
|
|
ret = split->context != NULL;
|
|
}
|
|
GST_LOG_OBJECT (split, "context query of type %s %i", context_type, ret);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return gst_pad_query_default (pad, parent, query);
|
|
}
|
|
/* FIXME: Handle caps query */
|
|
default:
|
|
return gst_pad_query_default (pad, parent, query);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
{
|
|
return gst_pad_event_default (pad, parent, event);
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
|
{
|
|
GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
|
|
|
|
GST_DEBUG_OBJECT (split, "sink query %s",
|
|
gst_query_type_get_name (GST_QUERY_TYPE (query)));
|
|
|
|
switch (GST_QUERY_TYPE (query)) {
|
|
case GST_QUERY_CONTEXT:
|
|
{
|
|
const gchar *context_type;
|
|
GstContext *context, *old_context;
|
|
gboolean ret;
|
|
|
|
ret = gst_gl_handle_context_query ((GstElement *) split, query,
|
|
&split->display, &split->other_context);
|
|
if (split->display)
|
|
gst_gl_display_filter_gl_api (split->display, SUPPORTED_GL_APIS);
|
|
gst_query_parse_context_type (query, &context_type);
|
|
|
|
if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
|
|
GstStructure *s;
|
|
|
|
gst_query_parse_context (query, &old_context);
|
|
|
|
if (old_context)
|
|
context = gst_context_copy (old_context);
|
|
else
|
|
context = gst_context_new ("gst.gl.local_context", FALSE);
|
|
|
|
s = gst_context_writable_structure (context);
|
|
gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, split->context,
|
|
NULL);
|
|
gst_query_set_context (query, context);
|
|
gst_context_unref (context);
|
|
|
|
ret = split->context != NULL;
|
|
}
|
|
GST_LOG_OBJECT (split, "context query of type %s %i", context_type, ret);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return gst_pad_query_default (pad, parent, query);
|
|
}
|
|
case GST_QUERY_ALLOCATION:
|
|
{
|
|
return stereosplit_propose_allocation (split, query);
|
|
}
|
|
case GST_QUERY_ACCEPT_CAPS:
|
|
{
|
|
GstCaps *possible, *caps;
|
|
gboolean allowed;
|
|
|
|
gst_query_parse_accept_caps (query, &caps);
|
|
|
|
if (!(possible = gst_pad_query_caps (split->sink_pad, caps)))
|
|
return FALSE;
|
|
|
|
allowed = gst_caps_is_subset (caps, possible);
|
|
gst_caps_unref (possible);
|
|
|
|
gst_query_set_accept_caps_result (query, allowed);
|
|
return allowed;
|
|
}
|
|
case GST_QUERY_CAPS:
|
|
{
|
|
GstCaps *filter, *left, *right, *combined, *ret, *templ_caps;
|
|
|
|
gst_query_parse_caps (query, &filter);
|
|
|
|
/* Calculate what downstream can collectively support */
|
|
if (!(left = gst_pad_peer_query_caps (split->left_pad, NULL)))
|
|
return FALSE;
|
|
if (!(right = gst_pad_peer_query_caps (split->right_pad, NULL)))
|
|
return FALSE;
|
|
|
|
/* Strip out multiview mode and flags that might break the
|
|
* intersection, since we can convert.
|
|
* We could keep downstream preferred flip/flopping and list
|
|
* separated as preferred in the future which might
|
|
* theoretically allow us an easier conversion, but it's not essential
|
|
*/
|
|
left = strip_mview_fields (left, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
|
|
right = strip_mview_fields (right, GST_VIDEO_MULTIVIEW_FLAGS_NONE);
|
|
|
|
combined = gst_caps_intersect (left, right);
|
|
gst_caps_unref (left);
|
|
gst_caps_unref (right);
|
|
|
|
/* Intersect peer caps with our template formats */
|
|
templ_caps = gst_pad_get_pad_template_caps (split->left_pad);
|
|
ret =
|
|
gst_caps_intersect_full (combined, templ_caps,
|
|
GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (templ_caps);
|
|
|
|
gst_caps_unref (combined);
|
|
combined = ret;
|
|
|
|
if (!combined || gst_caps_is_empty (combined)) {
|
|
gst_caps_unref (combined);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Convert from the src pad caps to input formats we support */
|
|
ret = stereosplit_transform_caps (split, GST_PAD_SRC, combined, filter);
|
|
gst_caps_unref (combined);
|
|
combined = ret;
|
|
|
|
/* Intersect with the sink pad template then */
|
|
templ_caps = gst_pad_get_pad_template_caps (split->sink_pad);
|
|
ret =
|
|
gst_caps_intersect_full (combined, templ_caps,
|
|
GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (templ_caps);
|
|
|
|
GST_LOG_OBJECT (split, "Returning sink pad caps %" GST_PTR_FORMAT, ret);
|
|
|
|
gst_query_set_caps_result (query, ret);
|
|
return !gst_caps_is_empty (ret);
|
|
}
|
|
default:
|
|
return gst_pad_query_default (pad, parent, query);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
stereosplit_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|
{
|
|
GstGLStereoSplit *split = GST_GL_STEREOSPLIT (parent);
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_CAPS:
|
|
{
|
|
GstCaps *caps;
|
|
|
|
gst_event_parse_caps (event, &caps);
|
|
|
|
return stereosplit_set_output_caps (split, caps);
|
|
}
|
|
default:
|
|
return gst_pad_event_default (pad, parent, event);
|
|
}
|
|
}
|