/* * GStreamer * Copyright (C) 2009 Julien Isorce * Copyright (C) 2014 Jan Schmidt * Copyright (C) 2015 Matthew Waters * * 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-glviewconvert * * Convert stereoscopic video between different representations using fragment shaders. * * The element can use either property settings or caps negotiation to choose the * input and output formats to process. * * * Examples * |[ * gst-launch-1.0 videotestsrc ! glviewconvert ! glimagesink * ]| * |[ * gst-launch-1.0 videotestsrc pattern=checkers-1 ! glviewconvert input-mode-override=side-by-side ! glimagesink -v * ]| * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "gstglviewconvert.h" #define GST_CAT_DEFAULT gst_gl_view_convert_element_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); enum { PROP_0, PROP_INPUT_LAYOUT, PROP_INPUT_FLAGS, PROP_OUTPUT_LAYOUT, PROP_OUTPUT_FLAGS, PROP_OUTPUT_DOWNMIX_MODE }; #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_gl_view_convert_element_debug, "glview_convertelement", 0, "glview_convert element"); G_DEFINE_TYPE_WITH_CODE (GstGLViewConvertElement, gst_gl_view_convert_element, GST_TYPE_GL_FILTER, DEBUG_INIT); #define parent_class gst_gl_view_convert_element_parent_class static void gst_gl_view_convert_dispose (GObject * object); static void gst_gl_view_convert_element_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_gl_view_convert_element_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_gl_view_convert_element_stop (GstBaseTransform * bt); static gboolean gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps, GstCaps * outcaps); static GstCaps *gst_gl_view_convert_element_transform_internal_caps (GstGLFilter * filter, GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps); static GstCaps *gst_gl_view_convert_element_fixate_caps (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); static GstFlowReturn gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont, GstBuffer * input); static gboolean gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt, GstBuffer ** outbuf); static void gst_gl_view_convert_element_class_init (GstGLViewConvertElementClass * klass) { GObjectClass *gobject_class; GstElementClass *element_class; gobject_class = (GObjectClass *) klass; element_class = GST_ELEMENT_CLASS (klass); gobject_class->set_property = gst_gl_view_convert_element_set_property; gobject_class->get_property = gst_gl_view_convert_element_get_property; gobject_class->dispose = gst_gl_view_convert_dispose; gst_element_class_set_metadata (element_class, "OpenGL Multiview/3D conversion filter", "Filter", "Convert stereoscopic/multiview video formats", "Jan Schmidt \n" "Matthew Waters "); GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_view_convert_element_set_caps; GST_GL_FILTER_CLASS (klass)->transform_internal_caps = gst_gl_view_convert_element_transform_internal_caps; GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_view_convert_element_stop; GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = gst_gl_view_convert_element_fixate_caps; GST_BASE_TRANSFORM_CLASS (klass)->submit_input_buffer = gst_gl_view_convert_element_submit_input_buffer; GST_BASE_TRANSFORM_CLASS (klass)->generate_output = gst_gl_view_convert_element_generate_output_buffer; g_object_class_install_property (gobject_class, PROP_INPUT_LAYOUT, g_param_spec_enum ("input-mode-override", "Input Multiview Mode Override", "Override any input information about multiview layout", GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING, GST_VIDEO_MULTIVIEW_MODE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_FLAGS, g_param_spec_flags ("input-flags-override", "Input Multiview Flags Override", "Override any input information about multiview layout flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_OUTPUT_LAYOUT, g_param_spec_enum ("output-mode-override", "Output Multiview Mode Override", "Override automatic output mode selection for multiview layout", GST_TYPE_VIDEO_MULTIVIEW_MODE, GST_VIDEO_MULTIVIEW_MODE_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_OUTPUT_FLAGS, g_param_spec_flags ("output-flags-override", "Output Multiview Flags Override", "Override automatic negotiation for output multiview layout flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_OUTPUT_DOWNMIX_MODE, g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output", "Output anaglyph type to generate when downmixing to mono", GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE, DEFAULT_DOWNMIX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_gl_view_convert_element_init (GstGLViewConvertElement * convert) { convert->viewconvert = gst_gl_view_convert_new (); } static void gst_gl_view_convert_dispose (GObject * object) { GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object); if (convert->viewconvert) { gst_object_unref (convert->viewconvert); convert->viewconvert = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } static gboolean gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps, GstCaps * outcaps) { GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (filter); GstCapsFeatures *gl_features; gboolean ret; GST_DEBUG_OBJECT (filter, "incaps %" GST_PTR_FORMAT " outcaps %" GST_PTR_FORMAT, incaps, outcaps); /* The view_convert component needs RGBA caps */ incaps = gst_caps_copy (incaps); outcaps = gst_caps_copy (outcaps); gst_caps_set_simple (incaps, "format", G_TYPE_STRING, "RGBA", NULL); gl_features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); gst_caps_set_features (incaps, 0, gl_features); gst_caps_set_simple (outcaps, "format", G_TYPE_STRING, "RGBA", NULL); gl_features = gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); gst_caps_set_features (outcaps, 0, gl_features); ret = gst_gl_view_convert_set_caps (viewconvert_filter->viewconvert, incaps, outcaps); gst_caps_unref (incaps); gst_caps_unref (outcaps); return ret; } static GstCaps * gst_gl_view_convert_element_transform_internal_caps (GstGLFilter * filter, GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps) { GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (filter); GstCaps *result; GST_DEBUG_OBJECT (filter, "dir %s transforming caps: %" GST_PTR_FORMAT, direction == GST_PAD_SINK ? "sink" : "src", caps); result = gst_gl_view_convert_transform_caps (viewconvert_filter->viewconvert, direction, caps, NULL); GST_DEBUG_OBJECT (filter, "returning caps: %" GST_PTR_FORMAT, result); return result; } static GstCaps * gst_gl_view_convert_element_fixate_caps (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) { GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (trans); othercaps = gst_gl_view_convert_fixate_caps (viewconvert_filter->viewconvert, direction, caps, othercaps); if (gst_caps_is_empty (othercaps)) return othercaps; /* Let GLfilter do the rest */ return GST_BASE_TRANSFORM_CLASS (gst_gl_view_convert_element_parent_class)->fixate_caps (trans, direction, caps, othercaps); } static gboolean gst_gl_view_convert_element_stop (GstBaseTransform * bt) { GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (bt); gst_gl_view_convert_reset (viewconvert_filter->viewconvert); return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt); } static void gst_gl_view_convert_element_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object); switch (prop_id) { case PROP_INPUT_LAYOUT: case PROP_INPUT_FLAGS: g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name, value); gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert)); break; case PROP_OUTPUT_LAYOUT: case PROP_OUTPUT_FLAGS: g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name, value); gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert)); break; case PROP_OUTPUT_DOWNMIX_MODE: g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name, value); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_gl_view_convert_element_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object); switch (prop_id) { case PROP_INPUT_LAYOUT: case PROP_INPUT_FLAGS: case PROP_OUTPUT_LAYOUT: case PROP_OUTPUT_FLAGS: case PROP_OUTPUT_DOWNMIX_MODE: g_object_get_property (G_OBJECT (convert->viewconvert), pspec->name, value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstFlowReturn gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont, GstBuffer * input) { GstGLContext *context = GST_GL_BASE_FILTER (trans)->context; GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (trans); GstFlowReturn ret; ret = GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans, is_discont, input); if (ret != GST_FLOW_OK || trans->queued_buf == NULL) return ret; gst_gl_view_convert_set_context (viewconvert_filter->viewconvert, context); /* Takes the ref to the input buffer */ ret = gst_gl_view_convert_submit_input_buffer (viewconvert_filter->viewconvert, is_discont, input); trans->queued_buf = NULL; return ret; } static GstFlowReturn gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt, GstBuffer ** outbuf_ptr) { GstGLFilter *filter = GST_GL_FILTER (bt); GstGLViewConvertElement *viewconvert_filter = GST_GL_VIEW_CONVERT_ELEMENT (bt); GstFlowReturn ret = GST_FLOW_OK; ret = gst_gl_view_convert_get_output (viewconvert_filter->viewconvert, outbuf_ptr); if (ret != GST_FLOW_OK) { GST_ELEMENT_ERROR (filter, RESOURCE, SETTINGS, ("failed to perform view conversion on input buffer"), (NULL)); return ret; } return ret; }