1/* 2 * GStreamer 3 * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it un der 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#import <OpenGLES/EAGL.h> 26#import <QuartzCore/QuartzCore.h> 27#import <UIKit/UIKit.h> 28 29#include "gstglwindow_eagl.h" 30#include "gstglcontext_eagl.h" 31#include "gstglios_utils.h" 32 33#define GST_CAT_DEFAULT gst_gl_window_eagl_debug 34GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 35 36#define GST_GL_WINDOW_EAGL_LAYER(obj) \ 37 ((__bridge CAEAGLLayer *)(obj->priv->layer)) 38#define GST_GL_WINDOW_EAGL_QUEUE(obj) \ 39 ((__bridge dispatch_queue_t)(obj->priv->gl_queue)) 40 41static void gst_gl_window_eagl_finalize (GObject * object); 42 43static guintptr gst_gl_window_eagl_get_display (GstGLWindow * window); 44static guintptr gst_gl_window_eagl_get_window_handle (GstGLWindow * window); 45static void gst_gl_window_eagl_set_window_handle (GstGLWindow * window, 46 guintptr handle); 47static void gst_gl_window_eagl_set_preferred_size (GstGLWindow * window, 48 gint width, gint height); 49static void gst_gl_window_eagl_draw (GstGLWindow * window); 50static void gst_gl_window_eagl_send_message_async (GstGLWindow * window, 51 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy); 52static void gst_gl_window_eagl_quit (GstGLWindow *window); 53 54struct _GstGLWindowEaglPrivate 55{ 56 gboolean pending_set_window_handle; 57 gpointer external_view; 58 gpointer internal_view; 59 gpointer layer; 60 CGFloat window_width, window_height; 61 gint preferred_width, preferred_height; 62 gpointer gl_queue; 63 GMutex draw_lock; 64 GCond cond; 65 66 gboolean shutting_down; 67 GstGLContext *last_context; 68}; 69 70#define DEBUG_INIT \ 71 GST_DEBUG_CATEGORY_GET (GST_CAT_DEFAULT, "glwindow"); 72#define gst_gl_window_eagl_parent_class parent_class 73G_DEFINE_TYPE_WITH_CODE (GstGLWindowEagl, gst_gl_window_eagl, 74 GST_TYPE_GL_WINDOW, G_ADD_PRIVATE (GstGLWindowEagl) DEBUG_INIT); 75 76static void 77gst_gl_window_eagl_class_init (GstGLWindowEaglClass * klass) 78{ 79 GObjectClass *gobject_class = (GObjectClass *) klass; 80 GstGLWindowClass *window_class = (GstGLWindowClass *) klass; 81 82 gobject_class->finalize = gst_gl_window_eagl_finalize; 83 84 window_class->get_display = 85 GST_DEBUG_FUNCPTR (gst_gl_window_eagl_get_display); 86 window_class->get_window_handle = 87 GST_DEBUG_FUNCPTR (gst_gl_window_eagl_get_window_handle); 88 window_class->set_window_handle = 89 GST_DEBUG_FUNCPTR (gst_gl_window_eagl_set_window_handle); 90 window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_eagl_draw); 91 window_class->set_preferred_size = 92 GST_DEBUG_FUNCPTR (gst_gl_window_eagl_set_preferred_size); 93 window_class->send_message_async = 94 GST_DEBUG_FUNCPTR (gst_gl_window_eagl_send_message_async); 95 window_class->quit = GST_DEBUG_FUNCPTR (gst_gl_window_eagl_quit); 96} 97 98static void 99gst_gl_window_eagl_init (GstGLWindowEagl * window) 100{ 101 window->priv = gst_gl_window_eagl_get_instance_private (window); 102 window->priv->gl_queue = 103 (__bridge_retained gpointer)dispatch_queue_create ("org.freedesktop.gstreamer.glwindow", NULL); 104 g_mutex_init (&window->priv->draw_lock); 105 g_cond_init (&window->priv->cond); 106} 107 108static void 109gst_gl_window_eagl_finalize (GObject * object) 110{ 111 GstGLWindowEagl *window = GST_GL_WINDOW_EAGL (object); 112 113 if (window->priv->layer) 114 CFRelease (window->priv->layer); 115 window->priv->layer = NULL; 116 CFRelease(window->priv->gl_queue); 117 g_mutex_clear (&window->priv->draw_lock); 118 g_cond_clear (&window->priv->cond); 119 120 G_OBJECT_CLASS (parent_class)->finalize (object); 121} 122 123/* Must be called in the gl thread */ 124GstGLWindowEagl * 125gst_gl_window_eagl_new (GstGLDisplay * display) 126{ 127 GstGLWindowEagl *window; 128 129 /* there isn't an eagl display type */ 130 window = g_object_new (GST_TYPE_GL_WINDOW_EAGL, NULL); 131 gst_object_ref_sink (window); 132 133 return window; 134} 135 136static guintptr 137gst_gl_window_eagl_get_display (GstGLWindow * window) 138{ 139 return 0; 140} 141 142static guintptr 143gst_gl_window_eagl_get_window_handle (GstGLWindow * window) 144{ 145 return (guintptr) GST_GL_WINDOW_EAGL (window)->priv->internal_view; 146} 147 148static void 149_create_gl_window (GstGLWindowEagl * window_eagl) 150{ 151 GstGLWindowEaglPrivate *priv = window_eagl->priv; 152 UIView *external_view; 153 CGRect rect; 154 GstGLUIView *view; 155 156 g_mutex_lock (&priv->draw_lock); 157 158 external_view = (__bridge UIView *) priv->external_view; 159 rect = CGRectMake (0, 0, external_view.frame.size.width, external_view.frame.size.height); 160 161 window_eagl->priv->window_width = rect.size.width; 162 window_eagl->priv->window_height = rect.size.height; 163 164 view = [[GstGLUIView alloc] initWithFrame:rect]; 165 [view setGstWindow:window_eagl]; 166 view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 167 view.contentMode = UIViewContentModeRedraw; 168 169 priv->internal_view = (__bridge_retained gpointer) view; 170 [external_view addSubview:view]; 171 priv->internal_view = (__bridge_retained gpointer) view; 172 priv->layer = (__bridge_retained gpointer) [view layer]; 173 174 NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: 175 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, 176 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; 177 178 [[view layer] setOpaque:YES]; 179 [(CAEAGLLayer *) [view layer] setDrawableProperties:dict]; 180 181 g_cond_broadcast (&priv->cond); 182 g_mutex_unlock (&priv->draw_lock); 183} 184 185static void 186ensure_window_handle_is_set_unlocked (GstGLWindowEagl * window_eagl) 187{ 188 GstGLContext *context; 189 190 if (!window_eagl->priv->pending_set_window_handle) 191 return; 192 193 context = gst_gl_window_get_context (GST_GL_WINDOW (window_eagl)); 194 if (!context) { 195 g_critical ("Window does not have a GstGLContext attached!. " 196 "Aborting set window handle."); 197 g_mutex_unlock (&window_eagl->priv->draw_lock); 198 return; 199 } 200 201 while (!window_eagl->priv->internal_view) 202 g_cond_wait (&window_eagl->priv->cond, &window_eagl->priv->draw_lock); 203 204 GST_INFO_OBJECT (context, "handle set, updating layer"); 205 gst_gl_context_eagl_update_layer (context, window_eagl->priv->layer); 206 gst_object_unref (context); 207 208 window_eagl->priv->pending_set_window_handle = FALSE; 209} 210 211static void 212gst_gl_window_eagl_set_window_handle (GstGLWindow * window, guintptr handle) 213{ 214 GstGLWindowEagl *window_eagl; 215 216 window_eagl = GST_GL_WINDOW_EAGL (window); 217 218 g_mutex_lock (&window_eagl->priv->draw_lock); 219 if (window_eagl->priv->external_view) 220 CFRelease (window_eagl->priv->external_view); 221 window_eagl->priv->external_view = (gpointer)handle; 222 window_eagl->priv->pending_set_window_handle = TRUE; 223 g_mutex_unlock (&window_eagl->priv->draw_lock); 224 225 /* XXX: Maybe we need an async set_window_handle? */ 226 _gl_invoke_on_main ((GstGLWindowEaglFunc) _create_gl_window, 227 gst_object_ref (window_eagl), gst_object_unref); 228} 229 230static void 231gst_gl_window_eagl_quit (GstGLWindow * window) 232{ 233 GstGLWindowEagl *window_eagl = (GstGLWindowEagl *) window; 234 235 window_eagl->priv->shutting_down = TRUE; 236 237 GST_GL_WINDOW_CLASS (parent_class)->quit (window); 238} 239 240static void 241gst_gl_window_eagl_set_preferred_size (GstGLWindow * window, gint width, gint height) 242{ 243 GstGLWindowEagl *window_eagl = GST_GL_WINDOW_EAGL (window); 244 245 window_eagl->priv->preferred_width = width; 246 window_eagl->priv->preferred_height = height; 247} 248 249static void 250gst_gl_window_eagl_send_message_async (GstGLWindow * window, 251 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy) 252{ 253 GstGLWindowEagl *window_eagl = (GstGLWindowEagl *) window; 254 GstGLContext *context = gst_gl_window_get_context (window); 255 GstGLContext *unref_context = NULL; 256 GThread *thread; 257 258 if (context) 259 window_eagl->priv->last_context = unref_context = context; 260 261 /* we may not have a context if we are shutting down */ 262 if (!context && window_eagl->priv->shutting_down) { 263 context = window_eagl->priv->last_context; 264 window_eagl->priv->shutting_down = FALSE; 265 } 266 267 g_return_if_fail (context != NULL); 268 269 thread = gst_gl_context_get_thread (context); 270 271 if (thread == g_thread_self()) { 272 /* this case happens for nested calls happening from inside the GCD queue */ 273 callback (data); 274 if (destroy) 275 destroy (data); 276 if (unref_context) 277 gst_object_unref (unref_context); 278 } else { 279 dispatch_async ((__bridge dispatch_queue_t)(window_eagl->priv->gl_queue), ^{ 280 gst_gl_context_activate (context, TRUE); 281 callback (data); 282 if (unref_context) 283 gst_object_unref (unref_context); 284 if (destroy) 285 destroy (data); 286 }); 287 } 288 if (thread) 289 g_thread_unref (thread); 290} 291 292static void 293draw_cb (gpointer data) 294{ 295 GstGLWindowEagl *window_eagl = data; 296 GstGLWindow *window = GST_GL_WINDOW (window_eagl); 297 GstGLContext *context = gst_gl_window_get_context (window); 298 GstGLContextEagl *eagl_context = GST_GL_CONTEXT_EAGL (context); 299 300 g_mutex_lock (&window_eagl->priv->draw_lock); 301 302 ensure_window_handle_is_set_unlocked (window_eagl); 303 304 if (window_eagl->priv->internal_view) { 305 CGSize size; 306 CAEAGLLayer *eagl_layer; 307 308 eagl_layer = GST_GL_WINDOW_EAGL_LAYER (window_eagl); 309 size = eagl_layer.frame.size; 310 311 size = CGSizeMake (size.width * eagl_layer.contentsScale, size.height * eagl_layer.contentsScale); 312 313 if (window->queue_resize || window_eagl->priv->window_width != size.width || 314 window_eagl->priv->window_height != size.height) { 315 316 window_eagl->priv->window_width = size.width; 317 window_eagl->priv->window_height = size.height; 318 319 gst_gl_context_eagl_resize (eagl_context); 320 321 gst_gl_window_resize (window, window_eagl->priv->window_width, 322 window_eagl->priv->window_height); 323 } 324 } 325 326 gst_gl_context_eagl_prepare_draw (eagl_context); 327 328 if (window->draw) 329 window->draw (window->draw_data); 330 331 gst_gl_context_swap_buffers (context); 332 333 gst_gl_context_eagl_finish_draw (eagl_context); 334 335 g_mutex_unlock (&window_eagl->priv->draw_lock); 336 337 gst_object_unref (context); 338} 339 340static void 341gst_gl_window_eagl_draw (GstGLWindow * window) 342{ 343 gst_gl_window_send_message (window, (GstGLWindowCB) draw_cb, window); 344} 345 346gpointer 347gst_gl_window_eagl_get_layer (GstGLWindowEagl * window_eagl) 348{ 349 gpointer layer; 350 351 g_mutex_lock (&window_eagl->priv->draw_lock); 352 if (window_eagl->priv->layer) 353 CFRetain (window_eagl->priv->layer); 354 layer = window_eagl->priv->layer; 355 g_mutex_unlock (&window_eagl->priv->draw_lock); 356 357 return layer; 358} 359 360@implementation GstGLUIView { 361 GstGLWindowEagl * window_eagl; 362}; 363 364+(Class) layerClass 365{ 366 return [CAEAGLLayer class]; 367} 368 369-(void) setGstWindow:(GstGLWindowEagl *) window 370{ 371 window_eagl = window; 372} 373 374@end 375 376void 377_gl_invoke_on_main (GstGLWindowEaglFunc func, gpointer data, GDestroyNotify notify) 378{ 379 if ([NSThread isMainThread]) { 380 func (data); 381 if (notify) 382 notify (data); 383 } else { 384 dispatch_async (dispatch_get_main_queue (), ^{ 385 func (data); 386 if (notify) 387 notify (data); 388 }); 389 } 390} 391