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