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