gl/cocoa: avoid deadlock when creating context on the main thread.
[platform/upstream/gstreamer.git] / ext / gl / caopengllayersink.m
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-caopengllayersink
23  *
24  * caopengllayersink renders incoming video frames to CAOpenGLLayer that
25  * can be retreived through the layer property and placed in the Core
26  * Animation render tree.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "caopengllayersink.h"
34 #include <QuartzCore/QuartzCore.h>
35
36 GST_DEBUG_CATEGORY (gst_debug_ca_sink);
37 #define GST_CAT_DEFAULT gst_debug_ca_sink
38
39 #define GST_CA_OPENGL_LAYER_SINK_GET_LOCK(glsink) \
40   (GST_CA_OPENGL_LAYER_SINK(glsink)->drawing_lock)
41 #define GST_CA_OPENGL_LAYER_SINK_LOCK(glsink) \
42   (g_mutex_lock(&GST_CA_OPENGL_LAYER_SINK_GET_LOCK (glsink)))
43 #define GST_CA_OPENGL_LAYER_SINK_UNLOCK(glsink) \
44   (g_mutex_unlock(&GST_CA_OPENGL_LAYER_SINK_GET_LOCK (glsink)))
45
46 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
47 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
48 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
49 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
50 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
51
52 #define SUPPORTED_GL_APIS GST_GL_API_OPENGL | GST_GL_API_GLES2 | GST_GL_API_OPENGL3
53
54 static void gst_ca_opengl_layer_sink_thread_init_redisplay (GstCAOpenGLLayerSink * ca_sink);
55 static void gst_ca_opengl_layer_sink_cleanup_glthread (GstCAOpenGLLayerSink * ca_sink);
56 static void gst_ca_opengl_layer_sink_on_resize (GstCAOpenGLLayerSink * ca_sink,
57     gint width, gint height);
58 static void gst_ca_opengl_layer_sink_on_draw (GstCAOpenGLLayerSink * ca_sink);
59
60 static void gst_ca_opengl_layer_sink_finalize (GObject * object);
61 static void gst_ca_opengl_layer_sink_set_property (GObject * object, guint prop_id,
62     const GValue * value, GParamSpec * param_spec);
63 static void gst_ca_opengl_layer_sink_get_property (GObject * object, guint prop_id,
64     GValue * value, GParamSpec * param_spec);
65
66 static gboolean gst_ca_opengl_layer_sink_stop (GstBaseSink * bsink);
67
68 static gboolean gst_ca_opengl_layer_sink_query (GstBaseSink * bsink, GstQuery * query);
69 static void gst_ca_opengl_layer_sink_set_context (GstElement * element,
70     GstContext * context);
71
72 static GstStateChangeReturn gst_ca_opengl_layer_sink_change_state (GstElement *
73     element, GstStateChange transition);
74
75 static void gst_ca_opengl_layer_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
76     GstClockTime * start, GstClockTime * end);
77 static gboolean gst_ca_opengl_layer_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
78 static GstFlowReturn gst_ca_opengl_layer_sink_prepare (GstBaseSink * bsink,
79     GstBuffer * buf);
80 static GstFlowReturn gst_ca_opengl_layer_sink_show_frame (GstVideoSink * bsink,
81     GstBuffer * buf);
82 static gboolean gst_ca_opengl_layer_sink_propose_allocation (GstBaseSink * bsink,
83     GstQuery * query);
84
85 static GstStaticPadTemplate gst_ca_opengl_layer_sink_template =
86     GST_STATIC_PAD_TEMPLATE ("sink",
87     GST_PAD_SINK,
88     GST_PAD_ALWAYS,
89     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
90         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY,
91             "RGBA") "; "
92         GST_VIDEO_CAPS_MAKE_WITH_FEATURES
93         (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META,
94             "RGBA") "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS))
95     );
96
97 enum
98 {
99   PROP_0,
100   PROP_FORCE_ASPECT_RATIO,
101   PROP_CONTEXT,
102   PROP_LAYER,
103 };
104
105 #define gst_ca_opengl_layer_sink_parent_class parent_class
106 G_DEFINE_TYPE_WITH_CODE (GstCAOpenGLLayerSink, gst_ca_opengl_layer_sink,
107     GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_ca_sink,
108         "caopengllayersink", 0, "CAOpenGLLayer Video Sink"));
109
110 static void
111 gst_ca_opengl_layer_sink_class_init (GstCAOpenGLLayerSinkClass * klass)
112 {
113   GObjectClass *gobject_class;
114   GstElementClass *gstelement_class;
115   GstBaseSinkClass *gstbasesink_class;
116   GstVideoSinkClass *gstvideosink_class;
117   GstElementClass *element_class;
118
119   gobject_class = (GObjectClass *) klass;
120   gstelement_class = (GstElementClass *) klass;
121   gstbasesink_class = (GstBaseSinkClass *) klass;
122   gstvideosink_class = (GstVideoSinkClass *) klass;
123   element_class = GST_ELEMENT_CLASS (klass);
124
125   gobject_class->set_property = gst_ca_opengl_layer_sink_set_property;
126   gobject_class->get_property = gst_ca_opengl_layer_sink_get_property;
127
128   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
129       g_param_spec_boolean ("force-aspect-ratio",
130           "Force aspect ratio",
131           "When enabled, scaling will respect original aspect ratio", TRUE,
132           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133
134   g_object_class_install_property (gobject_class, PROP_CONTEXT,
135       g_param_spec_object ("context",
136           "OpenGL context",
137           "Get OpenGL context",
138           GST_GL_TYPE_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
139
140   g_object_class_install_property (gobject_class, PROP_LAYER,
141       g_param_spec_pointer ("layer", "CAOpenGLLayer",
142           "OpenGL Core Animation layer",
143           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
144
145   gst_element_class_set_metadata (element_class, "CAOpenGLLayer video sink",
146       "Sink/Video", "A video sink based on CAOpenGLLayer",
147       "Matthew Waters <matthew@centricular.com>");
148
149   gst_element_class_add_pad_template (element_class,
150       gst_static_pad_template_get (&gst_ca_opengl_layer_sink_template));
151
152   gobject_class->finalize = gst_ca_opengl_layer_sink_finalize;
153
154   gstelement_class->change_state = gst_ca_opengl_layer_sink_change_state;
155   gstelement_class->set_context = gst_ca_opengl_layer_sink_set_context;
156   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_ca_opengl_layer_sink_query);
157   gstbasesink_class->set_caps = gst_ca_opengl_layer_sink_set_caps;
158   gstbasesink_class->get_times = gst_ca_opengl_layer_sink_get_times;
159   gstbasesink_class->prepare = gst_ca_opengl_layer_sink_prepare;
160   gstbasesink_class->propose_allocation = gst_ca_opengl_layer_sink_propose_allocation;
161   gstbasesink_class->stop = gst_ca_opengl_layer_sink_stop;
162
163   gstvideosink_class->show_frame =
164       GST_DEBUG_FUNCPTR (gst_ca_opengl_layer_sink_show_frame);
165 }
166
167 static void
168 gst_ca_opengl_layer_sink_init (GstCAOpenGLLayerSink * ca_sink)
169 {
170   ca_sink->display = NULL;
171   ca_sink->keep_aspect_ratio = TRUE;
172   ca_sink->pool = NULL;
173   ca_sink->stored_buffer = NULL;
174   ca_sink->redisplay_texture = 0;
175
176   g_mutex_init (&ca_sink->drawing_lock);
177 }
178
179 static void
180 gst_ca_opengl_layer_sink_finalize (GObject * object)
181 {
182   GstCAOpenGLLayerSink *ca_sink;
183
184   g_return_if_fail (GST_IS_CA_OPENGL_LAYER_SINK (object));
185
186   ca_sink = GST_CA_OPENGL_LAYER_SINK (object);
187
188   g_mutex_clear (&ca_sink->drawing_lock);
189
190   GST_DEBUG ("finalized");
191   G_OBJECT_CLASS (parent_class)->finalize (object);
192 }
193
194 static void
195 gst_ca_opengl_layer_sink_set_property (GObject * object, guint prop_id,
196     const GValue * value, GParamSpec * pspec)
197 {
198   GstCAOpenGLLayerSink *ca_sink;
199
200   g_return_if_fail (GST_IS_CA_OPENGL_LAYER_SINK (object));
201
202   ca_sink = GST_CA_OPENGL_LAYER_SINK (object);
203
204   switch (prop_id) {
205     case PROP_FORCE_ASPECT_RATIO:
206     {
207       ca_sink->keep_aspect_ratio = g_value_get_boolean (value);
208       break;
209     }
210     default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
212       break;
213   }
214 }
215
216 static void
217 gst_ca_opengl_layer_sink_get_property (GObject * object, guint prop_id,
218     GValue * value, GParamSpec * pspec)
219 {
220   GstCAOpenGLLayerSink *ca_sink;
221
222   g_return_if_fail (GST_IS_CA_OPENGL_LAYER_SINK (object));
223
224   ca_sink = GST_CA_OPENGL_LAYER_SINK (object);
225
226   switch (prop_id) {
227     case PROP_FORCE_ASPECT_RATIO:
228       g_value_set_boolean (value, ca_sink->keep_aspect_ratio);
229       break;
230     case PROP_CONTEXT:
231       g_value_set_object (value, ca_sink->context);
232       break;
233     case PROP_LAYER:
234       g_value_set_pointer (value, ca_sink->layer);
235       break;
236     default:
237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238       break;
239   }
240 }
241
242 static void
243 _create_layer (gpointer data)
244 {
245   GstCAOpenGLLayerSink *ca_sink = data;
246
247   if (!ca_sink->layer) {
248     ca_sink->layer = [[NSClassFromString(@"GstGLCAOpenGLLayer") alloc]
249         initWithGstGLContext:GST_GL_CONTEXT_COCOA (ca_sink->context)];
250     [ca_sink->layer setDrawCallback:(GstGLWindowCB)gst_ca_opengl_layer_sink_on_draw
251         data:ca_sink notify:NULL];
252     [ca_sink->layer setResizeCallback:(GstGLWindowResizeCB)gst_ca_opengl_layer_sink_on_resize
253         data:ca_sink notify:NULL];
254     g_object_notify (G_OBJECT (ca_sink), "layer");
255   }
256 }
257
258 static void
259 _invoke_on_main (GstGLWindowCB func, gpointer data)
260 {
261   if ([NSThread isMainThread]) {
262     func (data);
263   } else {
264     dispatch_sync (dispatch_get_main_queue (), ^{
265       func (data);
266     });
267   }
268 }
269
270 static gboolean
271 _ensure_gl_setup (GstCAOpenGLLayerSink * ca_sink)
272 {
273   GError *error = NULL;
274
275   if (!gst_gl_ensure_element_data (ca_sink, &ca_sink->display,
276           &ca_sink->other_context))
277     return FALSE;
278
279   gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
280
281   if (!ca_sink->context) {
282     ca_sink->context = gst_gl_context_new (ca_sink->display);
283     if (!ca_sink->context)
284       goto context_creation_error;
285
286     if (!gst_gl_context_create (ca_sink->context, ca_sink->other_context,
287             &error)) {
288       goto context_error;
289     }
290   }
291
292   if (!ca_sink->layer)
293     _invoke_on_main ((GstGLWindowCB) _create_layer, ca_sink);
294
295   return TRUE;
296
297 context_creation_error:
298   {
299     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
300         ("Failed to create GL context"), (NULL));
301     return FALSE;
302   }
303
304 context_error:
305   {
306     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND, ("%s", error->message),
307         (NULL));
308     gst_object_unref (ca_sink->context);
309     ca_sink->context = NULL;
310     return FALSE;
311   }
312 }
313
314 static gboolean
315 gst_ca_opengl_layer_sink_query (GstBaseSink * bsink, GstQuery * query)
316 {
317   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
318   gboolean res = FALSE;
319
320   switch (GST_QUERY_TYPE (query)) {
321     case GST_QUERY_CONTEXT:
322     {
323       gboolean ret =
324           gst_gl_handle_context_query ((GstElement *) ca_sink, query,
325           &ca_sink->display, &ca_sink->other_context);
326       if (ca_sink->display)
327         gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
328       return ret;
329     }
330     case GST_QUERY_DRAIN:
331     {
332       GstBuffer *buf = NULL;
333
334       GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
335       ca_sink->redisplay_texture = 0;
336       buf = ca_sink->stored_buffer;
337       ca_sink->stored_buffer = NULL;
338       GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
339
340       if (buf)
341         gst_buffer_unref (buf);
342
343       gst_buffer_replace (&ca_sink->next_buffer, NULL);
344       gst_gl_upload_release_buffer (ca_sink->upload);
345
346       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
347       break;
348     }
349     default:
350       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
351       break;
352   }
353
354   return res;
355 }
356
357 static gboolean
358 gst_ca_opengl_layer_sink_stop (GstBaseSink * bsink)
359 {
360   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
361
362   if (ca_sink->pool) {
363     gst_object_unref (ca_sink->pool);
364     ca_sink->pool = NULL;
365   }
366
367   if (ca_sink->gl_caps) {
368     gst_caps_unref (ca_sink->gl_caps);
369     ca_sink->gl_caps = NULL;
370   }
371
372   return TRUE;
373 }
374
375 static void
376 gst_ca_opengl_layer_sink_set_context (GstElement * element, GstContext * context)
377 {
378   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (element);
379
380   gst_gl_handle_set_context (element, context, &ca_sink->display,
381       &ca_sink->other_context);
382
383   if (ca_sink->display)
384     gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
385 }
386
387 static GstStateChangeReturn
388 gst_ca_opengl_layer_sink_change_state (GstElement * element, GstStateChange transition)
389 {
390   GstCAOpenGLLayerSink *ca_sink;
391   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
392
393   GST_DEBUG ("changing state: %s => %s",
394       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
395       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
396
397   ca_sink = GST_CA_OPENGL_LAYER_SINK (element);
398
399   switch (transition) {
400     case GST_STATE_CHANGE_NULL_TO_READY:
401       _ensure_gl_setup (ca_sink);
402       break;
403     case GST_STATE_CHANGE_READY_TO_PAUSED:
404       g_atomic_int_set (&ca_sink->to_quit, 0);
405       break;
406     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
407       break;
408     default:
409       break;
410   }
411
412   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
413   if (ret == GST_STATE_CHANGE_FAILURE)
414     return ret;
415
416   switch (transition) {
417     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
418       break;
419     case GST_STATE_CHANGE_PAUSED_TO_READY:
420     {
421       /* mark the redisplay_texture as unavailable (=0)
422        * to avoid drawing
423        */
424       GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
425       ca_sink->redisplay_texture = 0;
426       if (ca_sink->stored_buffer) {
427         gst_buffer_unref (ca_sink->stored_buffer);
428         ca_sink->stored_buffer = NULL;
429       }
430       GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
431       gst_buffer_replace (&ca_sink->next_buffer, NULL);
432
433       if (ca_sink->upload) {
434         gst_object_unref (ca_sink->upload);
435         ca_sink->upload = NULL;
436       }
437
438       if (ca_sink->convert) {
439         gst_object_unref (ca_sink->convert);
440         ca_sink->convert = NULL;
441       }
442
443       if (ca_sink->pool) {
444         gst_buffer_pool_set_active (ca_sink->pool, FALSE);
445         gst_object_unref (ca_sink->pool);
446         ca_sink->pool = NULL;
447       }
448
449       GST_VIDEO_SINK_WIDTH (ca_sink) = 1;
450       GST_VIDEO_SINK_HEIGHT (ca_sink) = 1;
451       if (ca_sink->context) {
452         gst_object_unref (ca_sink->context);
453         ca_sink->context = NULL;
454       }
455
456       if (ca_sink->display) {
457         gst_object_unref (ca_sink->display);
458         ca_sink->display = NULL;
459       }
460       break;
461     }
462     case GST_STATE_CHANGE_READY_TO_NULL:
463       break;
464     default:
465       break;
466   }
467
468   return ret;
469 }
470
471 static void
472 gst_ca_opengl_layer_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
473     GstClockTime * start, GstClockTime * end)
474 {
475   GstCAOpenGLLayerSink *ca_sink;
476
477   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
478
479   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
480     *start = GST_BUFFER_TIMESTAMP (buf);
481     if (GST_BUFFER_DURATION_IS_VALID (buf))
482       *end = *start + GST_BUFFER_DURATION (buf);
483     else {
484       if (GST_VIDEO_INFO_FPS_N (&ca_sink->info) > 0) {
485         *end = *start +
486             gst_util_uint64_scale_int (GST_SECOND,
487             GST_VIDEO_INFO_FPS_D (&ca_sink->info),
488             GST_VIDEO_INFO_FPS_N (&ca_sink->info));
489       }
490     }
491   }
492 }
493
494 static gboolean
495 gst_ca_opengl_layer_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
496 {
497   GstCAOpenGLLayerSink *ca_sink;
498   gint width;
499   gint height;
500   gboolean ok;
501   gint par_n, par_d;
502   gint display_par_n, display_par_d;
503   guint display_ratio_num, display_ratio_den;
504   GstVideoInfo vinfo;
505   GstStructure *structure;
506   GstBufferPool *newpool, *oldpool;
507   GstCapsFeatures *gl_features;
508   GstCaps *uploaded_caps;
509
510   GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
511
512   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
513
514   ok = gst_video_info_from_caps (&vinfo, caps);
515   if (!ok)
516     return FALSE;
517
518   width = GST_VIDEO_INFO_WIDTH (&vinfo);
519   height = GST_VIDEO_INFO_HEIGHT (&vinfo);
520
521   par_n = GST_VIDEO_INFO_PAR_N (&vinfo);
522   par_d = GST_VIDEO_INFO_PAR_D (&vinfo);
523
524   if (!par_n)
525     par_n = 1;
526
527   display_par_n = 1;
528   display_par_d = 1;
529
530   ok = gst_video_calculate_display_ratio (&display_ratio_num,
531       &display_ratio_den, width, height, par_n, par_d, display_par_n,
532       display_par_d);
533
534   if (!ok)
535     return FALSE;
536
537   GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
538       display_par_d);
539
540   if (height % display_ratio_den == 0) {
541     GST_DEBUG ("keeping video height");
542     GST_VIDEO_SINK_WIDTH (ca_sink) = (guint)
543         gst_util_uint64_scale_int (height, display_ratio_num,
544         display_ratio_den);
545     GST_VIDEO_SINK_HEIGHT (ca_sink) = height;
546   } else if (width % display_ratio_num == 0) {
547     GST_DEBUG ("keeping video width");
548     GST_VIDEO_SINK_WIDTH (ca_sink) = width;
549     GST_VIDEO_SINK_HEIGHT (ca_sink) = (guint)
550         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
551   } else {
552     GST_DEBUG ("approximating while keeping video height");
553     GST_VIDEO_SINK_WIDTH (ca_sink) = (guint)
554         gst_util_uint64_scale_int (height, display_ratio_num,
555         display_ratio_den);
556     GST_VIDEO_SINK_HEIGHT (ca_sink) = height;
557   }
558   GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (ca_sink),
559       GST_VIDEO_SINK_HEIGHT (ca_sink));
560
561   ca_sink->info = vinfo;
562   if (!_ensure_gl_setup (ca_sink))
563     return FALSE;
564
565   newpool = gst_gl_buffer_pool_new (ca_sink->context);
566   structure = gst_buffer_pool_get_config (newpool);
567   gst_buffer_pool_config_set_params (structure, caps, vinfo.size, 2, 0);
568   gst_buffer_pool_set_config (newpool, structure);
569
570   oldpool = ca_sink->pool;
571   /* we don't activate the pool yet, this will be done by downstream after it
572    * has configured the pool. If downstream does not want our pool we will
573    * activate it when we render into it */
574   ca_sink->pool = newpool;
575
576   /* unref the old sink */
577   if (oldpool) {
578     /* we don't deactivate, some elements might still be using it, it will
579      * be deactivated when the last ref is gone */
580     gst_object_unref (oldpool);
581   }
582
583   if (ca_sink->upload)
584     gst_object_unref (ca_sink->upload);
585   ca_sink->upload = gst_gl_upload_new (ca_sink->context);
586
587   gl_features =
588       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
589
590   uploaded_caps = gst_caps_copy (caps);
591   gst_caps_set_features (uploaded_caps, 0,
592       gst_caps_features_copy (gl_features));
593   gst_gl_upload_set_caps (ca_sink->upload, caps, uploaded_caps);
594
595   if (ca_sink->gl_caps)
596     gst_caps_unref (ca_sink->gl_caps);
597   ca_sink->gl_caps = gst_caps_copy (caps);
598   gst_caps_set_simple (ca_sink->gl_caps, "format", G_TYPE_STRING, "RGBA",
599       NULL);
600   gst_caps_set_features (ca_sink->gl_caps, 0,
601       gst_caps_features_copy (gl_features));
602
603   if (ca_sink->convert)
604     gst_object_unref (ca_sink->convert);
605   ca_sink->convert = gst_gl_color_convert_new (ca_sink->context);
606   if (!gst_gl_color_convert_set_caps (ca_sink->convert, uploaded_caps,
607           ca_sink->gl_caps)) {
608     gst_caps_unref (uploaded_caps);
609     gst_caps_features_free (gl_features);
610     return FALSE;
611   }
612   gst_caps_unref (uploaded_caps);
613   gst_caps_features_free (gl_features);
614
615   ca_sink->caps_change = TRUE;
616
617   return TRUE;
618 }
619
620 static GstFlowReturn
621 gst_ca_opengl_layer_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
622 {
623   GstCAOpenGLLayerSink *ca_sink;
624   GstBuffer *uploaded_buffer, *next_buffer = NULL;
625   GstVideoFrame gl_frame;
626   GstVideoInfo gl_info;
627
628   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
629
630   GST_TRACE ("preparing buffer:%p", buf);
631
632   if (GST_VIDEO_SINK_WIDTH (ca_sink) < 1 ||
633       GST_VIDEO_SINK_HEIGHT (ca_sink) < 1) {
634     return GST_FLOW_NOT_NEGOTIATED;
635   }
636
637   if (!_ensure_gl_setup (ca_sink))
638     return GST_FLOW_NOT_NEGOTIATED;
639
640   if (gst_gl_upload_perform_with_buffer (ca_sink->upload, buf,
641           &uploaded_buffer) != GST_GL_UPLOAD_DONE)
642     goto upload_failed;
643
644   if (!(next_buffer =
645           gst_gl_color_convert_perform (ca_sink->convert,
646               uploaded_buffer))) {
647     gst_buffer_unref (uploaded_buffer);
648     goto upload_failed;
649   }
650
651   gst_video_info_from_caps (&gl_info, ca_sink->gl_caps);
652
653   if (!gst_video_frame_map (&gl_frame, &gl_info, next_buffer,
654           GST_MAP_READ | GST_MAP_GL)) {
655     gst_buffer_unref (uploaded_buffer);
656     gst_buffer_unref (next_buffer);
657     goto upload_failed;
658   }
659   gst_buffer_unref (uploaded_buffer);
660
661   ca_sink->next_tex = *(guint *) gl_frame.data[0];
662
663   gst_buffer_replace (&ca_sink->next_buffer, next_buffer);
664   gst_buffer_unref (next_buffer);
665
666   gst_video_frame_unmap (&gl_frame);
667
668   return GST_FLOW_OK;
669
670 upload_failed:
671   {
672     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
673         ("%s", "Failed to upload buffer"), (NULL));
674     return GST_FLOW_ERROR;
675   }
676 }
677
678 static GstFlowReturn
679 gst_ca_opengl_layer_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
680 {
681   GstCAOpenGLLayerSink *ca_sink;
682   GstBuffer *stored_buffer;
683
684   GST_TRACE ("rendering buffer:%p", buf);
685
686   ca_sink = GST_CA_OPENGL_LAYER_SINK (vsink);
687
688   GST_TRACE ("redisplay texture:%u of size:%ux%u, window size:%ux%u",
689       ca_sink->next_tex, GST_VIDEO_INFO_WIDTH (&ca_sink->info),
690       GST_VIDEO_INFO_HEIGHT (&ca_sink->info),
691       GST_VIDEO_SINK_WIDTH (ca_sink),
692       GST_VIDEO_SINK_HEIGHT (ca_sink));
693
694   /* Avoid to release the texture while drawing */
695   GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
696   ca_sink->redisplay_texture = ca_sink->next_tex;
697   stored_buffer = ca_sink->stored_buffer;
698   ca_sink->stored_buffer = gst_buffer_ref (ca_sink->next_buffer);
699   GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
700
701   /* The layer will automatically call the draw callback to draw the new
702    * content */
703   [CATransaction begin];
704   [ca_sink->layer setNeedsDisplay];
705   [CATransaction commit];
706
707   GST_TRACE ("post redisplay");
708
709   if (stored_buffer)
710     gst_buffer_unref (stored_buffer);
711
712   if (g_atomic_int_get (&ca_sink->to_quit) != 0) {
713     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
714         ("%s", gst_gl_context_get_error ()), (NULL));
715     gst_gl_upload_release_buffer (ca_sink->upload);
716     return GST_FLOW_ERROR;
717   }
718
719   return GST_FLOW_OK;
720 }
721
722 static gboolean
723 gst_ca_opengl_layer_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
724 {
725   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
726   GstBufferPool *pool;
727   GstStructure *config;
728   GstCaps *caps;
729   guint size;
730   gboolean need_pool;
731   GstStructure *gl_context;
732   gchar *platform, *gl_apis;
733   gpointer handle;
734   GstAllocator *allocator = NULL;
735   GstAllocationParams params;
736
737   if (!_ensure_gl_setup (ca_sink))
738     return FALSE;
739
740   gst_query_parse_allocation (query, &caps, &need_pool);
741
742   if (caps == NULL)
743     goto no_caps;
744
745   if ((pool = ca_sink->pool))
746     gst_object_ref (pool);
747
748   if (pool != NULL) {
749     GstCaps *pcaps;
750
751     /* we had a pool, check caps */
752     GST_DEBUG_OBJECT (ca_sink, "check existing pool caps");
753     config = gst_buffer_pool_get_config (pool);
754     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
755
756     if (!gst_caps_is_equal (caps, pcaps)) {
757       GST_DEBUG_OBJECT (ca_sink, "pool has different caps");
758       /* different caps, we can't use this pool */
759       gst_object_unref (pool);
760       pool = NULL;
761     }
762     gst_structure_free (config);
763   }
764
765   if (pool == NULL && need_pool) {
766     GstVideoInfo info;
767
768     if (!gst_video_info_from_caps (&info, caps))
769       goto invalid_caps;
770
771     GST_DEBUG_OBJECT (ca_sink, "create new pool");
772     pool = gst_gl_buffer_pool_new (ca_sink->context);
773
774     /* the normal size of a frame */
775     size = info.size;
776
777     config = gst_buffer_pool_get_config (pool);
778     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
779     if (!gst_buffer_pool_set_config (pool, config))
780       goto config_failed;
781   }
782   /* we need at least 2 buffer because we hold on to the last one */
783   if (pool) {
784     gst_query_add_allocation_pool (query, pool, size, 2, 0);
785     gst_object_unref (pool);
786   }
787
788   /* we also support various metadata */
789   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
790   if (ca_sink->context->gl_vtable->FenceSync)
791     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
792
793   gl_apis =
794       gst_gl_api_to_string (gst_gl_context_get_gl_api (ca_sink->context));
795   platform =
796       gst_gl_platform_to_string (gst_gl_context_get_gl_platform
797       (ca_sink->context));
798   handle = (gpointer) gst_gl_context_get_gl_context (ca_sink->context);
799
800   gl_context =
801       gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext",
802       GST_GL_TYPE_CONTEXT, ca_sink->context, "gst.gl.context.handle",
803       G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform,
804       "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
805   gst_query_add_allocation_meta (query,
806       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context);
807
808   g_free (gl_apis);
809   g_free (platform);
810   gst_structure_free (gl_context);
811
812   gst_allocation_params_init (&params);
813
814   allocator = gst_allocator_find (GST_GL_MEMORY_ALLOCATOR);
815   gst_query_add_allocation_param (query, allocator, &params);
816   gst_object_unref (allocator);
817
818   return TRUE;
819
820   /* ERRORS */
821 no_caps:
822   {
823     GST_DEBUG_OBJECT (bsink, "no caps specified");
824     return FALSE;
825   }
826 invalid_caps:
827   {
828     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
829     return FALSE;
830   }
831 config_failed:
832   {
833     GST_DEBUG_OBJECT (bsink, "failed setting config");
834     return FALSE;
835   }
836 }
837
838 /* *INDENT-OFF* */
839 static const GLfloat vertices[] = {
840      1.0f,  1.0f, 0.0f, 1.0f, 0.0f,
841     -1.0f,  1.0f, 0.0f, 0.0f, 0.0f,
842     -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
843      1.0f, -1.0f, 0.0f, 1.0f, 1.0f
844 };
845 /* *INDENT-ON* */
846
847 static void
848 _bind_buffer (GstCAOpenGLLayerSink * ca_sink)
849 {
850   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
851
852   gl->BindBuffer (GL_ARRAY_BUFFER, ca_sink->vertex_buffer);
853   gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
854       GL_STATIC_DRAW);
855
856   /* Load the vertex position */
857   gl->VertexAttribPointer (ca_sink->attr_position, 3, GL_FLOAT, GL_FALSE,
858       5 * sizeof (GLfloat), (void *) 0);
859
860   /* Load the texture coordinate */
861   gl->VertexAttribPointer (ca_sink->attr_texture, 2, GL_FLOAT, GL_FALSE,
862       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
863
864   gl->EnableVertexAttribArray (ca_sink->attr_position);
865   gl->EnableVertexAttribArray (ca_sink->attr_texture);
866 }
867
868 static void
869 _unbind_buffer (GstCAOpenGLLayerSink * ca_sink)
870 {
871   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
872
873   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
874
875   gl->DisableVertexAttribArray (ca_sink->attr_position);
876   gl->DisableVertexAttribArray (ca_sink->attr_texture);
877 }
878
879 /* Called in the gl thread */
880 static void
881 gst_ca_opengl_layer_sink_thread_init_redisplay (GstCAOpenGLLayerSink * ca_sink)
882 {
883   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
884
885   ca_sink->redisplay_shader = gst_gl_shader_new (ca_sink->context);
886
887   if (!gst_gl_shader_compile_with_default_vf_and_check
888       (ca_sink->redisplay_shader, &ca_sink->attr_position,
889           &ca_sink->attr_texture))
890     gst_ca_opengl_layer_sink_cleanup_glthread (ca_sink);
891
892   if (gl->GenVertexArrays) {
893     gl->GenVertexArrays (1, &ca_sink->vao);
894     gl->BindVertexArray (ca_sink->vao);
895   }
896
897   gl->GenBuffers (1, &ca_sink->vertex_buffer);
898   _bind_buffer (ca_sink);
899
900   if (gl->GenVertexArrays) {
901     gl->BindVertexArray (0);
902     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
903   } else {
904     _unbind_buffer (ca_sink);
905   }
906 }
907
908 static void
909 gst_ca_opengl_layer_sink_cleanup_glthread (GstCAOpenGLLayerSink * ca_sink)
910 {
911   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
912
913   if (ca_sink->redisplay_shader) {
914     gst_object_unref (ca_sink->redisplay_shader);
915     ca_sink->redisplay_shader = NULL;
916   }
917
918   if (ca_sink->vao) {
919     gl->DeleteVertexArrays (1, &ca_sink->vao);
920     ca_sink->vao = 0;
921   }
922 }
923
924 static void
925 gst_ca_opengl_layer_sink_on_resize (GstCAOpenGLLayerSink * ca_sink, gint width, gint height)
926 {
927   /* Here ca_sink members (ex:ca_sink->info) have a life time of set_caps.
928    * It means that they cannot not change between two set_caps
929    */
930   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
931
932   GST_TRACE ("GL Window resized to %ux%u", width, height);
933
934   width = MAX (1, width);
935   height = MAX (1, height);
936
937   ca_sink->window_width = width;
938   ca_sink->window_height = height;
939
940   /* default reshape */
941   if (ca_sink->keep_aspect_ratio) {
942     GstVideoRectangle src, dst, result;
943
944     src.x = 0;
945     src.y = 0;
946     src.w = GST_VIDEO_SINK_WIDTH (ca_sink);
947     src.h = GST_VIDEO_SINK_HEIGHT (ca_sink);
948
949     dst.x = 0;
950     dst.y = 0;
951     dst.w = width;
952     dst.h = height;
953
954     gst_video_sink_center_rect (src, dst, &result, TRUE);
955     gl->Viewport (result.x, result.y, result.w, result.h);
956   } else {
957     gl->Viewport (0, 0, width, height);
958   }
959 }
960
961 static void
962 gst_ca_opengl_layer_sink_on_draw (GstCAOpenGLLayerSink * ca_sink)
963 {
964   /* Here ca_sink members (ex:ca_sink->info) have a life time of set_caps.
965    * It means that they cannot not change between two set_caps as well as
966    * for the redisplay_texture size.
967    * Whereas redisplay_texture id changes every sink_render
968    */
969
970   const GstGLFuncs *gl = NULL;
971   GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
972
973   g_return_if_fail (GST_IS_CA_OPENGL_LAYER_SINK (ca_sink));
974
975   gl = ca_sink->context->gl_vtable;
976
977   GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
978
979   if (G_UNLIKELY (!ca_sink->redisplay_shader)) {
980     gst_ca_opengl_layer_sink_thread_init_redisplay (ca_sink);
981   }
982
983   /* check if texture is ready for being drawn */
984   if (!ca_sink->redisplay_texture) {
985     gl->ClearColor (0.0f, 0.0f, 0.0f, 1.0f);
986     gl->Clear (GL_COLOR_BUFFER_BIT);
987     GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
988     return;
989   }
990
991   /* opengl scene */
992   GST_TRACE ("redrawing texture:%u", ca_sink->redisplay_texture);
993
994   if (ca_sink->caps_change) {
995     GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
996     gst_ca_opengl_layer_sink_on_resize (ca_sink, ca_sink->window_width,
997         ca_sink->window_height);
998     GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
999     ca_sink->caps_change = TRUE;
1000   }
1001
1002 #if GST_GL_HAVE_OPENGL
1003   if (USING_OPENGL (ca_sink->context))
1004     gl->Disable (GL_TEXTURE_2D);
1005 #endif
1006
1007   gl->BindTexture (GL_TEXTURE_2D, 0);
1008
1009   gl->ClearColor (0.0, 0.0, 0.0, 0.0);
1010   gl->Clear (GL_COLOR_BUFFER_BIT);
1011
1012   gst_gl_shader_use (ca_sink->redisplay_shader);
1013
1014   if (gl->GenVertexArrays)
1015     gl->BindVertexArray (ca_sink->vao);
1016   else
1017     _bind_buffer (ca_sink);
1018
1019   gl->ActiveTexture (GL_TEXTURE0);
1020   gl->BindTexture (GL_TEXTURE_2D, ca_sink->redisplay_texture);
1021   gst_gl_shader_set_uniform_1i (ca_sink->redisplay_shader, "tex", 0);
1022
1023   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
1024
1025   if (gl->GenVertexArrays)
1026     gl->BindVertexArray (0);
1027   else
1028     _unbind_buffer (ca_sink);
1029
1030   /* end default opengl scene */
1031   GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
1032 }