fdbcf7bba9e1cb480ac50d4f09cc84879878d7f0
[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   g_assert (![NSThread isMainThread]);
276
277   if (!gst_gl_ensure_element_data (ca_sink, &ca_sink->display,
278           &ca_sink->other_context))
279     return FALSE;
280
281   gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
282
283   if (!ca_sink->context) {
284     ca_sink->context = gst_gl_context_new (ca_sink->display);
285     if (!ca_sink->context)
286       goto context_creation_error;
287
288     if (!gst_gl_context_create (ca_sink->context, ca_sink->other_context,
289             &error)) {
290       goto context_error;
291     }
292   }
293
294   if (!ca_sink->layer)
295     _invoke_on_main ((GstGLWindowCB) _create_layer, ca_sink);
296
297   return TRUE;
298
299 context_creation_error:
300   {
301     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
302         ("Failed to create GL context"), (NULL));
303     return FALSE;
304   }
305
306 context_error:
307   {
308     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND, ("%s", error->message),
309         (NULL));
310     gst_object_unref (ca_sink->context);
311     ca_sink->context = NULL;
312     return FALSE;
313   }
314 }
315
316 static gboolean
317 gst_ca_opengl_layer_sink_query (GstBaseSink * bsink, GstQuery * query)
318 {
319   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
320   gboolean res = FALSE;
321
322   switch (GST_QUERY_TYPE (query)) {
323     case GST_QUERY_CONTEXT:
324     {
325       gboolean ret =
326           gst_gl_handle_context_query ((GstElement *) ca_sink, query,
327           &ca_sink->display, &ca_sink->other_context);
328       if (ca_sink->display)
329         gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
330       return ret;
331     }
332     case GST_QUERY_DRAIN:
333     {
334       GstBuffer *buf = NULL;
335
336       GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
337       ca_sink->redisplay_texture = 0;
338       buf = ca_sink->stored_buffer;
339       ca_sink->stored_buffer = NULL;
340       GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
341
342       if (buf)
343         gst_buffer_unref (buf);
344
345       gst_buffer_replace (&ca_sink->next_buffer, NULL);
346       gst_gl_upload_release_buffer (ca_sink->upload);
347
348       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
349       break;
350     }
351     default:
352       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
353       break;
354   }
355
356   return res;
357 }
358
359 static gboolean
360 gst_ca_opengl_layer_sink_stop (GstBaseSink * bsink)
361 {
362   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
363
364   if (ca_sink->pool) {
365     gst_object_unref (ca_sink->pool);
366     ca_sink->pool = NULL;
367   }
368
369   if (ca_sink->gl_caps) {
370     gst_caps_unref (ca_sink->gl_caps);
371     ca_sink->gl_caps = NULL;
372   }
373
374   return TRUE;
375 }
376
377 static void
378 gst_ca_opengl_layer_sink_set_context (GstElement * element, GstContext * context)
379 {
380   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (element);
381
382   gst_gl_handle_set_context (element, context, &ca_sink->display,
383       &ca_sink->other_context);
384
385   if (ca_sink->display)
386     gst_gl_display_filter_gl_api (ca_sink->display, SUPPORTED_GL_APIS);
387 }
388
389 static GstStateChangeReturn
390 gst_ca_opengl_layer_sink_change_state (GstElement * element, GstStateChange transition)
391 {
392   GstCAOpenGLLayerSink *ca_sink;
393   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
394
395   GST_DEBUG ("changing state: %s => %s",
396       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
397       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
398
399   ca_sink = GST_CA_OPENGL_LAYER_SINK (element);
400
401   switch (transition) {
402     case GST_STATE_CHANGE_NULL_TO_READY:
403       break;
404     case GST_STATE_CHANGE_READY_TO_PAUSED:
405       g_atomic_int_set (&ca_sink->to_quit, 0);
406       break;
407     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
408       break;
409     default:
410       break;
411   }
412
413   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
414   if (ret == GST_STATE_CHANGE_FAILURE)
415     return ret;
416
417   switch (transition) {
418     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
419       break;
420     case GST_STATE_CHANGE_PAUSED_TO_READY:
421     {
422       /* mark the redisplay_texture as unavailable (=0)
423        * to avoid drawing
424        */
425       GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
426       ca_sink->redisplay_texture = 0;
427       if (ca_sink->stored_buffer) {
428         gst_buffer_unref (ca_sink->stored_buffer);
429         ca_sink->stored_buffer = NULL;
430       }
431       GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
432       gst_buffer_replace (&ca_sink->next_buffer, NULL);
433
434       if (ca_sink->upload) {
435         gst_object_unref (ca_sink->upload);
436         ca_sink->upload = NULL;
437       }
438
439       if (ca_sink->convert) {
440         gst_object_unref (ca_sink->convert);
441         ca_sink->convert = NULL;
442       }
443
444       if (ca_sink->pool) {
445         gst_buffer_pool_set_active (ca_sink->pool, FALSE);
446         gst_object_unref (ca_sink->pool);
447         ca_sink->pool = NULL;
448       }
449
450       GST_VIDEO_SINK_WIDTH (ca_sink) = 1;
451       GST_VIDEO_SINK_HEIGHT (ca_sink) = 1;
452       if (ca_sink->context) {
453         gst_object_unref (ca_sink->context);
454         ca_sink->context = NULL;
455       }
456
457       if (ca_sink->display) {
458         gst_object_unref (ca_sink->display);
459         ca_sink->display = NULL;
460       }
461       break;
462     }
463     case GST_STATE_CHANGE_READY_TO_NULL:
464       break;
465     default:
466       break;
467   }
468
469   return ret;
470 }
471
472 static void
473 gst_ca_opengl_layer_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
474     GstClockTime * start, GstClockTime * end)
475 {
476   GstCAOpenGLLayerSink *ca_sink;
477
478   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
479
480   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
481     *start = GST_BUFFER_TIMESTAMP (buf);
482     if (GST_BUFFER_DURATION_IS_VALID (buf))
483       *end = *start + GST_BUFFER_DURATION (buf);
484     else {
485       if (GST_VIDEO_INFO_FPS_N (&ca_sink->info) > 0) {
486         *end = *start +
487             gst_util_uint64_scale_int (GST_SECOND,
488             GST_VIDEO_INFO_FPS_D (&ca_sink->info),
489             GST_VIDEO_INFO_FPS_N (&ca_sink->info));
490       }
491     }
492   }
493 }
494
495 static gboolean
496 gst_ca_opengl_layer_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
497 {
498   GstCAOpenGLLayerSink *ca_sink;
499   gint width;
500   gint height;
501   gboolean ok;
502   gint par_n, par_d;
503   gint display_par_n, display_par_d;
504   guint display_ratio_num, display_ratio_den;
505   GstVideoInfo vinfo;
506   GstStructure *structure;
507   GstBufferPool *newpool, *oldpool;
508   GstCapsFeatures *gl_features;
509   GstCaps *uploaded_caps;
510
511   GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
512
513   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
514
515   ok = gst_video_info_from_caps (&vinfo, caps);
516   if (!ok)
517     return FALSE;
518
519   width = GST_VIDEO_INFO_WIDTH (&vinfo);
520   height = GST_VIDEO_INFO_HEIGHT (&vinfo);
521
522   par_n = GST_VIDEO_INFO_PAR_N (&vinfo);
523   par_d = GST_VIDEO_INFO_PAR_D (&vinfo);
524
525   if (!par_n)
526     par_n = 1;
527
528   display_par_n = 1;
529   display_par_d = 1;
530
531   ok = gst_video_calculate_display_ratio (&display_ratio_num,
532       &display_ratio_den, width, height, par_n, par_d, display_par_n,
533       display_par_d);
534
535   if (!ok)
536     return FALSE;
537
538   GST_TRACE ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
539       display_par_d);
540
541   if (height % display_ratio_den == 0) {
542     GST_DEBUG ("keeping video height");
543     GST_VIDEO_SINK_WIDTH (ca_sink) = (guint)
544         gst_util_uint64_scale_int (height, display_ratio_num,
545         display_ratio_den);
546     GST_VIDEO_SINK_HEIGHT (ca_sink) = height;
547   } else if (width % display_ratio_num == 0) {
548     GST_DEBUG ("keeping video width");
549     GST_VIDEO_SINK_WIDTH (ca_sink) = width;
550     GST_VIDEO_SINK_HEIGHT (ca_sink) = (guint)
551         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
552   } else {
553     GST_DEBUG ("approximating while keeping video height");
554     GST_VIDEO_SINK_WIDTH (ca_sink) = (guint)
555         gst_util_uint64_scale_int (height, display_ratio_num,
556         display_ratio_den);
557     GST_VIDEO_SINK_HEIGHT (ca_sink) = height;
558   }
559   GST_DEBUG ("scaling to %dx%d", GST_VIDEO_SINK_WIDTH (ca_sink),
560       GST_VIDEO_SINK_HEIGHT (ca_sink));
561
562   ca_sink->info = vinfo;
563   if (!_ensure_gl_setup (ca_sink))
564     return FALSE;
565
566   newpool = gst_gl_buffer_pool_new (ca_sink->context);
567   structure = gst_buffer_pool_get_config (newpool);
568   gst_buffer_pool_config_set_params (structure, caps, vinfo.size, 2, 0);
569   gst_buffer_pool_set_config (newpool, structure);
570
571   oldpool = ca_sink->pool;
572   /* we don't activate the pool yet, this will be done by downstream after it
573    * has configured the pool. If downstream does not want our pool we will
574    * activate it when we render into it */
575   ca_sink->pool = newpool;
576
577   /* unref the old sink */
578   if (oldpool) {
579     /* we don't deactivate, some elements might still be using it, it will
580      * be deactivated when the last ref is gone */
581     gst_object_unref (oldpool);
582   }
583
584   if (ca_sink->upload)
585     gst_object_unref (ca_sink->upload);
586   ca_sink->upload = gst_gl_upload_new (ca_sink->context);
587
588   gl_features =
589       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
590
591   uploaded_caps = gst_caps_copy (caps);
592   gst_caps_set_features (uploaded_caps, 0,
593       gst_caps_features_copy (gl_features));
594   gst_gl_upload_set_caps (ca_sink->upload, caps, uploaded_caps);
595
596   if (ca_sink->gl_caps)
597     gst_caps_unref (ca_sink->gl_caps);
598   ca_sink->gl_caps = gst_caps_copy (caps);
599   gst_caps_set_simple (ca_sink->gl_caps, "format", G_TYPE_STRING, "RGBA",
600       NULL);
601   gst_caps_set_features (ca_sink->gl_caps, 0,
602       gst_caps_features_copy (gl_features));
603
604   if (ca_sink->convert)
605     gst_object_unref (ca_sink->convert);
606   ca_sink->convert = gst_gl_color_convert_new (ca_sink->context);
607   if (!gst_gl_color_convert_set_caps (ca_sink->convert, uploaded_caps,
608           ca_sink->gl_caps)) {
609     gst_caps_unref (uploaded_caps);
610     gst_caps_features_free (gl_features);
611     return FALSE;
612   }
613   gst_caps_unref (uploaded_caps);
614   gst_caps_features_free (gl_features);
615
616   ca_sink->caps_change = TRUE;
617
618   return TRUE;
619 }
620
621 static GstFlowReturn
622 gst_ca_opengl_layer_sink_prepare (GstBaseSink * bsink, GstBuffer * buf)
623 {
624   GstCAOpenGLLayerSink *ca_sink;
625   GstBuffer *uploaded_buffer, *next_buffer = NULL;
626   GstVideoFrame gl_frame;
627   GstVideoInfo gl_info;
628
629   ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
630
631   GST_TRACE ("preparing buffer:%p", buf);
632
633   if (GST_VIDEO_SINK_WIDTH (ca_sink) < 1 ||
634       GST_VIDEO_SINK_HEIGHT (ca_sink) < 1) {
635     return GST_FLOW_NOT_NEGOTIATED;
636   }
637
638   if (!_ensure_gl_setup (ca_sink))
639     return GST_FLOW_NOT_NEGOTIATED;
640
641   if (gst_gl_upload_perform_with_buffer (ca_sink->upload, buf,
642           &uploaded_buffer) != GST_GL_UPLOAD_DONE)
643     goto upload_failed;
644
645   if (!(next_buffer =
646           gst_gl_color_convert_perform (ca_sink->convert,
647               uploaded_buffer))) {
648     gst_buffer_unref (uploaded_buffer);
649     goto upload_failed;
650   }
651
652   gst_video_info_from_caps (&gl_info, ca_sink->gl_caps);
653
654   if (!gst_video_frame_map (&gl_frame, &gl_info, next_buffer,
655           GST_MAP_READ | GST_MAP_GL)) {
656     gst_buffer_unref (uploaded_buffer);
657     gst_buffer_unref (next_buffer);
658     goto upload_failed;
659   }
660   gst_buffer_unref (uploaded_buffer);
661
662   ca_sink->next_tex = *(guint *) gl_frame.data[0];
663
664   gst_buffer_replace (&ca_sink->next_buffer, next_buffer);
665   gst_buffer_unref (next_buffer);
666
667   gst_video_frame_unmap (&gl_frame);
668
669   return GST_FLOW_OK;
670
671 upload_failed:
672   {
673     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
674         ("%s", "Failed to upload buffer"), (NULL));
675     return GST_FLOW_ERROR;
676   }
677 }
678
679 static GstFlowReturn
680 gst_ca_opengl_layer_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
681 {
682   GstCAOpenGLLayerSink *ca_sink;
683   GstBuffer *stored_buffer;
684
685   GST_TRACE ("rendering buffer:%p", buf);
686
687   ca_sink = GST_CA_OPENGL_LAYER_SINK (vsink);
688
689   GST_TRACE ("redisplay texture:%u of size:%ux%u, window size:%ux%u",
690       ca_sink->next_tex, GST_VIDEO_INFO_WIDTH (&ca_sink->info),
691       GST_VIDEO_INFO_HEIGHT (&ca_sink->info),
692       GST_VIDEO_SINK_WIDTH (ca_sink),
693       GST_VIDEO_SINK_HEIGHT (ca_sink));
694
695   /* Avoid to release the texture while drawing */
696   GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
697   ca_sink->redisplay_texture = ca_sink->next_tex;
698   stored_buffer = ca_sink->stored_buffer;
699   ca_sink->stored_buffer = gst_buffer_ref (ca_sink->next_buffer);
700   GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
701
702   /* The layer will automatically call the draw callback to draw the new
703    * content */
704   [CATransaction begin];
705   [ca_sink->layer setNeedsDisplay];
706   [CATransaction commit];
707
708   GST_TRACE ("post redisplay");
709
710   if (stored_buffer)
711     gst_buffer_unref (stored_buffer);
712
713   if (g_atomic_int_get (&ca_sink->to_quit) != 0) {
714     GST_ELEMENT_ERROR (ca_sink, RESOURCE, NOT_FOUND,
715         ("%s", gst_gl_context_get_error ()), (NULL));
716     gst_gl_upload_release_buffer (ca_sink->upload);
717     return GST_FLOW_ERROR;
718   }
719
720   return GST_FLOW_OK;
721 }
722
723 static gboolean
724 gst_ca_opengl_layer_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
725 {
726   GstCAOpenGLLayerSink *ca_sink = GST_CA_OPENGL_LAYER_SINK (bsink);
727   GstBufferPool *pool;
728   GstStructure *config;
729   GstCaps *caps;
730   guint size;
731   gboolean need_pool;
732   GstStructure *gl_context;
733   gchar *platform, *gl_apis;
734   gpointer handle;
735   GstAllocator *allocator = NULL;
736   GstAllocationParams params;
737
738   if (!_ensure_gl_setup (ca_sink))
739     return FALSE;
740
741   gst_query_parse_allocation (query, &caps, &need_pool);
742
743   if (caps == NULL)
744     goto no_caps;
745
746   if ((pool = ca_sink->pool))
747     gst_object_ref (pool);
748
749   if (pool != NULL) {
750     GstCaps *pcaps;
751
752     /* we had a pool, check caps */
753     GST_DEBUG_OBJECT (ca_sink, "check existing pool caps");
754     config = gst_buffer_pool_get_config (pool);
755     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
756
757     if (!gst_caps_is_equal (caps, pcaps)) {
758       GST_DEBUG_OBJECT (ca_sink, "pool has different caps");
759       /* different caps, we can't use this pool */
760       gst_object_unref (pool);
761       pool = NULL;
762     }
763     gst_structure_free (config);
764   }
765
766   if (pool == NULL && need_pool) {
767     GstVideoInfo info;
768
769     if (!gst_video_info_from_caps (&info, caps))
770       goto invalid_caps;
771
772     GST_DEBUG_OBJECT (ca_sink, "create new pool");
773     pool = gst_gl_buffer_pool_new (ca_sink->context);
774
775     /* the normal size of a frame */
776     size = info.size;
777
778     config = gst_buffer_pool_get_config (pool);
779     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
780     if (!gst_buffer_pool_set_config (pool, config))
781       goto config_failed;
782   }
783   /* we need at least 2 buffer because we hold on to the last one */
784   if (pool) {
785     gst_query_add_allocation_pool (query, pool, size, 2, 0);
786     gst_object_unref (pool);
787   }
788
789   /* we also support various metadata */
790   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
791   if (ca_sink->context->gl_vtable->FenceSync)
792     gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
793
794   gl_apis =
795       gst_gl_api_to_string (gst_gl_context_get_gl_api (ca_sink->context));
796   platform =
797       gst_gl_platform_to_string (gst_gl_context_get_gl_platform
798       (ca_sink->context));
799   handle = (gpointer) gst_gl_context_get_gl_context (ca_sink->context);
800
801   gl_context =
802       gst_structure_new ("GstVideoGLTextureUploadMeta", "gst.gl.GstGLContext",
803       GST_GL_TYPE_CONTEXT, ca_sink->context, "gst.gl.context.handle",
804       G_TYPE_POINTER, handle, "gst.gl.context.type", G_TYPE_STRING, platform,
805       "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL);
806   gst_query_add_allocation_meta (query,
807       GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, gl_context);
808
809   g_free (gl_apis);
810   g_free (platform);
811   gst_structure_free (gl_context);
812
813   gst_allocation_params_init (&params);
814
815   allocator = gst_allocator_find (GST_GL_MEMORY_ALLOCATOR);
816   gst_query_add_allocation_param (query, allocator, &params);
817   gst_object_unref (allocator);
818
819   return TRUE;
820
821   /* ERRORS */
822 no_caps:
823   {
824     GST_DEBUG_OBJECT (bsink, "no caps specified");
825     return FALSE;
826   }
827 invalid_caps:
828   {
829     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
830     return FALSE;
831   }
832 config_failed:
833   {
834     GST_DEBUG_OBJECT (bsink, "failed setting config");
835     return FALSE;
836   }
837 }
838
839 /* *INDENT-OFF* */
840 static const GLfloat vertices[] = {
841      1.0f,  1.0f, 0.0f, 1.0f, 0.0f,
842     -1.0f,  1.0f, 0.0f, 0.0f, 0.0f,
843     -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
844      1.0f, -1.0f, 0.0f, 1.0f, 1.0f
845 };
846 /* *INDENT-ON* */
847
848 static void
849 _bind_buffer (GstCAOpenGLLayerSink * ca_sink)
850 {
851   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
852
853   gl->BindBuffer (GL_ARRAY_BUFFER, ca_sink->vertex_buffer);
854   gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
855       GL_STATIC_DRAW);
856
857   /* Load the vertex position */
858   gl->VertexAttribPointer (ca_sink->attr_position, 3, GL_FLOAT, GL_FALSE,
859       5 * sizeof (GLfloat), (void *) 0);
860
861   /* Load the texture coordinate */
862   gl->VertexAttribPointer (ca_sink->attr_texture, 2, GL_FLOAT, GL_FALSE,
863       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
864
865   gl->EnableVertexAttribArray (ca_sink->attr_position);
866   gl->EnableVertexAttribArray (ca_sink->attr_texture);
867 }
868
869 static void
870 _unbind_buffer (GstCAOpenGLLayerSink * ca_sink)
871 {
872   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
873
874   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
875
876   gl->DisableVertexAttribArray (ca_sink->attr_position);
877   gl->DisableVertexAttribArray (ca_sink->attr_texture);
878 }
879
880 /* Called in the gl thread */
881 static void
882 gst_ca_opengl_layer_sink_thread_init_redisplay (GstCAOpenGLLayerSink * ca_sink)
883 {
884   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
885
886   ca_sink->redisplay_shader = gst_gl_shader_new (ca_sink->context);
887
888   if (!gst_gl_shader_compile_with_default_vf_and_check
889       (ca_sink->redisplay_shader, &ca_sink->attr_position,
890           &ca_sink->attr_texture))
891     gst_ca_opengl_layer_sink_cleanup_glthread (ca_sink);
892
893   if (gl->GenVertexArrays) {
894     gl->GenVertexArrays (1, &ca_sink->vao);
895     gl->BindVertexArray (ca_sink->vao);
896   }
897
898   gl->GenBuffers (1, &ca_sink->vertex_buffer);
899   _bind_buffer (ca_sink);
900
901   if (gl->GenVertexArrays) {
902     gl->BindVertexArray (0);
903     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
904   } else {
905     _unbind_buffer (ca_sink);
906   }
907 }
908
909 static void
910 gst_ca_opengl_layer_sink_cleanup_glthread (GstCAOpenGLLayerSink * ca_sink)
911 {
912   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
913
914   if (ca_sink->redisplay_shader) {
915     gst_object_unref (ca_sink->redisplay_shader);
916     ca_sink->redisplay_shader = NULL;
917   }
918
919   if (ca_sink->vao) {
920     gl->DeleteVertexArrays (1, &ca_sink->vao);
921     ca_sink->vao = 0;
922   }
923 }
924
925 static void
926 gst_ca_opengl_layer_sink_on_resize (GstCAOpenGLLayerSink * ca_sink, gint width, gint height)
927 {
928   /* Here ca_sink members (ex:ca_sink->info) have a life time of set_caps.
929    * It means that they cannot not change between two set_caps
930    */
931   const GstGLFuncs *gl = ca_sink->context->gl_vtable;
932
933   GST_TRACE ("GL Window resized to %ux%u", width, height);
934
935   width = MAX (1, width);
936   height = MAX (1, height);
937
938   ca_sink->window_width = width;
939   ca_sink->window_height = height;
940
941   /* default reshape */
942   if (ca_sink->keep_aspect_ratio) {
943     GstVideoRectangle src, dst, result;
944
945     src.x = 0;
946     src.y = 0;
947     src.w = GST_VIDEO_SINK_WIDTH (ca_sink);
948     src.h = GST_VIDEO_SINK_HEIGHT (ca_sink);
949
950     dst.x = 0;
951     dst.y = 0;
952     dst.w = width;
953     dst.h = height;
954
955     gst_video_sink_center_rect (src, dst, &result, TRUE);
956     gl->Viewport (result.x, result.y, result.w, result.h);
957   } else {
958     gl->Viewport (0, 0, width, height);
959   }
960 }
961
962 static void
963 gst_ca_opengl_layer_sink_on_draw (GstCAOpenGLLayerSink * ca_sink)
964 {
965   /* Here ca_sink members (ex:ca_sink->info) have a life time of set_caps.
966    * It means that they cannot not change between two set_caps as well as
967    * for the redisplay_texture size.
968    * Whereas redisplay_texture id changes every sink_render
969    */
970
971   const GstGLFuncs *gl = NULL;
972   GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
973
974   g_return_if_fail (GST_IS_CA_OPENGL_LAYER_SINK (ca_sink));
975
976   gl = ca_sink->context->gl_vtable;
977
978   GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
979
980   if (G_UNLIKELY (!ca_sink->redisplay_shader)) {
981     gst_ca_opengl_layer_sink_thread_init_redisplay (ca_sink);
982   }
983
984   /* check if texture is ready for being drawn */
985   if (!ca_sink->redisplay_texture) {
986     gl->ClearColor (0.0f, 0.0f, 0.0f, 1.0f);
987     gl->Clear (GL_COLOR_BUFFER_BIT);
988     GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
989     return;
990   }
991
992   /* opengl scene */
993   GST_TRACE ("redrawing texture:%u", ca_sink->redisplay_texture);
994
995   if (ca_sink->caps_change) {
996     GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
997     gst_ca_opengl_layer_sink_on_resize (ca_sink, ca_sink->window_width,
998         ca_sink->window_height);
999     GST_CA_OPENGL_LAYER_SINK_LOCK (ca_sink);
1000     ca_sink->caps_change = TRUE;
1001   }
1002
1003 #if GST_GL_HAVE_OPENGL
1004   if (USING_OPENGL (ca_sink->context))
1005     gl->Disable (GL_TEXTURE_2D);
1006 #endif
1007
1008   gl->BindTexture (GL_TEXTURE_2D, 0);
1009
1010   gl->ClearColor (0.0, 0.0, 0.0, 0.0);
1011   gl->Clear (GL_COLOR_BUFFER_BIT);
1012
1013   gst_gl_shader_use (ca_sink->redisplay_shader);
1014
1015   if (gl->GenVertexArrays)
1016     gl->BindVertexArray (ca_sink->vao);
1017   else
1018     _bind_buffer (ca_sink);
1019
1020   gl->ActiveTexture (GL_TEXTURE0);
1021   gl->BindTexture (GL_TEXTURE_2D, ca_sink->redisplay_texture);
1022   gst_gl_shader_set_uniform_1i (ca_sink->redisplay_shader, "tex", 0);
1023
1024   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
1025
1026   if (gl->GenVertexArrays)
1027     gl->BindVertexArray (0);
1028   else
1029     _unbind_buffer (ca_sink);
1030
1031   /* end default opengl scene */
1032   GST_CA_OPENGL_LAYER_SINK_UNLOCK (ca_sink);
1033 }