glutils: fix matrix operations everywhere
[platform/upstream/gstreamer.git] / ext / gl / gstglimagesink.c
1 /*
2  * GStreamer
3  * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
4  * Copyright (C) 2005,2006,2007 David A. Schleef <ds@schleef.org>
5  * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
6  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:element-glimagesink
26  * @title: glimagesink
27  *
28  * glimagesink renders video frames to a drawable on a local or remote
29  * display using OpenGL. This element can receive a Window ID from the
30  * application through the VideoOverlay interface and will then render video
31  * frames in this drawable.
32  * If no Window ID was provided by the application, the element will
33  * create its own internal window and render into it.
34  *
35  * See the #GstGLDisplay documentation for a list of environment variables that
36  * can override window/platform detection.
37  *
38  * ## Scaling
39  *
40  * Depends on the driver, OpenGL handles hardware accelerated
41  * scaling of video frames. This means that the element will just accept
42  * incoming video frames no matter their geometry and will then put them to the
43  * drawable scaling them on the fly. Using the #GstGLImageSink:force-aspect-ratio
44  * property it is possible to enforce scaling with a constant aspect ratio,
45  * which means drawing black borders around the video frame.
46  *
47  * ## Events
48  *
49  * Through the gl thread, glimagesink handle some events coming from the drawable
50  * to manage its appearance even when the data is not flowing (GST_STATE_PAUSED).
51  * That means that even when the element is paused, it will receive expose events
52  * from the drawable and draw the latest frame with correct borders/aspect-ratio.
53  *
54  * ## Examples
55  * |[
56  * gst-launch-1.0 -v videotestsrc ! video/x-raw ! glimagesink
57  * ]| A pipeline to test hardware scaling.
58  * No special opengl extension is used in this pipeline, that's why it should work
59  * with OpenGL >= 1.1. That's the case if you are using the MESA3D driver v1.3.
60  * |[
61  * gst-launch-1.0 -v videotestsrc ! video/x-raw,format=I420 ! glimagesink
62  * ]| A pipeline to test hardware scaling and hardware colorspace conversion.
63  * When your driver supports GLSL (OpenGL Shading Language needs OpenGL >= 2.1),
64  * the 4 following format YUY2, UYVY, I420, YV12 and AYUV are converted to RGB32
65  * through some fragment shaders and using one framebuffer (FBO extension OpenGL >= 1.4).
66  * If your driver does not support GLSL but supports MESA_YCbCr extension then
67  * the you can use YUY2 and UYVY. In this case the colorspace conversion is automatically
68  * made when loading the texture and therefore no framebuffer is used.
69  * |[
70  * gst-launch-1.0 -v gltestsrc ! glimagesink
71  * ]| A pipeline 100% OpenGL.
72  * No special opengl extension is used in this pipeline, that's why it should work
73  * with OpenGL >= 1.1. That's the case if you are using the MESA3D driver v1.3.
74  * |[
75  * gst-plugins-bas/tests/examples/gl/generic/cube
76  * ]| The graphic FPS scene can be greater than the input video FPS.
77  * The graphic scene can be written from a client code through the
78  * two glfilterapp properties.
79  *
80  */
81
82 #ifdef HAVE_CONFIG_H
83 #include "config.h"
84 #endif
85
86 #include <gst/video/videooverlay.h>
87 #include <gst/video/navigation.h>
88 #include <gst/video/gstvideoaffinetransformationmeta.h>
89
90 #include "gstglimagesink.h"
91 #include "gstglsinkbin.h"
92 #include "gstglutils.h"
93
94 #include <gst/gl/gstglviewconvert.h>
95
96 GST_DEBUG_CATEGORY (gst_debug_glimage_sink);
97 #define GST_CAT_DEFAULT gst_debug_glimage_sink
98
99 #define DEFAULT_SHOW_PREROLL_FRAME  TRUE
100 #define DEFAULT_HANDLE_EVENTS       TRUE
101 #define DEFAULT_FORCE_ASPECT_RATIO  TRUE
102 #define DEFAULT_IGNORE_ALPHA        TRUE
103
104 #define DEFAULT_MULTIVIEW_MODE GST_VIDEO_MULTIVIEW_MODE_MONO
105 #define DEFAULT_MULTIVIEW_FLAGS GST_VIDEO_MULTIVIEW_FLAGS_NONE
106 #define DEFAULT_MULTIVIEW_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
107
108 typedef GstGLSinkBin GstGLImageSinkBin;
109 typedef GstGLSinkBinClass GstGLImageSinkBinClass;
110
111 G_DEFINE_TYPE (GstGLImageSinkBin, gst_gl_image_sink_bin, GST_TYPE_GL_SINK_BIN);
112
113 enum
114 {
115   PROP_BIN_0,
116   PROP_BIN_ROTATE_METHOD,
117   PROP_BIN_FORCE_ASPECT_RATIO,
118   PROP_BIN_PIXEL_ASPECT_RATIO,
119   PROP_BIN_HANDLE_EVENTS,
120   PROP_BIN_CONTEXT,
121   PROP_BIN_IGNORE_ALPHA,
122   PROP_BIN_SHOW_PREROLL_FRAME,
123   PROP_BIN_OUTPUT_MULTIVIEW_LAYOUT,
124   PROP_BIN_OUTPUT_MULTIVIEW_FLAGS,
125   PROP_BIN_OUTPUT_MULTIVIEW_DOWNMIX_MODE
126 };
127
128 enum
129 {
130   SIGNAL_BIN_0,
131   SIGNAL_BIN_CLIENT_DRAW,
132   SIGNAL_BIN_CLIENT_RESHAPE,
133   SIGNAL_BIN_LAST,
134 };
135
136 static guint gst_gl_image_sink_bin_signals[SIGNAL_BIN_LAST] = { 0 };
137
138 static void
139 gst_gl_image_sink_bin_set_property (GObject * object, guint prop_id,
140     const GValue * value, GParamSpec * param_spec)
141 {
142   g_object_set_property (G_OBJECT (GST_GL_SINK_BIN (object)->sink),
143       param_spec->name, value);
144 }
145
146 static void
147 gst_gl_image_sink_bin_get_property (GObject * object, guint prop_id,
148     GValue * value, GParamSpec * param_spec)
149 {
150   g_object_get_property (G_OBJECT (GST_GL_SINK_BIN (object)->sink),
151       param_spec->name, value);
152 }
153
154 static gboolean
155 _on_client_reshape (GstGLImageSink * sink, GstGLContext * context,
156     guint width, guint height, gpointer data)
157 {
158   gboolean ret;
159
160   g_signal_emit (data, gst_gl_image_sink_bin_signals[SIGNAL_BIN_CLIENT_RESHAPE],
161       0, context, width, height, &ret);
162
163   return ret;
164 }
165
166 static gboolean
167 _on_client_draw (GstGLImageSink * sink, GstGLContext * context,
168     GstSample * sample, gpointer data)
169 {
170   gboolean ret;
171
172   g_signal_emit (data, gst_gl_image_sink_bin_signals[SIGNAL_BIN_CLIENT_DRAW], 0,
173       context, sample, &ret);
174
175   return ret;
176 }
177
178 #define DEFAULT_ROTATE_METHOD GST_GL_ROTATE_METHOD_IDENTITY
179
180 #define GST_TYPE_GL_ROTATE_METHOD (gst_gl_rotate_method_get_type())
181
182 static const GEnumValue rotate_methods[] = {
183   {GST_GL_ROTATE_METHOD_IDENTITY, "Identity (no rotation)", "none"},
184   {GST_GL_ROTATE_METHOD_90R, "Rotate clockwise 90 degrees", "clockwise"},
185   {GST_GL_ROTATE_METHOD_180, "Rotate 180 degrees", "rotate-180"},
186   {GST_GL_ROTATE_METHOD_90L, "Rotate counter-clockwise 90 degrees",
187       "counterclockwise"},
188   {GST_GL_ROTATE_METHOD_FLIP_HORIZ, "Flip horizontally", "horizontal-flip"},
189   {GST_GL_ROTATE_METHOD_FLIP_VERT, "Flip vertically", "vertical-flip"},
190   {GST_GL_ROTATE_METHOD_FLIP_UL_LR,
191       "Flip across upper left/lower right diagonal", "upper-left-diagonal"},
192   {GST_GL_ROTATE_METHOD_FLIP_UR_LL,
193       "Flip across upper right/lower left diagonal", "upper-right-diagonal"},
194   {GST_GL_ROTATE_METHOD_AUTO,
195       "Select rotate method based on image-orientation tag", "automatic"},
196   {0, NULL, NULL},
197 };
198
199 static GType
200 gst_gl_rotate_method_get_type (void)
201 {
202   static GType rotate_method_type = 0;
203
204   if (!rotate_method_type) {
205     rotate_method_type = g_enum_register_static ("GstGLRotateMethod",
206         rotate_methods);
207   }
208   return rotate_method_type;
209 }
210
211 static void
212 gst_gl_image_sink_bin_init (GstGLImageSinkBin * self)
213 {
214   GstGLImageSink *sink = g_object_new (GST_TYPE_GLIMAGE_SINK, NULL);
215
216   g_signal_connect (sink, "client-reshape", (GCallback) _on_client_reshape,
217       self);
218   g_signal_connect (sink, "client-draw", (GCallback) _on_client_draw, self);
219
220   gst_gl_sink_bin_finish_init_with_element (GST_GL_SINK_BIN (self),
221       GST_ELEMENT (sink));
222 }
223
224 static void
225 gst_gl_image_sink_bin_class_init (GstGLImageSinkBinClass * klass)
226 {
227   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
228
229   gobject_class->get_property = gst_gl_image_sink_bin_get_property;
230   gobject_class->set_property = gst_gl_image_sink_bin_set_property;
231
232   /* gl sink */
233   g_object_class_install_property (gobject_class, PROP_BIN_ROTATE_METHOD,
234       g_param_spec_enum ("rotate-method",
235           "rotate method",
236           "rotate method",
237           GST_TYPE_GL_ROTATE_METHOD, DEFAULT_ROTATE_METHOD,
238           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
239   g_object_class_install_property (gobject_class, PROP_BIN_FORCE_ASPECT_RATIO,
240       g_param_spec_boolean ("force-aspect-ratio",
241           "Force aspect ratio",
242           "When enabled, scaling will respect original aspect ratio",
243           DEFAULT_FORCE_ASPECT_RATIO,
244           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245   g_object_class_install_property (gobject_class, PROP_BIN_HANDLE_EVENTS,
246       g_param_spec_boolean ("handle-events", "Handle XEvents",
247           "When enabled, XEvents will be selected and handled",
248           DEFAULT_HANDLE_EVENTS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
249   g_object_class_install_property (gobject_class, PROP_BIN_IGNORE_ALPHA,
250       g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
251           "When enabled, alpha will be ignored and converted to black",
252           DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
253   g_object_class_install_property (gobject_class, PROP_BIN_CONTEXT,
254       g_param_spec_object ("context", "OpenGL context", "Get OpenGL context",
255           GST_TYPE_GL_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
256   g_object_class_install_property (gobject_class, PROP_BIN_PIXEL_ASPECT_RATIO,
257       gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
258           "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
259           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
260
261   /* video sink */
262   g_object_class_install_property (gobject_class, PROP_BIN_SHOW_PREROLL_FRAME,
263       g_param_spec_boolean ("show-preroll-frame", "Show preroll frame",
264           "Whether to render video frames during preroll",
265           DEFAULT_SHOW_PREROLL_FRAME,
266           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
267
268   g_object_class_install_property (gobject_class,
269       PROP_BIN_OUTPUT_MULTIVIEW_LAYOUT,
270       g_param_spec_enum ("output-multiview-mode", "Output Multiview Mode",
271           "Choose output mode for multiview/3D video",
272           GST_TYPE_VIDEO_MULTIVIEW_MODE, DEFAULT_MULTIVIEW_MODE,
273           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
274   g_object_class_install_property (gobject_class,
275       PROP_BIN_OUTPUT_MULTIVIEW_FLAGS,
276       g_param_spec_flags ("output-multiview-flags", "Output Multiview Flags",
277           "Output multiview layout modifier flags",
278           GST_TYPE_VIDEO_MULTIVIEW_FLAGS, DEFAULT_MULTIVIEW_FLAGS,
279           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
280   g_object_class_install_property (gobject_class,
281       PROP_BIN_OUTPUT_MULTIVIEW_DOWNMIX_MODE,
282       g_param_spec_enum ("output-multiview-downmix-mode",
283           "Mode for mono downmixed output",
284           "Output anaglyph type to generate when downmixing to mono",
285           GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE, DEFAULT_MULTIVIEW_DOWNMIX,
286           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
287   gst_gl_image_sink_bin_signals[SIGNAL_BIN_CLIENT_DRAW] =
288       g_signal_new ("client-draw", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
289       0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 2,
290       GST_TYPE_GL_CONTEXT, GST_TYPE_SAMPLE);
291
292   gst_gl_image_sink_bin_signals[SIGNAL_BIN_CLIENT_RESHAPE] =
293       g_signal_new ("client-reshape", G_TYPE_FROM_CLASS (klass),
294       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
295       G_TYPE_BOOLEAN, 3, GST_TYPE_GL_CONTEXT, G_TYPE_UINT, G_TYPE_UINT);
296 }
297
298 #define GST_GLIMAGE_SINK_GET_LOCK(glsink) \
299   (GST_GLIMAGE_SINK(glsink)->drawing_lock)
300 #define GST_GLIMAGE_SINK_LOCK(glsink) \
301   (g_mutex_lock(&GST_GLIMAGE_SINK_GET_LOCK (glsink)))
302 #define GST_GLIMAGE_SINK_UNLOCK(glsink) \
303   (g_mutex_unlock(&GST_GLIMAGE_SINK_GET_LOCK (glsink)))
304
305 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
306 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
307 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
308 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
309 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
310
311 #define SUPPORTED_GL_APIS GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3
312
313 static void gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink);
314 static void gst_glimage_sink_cleanup_glthread (GstGLImageSink * gl_sink);
315 static void gst_glimage_sink_on_close (GstGLImageSink * gl_sink);
316 static void gst_glimage_sink_on_resize (GstGLImageSink * gl_sink,
317     gint width, gint height);
318 static void gst_glimage_sink_on_draw (GstGLImageSink * gl_sink);
319 static gboolean gst_glimage_sink_redisplay (GstGLImageSink * gl_sink);
320
321 static void gst_glimage_sink_finalize (GObject * object);
322 static void gst_glimage_sink_set_property (GObject * object, guint prop_id,
323     const GValue * value, GParamSpec * param_spec);
324 static void gst_glimage_sink_get_property (GObject * object, guint prop_id,
325     GValue * value, GParamSpec * param_spec);
326
327 static gboolean gst_glimage_sink_event (GstBaseSink * sink, GstEvent * event);
328 static gboolean gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query);
329 static void gst_glimage_sink_set_context (GstElement * element,
330     GstContext * context);
331
332 static GstStateChangeReturn
333 gst_glimage_sink_change_state (GstElement * element, GstStateChange transition);
334
335 static void gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
336     GstClockTime * start, GstClockTime * end);
337 static gboolean gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
338 static GstCaps *gst_glimage_sink_get_caps (GstBaseSink * bsink,
339     GstCaps * filter);
340 static GstFlowReturn gst_glimage_sink_prepare (GstBaseSink * bsink,
341     GstBuffer * buf);
342 static GstFlowReturn gst_glimage_sink_show_frame (GstVideoSink * bsink,
343     GstBuffer * buf);
344 static gboolean gst_glimage_sink_propose_allocation (GstBaseSink * bsink,
345     GstQuery * query);
346
347 static void gst_glimage_sink_video_overlay_init (GstVideoOverlayInterface *
348     iface);
349 static void gst_glimage_sink_set_window_handle (GstVideoOverlay * overlay,
350     guintptr id);
351 static void gst_glimage_sink_expose (GstVideoOverlay * overlay);
352 static void gst_glimage_sink_set_render_rectangle (GstVideoOverlay * overlay,
353     gint x, gint y, gint width, gint height);
354 static void gst_glimage_sink_handle_events (GstVideoOverlay * overlay,
355     gboolean handle_events);
356 static gboolean update_output_format (GstGLImageSink * glimage_sink);
357
358 #define GST_GL_SINK_CAPS \
359     "video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "              \
360     "format = (string) RGBA, "                                          \
361     "width = " GST_VIDEO_SIZE_RANGE ", "                                \
362     "height = " GST_VIDEO_SIZE_RANGE ", "                               \
363     "framerate = " GST_VIDEO_FPS_RANGE ", "                             \
364     "texture-target = (string) { 2D, external-oes } "                   \
365     " ; "                                                               \
366     "video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY ","                \
367     GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), "           \
368     "format = (string) RGBA, "                                          \
369     "width = " GST_VIDEO_SIZE_RANGE ", "                                \
370     "height = " GST_VIDEO_SIZE_RANGE ", "                               \
371     "framerate = " GST_VIDEO_FPS_RANGE ", "                             \
372     "texture-target = (string) { 2D, external-oes } "
373
374 static GstStaticPadTemplate gst_glimage_sink_template =
375 GST_STATIC_PAD_TEMPLATE ("sink",
376     GST_PAD_SINK,
377     GST_PAD_ALWAYS,
378     GST_STATIC_CAPS (GST_GL_SINK_CAPS));
379
380 enum
381 {
382   ARG_0,
383   ARG_DISPLAY,
384   PROP_ROTATE_METHOD,
385   PROP_FORCE_ASPECT_RATIO,
386   PROP_PIXEL_ASPECT_RATIO,
387   PROP_CONTEXT,
388   PROP_HANDLE_EVENTS,
389   PROP_IGNORE_ALPHA,
390   PROP_OUTPUT_MULTIVIEW_LAYOUT,
391   PROP_OUTPUT_MULTIVIEW_FLAGS,
392   PROP_OUTPUT_MULTIVIEW_DOWNMIX_MODE
393 };
394
395 enum
396 {
397   SIGNAL_0,
398   CLIENT_DRAW_SIGNAL,
399   CLIENT_RESHAPE_SIGNAL,
400   LAST_SIGNAL
401 };
402
403 static guint gst_glimage_sink_signals[LAST_SIGNAL] = { 0 };
404
405 static void
406 _display_size_to_stream_size (GstGLImageSink * gl_sink, gdouble x,
407     gdouble y, gdouble * stream_x, gdouble * stream_y)
408 {
409   gdouble stream_width, stream_height;
410
411   stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&gl_sink->out_info);
412   stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&gl_sink->out_info);
413
414   /* from display coordinates to stream coordinates */
415   if (gl_sink->display_rect.w > 0)
416     *stream_x =
417         (x - gl_sink->display_rect.x) / gl_sink->display_rect.w * stream_width;
418   else
419     *stream_x = 0.;
420
421   /* clip to stream size */
422   if (*stream_x < 0.)
423     *stream_x = 0.;
424   if (*stream_x > GST_VIDEO_INFO_WIDTH (&gl_sink->out_info))
425     *stream_x = GST_VIDEO_INFO_WIDTH (&gl_sink->out_info);
426
427   /* same for y-axis */
428   if (gl_sink->display_rect.h > 0)
429     *stream_y =
430         (y - gl_sink->display_rect.y) / gl_sink->display_rect.h * stream_height;
431   else
432     *stream_y = 0.;
433
434   if (*stream_y < 0.)
435     *stream_y = 0.;
436   if (*stream_y > GST_VIDEO_INFO_HEIGHT (&gl_sink->out_info))
437     *stream_y = GST_VIDEO_INFO_HEIGHT (&gl_sink->out_info);
438
439   GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y);
440 }
441
442 /* rotate 90 */
443 static const gfloat clockwise_matrix[] = {
444   0.0f, -1.0f, 0.0f, 0.0f,
445   1.0f, 0.0f, 0.0f, 0.0f,
446   0.0f, 0.0f, 1.0f, 0.0f,
447   0.0f, 0.0f, 0.0f, 1.0f,
448 };
449
450 /* rotate 180 */
451 static const gfloat clockwise_180_matrix[] = {
452   -1.0f, 0.0f, 0.0f, 0.0f,
453   0.0f, -1.0f, 0.0f, 0.0f,
454   0.0f, 0.0f, 1.0f, 0.0f,
455   0.0f, 0.0f, 0.0f, 1.0f,
456 };
457
458 /* rotate 270 */
459 static const gfloat counterclockwise_matrix[] = {
460   0.0f, 1.0f, 0.0f, 0.0f,
461   -1.0f, 0.0f, 0.0f, 0.0f,
462   0.0f, 0.0f, 1.0f, 0.0f,
463   0.0f, 0.0f, 0.0f, 1.0f,
464 };
465
466 /* horizontal-flip */
467 static const gfloat horizontal_flip_matrix[] = {
468   -1.0f, 0.0f, 0.0f, 0.0f,
469   0.0f, 1.0f, 0.0f, 0.0f,
470   0.0f, 0.0f, 1.0f, 0.0f,
471   0.0f, 0.0f, 0.0f, 1.0f,
472 };
473
474 /* vertical-flip */
475 static const gfloat vertical_flip_matrix[] = {
476   1.0f, 0.0f, 0.0f, 0.0f,
477   0.0f, -1.0f, 0.0f, 0.0f,
478   0.0f, 0.0f, 1.0f, 0.0f,
479   0.0f, 0.0f, 0.0f, 1.0f,
480 };
481
482 /* upper-left-diagonal */
483 static const gfloat upper_left_matrix[] = {
484   0.0f, 1.0f, 0.0f, 0.0f,
485   1.0f, 0.0f, 0.0f, 0.0f,
486   0.0f, 0.0f, 1.0f, 0.0f,
487   0.0f, 0.0f, 0.0f, 1.0f,
488 };
489
490 /* upper-right-diagonal */
491 static const gfloat upper_right_matrix[] = {
492   0.0f, -1.0f, 0.0f, 0.0f,
493   -1.0f, 0.0f, 0.0f, 0.0f,
494   0.0f, 0.0f, 1.0f, 0.0f,
495   0.0f, 0.0f, 0.0f, 1.0f,
496 };
497
498 static void
499 gst_glimage_sink_set_rotate_method (GstGLImageSink * gl_sink,
500     GstGLRotateMethod method, gboolean from_tag)
501 {
502   GstGLRotateMethod tag_method = DEFAULT_ROTATE_METHOD;
503   GST_GLIMAGE_SINK_LOCK (gl_sink);
504   if (from_tag)
505     tag_method = method;
506   else
507     gl_sink->rotate_method = method;
508
509   if (gl_sink->rotate_method == GST_GL_ROTATE_METHOD_AUTO)
510     method = tag_method;
511   else
512     method = gl_sink->rotate_method;
513
514   if (method != gl_sink->current_rotate_method) {
515     GST_DEBUG_OBJECT (gl_sink, "Changing method from %s to %s",
516         rotate_methods[gl_sink->current_rotate_method].value_nick,
517         rotate_methods[method].value_nick);
518
519     switch (method) {
520       case GST_GL_ROTATE_METHOD_IDENTITY:
521         gl_sink->transform_matrix = NULL;
522         gl_sink->output_mode_changed = TRUE;
523         break;
524       case GST_GL_ROTATE_METHOD_90R:
525         gl_sink->transform_matrix = clockwise_matrix;
526         gl_sink->output_mode_changed = TRUE;
527         break;
528       case GST_GL_ROTATE_METHOD_180:
529         gl_sink->transform_matrix = clockwise_180_matrix;
530         gl_sink->output_mode_changed = TRUE;
531         break;
532       case GST_GL_ROTATE_METHOD_90L:
533         gl_sink->transform_matrix = counterclockwise_matrix;
534         gl_sink->output_mode_changed = TRUE;
535         break;
536       case GST_GL_ROTATE_METHOD_FLIP_HORIZ:
537         gl_sink->transform_matrix = horizontal_flip_matrix;
538         gl_sink->output_mode_changed = TRUE;
539         break;
540       case GST_GL_ROTATE_METHOD_FLIP_VERT:
541         gl_sink->transform_matrix = vertical_flip_matrix;
542         gl_sink->output_mode_changed = TRUE;
543         break;
544       case GST_GL_ROTATE_METHOD_FLIP_UL_LR:
545         gl_sink->transform_matrix = upper_left_matrix;
546         gl_sink->output_mode_changed = TRUE;
547         break;
548       case GST_GL_ROTATE_METHOD_FLIP_UR_LL:
549         gl_sink->transform_matrix = upper_right_matrix;
550         gl_sink->output_mode_changed = TRUE;
551         break;
552       default:
553         g_assert_not_reached ();
554         break;
555     }
556
557     gl_sink->current_rotate_method = method;
558   }
559   GST_GLIMAGE_SINK_UNLOCK (gl_sink);
560 }
561
562 static void
563 gst_glimage_sink_navigation_send_event (GstNavigation * navigation, GstStructure
564     * structure)
565 {
566   GstGLImageSink *sink = GST_GLIMAGE_SINK (navigation);
567   gboolean handled = FALSE;
568   GstEvent *event = NULL;
569   GstGLWindow *window;
570   guint width, height;
571   gdouble x, y;
572
573   if (!sink->context) {
574     gst_structure_free (structure);
575     return;
576   }
577
578   window = gst_gl_context_get_window (sink->context);
579   g_return_if_fail (GST_IS_GL_WINDOW (window));
580
581   width = GST_VIDEO_SINK_WIDTH (sink);
582   height = GST_VIDEO_SINK_HEIGHT (sink);
583   gst_gl_window_get_surface_dimensions (window, &width, &height);
584
585   /* Converting pointer coordinates to the non scaled geometry */
586   if (width != 0 && gst_structure_get_double (structure, "pointer_x", &x)
587       && height != 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
588     gdouble stream_x, stream_y;
589
590     _display_size_to_stream_size (sink, x, y, &stream_x, &stream_y);
591
592     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
593         stream_x, "pointer_y", G_TYPE_DOUBLE, stream_y, NULL);
594   }
595
596   event = gst_event_new_navigation (structure);
597   if (event) {
598     gst_event_ref (event);
599     handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (sink), event);
600
601     if (!handled)
602       gst_element_post_message ((GstElement *) sink,
603           gst_navigation_message_new_event ((GstObject *) sink, event));
604
605     gst_event_unref (event);
606   }
607   gst_object_unref (window);
608 }
609
610 static void
611 gst_glimage_sink_navigation_interface_init (GstNavigationInterface * iface)
612 {
613   iface->send_event = gst_glimage_sink_navigation_send_event;
614 }
615
616 #define gst_glimage_sink_parent_class parent_class
617 G_DEFINE_TYPE_WITH_CODE (GstGLImageSink, gst_glimage_sink,
618     GST_TYPE_VIDEO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
619         gst_glimage_sink_video_overlay_init);
620     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
621         gst_glimage_sink_navigation_interface_init);
622     GST_DEBUG_CATEGORY_INIT (gst_debug_glimage_sink, "glimagesink", 0,
623         "OpenGL Video Sink"));
624
625 static void
626 gst_glimage_sink_class_init (GstGLImageSinkClass * klass)
627 {
628   GObjectClass *gobject_class;
629   GstElementClass *gstelement_class;
630   GstBaseSinkClass *gstbasesink_class;
631   GstVideoSinkClass *gstvideosink_class;
632   GstElementClass *element_class;
633
634   gobject_class = (GObjectClass *) klass;
635   gstelement_class = (GstElementClass *) klass;
636   gstbasesink_class = (GstBaseSinkClass *) klass;
637   gstvideosink_class = (GstVideoSinkClass *) klass;
638   element_class = GST_ELEMENT_CLASS (klass);
639
640   gobject_class->set_property = gst_glimage_sink_set_property;
641   gobject_class->get_property = gst_glimage_sink_get_property;
642
643   g_object_class_install_property (gobject_class, PROP_ROTATE_METHOD,
644       g_param_spec_enum ("rotate-method",
645           "rotate method",
646           "rotate method",
647           GST_TYPE_GL_ROTATE_METHOD, DEFAULT_ROTATE_METHOD,
648           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
649
650   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
651       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
652           "When enabled, scaling will respect original aspect ratio",
653           DEFAULT_FORCE_ASPECT_RATIO,
654           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
655
656   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
657       gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
658           "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
659           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
660
661   g_object_class_install_property (gobject_class, PROP_CONTEXT,
662       g_param_spec_object ("context", "OpenGL context", "Get OpenGL context",
663           GST_TYPE_GL_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
664
665   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
666       g_param_spec_boolean ("handle-events", "Handle XEvents",
667           "When enabled, XEvents will be selected and handled",
668           DEFAULT_HANDLE_EVENTS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
669
670   g_object_class_install_property (gobject_class, PROP_IGNORE_ALPHA,
671       g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
672           "When enabled, alpha will be ignored and converted to black",
673           DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
674
675   g_object_class_install_property (gobject_class, PROP_OUTPUT_MULTIVIEW_LAYOUT,
676       g_param_spec_enum ("output-multiview-mode",
677           "Output Multiview Mode",
678           "Choose output mode for multiview/3D video",
679           GST_TYPE_VIDEO_MULTIVIEW_MODE, DEFAULT_MULTIVIEW_MODE,
680           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
681   g_object_class_install_property (gobject_class, PROP_OUTPUT_MULTIVIEW_FLAGS,
682       g_param_spec_flags ("output-multiview-flags",
683           "Output Multiview Flags",
684           "Output multiview layout modifier flags",
685           GST_TYPE_VIDEO_MULTIVIEW_FLAGS, DEFAULT_MULTIVIEW_FLAGS,
686           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
687   g_object_class_install_property (gobject_class,
688       PROP_OUTPUT_MULTIVIEW_DOWNMIX_MODE,
689       g_param_spec_enum ("output-multiview-downmix-mode",
690           "Mode for mono downmixed output",
691           "Output anaglyph type to generate when downmixing to mono",
692           GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE, DEFAULT_MULTIVIEW_DOWNMIX,
693           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
694
695   gst_element_class_set_metadata (element_class, "OpenGL video sink",
696       "Sink/Video", "A videosink based on OpenGL",
697       "Julien Isorce <julien.isorce@gmail.com>");
698
699   /**
700    * GstGLImageSink::client-draw:
701    * @object: the #GstGLImageSink
702    * @texture: the #guint id of the texture.
703    * @width: the #guint width of the texture.
704    * @height: the #guint height of the texture.
705    *
706    * Will be emitted before actually drawing the texture.  The client should
707    * redraw the surface/contents with the @texture, @width and @height and
708    * and return %TRUE.
709    *
710    * Returns: whether the texture was redrawn by the signal.  If not, a
711    *          default redraw will occur.
712    */
713   gst_glimage_sink_signals[CLIENT_DRAW_SIGNAL] =
714       g_signal_new ("client-draw", G_TYPE_FROM_CLASS (klass),
715       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
716       G_TYPE_BOOLEAN, 2, GST_TYPE_GL_CONTEXT, GST_TYPE_SAMPLE);
717
718   /**
719    * GstGLImageSink::client-reshape:
720    * @object: the #GstGLImageSink
721    * @width: the #guint width of the texture.
722    * @height: the #guint height of the texture.
723    *
724    * The client should resize the surface/window/viewport with the @width and
725    * @height and return %TRUE.
726    *
727    * Returns: whether the content area was resized by the signal.  If not, a
728    *          default viewport resize will occur.
729    */
730   gst_glimage_sink_signals[CLIENT_RESHAPE_SIGNAL] =
731       g_signal_new ("client-reshape", G_TYPE_FROM_CLASS (klass),
732       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
733       G_TYPE_BOOLEAN, 3, GST_TYPE_GL_CONTEXT, G_TYPE_UINT, G_TYPE_UINT);
734
735   gst_element_class_add_static_pad_template (element_class,
736       &gst_glimage_sink_template);
737
738   gobject_class->finalize = gst_glimage_sink_finalize;
739
740   gstelement_class->change_state = gst_glimage_sink_change_state;
741   gstelement_class->set_context = gst_glimage_sink_set_context;
742   gstbasesink_class->event = gst_glimage_sink_event;
743   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_glimage_sink_query);
744   gstbasesink_class->set_caps = gst_glimage_sink_set_caps;
745   gstbasesink_class->get_caps = gst_glimage_sink_get_caps;
746   gstbasesink_class->get_times = gst_glimage_sink_get_times;
747   gstbasesink_class->prepare = gst_glimage_sink_prepare;
748   gstbasesink_class->propose_allocation = gst_glimage_sink_propose_allocation;
749
750   gstvideosink_class->show_frame =
751       GST_DEBUG_FUNCPTR (gst_glimage_sink_show_frame);
752 }
753
754 static void
755 gst_glimage_sink_init (GstGLImageSink * glimage_sink)
756 {
757   glimage_sink->window_id = 0;
758   glimage_sink->new_window_id = 0;
759   glimage_sink->display = NULL;
760   glimage_sink->keep_aspect_ratio = TRUE;
761   glimage_sink->par_n = 0;
762   glimage_sink->par_d = 1;
763   glimage_sink->redisplay_texture = 0;
764   glimage_sink->handle_events = TRUE;
765   glimage_sink->ignore_alpha = TRUE;
766   glimage_sink->overlay_compositor = NULL;
767
768   glimage_sink->mview_output_mode = DEFAULT_MULTIVIEW_MODE;
769   glimage_sink->mview_output_flags = DEFAULT_MULTIVIEW_FLAGS;
770   glimage_sink->mview_downmix_mode = DEFAULT_MULTIVIEW_DOWNMIX;
771
772   glimage_sink->current_rotate_method = DEFAULT_ROTATE_METHOD;
773   glimage_sink->transform_matrix = NULL;
774
775   g_mutex_init (&glimage_sink->drawing_lock);
776 }
777
778 static void
779 gst_glimage_sink_set_property (GObject * object, guint prop_id,
780     const GValue * value, GParamSpec * pspec)
781 {
782   GstGLImageSink *glimage_sink;
783
784   g_return_if_fail (GST_IS_GLIMAGE_SINK (object));
785
786   glimage_sink = GST_GLIMAGE_SINK (object);
787
788   switch (prop_id) {
789     case PROP_ROTATE_METHOD:
790       gst_glimage_sink_set_rotate_method (glimage_sink,
791           g_value_get_enum (value), FALSE);
792       break;
793     case PROP_FORCE_ASPECT_RATIO:
794     {
795       glimage_sink->keep_aspect_ratio = g_value_get_boolean (value);
796       break;
797     }
798     case PROP_PIXEL_ASPECT_RATIO:
799     {
800       glimage_sink->par_n = gst_value_get_fraction_numerator (value);
801       glimage_sink->par_d = gst_value_get_fraction_denominator (value);
802       break;
803     }
804     case PROP_HANDLE_EVENTS:
805       gst_glimage_sink_handle_events (GST_VIDEO_OVERLAY (glimage_sink),
806           g_value_get_boolean (value));
807       break;
808     case PROP_IGNORE_ALPHA:
809       glimage_sink->ignore_alpha = g_value_get_boolean (value);
810       break;
811     case PROP_OUTPUT_MULTIVIEW_LAYOUT:
812       GST_GLIMAGE_SINK_LOCK (glimage_sink);
813       glimage_sink->mview_output_mode = g_value_get_enum (value);
814       glimage_sink->output_mode_changed = TRUE;
815       GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
816       break;
817     case PROP_OUTPUT_MULTIVIEW_FLAGS:
818       GST_GLIMAGE_SINK_LOCK (glimage_sink);
819       glimage_sink->mview_output_flags = g_value_get_flags (value);
820       glimage_sink->output_mode_changed = TRUE;
821       GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
822       break;
823     case PROP_OUTPUT_MULTIVIEW_DOWNMIX_MODE:
824       GST_GLIMAGE_SINK_LOCK (glimage_sink);
825       glimage_sink->mview_downmix_mode = g_value_get_enum (value);
826       glimage_sink->output_mode_changed = TRUE;
827       GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
828       break;
829     default:
830       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
831       break;
832   }
833 }
834
835 static void
836 gst_glimage_sink_finalize (GObject * object)
837 {
838   GstGLImageSink *glimage_sink;
839
840   g_return_if_fail (GST_IS_GLIMAGE_SINK (object));
841
842   glimage_sink = GST_GLIMAGE_SINK (object);
843   g_mutex_clear (&glimage_sink->drawing_lock);
844
845   GST_DEBUG ("finalized");
846   G_OBJECT_CLASS (parent_class)->finalize (object);
847 }
848
849 static void
850 gst_glimage_sink_get_property (GObject * object, guint prop_id,
851     GValue * value, GParamSpec * pspec)
852 {
853   GstGLImageSink *glimage_sink;
854
855   g_return_if_fail (GST_IS_GLIMAGE_SINK (object));
856
857   glimage_sink = GST_GLIMAGE_SINK (object);
858
859   switch (prop_id) {
860     case PROP_ROTATE_METHOD:
861       g_value_set_enum (value, glimage_sink->current_rotate_method);
862       break;
863     case PROP_FORCE_ASPECT_RATIO:
864       g_value_set_boolean (value, glimage_sink->keep_aspect_ratio);
865       break;
866     case PROP_PIXEL_ASPECT_RATIO:
867       gst_value_set_fraction (value, glimage_sink->par_n, glimage_sink->par_d);
868       break;
869     case PROP_CONTEXT:
870       g_value_set_object (value, glimage_sink->context);
871       break;
872     case PROP_HANDLE_EVENTS:
873       g_value_set_boolean (value, glimage_sink->handle_events);
874       break;
875     case PROP_IGNORE_ALPHA:
876       g_value_set_boolean (value, glimage_sink->ignore_alpha);
877       break;
878     case PROP_OUTPUT_MULTIVIEW_LAYOUT:
879       g_value_set_enum (value, glimage_sink->mview_output_mode);
880       break;
881     case PROP_OUTPUT_MULTIVIEW_FLAGS:
882       g_value_set_flags (value, glimage_sink->mview_output_flags);
883       break;
884     case PROP_OUTPUT_MULTIVIEW_DOWNMIX_MODE:
885       g_value_set_enum (value, glimage_sink->mview_downmix_mode);
886       break;
887     default:
888       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
889       break;
890   }
891 }
892
893 static void
894 gst_glimage_sink_key_event_cb (GstGLWindow * window, char *event_name, char
895     *key_string, GstGLImageSink * gl_sink)
896 {
897   GST_DEBUG_OBJECT (gl_sink, "event %s key %s pressed", event_name, key_string);
898   gst_navigation_send_key_event (GST_NAVIGATION (gl_sink),
899       event_name, key_string);
900 }
901
902 static void
903 gst_glimage_sink_mouse_event_cb (GstGLWindow * window, char *event_name,
904     int button, double posx, double posy, GstGLImageSink * gl_sink)
905 {
906   GST_DEBUG_OBJECT (gl_sink, "event %s at %g, %g", event_name, posx, posy);
907   gst_navigation_send_mouse_event (GST_NAVIGATION (gl_sink),
908       event_name, button, posx, posy);
909 }
910
911 static gboolean
912 _ensure_gl_setup (GstGLImageSink * gl_sink)
913 {
914   GError *error = NULL;
915
916   GST_TRACE_OBJECT (gl_sink, "Ensuring setup");
917
918   if (!gl_sink->context) {
919     GST_OBJECT_LOCK (gl_sink->display);
920     do {
921       GstGLContext *other_context = NULL;
922       GstGLWindow *window = NULL;
923
924       if (gl_sink->context) {
925         gst_object_unref (gl_sink->context);
926         gl_sink->context = NULL;
927       }
928
929       GST_DEBUG_OBJECT (gl_sink,
930           "No current context, creating one for %" GST_PTR_FORMAT,
931           gl_sink->display);
932
933       if (gl_sink->other_context) {
934         other_context = gst_object_ref (gl_sink->other_context);
935       } else {
936         other_context =
937             gst_gl_display_get_gl_context_for_thread (gl_sink->display, NULL);
938       }
939
940       if (!gst_gl_display_create_context (gl_sink->display,
941               other_context, &gl_sink->context, &error)) {
942         if (other_context)
943           gst_object_unref (other_context);
944         GST_OBJECT_UNLOCK (gl_sink->display);
945         goto context_error;
946       }
947
948       GST_DEBUG_OBJECT (gl_sink,
949           "created context %" GST_PTR_FORMAT " from other context %"
950           GST_PTR_FORMAT, gl_sink->context, gl_sink->other_context);
951
952       window = gst_gl_context_get_window (gl_sink->context);
953
954       GST_DEBUG_OBJECT (gl_sink, "got window %" GST_PTR_FORMAT, window);
955
956       if (!gl_sink->window_id && !gl_sink->new_window_id)
957         gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (gl_sink));
958
959       GST_DEBUG_OBJECT (gl_sink,
960           "window_id : %" G_GUINTPTR_FORMAT " , new_window_id : %"
961           G_GUINTPTR_FORMAT, gl_sink->window_id, gl_sink->new_window_id);
962
963       if (gl_sink->window_id != gl_sink->new_window_id) {
964         gl_sink->window_id = gl_sink->new_window_id;
965         GST_DEBUG_OBJECT (gl_sink, "Setting window handle on gl window");
966         gst_gl_window_set_window_handle (window, gl_sink->window_id);
967       }
968
969       gst_gl_window_handle_events (window, gl_sink->handle_events);
970
971       /* setup callbacks */
972       gst_gl_window_set_resize_callback (window,
973           GST_GL_WINDOW_RESIZE_CB (gst_glimage_sink_on_resize),
974           gst_object_ref (gl_sink), (GDestroyNotify) gst_object_unref);
975       gst_gl_window_set_draw_callback (window,
976           GST_GL_WINDOW_CB (gst_glimage_sink_on_draw),
977           gst_object_ref (gl_sink), (GDestroyNotify) gst_object_unref);
978       gst_gl_window_set_close_callback (window,
979           GST_GL_WINDOW_CB (gst_glimage_sink_on_close),
980           gst_object_ref (gl_sink), (GDestroyNotify) gst_object_unref);
981       gl_sink->key_sig_id = g_signal_connect (window, "key-event", G_CALLBACK
982           (gst_glimage_sink_key_event_cb), gl_sink);
983       gl_sink->mouse_sig_id =
984           g_signal_connect (window, "mouse-event",
985           G_CALLBACK (gst_glimage_sink_mouse_event_cb), gl_sink);
986
987       if (gl_sink->x >= 0 && gl_sink->y >= 0 && gl_sink->width > 0 &&
988           gl_sink->height > 0) {
989         gst_gl_window_set_render_rectangle (window, gl_sink->x, gl_sink->y,
990             gl_sink->width, gl_sink->height);
991       }
992
993       if (other_context)
994         gst_object_unref (other_context);
995       gst_object_unref (window);
996     } while (!gst_gl_display_add_context (gl_sink->display, gl_sink->context));
997     GST_OBJECT_UNLOCK (gl_sink->display);
998   } else
999     GST_TRACE_OBJECT (gl_sink, "Already have a context");
1000
1001   return TRUE;
1002
1003 context_error:
1004   {
1005     GST_ELEMENT_ERROR (gl_sink, RESOURCE, NOT_FOUND, ("%s", error->message),
1006         (NULL));
1007
1008     if (gl_sink->context) {
1009       gst_object_unref (gl_sink->context);
1010       gl_sink->context = NULL;
1011     }
1012
1013     g_clear_error (&error);
1014
1015     return FALSE;
1016   }
1017 }
1018
1019 static gboolean
1020 gst_glimage_sink_event (GstBaseSink * sink, GstEvent * event)
1021 {
1022   GstGLImageSink *gl_sink = GST_GLIMAGE_SINK (sink);
1023   GstTagList *taglist;
1024   gchar *orientation;
1025   gboolean ret;
1026
1027   GST_DEBUG_OBJECT (gl_sink, "handling %s event", GST_EVENT_TYPE_NAME (event));
1028
1029   switch (GST_EVENT_TYPE (event)) {
1030     case GST_EVENT_TAG:
1031       gst_event_parse_tag (event, &taglist);
1032
1033       if (gst_tag_list_get_string (taglist, "image-orientation", &orientation)) {
1034         if (!g_strcmp0 ("rotate-0", orientation))
1035           gst_glimage_sink_set_rotate_method (gl_sink,
1036               GST_GL_ROTATE_METHOD_IDENTITY, TRUE);
1037         else if (!g_strcmp0 ("rotate-90", orientation))
1038           gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_90R,
1039               TRUE);
1040         else if (!g_strcmp0 ("rotate-180", orientation))
1041           gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_180,
1042               TRUE);
1043         else if (!g_strcmp0 ("rotate-270", orientation))
1044           gst_glimage_sink_set_rotate_method (gl_sink, GST_GL_ROTATE_METHOD_90L,
1045               TRUE);
1046         else if (!g_strcmp0 ("flip-rotate-0", orientation))
1047           gst_glimage_sink_set_rotate_method (gl_sink,
1048               GST_GL_ROTATE_METHOD_FLIP_HORIZ, TRUE);
1049         else if (!g_strcmp0 ("flip-rotate-90", orientation))
1050           gst_glimage_sink_set_rotate_method (gl_sink,
1051               GST_GL_ROTATE_METHOD_FLIP_UR_LL, TRUE);
1052         else if (!g_strcmp0 ("flip-rotate-180", orientation))
1053           gst_glimage_sink_set_rotate_method (gl_sink,
1054               GST_GL_ROTATE_METHOD_FLIP_VERT, TRUE);
1055         else if (!g_strcmp0 ("flip-rotate-270", orientation))
1056           gst_glimage_sink_set_rotate_method (gl_sink,
1057               GST_GL_ROTATE_METHOD_FLIP_UL_LR, TRUE);
1058
1059         g_free (orientation);
1060       }
1061       break;
1062     default:
1063       break;
1064   }
1065
1066   ret = GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1067
1068   return ret;
1069 }
1070
1071 static gboolean
1072 gst_glimage_sink_query (GstBaseSink * bsink, GstQuery * query)
1073 {
1074   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink);
1075   gboolean res = FALSE;
1076
1077   switch (GST_QUERY_TYPE (query)) {
1078     case GST_QUERY_CONTEXT:
1079     {
1080       if (gst_gl_handle_context_query ((GstElement *) glimage_sink, query,
1081               glimage_sink->display, glimage_sink->context,
1082               glimage_sink->other_context))
1083         return TRUE;
1084       break;
1085     }
1086     case GST_QUERY_DRAIN:
1087     {
1088       GstBuffer *buf[2];
1089
1090       GST_GLIMAGE_SINK_LOCK (glimage_sink);
1091       glimage_sink->redisplay_texture = 0;
1092       buf[0] = glimage_sink->stored_buffer[0];
1093       buf[1] = glimage_sink->stored_buffer[1];
1094       glimage_sink->stored_buffer[0] = glimage_sink->stored_buffer[1] = NULL;
1095       glimage_sink->stored_sync_meta = glimage_sink->next_sync_meta = NULL;
1096       GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1097
1098       gst_buffer_replace (buf, NULL);
1099       gst_buffer_replace (buf + 1, NULL);
1100
1101       gst_buffer_replace (&glimage_sink->input_buffer, NULL);
1102       gst_buffer_replace (&glimage_sink->input_buffer2, NULL);
1103       gst_buffer_replace (&glimage_sink->next_buffer, NULL);
1104       gst_buffer_replace (&glimage_sink->next_buffer2, NULL);
1105       gst_buffer_replace (&glimage_sink->next_sync, NULL);
1106
1107       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
1108       break;
1109     }
1110     default:
1111       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
1112       break;
1113   }
1114
1115   return res;
1116 }
1117
1118 static void
1119 gst_glimage_sink_set_context (GstElement * element, GstContext * context)
1120 {
1121   GstGLImageSink *gl_sink = GST_GLIMAGE_SINK (element);
1122
1123   gst_gl_handle_set_context (element, context, &gl_sink->display,
1124       &gl_sink->other_context);
1125
1126   if (gl_sink->display)
1127     gst_gl_display_filter_gl_api (gl_sink->display, SUPPORTED_GL_APIS);
1128
1129   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1130 }
1131
1132 static GstStateChangeReturn
1133 gst_glimage_sink_change_state (GstElement * element, GstStateChange transition)
1134 {
1135   GstGLImageSink *glimage_sink;
1136   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1137
1138   GST_DEBUG ("changing state: %s => %s",
1139       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1140       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1141
1142   glimage_sink = GST_GLIMAGE_SINK (element);
1143
1144   switch (transition) {
1145     case GST_STATE_CHANGE_NULL_TO_READY:
1146       if (!gst_gl_ensure_element_data (glimage_sink, &glimage_sink->display,
1147               &glimage_sink->other_context))
1148         return GST_STATE_CHANGE_FAILURE;
1149
1150       gst_gl_display_filter_gl_api (glimage_sink->display, SUPPORTED_GL_APIS);
1151
1152       if (!_ensure_gl_setup (glimage_sink))
1153         return GST_STATE_CHANGE_FAILURE;
1154       break;
1155     case GST_STATE_CHANGE_READY_TO_PAUSED:
1156       g_atomic_int_set (&glimage_sink->to_quit, 0);
1157       break;
1158     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1159       break;
1160     default:
1161       break;
1162   }
1163
1164   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1165   if (ret == GST_STATE_CHANGE_FAILURE)
1166     return ret;
1167
1168   switch (transition) {
1169     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1170       break;
1171     case GST_STATE_CHANGE_PAUSED_TO_READY:
1172     {
1173       GstBuffer *buf[2];
1174
1175       GST_GLIMAGE_SINK_LOCK (glimage_sink);
1176       /* mark the redisplay_texture as unavailable (=0)
1177        * to avoid drawing
1178        */
1179       glimage_sink->redisplay_texture = 0;
1180       buf[0] = glimage_sink->stored_buffer[0];
1181       buf[1] = glimage_sink->stored_buffer[1];
1182       glimage_sink->stored_buffer[0] = glimage_sink->stored_buffer[1] = NULL;
1183       glimage_sink->stored_sync_meta = glimage_sink->next_sync_meta = NULL;
1184
1185       if (glimage_sink->stored_sync)
1186         gst_buffer_unref (glimage_sink->stored_sync);
1187       glimage_sink->stored_sync = NULL;
1188
1189       GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1190
1191       gst_buffer_replace (buf, NULL);
1192       gst_buffer_replace (buf + 1, NULL);
1193
1194       gst_object_replace ((GstObject **) & glimage_sink->convert_views, NULL);
1195       gst_buffer_replace (&glimage_sink->input_buffer, NULL);
1196       gst_buffer_replace (&glimage_sink->input_buffer2, NULL);
1197       gst_buffer_replace (&glimage_sink->next_buffer, NULL);
1198       gst_buffer_replace (&glimage_sink->next_buffer2, NULL);
1199       gst_buffer_replace (&glimage_sink->next_sync, NULL);
1200
1201       glimage_sink->window_id = 0;
1202       /* but do not reset glimage_sink->new_window_id */
1203
1204       GST_VIDEO_SINK_WIDTH (glimage_sink) = 1;
1205       GST_VIDEO_SINK_HEIGHT (glimage_sink) = 1;
1206       /* Clear cached caps */
1207       if (glimage_sink->out_caps) {
1208         gst_caps_unref (glimage_sink->out_caps);
1209         glimage_sink->out_caps = NULL;
1210       }
1211       if (glimage_sink->in_caps) {
1212         gst_caps_unref (glimage_sink->in_caps);
1213         glimage_sink->in_caps = NULL;
1214       }
1215       break;
1216     }
1217     case GST_STATE_CHANGE_READY_TO_NULL:
1218       if (glimage_sink->overlay_compositor) {
1219         gst_object_unref (glimage_sink->overlay_compositor);
1220         glimage_sink->overlay_compositor = NULL;
1221       }
1222
1223       if (glimage_sink->context) {
1224         GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context);
1225
1226         gst_gl_window_send_message (window,
1227             GST_GL_WINDOW_CB (gst_glimage_sink_cleanup_glthread), glimage_sink);
1228
1229         gst_gl_window_set_resize_callback (window, NULL, NULL, NULL);
1230         gst_gl_window_set_draw_callback (window, NULL, NULL, NULL);
1231         gst_gl_window_set_close_callback (window, NULL, NULL, NULL);
1232
1233         if (glimage_sink->key_sig_id)
1234           g_signal_handler_disconnect (window, glimage_sink->key_sig_id);
1235         glimage_sink->key_sig_id = 0;
1236         if (glimage_sink->mouse_sig_id)
1237           g_signal_handler_disconnect (window, glimage_sink->mouse_sig_id);
1238         glimage_sink->mouse_sig_id = 0;
1239
1240         gst_object_unref (window);
1241         gst_object_unref (glimage_sink->context);
1242         glimage_sink->context = NULL;
1243       }
1244
1245       glimage_sink->window_id = 0;
1246
1247       if (glimage_sink->other_context) {
1248         gst_object_unref (glimage_sink->other_context);
1249         glimage_sink->other_context = NULL;
1250       }
1251
1252       if (glimage_sink->display) {
1253         gst_object_unref (glimage_sink->display);
1254         glimage_sink->display = NULL;
1255       }
1256       break;
1257     default:
1258       break;
1259   }
1260
1261   return ret;
1262 }
1263
1264 static void
1265 gst_glimage_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1266     GstClockTime * start, GstClockTime * end)
1267 {
1268   GstGLImageSink *glimagesink;
1269
1270   glimagesink = GST_GLIMAGE_SINK (bsink);
1271
1272   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1273     *start = GST_BUFFER_TIMESTAMP (buf);
1274     if (GST_BUFFER_DURATION_IS_VALID (buf))
1275       *end = *start + GST_BUFFER_DURATION (buf);
1276     else {
1277       if (GST_VIDEO_INFO_FPS_N (&glimagesink->out_info) > 0) {
1278         *end = *start +
1279             gst_util_uint64_scale_int (GST_SECOND,
1280             GST_VIDEO_INFO_FPS_D (&glimagesink->out_info),
1281             GST_VIDEO_INFO_FPS_N (&glimagesink->out_info));
1282       }
1283     }
1284   }
1285 }
1286
1287 static GstCaps *
1288 gst_glimage_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
1289 {
1290   GstCaps *tmp = NULL;
1291   GstCaps *result = NULL;
1292
1293   tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
1294
1295   if (filter) {
1296     GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
1297         filter);
1298
1299     result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1300     gst_caps_unref (tmp);
1301   } else {
1302     result = tmp;
1303   }
1304
1305   result = gst_gl_overlay_compositor_add_caps (result);
1306
1307   GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
1308
1309   return result;
1310 }
1311
1312 static gboolean
1313 configure_display_from_info (GstGLImageSink * glimage_sink,
1314     GstVideoInfo * vinfo)
1315 {
1316   gint width;
1317   gint height;
1318   gboolean ok;
1319   gint par_n, par_d;
1320   gint display_par_n, display_par_d;
1321   guint display_ratio_num, display_ratio_den;
1322
1323   width = GST_VIDEO_INFO_WIDTH (vinfo);
1324   height = GST_VIDEO_INFO_HEIGHT (vinfo);
1325
1326   par_n = GST_VIDEO_INFO_PAR_N (vinfo);
1327   par_d = GST_VIDEO_INFO_PAR_D (vinfo);
1328
1329   if (!par_n)
1330     par_n = 1;
1331
1332   /* get display's PAR */
1333   if (glimage_sink->par_n != 0 && glimage_sink->par_d != 0) {
1334     display_par_n = glimage_sink->par_n;
1335     display_par_d = glimage_sink->par_d;
1336   } else {
1337     display_par_n = 1;
1338     display_par_d = 1;
1339   }
1340
1341   ok = gst_video_calculate_display_ratio (&display_ratio_num,
1342       &display_ratio_den, width, height, par_n, par_d, display_par_n,
1343       display_par_d);
1344
1345   if (!ok)
1346     return FALSE;
1347
1348   GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
1349       display_par_d);
1350
1351   if (height % display_ratio_den == 0) {
1352     GST_DEBUG ("keeping video height");
1353     GST_VIDEO_SINK_WIDTH (glimage_sink) = (guint)
1354         gst_util_uint64_scale_int (height, display_ratio_num,
1355         display_ratio_den);
1356     GST_VIDEO_SINK_HEIGHT (glimage_sink) = height;
1357   } else if (width % display_ratio_num == 0) {
1358     GST_DEBUG ("keeping video width");
1359     GST_VIDEO_SINK_WIDTH (glimage_sink) = width;
1360     GST_VIDEO_SINK_HEIGHT (glimage_sink) = (guint)
1361         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
1362   } else {
1363     GST_DEBUG ("approximating while keeping video height");
1364     GST_VIDEO_SINK_WIDTH (glimage_sink) = (guint)
1365         gst_util_uint64_scale_int (height, display_ratio_num,
1366         display_ratio_den);
1367     GST_VIDEO_SINK_HEIGHT (glimage_sink) = height;
1368   }
1369   GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (glimage_sink),
1370       GST_VIDEO_SINK_HEIGHT (glimage_sink));
1371
1372   return TRUE;
1373 }
1374
1375 /* Called with GST_GLIMAGE_SINK lock held, to
1376  * copy in_info to out_info and update out_caps */
1377 static gboolean
1378 update_output_format (GstGLImageSink * glimage_sink)
1379 {
1380   GstVideoInfo *out_info = &glimage_sink->out_info;
1381   gboolean input_is_mono = FALSE;
1382   GstVideoMultiviewMode mv_mode;
1383   GstGLWindow *window = NULL;
1384   GstGLTextureTarget previous_target;
1385   GstStructure *s;
1386   const gchar *target_str;
1387   GstCaps *out_caps;
1388   gboolean ret;
1389
1390   *out_info = glimage_sink->in_info;
1391   previous_target = glimage_sink->texture_target;
1392
1393   mv_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (&glimage_sink->in_info);
1394
1395   if (mv_mode == GST_VIDEO_MULTIVIEW_MODE_NONE ||
1396       mv_mode == GST_VIDEO_MULTIVIEW_MODE_MONO ||
1397       mv_mode == GST_VIDEO_MULTIVIEW_MODE_LEFT ||
1398       mv_mode == GST_VIDEO_MULTIVIEW_MODE_RIGHT)
1399     input_is_mono = TRUE;
1400
1401   if (input_is_mono == FALSE &&
1402       glimage_sink->mview_output_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) {
1403     /* Input is multiview, and output wants a conversion - configure 3d converter now,
1404      * otherwise defer it until either the caps or the 3D output mode changes */
1405     gst_video_multiview_video_info_change_mode (out_info,
1406         glimage_sink->mview_output_mode, glimage_sink->mview_output_flags);
1407
1408     if (glimage_sink->convert_views == NULL) {
1409       glimage_sink->convert_views = gst_gl_view_convert_new ();
1410       gst_gl_view_convert_set_context (glimage_sink->convert_views,
1411           glimage_sink->context);
1412     }
1413   } else {
1414     if (glimage_sink->convert_views) {
1415       gst_object_unref (glimage_sink->convert_views);
1416       glimage_sink->convert_views = NULL;
1417     }
1418   }
1419
1420   ret = configure_display_from_info (glimage_sink, out_info);
1421
1422   if (glimage_sink->convert_views) {
1423     /* Match actual output window size for pixel-aligned output,
1424      * even though we can't necessarily match the starting left/right
1425      * view parity properly */
1426     glimage_sink->out_info.width = MAX (1, glimage_sink->display_rect.w);
1427     glimage_sink->out_info.height = MAX (1, glimage_sink->display_rect.h);
1428     GST_LOG_OBJECT (glimage_sink, "Set 3D output scale to %d,%d",
1429         glimage_sink->display_rect.w, glimage_sink->display_rect.h);
1430   }
1431
1432   s = gst_caps_get_structure (glimage_sink->in_caps, 0);
1433   target_str = gst_structure_get_string (s, "texture-target");
1434
1435   if (!target_str)
1436     target_str = GST_GL_TEXTURE_TARGET_2D_STR;
1437
1438   glimage_sink->texture_target = gst_gl_texture_target_from_string (target_str);
1439   if (!glimage_sink->texture_target)
1440     return FALSE;
1441
1442   out_caps = gst_video_info_to_caps (out_info);
1443   gst_caps_set_features (out_caps, 0,
1444       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));
1445   gst_caps_set_simple (out_caps, "texture-target", G_TYPE_STRING,
1446       target_str, NULL);
1447
1448   if (glimage_sink->convert_views) {
1449     gst_caps_set_simple (out_caps, "texture-target", G_TYPE_STRING,
1450         GST_GL_TEXTURE_TARGET_2D_STR, NULL);
1451
1452     glimage_sink->texture_target = GST_GL_TEXTURE_TARGET_2D;
1453
1454     GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1455     gst_gl_view_convert_set_caps (glimage_sink->convert_views,
1456         glimage_sink->in_caps, out_caps);
1457     g_object_set (glimage_sink->convert_views, "downmix-mode",
1458         glimage_sink->mview_downmix_mode, NULL);
1459     GST_GLIMAGE_SINK_LOCK (glimage_sink);
1460   }
1461
1462   if (glimage_sink->out_caps)
1463     gst_caps_unref (glimage_sink->out_caps);
1464   glimage_sink->out_caps = out_caps;
1465
1466   if (previous_target != GST_GL_TEXTURE_TARGET_NONE &&
1467       glimage_sink->texture_target != previous_target) {
1468     /* regenerate the shader for the changed target */
1469     GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context);
1470     gst_gl_window_send_message (window,
1471         GST_GL_WINDOW_CB (gst_glimage_sink_cleanup_glthread), glimage_sink);
1472     gst_object_unref (window);
1473   }
1474
1475   glimage_sink->output_mode_changed = FALSE;
1476
1477   if (glimage_sink->context)
1478     window = gst_gl_context_get_window (glimage_sink->context);
1479   if (window) {
1480     gst_gl_window_queue_resize (window);
1481     gst_object_unref (window);
1482   }
1483
1484   return ret;
1485 }
1486
1487 static gboolean
1488 gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1489 {
1490   GstGLImageSink *glimage_sink;
1491   gboolean ok;
1492   GstVideoInfo vinfo;
1493
1494   GST_DEBUG_OBJECT (bsink, "set caps with %" GST_PTR_FORMAT, caps);
1495
1496   glimage_sink = GST_GLIMAGE_SINK (bsink);
1497
1498   ok = gst_video_info_from_caps (&vinfo, caps);
1499   if (!ok)
1500     return FALSE;
1501
1502   if (!_ensure_gl_setup (glimage_sink))
1503     return FALSE;
1504
1505   GST_GLIMAGE_SINK_LOCK (glimage_sink);
1506   if (glimage_sink->in_caps)
1507     gst_caps_unref (glimage_sink->in_caps);
1508   glimage_sink->in_caps = gst_caps_ref (caps);
1509   glimage_sink->in_info = vinfo;
1510   ok = update_output_format (glimage_sink);
1511
1512   GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1513
1514   return ok;
1515 }
1516
1517 /* Take the input_buffer and run it through 3D conversion if needed.
1518  * Called with glimagesink lock, but might drop it temporarily */
1519 static gboolean
1520 prepare_next_buffer (GstGLImageSink * glimage_sink)
1521 {
1522   GstBuffer *in_buffer, *next_buffer, *old_buffer;
1523   GstBuffer *in_buffer2 = NULL, *next_buffer2 = NULL, *old_buffer2;
1524   GstBuffer *next_sync = NULL, *old_sync;
1525   GstGLSyncMeta *sync_meta;
1526   GstVideoFrame gl_frame;
1527   GstGLViewConvert *convert_views = NULL;
1528   GstVideoInfo *info;
1529
1530   if (glimage_sink->input_buffer == NULL)
1531     return TRUE;                /* No input buffer to process */
1532
1533   if (GST_VIDEO_INFO_MULTIVIEW_MODE (&glimage_sink->in_info) ==
1534       GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME) {
1535     if (glimage_sink->input_buffer2 == NULL)
1536       return TRUE;              /* Need 2nd input buffer to process */
1537     in_buffer2 = gst_buffer_ref (glimage_sink->input_buffer2);
1538   }
1539
1540   in_buffer = gst_buffer_ref (glimage_sink->input_buffer);
1541   if (glimage_sink->convert_views &&
1542       (GST_VIDEO_INFO_MULTIVIEW_MODE (&glimage_sink->in_info) !=
1543           GST_VIDEO_INFO_MULTIVIEW_MODE (&glimage_sink->out_info) ||
1544           GST_VIDEO_INFO_MULTIVIEW_FLAGS (&glimage_sink->in_info) !=
1545           GST_VIDEO_INFO_MULTIVIEW_FLAGS (&glimage_sink->out_info)))
1546     convert_views = gst_object_ref (glimage_sink->convert_views);
1547
1548   GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1549
1550   if (convert_views) {
1551     info = &glimage_sink->out_info;
1552
1553     if (gst_gl_view_convert_submit_input_buffer (glimage_sink->convert_views,
1554             GST_BUFFER_IS_DISCONT (in_buffer), in_buffer) != GST_FLOW_OK) {
1555       gst_buffer_replace (&in_buffer2, NULL);
1556       goto fail;
1557     }
1558     if (in_buffer2) {
1559       if (gst_gl_view_convert_submit_input_buffer (glimage_sink->convert_views,
1560               GST_BUFFER_IS_DISCONT (in_buffer2), in_buffer2) != GST_FLOW_OK) {
1561         goto fail;
1562       }
1563     }
1564
1565     if (gst_gl_view_convert_get_output (glimage_sink->convert_views,
1566             &next_buffer) != GST_FLOW_OK)
1567       goto fail;
1568     if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) ==
1569         GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME) {
1570       if (gst_gl_view_convert_get_output (glimage_sink->convert_views,
1571               &next_buffer2) != GST_FLOW_OK)
1572         goto fail;
1573     }
1574     gst_object_unref (convert_views);
1575     convert_views = NULL;
1576
1577     if (next_buffer == NULL) {
1578       /* Not ready to paint a buffer yet */
1579       GST_GLIMAGE_SINK_LOCK (glimage_sink);
1580       return TRUE;
1581     }
1582   } else {
1583     next_buffer = in_buffer;
1584     info = &glimage_sink->in_info;
1585   }
1586
1587   if (!glimage_sink->overlay_compositor) {
1588     if (!(glimage_sink->overlay_compositor =
1589             gst_gl_overlay_compositor_new (glimage_sink->context))) {
1590       gst_buffer_unref (next_buffer);
1591       goto fail;
1592     }
1593   }
1594
1595   gst_gl_overlay_compositor_upload_overlays (glimage_sink->overlay_compositor,
1596       next_buffer);
1597
1598   sync_meta = gst_buffer_get_gl_sync_meta (next_buffer);
1599
1600   if (!sync_meta) {
1601     next_sync = gst_buffer_new ();
1602     sync_meta = gst_buffer_add_gl_sync_meta (glimage_sink->context, next_sync);
1603     gst_gl_sync_meta_set_sync_point (sync_meta, glimage_sink->context);
1604   }
1605
1606   /* in_buffer invalid now */
1607   if (!gst_video_frame_map (&gl_frame, info, next_buffer,
1608           GST_MAP_READ | GST_MAP_GL)) {
1609     gst_buffer_unref (next_buffer);
1610     GST_ERROR ("Failed to map video frame.");
1611     goto fail;
1612   }
1613
1614   GST_GLIMAGE_SINK_LOCK (glimage_sink);
1615   glimage_sink->next_tex = *(guint *) gl_frame.data[0];
1616
1617   old_buffer = glimage_sink->next_buffer;
1618   glimage_sink->next_buffer = next_buffer;
1619   old_buffer2 = glimage_sink->next_buffer2;
1620   glimage_sink->next_buffer2 = next_buffer2;
1621
1622   old_sync = glimage_sink->next_sync;
1623   glimage_sink->next_sync = next_sync;
1624   glimage_sink->next_sync_meta = sync_meta;
1625
1626   /* Need to drop the lock again, to avoid a deadlock if we're
1627    * dropping the last ref on this buffer and it goes back to our
1628    * allocator */
1629   GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1630
1631   if (old_buffer)
1632     gst_buffer_unref (old_buffer);
1633   if (old_buffer2)
1634     gst_buffer_unref (old_buffer2);
1635   if (old_sync)
1636     gst_buffer_unref (old_sync);
1637   gst_video_frame_unmap (&gl_frame);
1638
1639   GST_GLIMAGE_SINK_LOCK (glimage_sink);
1640
1641   return TRUE;
1642
1643 fail:
1644   if (convert_views)
1645     gst_object_unref (convert_views);
1646   GST_GLIMAGE_SINK_LOCK (glimage_sink);
1647   return FALSE;
1648 }
1649
1650 static GstFlowReturn
1651 gst_glimage_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
1652 {
1653   GstGLImageSink *glimage_sink;
1654   GstGLSyncMeta *sync_meta;
1655   GstBuffer **target;
1656   GstBuffer *old_input;
1657
1658   glimage_sink = GST_GLIMAGE_SINK (bsink);
1659
1660   GST_TRACE ("preparing buffer:%p", buf);
1661
1662   if (GST_VIDEO_SINK_WIDTH (glimage_sink) < 1 ||
1663       GST_VIDEO_SINK_HEIGHT (glimage_sink) < 1) {
1664     return GST_FLOW_NOT_NEGOTIATED;
1665   }
1666
1667   if (!_ensure_gl_setup (glimage_sink))
1668     return GST_FLOW_NOT_NEGOTIATED;
1669
1670   sync_meta = gst_buffer_get_gl_sync_meta (buf);
1671   if (sync_meta)
1672     gst_gl_sync_meta_wait (sync_meta, glimage_sink->context);
1673
1674   GST_GLIMAGE_SINK_LOCK (glimage_sink);
1675   if (glimage_sink->window_resized) {
1676     glimage_sink->window_resized = FALSE;
1677     GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1678     GST_DEBUG_OBJECT (glimage_sink, "Sending reconfigure event on sinkpad.");
1679     gst_pad_push_event (GST_BASE_SINK (glimage_sink)->sinkpad,
1680         gst_event_new_reconfigure ());
1681     GST_GLIMAGE_SINK_LOCK (glimage_sink);
1682   }
1683
1684   target = &glimage_sink->input_buffer;
1685   if (GST_VIDEO_INFO_MULTIVIEW_MODE (&glimage_sink->in_info) ==
1686       GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME &&
1687       !GST_BUFFER_FLAG_IS_SET (buf, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE)) {
1688     target = &glimage_sink->input_buffer2;
1689   }
1690   old_input = *target;
1691   *target = gst_buffer_ref (buf);
1692
1693   if (glimage_sink->output_mode_changed)
1694     update_output_format (glimage_sink);
1695
1696   if (!prepare_next_buffer (glimage_sink)) {
1697     GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1698     if (old_input)
1699       gst_buffer_unref (old_input);
1700     goto convert_views_failed;
1701   }
1702   GST_GLIMAGE_SINK_UNLOCK (glimage_sink);
1703
1704   if (old_input)
1705     gst_buffer_unref (old_input);
1706
1707   if (glimage_sink->window_id != glimage_sink->new_window_id) {
1708     GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context);
1709
1710     glimage_sink->window_id = glimage_sink->new_window_id;
1711     gst_gl_window_set_window_handle (window, glimage_sink->window_id);
1712
1713     gst_object_unref (window);
1714   }
1715
1716   return GST_FLOW_OK;
1717 convert_views_failed:
1718   {
1719     GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND,
1720         ("%s", "Failed to convert multiview video buffer"), (NULL));
1721     return GST_FLOW_ERROR;
1722   }
1723 }
1724
1725 static GstFlowReturn
1726 gst_glimage_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1727 {
1728   GstGLImageSink *glimage_sink;
1729
1730   GST_TRACE ("rendering buffer:%p", buf);
1731
1732   glimage_sink = GST_GLIMAGE_SINK (vsink);
1733
1734   GST_TRACE ("redisplay texture:%u of size:%ux%u, window size:%ux%u",
1735       glimage_sink->next_tex, GST_VIDEO_INFO_WIDTH (&glimage_sink->out_info),
1736       GST_VIDEO_INFO_HEIGHT (&glimage_sink->out_info),
1737       GST_VIDEO_SINK_WIDTH (glimage_sink),
1738       GST_VIDEO_SINK_HEIGHT (glimage_sink));
1739
1740   /* Ask the underlying window to redraw its content */
1741   if (!gst_glimage_sink_redisplay (glimage_sink))
1742     goto redisplay_failed;
1743
1744   GST_TRACE ("post redisplay");
1745
1746   if (g_atomic_int_get (&glimage_sink->to_quit) != 0) {
1747     GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND,
1748         ("%s", "Quit requested"), (NULL));
1749     return GST_FLOW_ERROR;
1750   }
1751
1752   return GST_FLOW_OK;
1753
1754 /* ERRORS */
1755 redisplay_failed:
1756   {
1757     GST_ELEMENT_ERROR (glimage_sink, RESOURCE, NOT_FOUND,
1758         ("%s", "Window redisplay failed"), (NULL));
1759     return GST_FLOW_ERROR;
1760   }
1761 }
1762
1763 static void
1764 gst_glimage_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1765 {
1766   iface->set_window_handle = gst_glimage_sink_set_window_handle;
1767   iface->set_render_rectangle = gst_glimage_sink_set_render_rectangle;
1768   iface->handle_events = gst_glimage_sink_handle_events;
1769   iface->expose = gst_glimage_sink_expose;
1770 }
1771
1772 static void
1773 gst_glimage_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1774 {
1775   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay);
1776   guintptr window_id = (guintptr) id;
1777
1778   g_return_if_fail (GST_IS_GLIMAGE_SINK (overlay));
1779
1780   GST_DEBUG ("set_xwindow_id %" G_GUINT64_FORMAT, (guint64) window_id);
1781
1782   glimage_sink->new_window_id = window_id;
1783 }
1784
1785
1786 static void
1787 gst_glimage_sink_expose (GstVideoOverlay * overlay)
1788 {
1789   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay);
1790
1791   /* redisplay opengl scene */
1792   if (glimage_sink->display && glimage_sink->window_id) {
1793
1794     if (glimage_sink->window_id != glimage_sink->new_window_id) {
1795       GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context);
1796
1797       glimage_sink->window_id = glimage_sink->new_window_id;
1798       gst_gl_window_set_window_handle (window, glimage_sink->window_id);
1799
1800       gst_object_unref (window);
1801     }
1802
1803     gst_glimage_sink_redisplay (glimage_sink);
1804   }
1805 }
1806
1807 static void
1808 gst_glimage_sink_handle_events (GstVideoOverlay * overlay,
1809     gboolean handle_events)
1810 {
1811   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay);
1812
1813   glimage_sink->handle_events = handle_events;
1814   if (G_LIKELY (glimage_sink->context)) {
1815     GstGLWindow *window;
1816     window = gst_gl_context_get_window (glimage_sink->context);
1817     gst_gl_window_handle_events (window, handle_events);
1818     gst_object_unref (window);
1819   }
1820 }
1821
1822 static void
1823 gst_glimage_sink_set_render_rectangle (GstVideoOverlay * overlay,
1824     gint x, gint y, gint width, gint height)
1825 {
1826   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (overlay);
1827
1828   if (G_LIKELY (glimage_sink->context)) {
1829     GstGLWindow *window;
1830     window = gst_gl_context_get_window (glimage_sink->context);
1831     gst_gl_window_set_render_rectangle (window, x, y, width, height);
1832     gst_object_unref (window);
1833   }
1834
1835   glimage_sink->x = x;
1836   glimage_sink->y = y;
1837   glimage_sink->width = width;
1838   glimage_sink->height = height;
1839 }
1840
1841 static gboolean
1842 gst_glimage_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1843 {
1844   GstGLImageSink *glimage_sink = GST_GLIMAGE_SINK (bsink);
1845   GstStructure *config;
1846   GstCaps *caps;
1847   guint size;
1848   gboolean need_pool;
1849   GstStructure *allocation_meta = NULL;
1850
1851   if (!_ensure_gl_setup (glimage_sink))
1852     return FALSE;
1853
1854   gst_query_parse_allocation (query, &caps, &need_pool);
1855
1856   if (caps == NULL)
1857     goto no_caps;
1858
1859   if (need_pool) {
1860     GstBufferPool *pool;
1861     GstVideoInfo info;
1862
1863     if (!gst_video_info_from_caps (&info, caps))
1864       goto invalid_caps;
1865
1866     /* the normal size of a frame */
1867     size = info.size;
1868
1869     GST_DEBUG_OBJECT (glimage_sink, "create new pool");
1870
1871     pool = gst_gl_buffer_pool_new (glimage_sink->context);
1872     config = gst_buffer_pool_get_config (pool);
1873     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1874     gst_buffer_pool_config_add_option (config,
1875         GST_BUFFER_POOL_OPTION_GL_SYNC_META);
1876
1877     if (!gst_buffer_pool_set_config (pool, config)) {
1878       g_object_unref (pool);
1879       goto config_failed;
1880     }
1881
1882     /* we need at least 2 buffer because we hold on to the last one */
1883     gst_query_add_allocation_pool (query, pool, size, 2, 0);
1884     g_object_unref (pool);
1885   }
1886
1887   if (glimage_sink->context->gl_vtable->FenceSync)
1888     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
1889
1890   if (glimage_sink->window_width != 0 && glimage_sink->window_height != 0) {
1891     allocation_meta =
1892         gst_structure_new ("GstVideoOverlayCompositionMeta",
1893         "width", G_TYPE_UINT, glimage_sink->window_width,
1894         "height", G_TYPE_UINT, glimage_sink->window_height, NULL);
1895     GST_DEBUG ("sending alloc query with size %dx%d",
1896         glimage_sink->window_width, glimage_sink->window_height);
1897   }
1898
1899   gst_query_add_allocation_meta (query,
1900       GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
1901   gst_query_add_allocation_meta (query,
1902       GST_VIDEO_AFFINE_TRANSFORMATION_META_API_TYPE, 0);
1903
1904   if (allocation_meta)
1905     gst_structure_free (allocation_meta);
1906
1907   return TRUE;
1908
1909   /* ERRORS */
1910 no_caps:
1911   {
1912     GST_WARNING_OBJECT (bsink, "no caps specified");
1913     return FALSE;
1914   }
1915 invalid_caps:
1916   {
1917     GST_WARNING_OBJECT (bsink, "invalid caps specified");
1918     return FALSE;
1919   }
1920 config_failed:
1921   {
1922     GST_WARNING_OBJECT (bsink, "failed setting config");
1923     return FALSE;
1924   }
1925 }
1926
1927 /* *INDENT-OFF* */
1928 static const GLfloat vertices[] = {
1929      1.0f,  1.0f, 0.0f, 1.0f, 0.0f,
1930     -1.0f,  1.0f, 0.0f, 0.0f, 0.0f,
1931     -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1932      1.0f, -1.0f, 0.0f, 1.0f, 1.0f
1933 };
1934
1935 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
1936 /* *INDENT-ON* */
1937
1938 static void
1939 _bind_buffer (GstGLImageSink * gl_sink)
1940 {
1941   const GstGLFuncs *gl = gl_sink->context->gl_vtable;
1942
1943   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, gl_sink->vbo_indices);
1944   gl->BindBuffer (GL_ARRAY_BUFFER, gl_sink->vertex_buffer);
1945
1946   /* Load the vertex position */
1947   gl->VertexAttribPointer (gl_sink->attr_position, 3, GL_FLOAT, GL_FALSE,
1948       5 * sizeof (GLfloat), (void *) 0);
1949
1950   /* Load the texture coordinate */
1951   gl->VertexAttribPointer (gl_sink->attr_texture, 2, GL_FLOAT, GL_FALSE,
1952       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
1953
1954   gl->EnableVertexAttribArray (gl_sink->attr_position);
1955   gl->EnableVertexAttribArray (gl_sink->attr_texture);
1956 }
1957
1958 static void
1959 _unbind_buffer (GstGLImageSink * gl_sink)
1960 {
1961   const GstGLFuncs *gl = gl_sink->context->gl_vtable;
1962
1963   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
1964   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
1965
1966   gl->DisableVertexAttribArray (gl_sink->attr_position);
1967   gl->DisableVertexAttribArray (gl_sink->attr_texture);
1968 }
1969
1970 /* Called in the gl thread */
1971 static void
1972 gst_glimage_sink_thread_init_redisplay (GstGLImageSink * gl_sink)
1973 {
1974   const GstGLFuncs *gl = gl_sink->context->gl_vtable;
1975   GError *error = NULL;
1976   GstGLSLStage *frag_stage, *vert_stage;
1977
1978   vert_stage = gst_glsl_stage_new_with_string (gl_sink->context,
1979       GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
1980       GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
1981       gst_gl_shader_string_vertex_mat4_vertex_transform);
1982   if (gl_sink->texture_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
1983     frag_stage = gst_glsl_stage_new_with_string (gl_sink->context,
1984         GL_FRAGMENT_SHADER, GST_GLSL_VERSION_NONE,
1985         GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
1986         gst_gl_shader_string_fragment_external_oes_default);
1987   } else {
1988     frag_stage = gst_glsl_stage_new_default_fragment (gl_sink->context);
1989   }
1990   if (!vert_stage || !frag_stage) {
1991     GST_ERROR_OBJECT (gl_sink, "Failed to retreive fragment shader for "
1992         "texture target");
1993     if (vert_stage)
1994       gst_object_unref (vert_stage);
1995     if (frag_stage)
1996       gst_object_unref (frag_stage);
1997     gst_glimage_sink_cleanup_glthread (gl_sink);
1998     return;
1999   }
2000
2001   if (!(gl_sink->redisplay_shader =
2002           gst_gl_shader_new_link_with_stages (gl_sink->context, &error,
2003               vert_stage, frag_stage, NULL))) {
2004     GST_ERROR_OBJECT (gl_sink, "Failed to link shader: %s", error->message);
2005     gst_glimage_sink_cleanup_glthread (gl_sink);
2006     return;
2007   }
2008
2009   gl_sink->attr_position =
2010       gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader,
2011       "a_position");
2012   gl_sink->attr_texture =
2013       gst_gl_shader_get_attribute_location (gl_sink->redisplay_shader,
2014       "a_texcoord");
2015
2016   if (gl->GenVertexArrays) {
2017     gl->GenVertexArrays (1, &gl_sink->vao);
2018     gl->BindVertexArray (gl_sink->vao);
2019   }
2020
2021   if (!gl_sink->vertex_buffer) {
2022     gl->GenBuffers (1, &gl_sink->vertex_buffer);
2023     gl->BindBuffer (GL_ARRAY_BUFFER, gl_sink->vertex_buffer);
2024     gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
2025         GL_STATIC_DRAW);
2026   }
2027
2028   if (!gl_sink->vbo_indices) {
2029     gl->GenBuffers (1, &gl_sink->vbo_indices);
2030     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, gl_sink->vbo_indices);
2031     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
2032         GL_STATIC_DRAW);
2033   }
2034
2035   if (gl->GenVertexArrays) {
2036     _bind_buffer (gl_sink);
2037     gl->BindVertexArray (0);
2038   }
2039
2040   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
2041   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
2042 }
2043
2044 static void
2045 gst_glimage_sink_cleanup_glthread (GstGLImageSink * gl_sink)
2046 {
2047   const GstGLFuncs *gl = gl_sink->context->gl_vtable;
2048
2049   if (gl_sink->redisplay_shader) {
2050     gst_object_unref (gl_sink->redisplay_shader);
2051     gl_sink->redisplay_shader = NULL;
2052   }
2053
2054   if (gl_sink->vao) {
2055     gl->DeleteVertexArrays (1, &gl_sink->vao);
2056     gl_sink->vao = 0;
2057   }
2058
2059   if (gl_sink->vertex_buffer) {
2060     gl->DeleteBuffers (1, &gl_sink->vertex_buffer);
2061     gl_sink->vertex_buffer = 0;
2062   }
2063
2064   if (gl_sink->vbo_indices) {
2065     gl->DeleteBuffers (1, &gl_sink->vbo_indices);
2066     gl_sink->vbo_indices = 0;
2067   }
2068
2069   if (gl_sink->overlay_compositor)
2070     gst_gl_overlay_compositor_free_overlays (gl_sink->overlay_compositor);
2071 }
2072
2073 /* Called with object lock held */
2074 static void
2075 gst_glimage_sink_on_resize (GstGLImageSink * gl_sink, gint width, gint height)
2076 {
2077   /* Here gl_sink members (ex:gl_sink->out_info) have a life time of set_caps.
2078    * It means that they cannot change between two set_caps
2079    */
2080   const GstGLFuncs *gl;
2081   gboolean do_reshape;
2082
2083   GST_DEBUG_OBJECT (gl_sink, "GL Window resized to %ux%u", width, height);
2084
2085   /* check if a client reshape callback is registered */
2086   g_signal_emit (gl_sink, gst_glimage_sink_signals[CLIENT_RESHAPE_SIGNAL], 0,
2087       gl_sink->context, width, height, &do_reshape);
2088   GST_GLIMAGE_SINK_LOCK (gl_sink);
2089
2090   gl = gl_sink->context->gl_vtable;
2091   width = MAX (1, width);
2092   height = MAX (1, height);
2093
2094   /* Check if we would suggest a different width/height now */
2095   gl_sink->window_resized = ((gl_sink->window_width != width)
2096       || (gl_sink->window_height != height))
2097       && (gl_sink->window_width != 0)
2098       && (gl_sink->window_height != 0);
2099
2100   gl_sink->window_width = width;
2101   gl_sink->window_height = height;
2102
2103   gst_gl_insert_debug_marker (gl_sink->context, "%s window resize to %ix%i",
2104       GST_OBJECT_NAME (gl_sink), width, height);
2105
2106   /* default reshape */
2107   if (!do_reshape) {
2108     if (gl_sink->keep_aspect_ratio) {
2109       GstVideoRectangle src, dst, result;
2110
2111       src.x = 0;
2112       src.y = 0;
2113       if (gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_90R
2114           || gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_90L
2115           || gl_sink->current_rotate_method == GST_GL_ROTATE_METHOD_FLIP_UL_LR
2116           || gl_sink->current_rotate_method ==
2117           GST_GL_ROTATE_METHOD_FLIP_UR_LL) {
2118         src.h = GST_VIDEO_SINK_WIDTH (gl_sink);
2119         src.w = GST_VIDEO_SINK_HEIGHT (gl_sink);
2120       } else {
2121         src.w = GST_VIDEO_SINK_WIDTH (gl_sink);
2122         src.h = GST_VIDEO_SINK_HEIGHT (gl_sink);
2123       }
2124
2125       dst.x = 0;
2126       dst.y = 0;
2127       dst.w = width;
2128       dst.h = height;
2129
2130       gst_video_sink_center_rect (src, dst, &result, TRUE);
2131       gl_sink->output_mode_changed |= (result.w != gl_sink->display_rect.w);
2132       gl_sink->output_mode_changed |= (result.h != gl_sink->display_rect.h);
2133       gl_sink->display_rect = result;
2134     } else {
2135       gl_sink->output_mode_changed |= (width != gl_sink->display_rect.w);
2136       gl_sink->output_mode_changed |= (height != gl_sink->display_rect.h);
2137
2138       gl_sink->display_rect.x = 0;
2139       gl_sink->display_rect.y = 0;
2140       gl_sink->display_rect.w = width;
2141       gl_sink->display_rect.h = height;
2142     }
2143
2144     gl->Viewport (gl_sink->display_rect.x, gl_sink->display_rect.y,
2145         gl_sink->display_rect.w, gl_sink->display_rect.h);
2146     GST_DEBUG_OBJECT (gl_sink, "GL output area now %u,%u %ux%u",
2147         gl_sink->display_rect.x, gl_sink->display_rect.y,
2148         gl_sink->display_rect.w, gl_sink->display_rect.h);
2149   }
2150   GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2151 }
2152
2153 static void
2154 gst_glimage_sink_on_draw (GstGLImageSink * gl_sink)
2155 {
2156   /* Here gl_sink members (ex:gl_sink->out_info) have a life time of set_caps.
2157    * It means that they cannot not change between two set_caps as well as
2158    * for the redisplay_texture size.
2159    * Whereas redisplay_texture id changes every sink_render
2160    */
2161
2162   const GstGLFuncs *gl = NULL;
2163   GstGLWindow *window = NULL;
2164   gboolean do_redisplay = FALSE;
2165   GstSample *sample = NULL;
2166   guint gl_target = gst_gl_texture_target_to_gl (gl_sink->texture_target);
2167
2168   g_return_if_fail (GST_IS_GLIMAGE_SINK (gl_sink));
2169
2170   gl = gl_sink->context->gl_vtable;
2171
2172   GST_GLIMAGE_SINK_LOCK (gl_sink);
2173
2174   /* check if texture is ready for being drawn */
2175   if (!gl_sink->redisplay_texture) {
2176     GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2177     return;
2178   }
2179
2180   window = gst_gl_context_get_window (gl_sink->context);
2181   window->is_drawing = TRUE;
2182
2183   /* opengl scene */
2184   gst_gl_insert_debug_marker (gl_sink->context, "%s element drawing texture %u",
2185       GST_OBJECT_NAME (gl_sink), gl_sink->redisplay_texture);
2186   GST_TRACE ("redrawing texture:%u", gl_sink->redisplay_texture);
2187
2188   if (gl_sink->stored_sync_meta)
2189     gst_gl_sync_meta_wait (gl_sink->stored_sync_meta,
2190         gst_gl_context_get_current ());
2191
2192   /* make sure that the environnement is clean */
2193   gst_gl_context_clear_shader (gl_sink->context);
2194   gl->BindTexture (gl_target, 0);
2195
2196   sample = gst_sample_new (gl_sink->stored_buffer[0],
2197       gl_sink->out_caps, &GST_BASE_SINK (gl_sink)->segment, NULL);
2198   g_signal_emit (gl_sink, gst_glimage_sink_signals[CLIENT_DRAW_SIGNAL], 0,
2199       gl_sink->context, sample, &do_redisplay);
2200   gst_sample_unref (sample);
2201
2202   if (gl_sink->stored_buffer[1]) {
2203     sample = gst_sample_new (gl_sink->stored_buffer[1],
2204         gl_sink->out_caps, &GST_BASE_SINK (gl_sink)->segment, NULL);
2205     g_signal_emit (gl_sink, gst_glimage_sink_signals[CLIENT_DRAW_SIGNAL], 0,
2206         gl_sink->context, sample, &do_redisplay);
2207     gst_sample_unref (sample);
2208   }
2209
2210   if (!do_redisplay) {
2211     gfloat alpha = gl_sink->ignore_alpha ? 1.0f : 0.0f;
2212
2213     gl->ClearColor (0.0, 0.0, 0.0, alpha);
2214     gl->Clear (GL_COLOR_BUFFER_BIT);
2215
2216     if (gl_sink->ignore_alpha) {
2217       gl->BlendColor (0.0, 0.0, 0.0, alpha);
2218       gl->BlendFunc (GL_SRC_ALPHA, GL_CONSTANT_COLOR);
2219       gl->BlendEquation (GL_FUNC_ADD);
2220       gl->Enable (GL_BLEND);
2221     }
2222
2223     gst_gl_shader_use (gl_sink->redisplay_shader);
2224
2225     if (gl->GenVertexArrays)
2226       gl->BindVertexArray (gl_sink->vao);
2227     _bind_buffer (gl_sink);
2228
2229     gl->ActiveTexture (GL_TEXTURE0);
2230     gl->BindTexture (gl_target, gl_sink->redisplay_texture);
2231     gst_gl_shader_set_uniform_1i (gl_sink->redisplay_shader, "tex", 0);
2232     {
2233       GstVideoAffineTransformationMeta *af_meta;
2234       gfloat matrix[16];
2235
2236       af_meta =
2237           gst_buffer_get_video_affine_transformation_meta
2238           (gl_sink->stored_buffer[0]);
2239
2240       if (gl_sink->transform_matrix) {
2241         gfloat tmp[16];
2242
2243         gst_gl_get_affine_transformation_meta_as_ndc_ext (af_meta, tmp);
2244         gst_gl_multiply_matrix4 (tmp, gl_sink->transform_matrix, matrix);
2245       } else {
2246         gst_gl_get_affine_transformation_meta_as_ndc_ext (af_meta, matrix);
2247       }
2248
2249       gst_gl_shader_set_uniform_matrix_4fv (gl_sink->redisplay_shader,
2250           "u_transformation", 1, FALSE, matrix);
2251     }
2252
2253     gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
2254
2255     gl->BindTexture (gl_target, 0);
2256     gst_gl_context_clear_shader (gl_sink->context);
2257
2258     if (gl->GenVertexArrays)
2259       gl->BindVertexArray (0);
2260     _unbind_buffer (gl_sink);
2261
2262     if (gl_sink->ignore_alpha)
2263       gl->Disable (GL_BLEND);
2264
2265     gst_gl_overlay_compositor_draw_overlays (gl_sink->overlay_compositor);
2266   }
2267   /* end default opengl scene */
2268   window->is_drawing = FALSE;
2269   gst_object_unref (window);
2270
2271   GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2272 }
2273
2274 static void
2275 gst_glimage_sink_on_close (GstGLImageSink * gl_sink)
2276 {
2277   GstGLWindow *window;
2278
2279   GST_WARNING_OBJECT (gl_sink, "Output window was closed");
2280
2281   window = gst_gl_context_get_window (gl_sink->context);
2282
2283   if (gl_sink->key_sig_id)
2284     g_signal_handler_disconnect (window, gl_sink->key_sig_id);
2285   gl_sink->key_sig_id = 0;
2286   if (gl_sink->mouse_sig_id)
2287     g_signal_handler_disconnect (window, gl_sink->mouse_sig_id);
2288   gl_sink->mouse_sig_id = 0;
2289
2290   g_atomic_int_set (&gl_sink->to_quit, 1);
2291
2292   gst_object_unref (window);
2293 }
2294
2295 static gboolean
2296 gst_glimage_sink_redisplay (GstGLImageSink * gl_sink)
2297 {
2298   GstGLWindow *window;
2299   GstBuffer *old_stored_buffer[2], *old_sync;
2300   gulong handler_id;
2301
2302   window = gst_gl_context_get_window (gl_sink->context);
2303   if (!window)
2304     return FALSE;
2305
2306   handler_id =
2307       g_signal_handler_find (GST_ELEMENT_PARENT (gl_sink), G_SIGNAL_MATCH_ID,
2308       gst_gl_image_sink_bin_signals[SIGNAL_BIN_CLIENT_DRAW], 0,
2309       NULL, NULL, NULL);
2310
2311   if (G_UNLIKELY (!gl_sink->redisplay_shader) && (!handler_id
2312           || !gl_sink->other_context)) {
2313     gst_gl_window_send_message (window,
2314         GST_GL_WINDOW_CB (gst_glimage_sink_thread_init_redisplay), gl_sink);
2315
2316     /* if the shader is still null it means it failed to be useable */
2317     if (G_UNLIKELY (!gl_sink->redisplay_shader)) {
2318       gst_object_unref (window);
2319       return FALSE;
2320     }
2321
2322     gst_gl_window_set_preferred_size (window, GST_VIDEO_SINK_WIDTH (gl_sink),
2323         GST_VIDEO_SINK_HEIGHT (gl_sink));
2324     gst_gl_window_show (window);
2325   }
2326
2327   /* Recreate the output texture if needed */
2328   GST_GLIMAGE_SINK_LOCK (gl_sink);
2329   if (gl_sink->window_resized) {
2330     gl_sink->window_resized = FALSE;
2331     GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2332     GST_DEBUG_OBJECT (gl_sink, "Sending reconfigure event on sinkpad.");
2333     gst_pad_push_event (GST_BASE_SINK (gl_sink)->sinkpad,
2334         gst_event_new_reconfigure ());
2335     GST_GLIMAGE_SINK_LOCK (gl_sink);
2336   }
2337
2338   if (gl_sink->output_mode_changed && gl_sink->input_buffer != NULL) {
2339     GST_DEBUG ("Recreating output after mode/size change");
2340     update_output_format (gl_sink);
2341     prepare_next_buffer (gl_sink);
2342   }
2343
2344   if (gl_sink->next_buffer == NULL) {
2345     /* Nothing to display yet */
2346     GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2347     gst_object_unref (window);
2348     return TRUE;
2349   }
2350
2351   /* Avoid to release the texture while drawing */
2352   gl_sink->redisplay_texture = gl_sink->next_tex;
2353   old_stored_buffer[0] = gl_sink->stored_buffer[0];
2354   old_stored_buffer[1] = gl_sink->stored_buffer[1];
2355   gl_sink->stored_buffer[0] = gst_buffer_ref (gl_sink->next_buffer);
2356   if (gl_sink->next_buffer2)
2357     gl_sink->stored_buffer[1] = gst_buffer_ref (gl_sink->next_buffer2);
2358   else
2359     gl_sink->stored_buffer[1] = NULL;
2360
2361   old_sync = gl_sink->stored_sync;
2362   if (gl_sink->next_sync)
2363     gl_sink->stored_sync = gst_buffer_ref (gl_sink->next_sync);
2364   else
2365     gl_sink->stored_sync = NULL;
2366   gl_sink->stored_sync_meta = gl_sink->next_sync_meta;
2367   GST_GLIMAGE_SINK_UNLOCK (gl_sink);
2368
2369   gst_buffer_replace (old_stored_buffer, NULL);
2370   gst_buffer_replace (old_stored_buffer + 1, NULL);
2371   if (old_sync)
2372     gst_buffer_unref (old_sync);
2373
2374   /* Drawing is asynchronous: gst_gl_window_draw is not blocking
2375    * It means that it does not wait for stuff to be executed in other threads
2376    */
2377   gst_gl_window_draw (window);
2378   gst_object_unref (window);
2379
2380   return TRUE;
2381 }