gl/cocoa: Return a strong ref to the parent GstGLContext
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / gl / cocoa / gstglcaopengllayer.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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #if !defined(MAC_OS_X_VERSION_MAX_ALLOWED) || MAC_OS_X_VERSION_MAX_ALLOWED >= 1014
26 # define GL_SILENCE_DEPRECATION
27 #endif
28
29 #include <Cocoa/Cocoa.h>
30
31 #include "gstglcaopengllayer.h"
32 #include "gstgl_cocoa_private.h"
33
34 #define GST_CAT_DEFAULT gst_gl_ca_opengl_layer_debug
35 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
36
37 static void
38 _init_debug (void)
39 {
40   static gsize _init = 0;
41
42   if (g_once_init_enter (&_init)) {
43     GST_DEBUG_CATEGORY_INIT(gst_gl_ca_opengl_layer_debug, "glcaopengllayer",
44         0, "CAOpenGLLayer");
45
46     g_once_init_leave (&_init, 1);
47   }
48 }
49
50 @implementation GstGLCAOpenGLLayer
51 - (void)dealloc {
52   if (self->draw_notify)
53     self->draw_notify (self->draw_data);
54
55   if (self->draw_context)
56     gst_object_unref (self->draw_context);
57
58   g_weak_ref_clear (&self->gst_gl_window_ref);
59   self->gst_gl_context_ref = NULL;
60
61   GST_TRACE ("dealloc GstGLCAOpenGLLayer %p", self);
62 }
63
64 - (GstGLContext *)getGLContext {
65   GstGLWindow *gst_gl_window;
66   GstGLContext *gst_gl_context = NULL;
67   if (gst_gl_context_ref)
68     return gst_object_ref(gst_gl_context_ref);
69   gst_gl_window = g_weak_ref_get (&self->gst_gl_window_ref);
70   if (gst_gl_window) {
71     gst_gl_context = gst_gl_window_get_context (gst_gl_window);
72     gst_object_unref (gst_gl_window);
73   }
74   return gst_gl_context;
75 }
76
77 static void
78 _context_ready (gpointer data)
79 {
80   GstGLCAOpenGLLayer *ca_layer = (__bridge GstGLCAOpenGLLayer *) data;
81
82   g_atomic_int_set (&ca_layer->can_draw, 1);
83 }
84
85 - (id)initWithGstGLContext:(GstGLContext *)parent_gl_context {
86   g_return_val_if_fail (GST_IS_GL_CONTEXT_COCOA (parent_gl_context), nil);
87
88   self = [super init];
89
90   _init_debug();
91
92   GST_LOG ("init CAOpenGLLayer with context");
93
94   self->gst_gl_context_ref = parent_gl_context;
95   self.needsDisplayOnBoundsChange = YES;
96
97   gst_gl_window_send_message_async (parent_gl_context->window,
98       (GstGLWindowCB) _context_ready, (__bridge_retained gpointer)self, (GDestroyNotify)CFRelease);
99
100   return self;
101 }
102
103 - (id)initWithGstGLWindow:(GstGLWindow *)parent_gl_window {
104   g_return_val_if_fail (GST_IS_GL_WINDOW_COCOA (parent_gl_window), nil);
105
106   self = [super init];
107
108   _init_debug();
109
110   GST_LOG ("init CAOpenGLLayer with window");
111
112   g_weak_ref_init (&self->gst_gl_window_ref, parent_gl_window);
113   self->gst_gl_context_ref = NULL;
114   self.needsDisplayOnBoundsChange = YES;
115
116   gst_gl_window_send_message_async (parent_gl_window,
117       (GstGLWindowCB) _context_ready, (__bridge_retained gpointer)self, (GDestroyNotify)CFRelease);
118
119   return self;
120 }
121
122 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
123   CGLPixelFormatObj fmt = NULL;
124   GstGLContext *gst_gl_context = [self getGLContext];
125
126   if (gst_gl_context) {
127     fmt = gst_gl_context_cocoa_get_pixel_format (GST_GL_CONTEXT_COCOA (gst_gl_context));
128     gst_object_unref (gst_gl_context);
129   }
130
131   if (!fmt) {
132     CGLPixelFormatAttribute attribs[] = {
133       kCGLPFADoubleBuffer,
134       kCGLPFAAccumSize, 32,
135       0
136     };
137     CGLError ret;
138     gint npix = 0;
139
140     GST_DEBUG ("creating new pixel format for CAOpenGLLayer %p", self);
141
142     ret = CGLChoosePixelFormat (attribs, &fmt, &npix);
143     if (ret != kCGLNoError) {
144       GST_ERROR ("CAOpenGLLayer cannot choose a pixel format: %s", CGLErrorString (ret));
145     }
146   }
147
148   return fmt;
149 }
150
151 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
152   GstGLDisplay *display;
153   CGLContextObj external_context = NULL;
154   CGLError ret;
155   GError *error = NULL;
156   GstGLContext *gst_gl_context = NULL;
157
158   gst_gl_context = [self getGLContext];
159
160   if (!gst_gl_context) {
161     GST_ERROR ("failed to retrieve GStreamer GL context in CAOpenGLLayer");
162     gst_clear_object (&gst_gl_context);
163     return NULL;
164   }
165   external_context = (CGLContextObj) gst_gl_context_get_gl_context (gst_gl_context);
166
167   GST_INFO ("attempting to create CGLContext for CAOpenGLLayer with "
168       "share context %p", external_context);
169
170   ret = CGLCreateContext (pixelFormat, external_context, &self->gl_context);
171   if (ret != kCGLNoError) {
172     GST_ERROR ("failed to create CGL context in CAOpenGLLayer with share context %p: %s", external_context, CGLErrorString(ret));
173     gst_clear_object (&gst_gl_context);
174     return NULL;
175   }
176
177   if (self->draw_context)
178     gst_object_unref (self->draw_context);
179
180   if (kCGLNoError != CGLSetCurrentContext (self->gl_context)) {
181     GST_ERROR ("failed set cgl context %p current", self->gl_context);
182     gst_clear_object (&gst_gl_context);
183     return NULL;
184   }
185
186   display = gst_gl_context_get_display (gst_gl_context);
187   self->draw_context = gst_gl_context_new_wrapped (display,
188       (guintptr) self->gl_context, GST_GL_PLATFORM_CGL,
189       gst_gl_context_get_current_gl_api (GST_GL_PLATFORM_CGL, NULL, NULL));
190   gst_object_unref (display);
191
192   if (!self->draw_context) {
193     GST_ERROR ("failed to create wrapped context");
194     gst_clear_object (&gst_gl_context);
195     return NULL;
196   }
197
198   gst_gl_context_activate (self->draw_context, TRUE);
199   gst_gl_context_set_shared_with (self->draw_context, gst_gl_context);
200   if (!gst_gl_context_fill_info (self->draw_context, &error)) {
201     GST_ERROR ("failed to fill wrapped context information: %s", error->message);
202     gst_clear_object (&gst_gl_context);
203     return NULL;
204   }
205   gst_gl_context_activate (self->draw_context, FALSE);
206
207   gst_clear_object (&gst_gl_context);
208
209   return self->gl_context;
210 }
211
212 - (void)queueResize {
213   self->queue_resize = TRUE;
214 }
215
216 - (void)releaseCGLContext:(CGLContextObj)glContext {
217   CGLReleaseContext (glContext);
218 }
219
220 - (void)setDrawCallback:(GstGLWindowCB)cb data:(gpointer)data
221       notify:(GDestroyNotify)notify {
222   g_return_if_fail (cb);
223
224   if (self->draw_notify)
225     self->draw_notify (self->draw_data);
226
227   self->draw_cb = cb;
228   self->draw_data = data;
229   self->draw_notify = notify;
230 }
231
232 - (void)setResizeCallback:(GstGLWindowResizeCB)cb data:(gpointer)data
233       notify:(GDestroyNotify)notify {
234   if (self->resize_notify)
235     self->resize_notify (self->resize_data);
236
237   self->resize_cb = cb;
238   self->resize_data = data;
239   self->resize_notify = notify;
240 }
241
242 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
243                pixelFormat:(CGLPixelFormatObj)pixelFormat
244             forLayerTime:(CFTimeInterval)interval
245              displayTime:(const CVTimeStamp *)timeStamp {
246   return g_atomic_int_get (&self->can_draw);
247 }
248
249 - (void)drawInCGLContext:(CGLContextObj)glContext
250                pixelFormat:(CGLPixelFormatObj)pixelFormat
251             forLayerTime:(CFTimeInterval)interval
252              displayTime:(const CVTimeStamp *)timeStamp {
253
254   GstGLContext *gst_gl_context = [self getGLContext];
255   if (!gst_gl_context)
256     return;
257
258   const GstGLFuncs *gl = gst_gl_context->gl_vtable;
259   GstVideoRectangle src, dst, result;
260   gint ca_viewport[4];
261
262   GST_LOG ("CAOpenGLLayer drawing with cgl context %p", glContext);
263
264   /* attempt to get the correct viewport back due to CA being too smart
265    * and messing around with it so center the expected viewport into
266    * the CA viewport set up on entry to this function */
267   gl->GetIntegerv (GL_VIEWPORT, ca_viewport);
268
269   GST_TRACE ("retrieved viewport from CA %u,%u %ux%u", self->expected_dims[0],
270       self->expected_dims[1], self->expected_dims[2], self->expected_dims[3]);
271   gst_gl_context_activate (self->draw_context, TRUE);
272   if (self->queue_resize || self->last_bounds.size.width != self.bounds.size.width
273       || self->last_bounds.size.height != self.bounds.size.height) {
274     if (self->resize_cb) {
275       self->resize_cb (self->resize_data,
276           self.bounds.size.width*self.contentsScale,
277           self.bounds.size.height*self.contentsScale);
278
279       gl->GetIntegerv (GL_VIEWPORT, self->expected_dims);
280
281       GST_LOG ("resize callback wants viewport %u,%u %ux%u",
282           self->expected_dims[0], self->expected_dims[1],
283           self->expected_dims[2], self->expected_dims[3]);
284     } else {
285       /* default to whatever ca gives us */
286       self->expected_dims[0] = ca_viewport[0];
287       self->expected_dims[1] = ca_viewport[1];
288       self->expected_dims[2] = ca_viewport[2];
289       self->expected_dims[3] = ca_viewport[3];
290     }
291
292     self->last_bounds = self.bounds;
293     self->queue_resize = FALSE;
294   }
295
296   src.x = self->expected_dims[0];
297   src.y = self->expected_dims[1];
298   src.w = self->expected_dims[2];
299   src.h = self->expected_dims[3];
300
301   dst.x = ca_viewport[0];
302   dst.y = ca_viewport[1];
303   dst.w = ca_viewport[2];
304   dst.h = ca_viewport[3];
305
306   gst_video_sink_center_rect (src, dst, &result, TRUE);
307
308   GST_TRACE ("Using viewport %u,%u %ux%u", result.x, result.y, result.w,
309       result.h);
310   gl->Viewport (result.x, result.y, result.w, result.h);
311
312   if (self->draw_cb)
313     self->draw_cb (self->draw_data);
314   gst_gl_context_activate (self->draw_context, FALSE);
315
316   /* flushes the buffer */
317   [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:interval displayTime:timeStamp];
318
319   gst_clear_object (&gst_gl_context);
320 }
321
322 @end