1 /*
2 * GStreamer
3 * Copyright (C) 2009 Julien Isorce <julien.isorce@mail.com>
4 * Copyright (C) 2014 Jan Schmidt <jan@centricular.com>
5 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 /**
24 * SECTION:element-glviewconvert
25 * @title: glviewconvert
26 *
27 * Convert stereoscopic video between different representations using fragment shaders.
28 *
29 * The element can use either property settings or caps negotiation to choose the
30 * input and output formats to process.
31 *
32 * ## Examples
33 * |[
34 * gst-launch-1.0 videotestsrc ! glupload ! glviewconvert ! glimagesink
35 * ]| Simple placebo example demonstrating identity passthrough of mono video
36 * |[
37 * gst-launch-1.0 videotestsrc pattern=checkers-1 ! glupload ! \
38 * glviewconvert input-mode-override=side-by-side ! glimagesink -v
39 * ]| Force re-interpretation of the input checkers pattern as a side-by-side stereoscopic
40 * image and display in glimagesink.
41 * FBO (Frame Buffer Object) and GLSL (OpenGL Shading Language) are required.
42 *
43 */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #include <gst/base/gstbasetransform.h>
50
51 #include "gstglelements.h"
52 #include "gstglviewconvert.h"
53
54 #define GST_CAT_DEFAULT gst_gl_view_convert_element_debug
55 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
56
57 enum
58 {
59 PROP_0,
60 PROP_INPUT_LAYOUT,
61 PROP_INPUT_FLAGS,
62 PROP_OUTPUT_LAYOUT,
63 PROP_OUTPUT_FLAGS,
64 PROP_OUTPUT_DOWNMIX_MODE
65 };
66
67 #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
68
69 #define DEBUG_INIT \
70 GST_DEBUG_CATEGORY_INIT (gst_gl_view_convert_element_debug, "glview_convertelement", 0, "glview_convert element");
71
72 #define parent_class gst_gl_view_convert_element_parent_class
73 G_DEFINE_TYPE_WITH_CODE (GstGLViewConvertElement, gst_gl_view_convert_element,
74 GST_TYPE_GL_FILTER, DEBUG_INIT);
75 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (glviewconvert, "glviewconvert",
76 GST_RANK_NONE, GST_TYPE_GL_VIEW_CONVERT_ELEMENT, gl_element_init (plugin));
77
78 static void gst_gl_view_convert_dispose (GObject * object);
79 static void gst_gl_view_convert_element_set_property (GObject * object,
80 guint prop_id, const GValue * value, GParamSpec * pspec);
81 static void gst_gl_view_convert_element_get_property (GObject * object,
82 guint prop_id, GValue * value, GParamSpec * pspec);
83
84 static gboolean gst_gl_view_convert_element_stop (GstBaseTransform * bt);
85 static gboolean
86 gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps,
87 GstCaps * outcaps);
88 static GstCaps *gst_gl_view_convert_element_transform_internal_caps (GstGLFilter
89 * filter, GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
90 static GstCaps *gst_gl_view_convert_element_fixate_caps (GstBaseTransform *
91 trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
92 static GstFlowReturn
93 gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans,
94 gboolean is_discont, GstBuffer * input);
95 static GstFlowReturn
96 gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt,
97 GstBuffer ** outbuf);
98
99 static void
gst_gl_view_convert_element_class_init(GstGLViewConvertElementClass * klass)100 gst_gl_view_convert_element_class_init (GstGLViewConvertElementClass * klass)
101 {
102 GObjectClass *gobject_class;
103 GstElementClass *element_class;
104
105 gobject_class = (GObjectClass *) klass;
106 element_class = GST_ELEMENT_CLASS (klass);
107
108 gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
109
110 gobject_class->set_property = gst_gl_view_convert_element_set_property;
111 gobject_class->get_property = gst_gl_view_convert_element_get_property;
112 gobject_class->dispose = gst_gl_view_convert_dispose;
113
114 gst_element_class_set_metadata (element_class,
115 "OpenGL Multiview/3D conversion filter", "Filter",
116 "Convert stereoscopic/multiview video formats",
117 "Jan Schmidt <jan@centricular.com>\n"
118 "Matthew Waters <matthew@centricular.com>");
119
120 GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_view_convert_element_set_caps;
121
122 GST_GL_FILTER_CLASS (klass)->transform_internal_caps =
123 gst_gl_view_convert_element_transform_internal_caps;
124 GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_view_convert_element_stop;
125 GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
126 gst_gl_view_convert_element_fixate_caps;
127 GST_BASE_TRANSFORM_CLASS (klass)->submit_input_buffer =
128 gst_gl_view_convert_element_submit_input_buffer;
129 GST_BASE_TRANSFORM_CLASS (klass)->generate_output =
130 gst_gl_view_convert_element_generate_output_buffer;
131
132 g_object_class_install_property (gobject_class, PROP_INPUT_LAYOUT,
133 g_param_spec_enum ("input-mode-override",
134 "Input Multiview Mode Override",
135 "Override any input information about multiview layout",
136 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
137 GST_VIDEO_MULTIVIEW_MODE_NONE,
138 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139 g_object_class_install_property (gobject_class, PROP_INPUT_FLAGS,
140 g_param_spec_flags ("input-flags-override",
141 "Input Multiview Flags Override",
142 "Override any input information about multiview layout flags",
143 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
144 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145 g_object_class_install_property (gobject_class, PROP_OUTPUT_LAYOUT,
146 g_param_spec_enum ("output-mode-override",
147 "Output Multiview Mode Override",
148 "Override automatic output mode selection for multiview layout",
149 GST_TYPE_VIDEO_MULTIVIEW_MODE, GST_VIDEO_MULTIVIEW_MODE_NONE,
150 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 g_object_class_install_property (gobject_class, PROP_OUTPUT_FLAGS,
152 g_param_spec_flags ("output-flags-override",
153 "Output Multiview Flags Override",
154 "Override automatic negotiation for output multiview layout flags",
155 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
156 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
157 g_object_class_install_property (gobject_class, PROP_OUTPUT_DOWNMIX_MODE,
158 g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output",
159 "Output anaglyph type to generate when downmixing to mono",
160 GST_TYPE_GL_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
161 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
162 }
163
164 static void
gst_gl_view_convert_element_init(GstGLViewConvertElement * convert)165 gst_gl_view_convert_element_init (GstGLViewConvertElement * convert)
166 {
167 convert->viewconvert = gst_gl_view_convert_new ();
168 }
169
170 static void
gst_gl_view_convert_dispose(GObject * object)171 gst_gl_view_convert_dispose (GObject * object)
172 {
173 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
174
175 if (convert->viewconvert) {
176 gst_object_unref (convert->viewconvert);
177 convert->viewconvert = NULL;
178 }
179 G_OBJECT_CLASS (parent_class)->dispose (object);
180 }
181
182 static gboolean
gst_gl_view_convert_element_set_caps(GstGLFilter * filter,GstCaps * incaps,GstCaps * outcaps)183 gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps,
184 GstCaps * outcaps)
185 {
186 GstGLViewConvertElement *viewconvert_filter =
187 GST_GL_VIEW_CONVERT_ELEMENT (filter);
188 GstCapsFeatures *gl_features;
189 gboolean ret;
190
191 GST_DEBUG_OBJECT (filter, "incaps %" GST_PTR_FORMAT
192 " outcaps %" GST_PTR_FORMAT, incaps, outcaps);
193 /* The view_convert component needs RGBA caps */
194 incaps = gst_caps_copy (incaps);
195 outcaps = gst_caps_copy (outcaps);
196
197 gst_caps_set_simple (incaps, "format", G_TYPE_STRING, "RGBA", NULL);
198 gl_features =
199 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
200 gst_caps_set_features (incaps, 0, gl_features);
201
202 gst_caps_set_simple (outcaps, "format", G_TYPE_STRING, "RGBA", NULL);
203 gl_features =
204 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
205 gst_caps_set_features (outcaps, 0, gl_features);
206
207 ret = gst_gl_view_convert_set_caps (viewconvert_filter->viewconvert,
208 incaps, outcaps);
209
210 gst_caps_unref (incaps);
211 gst_caps_unref (outcaps);
212
213 return ret;
214 }
215
216 static GstCaps *
gst_gl_view_convert_element_transform_internal_caps(GstGLFilter * filter,GstPadDirection direction,GstCaps * caps,GstCaps * filter_caps)217 gst_gl_view_convert_element_transform_internal_caps (GstGLFilter * filter,
218 GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
219 {
220 GstGLViewConvertElement *viewconvert_filter =
221 GST_GL_VIEW_CONVERT_ELEMENT (filter);
222 GstCaps *result;
223
224 GST_DEBUG_OBJECT (filter, "dir %s transforming caps: %" GST_PTR_FORMAT,
225 direction == GST_PAD_SINK ? "sink" : "src", caps);
226
227 result =
228 gst_gl_view_convert_transform_caps (viewconvert_filter->viewconvert,
229 direction, caps, NULL);
230
231 GST_DEBUG_OBJECT (filter, "returning caps: %" GST_PTR_FORMAT, result);
232
233 return result;
234 }
235
236 static GstCaps *
gst_gl_view_convert_element_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)237 gst_gl_view_convert_element_fixate_caps (GstBaseTransform * trans,
238 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
239 {
240 GstGLViewConvertElement *viewconvert_filter =
241 GST_GL_VIEW_CONVERT_ELEMENT (trans);
242
243 othercaps = gst_gl_view_convert_fixate_caps (viewconvert_filter->viewconvert,
244 direction, caps, othercaps);
245
246 if (gst_caps_is_empty (othercaps))
247 return othercaps;
248
249 /* Let GLfilter do the rest */
250 return
251 GST_BASE_TRANSFORM_CLASS
252 (gst_gl_view_convert_element_parent_class)->fixate_caps (trans, direction,
253 caps, othercaps);
254 }
255
256 static gboolean
gst_gl_view_convert_element_stop(GstBaseTransform * bt)257 gst_gl_view_convert_element_stop (GstBaseTransform * bt)
258 {
259 GstGLViewConvertElement *viewconvert_filter =
260 GST_GL_VIEW_CONVERT_ELEMENT (bt);
261
262 gst_gl_view_convert_reset (viewconvert_filter->viewconvert);
263
264 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
265 }
266
267 static void
gst_gl_view_convert_element_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)268 gst_gl_view_convert_element_set_property (GObject * object, guint prop_id,
269 const GValue * value, GParamSpec * pspec)
270 {
271 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
272
273 switch (prop_id) {
274 case PROP_INPUT_LAYOUT:
275 case PROP_INPUT_FLAGS:
276 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
277 value);
278 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert));
279 break;
280 case PROP_OUTPUT_LAYOUT:
281 case PROP_OUTPUT_FLAGS:
282 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
283 value);
284 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert));
285 break;
286 case PROP_OUTPUT_DOWNMIX_MODE:
287 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
288 value);
289 break;
290 default:
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292 break;
293 }
294 }
295
296 static void
gst_gl_view_convert_element_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)297 gst_gl_view_convert_element_get_property (GObject * object, guint prop_id,
298 GValue * value, GParamSpec * pspec)
299 {
300 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
301
302 switch (prop_id) {
303 case PROP_INPUT_LAYOUT:
304 case PROP_INPUT_FLAGS:
305 case PROP_OUTPUT_LAYOUT:
306 case PROP_OUTPUT_FLAGS:
307 case PROP_OUTPUT_DOWNMIX_MODE:
308 g_object_get_property (G_OBJECT (convert->viewconvert), pspec->name,
309 value);
310 break;
311 default:
312 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313 break;
314 }
315 }
316
317 static GstFlowReturn
gst_gl_view_convert_element_submit_input_buffer(GstBaseTransform * trans,gboolean is_discont,GstBuffer * input)318 gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans,
319 gboolean is_discont, GstBuffer * input)
320 {
321 GstGLContext *context = GST_GL_BASE_FILTER (trans)->context;
322 GstGLViewConvertElement *viewconvert_filter =
323 GST_GL_VIEW_CONVERT_ELEMENT (trans);
324 GstFlowReturn ret;
325
326 ret =
327 GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans,
328 is_discont, input);
329 if (ret != GST_FLOW_OK || trans->queued_buf == NULL)
330 return ret;
331
332 gst_gl_view_convert_set_context (viewconvert_filter->viewconvert, context);
333
334 /* Takes the ref to the input buffer */
335 ret =
336 gst_gl_view_convert_submit_input_buffer (viewconvert_filter->viewconvert,
337 is_discont, input);
338 trans->queued_buf = NULL;
339
340 return ret;
341 }
342
343 static GstFlowReturn
gst_gl_view_convert_element_generate_output_buffer(GstBaseTransform * bt,GstBuffer ** outbuf_ptr)344 gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt,
345 GstBuffer ** outbuf_ptr)
346 {
347 GstGLFilter *filter = GST_GL_FILTER (bt);
348 GstGLViewConvertElement *viewconvert_filter =
349 GST_GL_VIEW_CONVERT_ELEMENT (bt);
350 GstFlowReturn ret = GST_FLOW_OK;
351
352 ret = gst_gl_view_convert_get_output (viewconvert_filter->viewconvert,
353 outbuf_ptr);
354
355 if (ret != GST_FLOW_OK) {
356 GST_ELEMENT_ERROR (filter, RESOURCE, SETTINGS,
357 ("failed to perform view conversion on input buffer"), (NULL));
358 return ret;
359 }
360
361 return ret;
362 }
363