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 "gstglviewconvert.h"
52
53 #define GST_CAT_DEFAULT gst_gl_view_convert_element_debug
54 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
55
56 enum
57 {
58 PROP_0,
59 PROP_INPUT_LAYOUT,
60 PROP_INPUT_FLAGS,
61 PROP_OUTPUT_LAYOUT,
62 PROP_OUTPUT_FLAGS,
63 PROP_OUTPUT_DOWNMIX_MODE
64 };
65
66 #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
67
68 #define DEBUG_INIT \
69 GST_DEBUG_CATEGORY_INIT (gst_gl_view_convert_element_debug, "glview_convertelement", 0, "glview_convert element");
70
71 G_DEFINE_TYPE_WITH_CODE (GstGLViewConvertElement, gst_gl_view_convert_element,
72 GST_TYPE_GL_FILTER, DEBUG_INIT);
73 #define parent_class gst_gl_view_convert_element_parent_class
74
75 static void gst_gl_view_convert_dispose (GObject * object);
76 static void gst_gl_view_convert_element_set_property (GObject * object,
77 guint prop_id, const GValue * value, GParamSpec * pspec);
78 static void gst_gl_view_convert_element_get_property (GObject * object,
79 guint prop_id, GValue * value, GParamSpec * pspec);
80
81 static gboolean gst_gl_view_convert_element_stop (GstBaseTransform * bt);
82 static gboolean
83 gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps,
84 GstCaps * outcaps);
85 static GstCaps *gst_gl_view_convert_element_transform_internal_caps (GstGLFilter
86 * filter, GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps);
87 static GstCaps *gst_gl_view_convert_element_fixate_caps (GstBaseTransform *
88 trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
89 static GstFlowReturn
90 gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans,
91 gboolean is_discont, GstBuffer * input);
92 static GstFlowReturn
93 gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt,
94 GstBuffer ** outbuf);
95
96 static void
gst_gl_view_convert_element_class_init(GstGLViewConvertElementClass * klass)97 gst_gl_view_convert_element_class_init (GstGLViewConvertElementClass * klass)
98 {
99 GObjectClass *gobject_class;
100 GstElementClass *element_class;
101
102 gobject_class = (GObjectClass *) klass;
103 element_class = GST_ELEMENT_CLASS (klass);
104
105 gst_gl_filter_add_rgba_pad_templates (GST_GL_FILTER_CLASS (klass));
106
107 gobject_class->set_property = gst_gl_view_convert_element_set_property;
108 gobject_class->get_property = gst_gl_view_convert_element_get_property;
109 gobject_class->dispose = gst_gl_view_convert_dispose;
110
111 gst_element_class_set_metadata (element_class,
112 "OpenGL Multiview/3D conversion filter", "Filter",
113 "Convert stereoscopic/multiview video formats",
114 "Jan Schmidt <jan@centricular.com>\n"
115 "Matthew Waters <matthew@centricular.com>");
116
117 GST_GL_FILTER_CLASS (klass)->set_caps = gst_gl_view_convert_element_set_caps;
118
119 GST_GL_FILTER_CLASS (klass)->transform_internal_caps =
120 gst_gl_view_convert_element_transform_internal_caps;
121 GST_BASE_TRANSFORM_CLASS (klass)->stop = gst_gl_view_convert_element_stop;
122 GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
123 gst_gl_view_convert_element_fixate_caps;
124 GST_BASE_TRANSFORM_CLASS (klass)->submit_input_buffer =
125 gst_gl_view_convert_element_submit_input_buffer;
126 GST_BASE_TRANSFORM_CLASS (klass)->generate_output =
127 gst_gl_view_convert_element_generate_output_buffer;
128
129 g_object_class_install_property (gobject_class, PROP_INPUT_LAYOUT,
130 g_param_spec_enum ("input-mode-override",
131 "Input Multiview Mode Override",
132 "Override any input information about multiview layout",
133 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
134 GST_VIDEO_MULTIVIEW_MODE_NONE,
135 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
136 g_object_class_install_property (gobject_class, PROP_INPUT_FLAGS,
137 g_param_spec_flags ("input-flags-override",
138 "Input Multiview Flags Override",
139 "Override any input information about multiview layout flags",
140 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
141 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142 g_object_class_install_property (gobject_class, PROP_OUTPUT_LAYOUT,
143 g_param_spec_enum ("output-mode-override",
144 "Output Multiview Mode Override",
145 "Override automatic output mode selection for multiview layout",
146 GST_TYPE_VIDEO_MULTIVIEW_MODE, GST_VIDEO_MULTIVIEW_MODE_NONE,
147 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
148 g_object_class_install_property (gobject_class, PROP_OUTPUT_FLAGS,
149 g_param_spec_flags ("output-flags-override",
150 "Output Multiview Flags Override",
151 "Override automatic negotiation for output multiview layout flags",
152 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
153 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154 g_object_class_install_property (gobject_class, PROP_OUTPUT_DOWNMIX_MODE,
155 g_param_spec_enum ("downmix-mode", "Mode for mono downmixed output",
156 "Output anaglyph type to generate when downmixing to mono",
157 GST_TYPE_GL_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
158 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159 }
160
161 static void
gst_gl_view_convert_element_init(GstGLViewConvertElement * convert)162 gst_gl_view_convert_element_init (GstGLViewConvertElement * convert)
163 {
164 convert->viewconvert = gst_gl_view_convert_new ();
165 }
166
167 static void
gst_gl_view_convert_dispose(GObject * object)168 gst_gl_view_convert_dispose (GObject * object)
169 {
170 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
171
172 if (convert->viewconvert) {
173 gst_object_unref (convert->viewconvert);
174 convert->viewconvert = NULL;
175 }
176 G_OBJECT_CLASS (parent_class)->dispose (object);
177 }
178
179 static gboolean
gst_gl_view_convert_element_set_caps(GstGLFilter * filter,GstCaps * incaps,GstCaps * outcaps)180 gst_gl_view_convert_element_set_caps (GstGLFilter * filter, GstCaps * incaps,
181 GstCaps * outcaps)
182 {
183 GstGLViewConvertElement *viewconvert_filter =
184 GST_GL_VIEW_CONVERT_ELEMENT (filter);
185 GstCapsFeatures *gl_features;
186 gboolean ret;
187
188 GST_DEBUG_OBJECT (filter, "incaps %" GST_PTR_FORMAT
189 " outcaps %" GST_PTR_FORMAT, incaps, outcaps);
190 /* The view_convert component needs RGBA caps */
191 incaps = gst_caps_copy (incaps);
192 outcaps = gst_caps_copy (outcaps);
193
194 gst_caps_set_simple (incaps, "format", G_TYPE_STRING, "RGBA", NULL);
195 gl_features =
196 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
197 gst_caps_set_features (incaps, 0, gl_features);
198
199 gst_caps_set_simple (outcaps, "format", G_TYPE_STRING, "RGBA", NULL);
200 gl_features =
201 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
202 gst_caps_set_features (outcaps, 0, gl_features);
203
204 ret = gst_gl_view_convert_set_caps (viewconvert_filter->viewconvert,
205 incaps, outcaps);
206
207 gst_caps_unref (incaps);
208 gst_caps_unref (outcaps);
209
210 return ret;
211 }
212
213 static GstCaps *
gst_gl_view_convert_element_transform_internal_caps(GstGLFilter * filter,GstPadDirection direction,GstCaps * caps,GstCaps * filter_caps)214 gst_gl_view_convert_element_transform_internal_caps (GstGLFilter * filter,
215 GstPadDirection direction, GstCaps * caps, GstCaps * filter_caps)
216 {
217 GstGLViewConvertElement *viewconvert_filter =
218 GST_GL_VIEW_CONVERT_ELEMENT (filter);
219 GstCaps *result;
220
221 GST_DEBUG_OBJECT (filter, "dir %s transforming caps: %" GST_PTR_FORMAT,
222 direction == GST_PAD_SINK ? "sink" : "src", caps);
223
224 result =
225 gst_gl_view_convert_transform_caps (viewconvert_filter->viewconvert,
226 direction, caps, NULL);
227
228 GST_DEBUG_OBJECT (filter, "returning caps: %" GST_PTR_FORMAT, result);
229
230 return result;
231 }
232
233 static GstCaps *
gst_gl_view_convert_element_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)234 gst_gl_view_convert_element_fixate_caps (GstBaseTransform * trans,
235 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
236 {
237 GstGLViewConvertElement *viewconvert_filter =
238 GST_GL_VIEW_CONVERT_ELEMENT (trans);
239
240 othercaps = gst_gl_view_convert_fixate_caps (viewconvert_filter->viewconvert,
241 direction, caps, othercaps);
242
243 if (gst_caps_is_empty (othercaps))
244 return othercaps;
245
246 /* Let GLfilter do the rest */
247 return
248 GST_BASE_TRANSFORM_CLASS
249 (gst_gl_view_convert_element_parent_class)->fixate_caps (trans, direction,
250 caps, othercaps);
251 }
252
253 static gboolean
gst_gl_view_convert_element_stop(GstBaseTransform * bt)254 gst_gl_view_convert_element_stop (GstBaseTransform * bt)
255 {
256 GstGLViewConvertElement *viewconvert_filter =
257 GST_GL_VIEW_CONVERT_ELEMENT (bt);
258
259 gst_gl_view_convert_reset (viewconvert_filter->viewconvert);
260
261 return GST_BASE_TRANSFORM_CLASS (parent_class)->stop (bt);
262 }
263
264 static void
gst_gl_view_convert_element_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)265 gst_gl_view_convert_element_set_property (GObject * object, guint prop_id,
266 const GValue * value, GParamSpec * pspec)
267 {
268 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
269
270 switch (prop_id) {
271 case PROP_INPUT_LAYOUT:
272 case PROP_INPUT_FLAGS:
273 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
274 value);
275 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert));
276 break;
277 case PROP_OUTPUT_LAYOUT:
278 case PROP_OUTPUT_FLAGS:
279 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
280 value);
281 gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM (convert));
282 break;
283 case PROP_OUTPUT_DOWNMIX_MODE:
284 g_object_set_property (G_OBJECT (convert->viewconvert), pspec->name,
285 value);
286 break;
287 default:
288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
289 break;
290 }
291 }
292
293 static void
gst_gl_view_convert_element_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)294 gst_gl_view_convert_element_get_property (GObject * object, guint prop_id,
295 GValue * value, GParamSpec * pspec)
296 {
297 GstGLViewConvertElement *convert = GST_GL_VIEW_CONVERT_ELEMENT (object);
298
299 switch (prop_id) {
300 case PROP_INPUT_LAYOUT:
301 case PROP_INPUT_FLAGS:
302 case PROP_OUTPUT_LAYOUT:
303 case PROP_OUTPUT_FLAGS:
304 case PROP_OUTPUT_DOWNMIX_MODE:
305 g_object_get_property (G_OBJECT (convert->viewconvert), pspec->name,
306 value);
307 break;
308 default:
309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310 break;
311 }
312 }
313
314 static GstFlowReturn
gst_gl_view_convert_element_submit_input_buffer(GstBaseTransform * trans,gboolean is_discont,GstBuffer * input)315 gst_gl_view_convert_element_submit_input_buffer (GstBaseTransform * trans,
316 gboolean is_discont, GstBuffer * input)
317 {
318 GstGLContext *context = GST_GL_BASE_FILTER (trans)->context;
319 GstGLViewConvertElement *viewconvert_filter =
320 GST_GL_VIEW_CONVERT_ELEMENT (trans);
321 GstFlowReturn ret;
322
323 ret =
324 GST_BASE_TRANSFORM_CLASS (parent_class)->submit_input_buffer (trans,
325 is_discont, input);
326 if (ret != GST_FLOW_OK || trans->queued_buf == NULL)
327 return ret;
328
329 gst_gl_view_convert_set_context (viewconvert_filter->viewconvert, context);
330
331 /* Takes the ref to the input buffer */
332 ret =
333 gst_gl_view_convert_submit_input_buffer (viewconvert_filter->viewconvert,
334 is_discont, input);
335 trans->queued_buf = NULL;
336
337 return ret;
338 }
339
340 static GstFlowReturn
gst_gl_view_convert_element_generate_output_buffer(GstBaseTransform * bt,GstBuffer ** outbuf_ptr)341 gst_gl_view_convert_element_generate_output_buffer (GstBaseTransform * bt,
342 GstBuffer ** outbuf_ptr)
343 {
344 GstGLFilter *filter = GST_GL_FILTER (bt);
345 GstGLViewConvertElement *viewconvert_filter =
346 GST_GL_VIEW_CONVERT_ELEMENT (bt);
347 GstFlowReturn ret = GST_FLOW_OK;
348
349 ret = gst_gl_view_convert_get_output (viewconvert_filter->viewconvert,
350 outbuf_ptr);
351
352 if (ret != GST_FLOW_OK) {
353 GST_ELEMENT_ERROR (filter, RESOURCE, SETTINGS,
354 ("failed to perform view conversion on input buffer"), (NULL));
355 return ret;
356 }
357
358 return ret;
359 }
360