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 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#import <OpenGLES/EAGL.h> 26#import <QuartzCore/QuartzCore.h> 27#import <UIKit/UIKit.h> 28#include <OpenGLES/ES2/gl.h> 29 30#include "gstglcontext_eagl.h" 31#include "../gstglcontext_private.h" 32 33#define GST_CAT_DEFAULT gst_gl_context_debug 34 35static gboolean gst_gl_context_eagl_create_context (GstGLContext * context, 36 GstGLAPI gl_api, GstGLContext * other_context, GError ** error); 37static void gst_gl_context_eagl_destroy_context (GstGLContext * context); 38static gboolean gst_gl_context_eagl_choose_format (GstGLContext * context, 39 GError ** error); 40static guintptr gst_gl_context_eagl_get_gl_context (GstGLContext * window); 41static gboolean gst_gl_context_eagl_activate (GstGLContext * context, 42 gboolean activate); 43static void gst_gl_context_eagl_swap_buffers (GstGLContext * context); 44static GstGLAPI gst_gl_context_eagl_get_gl_api (GstGLContext * context); 45static GstGLPlatform gst_gl_context_eagl_get_gl_platform (GstGLContext * 46 context); 47 48struct _GstGLContextEaglPrivate 49{ 50 gpointer eagl_context; 51 52 /* Used if we render to a window */ 53 gpointer eagl_layer; 54 GLuint framebuffer; 55 GLuint color_renderbuffer; 56 GLuint depth_renderbuffer; 57}; 58 59G_DEFINE_TYPE_WITH_PRIVATE (GstGLContextEagl, gst_gl_context_eagl, 60 GST_TYPE_GL_CONTEXT); 61 62static void 63gst_gl_context_eagl_class_init (GstGLContextEaglClass * klass) 64{ 65 GstGLContextClass *context_class; 66 67 context_class = (GstGLContextClass *) klass; 68 69 context_class->destroy_context = 70 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_destroy_context); 71 context_class->create_context = 72 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_create_context); 73 context_class->choose_format = 74 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_choose_format); 75 context_class->get_gl_context = 76 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_context); 77 context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_activate); 78 context_class->swap_buffers = 79 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_swap_buffers); 80 context_class->get_gl_api = 81 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_api); 82 context_class->get_gl_platform = 83 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_platform); 84} 85 86static void 87gst_gl_context_eagl_init (GstGLContextEagl * context) 88{ 89 context->priv = gst_gl_context_eagl_get_instance_private (context); 90} 91 92/* Must be called in the gl thread */ 93GstGLContextEagl * 94gst_gl_context_eagl_new (GstGLDisplay * display) 95{ 96 GstGLContextEagl *context; 97 98 /* there isn't actually a display type for eagl yet? */ 99 context = g_object_new (GST_TYPE_GL_CONTEXT_EAGL, NULL); 100 gst_object_ref_sink (context); 101 102 return context; 103} 104 105void 106gst_gl_context_eagl_resize (GstGLContextEagl * eagl_context) 107{ 108 int width, height; 109 110 glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->color_renderbuffer); 111 [GS_GL_CONTEXT_EAGL_CONTEXT(eagl_context) renderbufferStorage:GL_RENDERBUFFER fromDrawable:GS_GL_CONTEXT_EAGL_LAYER(eagl_context)]; 112 glGetRenderbufferParameteriv (GL_RENDERBUFFER, 113 GL_RENDERBUFFER_WIDTH, &width); 114 glGetRenderbufferParameteriv (GL_RENDERBUFFER, 115 GL_RENDERBUFFER_HEIGHT, &height); 116 glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->depth_renderbuffer); 117 glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, 118 height); 119} 120 121static void 122gst_gl_context_eagl_release_layer (GstGLContext * context) 123{ 124 GstGLContextEagl *context_eagl; 125 126 context_eagl = GST_GL_CONTEXT_EAGL (context); 127 128 if (context_eagl->priv->eagl_layer) { 129 gst_gl_context_eagl_activate (context, TRUE); 130 131 [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:nil]; 132 133 glDeleteFramebuffers (1, &context_eagl->priv->framebuffer); 134 context_eagl->priv->framebuffer = 0; 135 136 glDeleteRenderbuffers (1, &context_eagl->priv->depth_renderbuffer); 137 context_eagl->priv->depth_renderbuffer = 0; 138 glDeleteRenderbuffers (1, &context_eagl->priv->color_renderbuffer); 139 context_eagl->priv->color_renderbuffer = 0; 140 141 context_eagl->priv->eagl_layer = nil; 142 gst_gl_context_eagl_activate (context, FALSE); 143 } 144} 145 146void 147gst_gl_context_eagl_update_layer (GstGLContext * context) 148{ 149 GLuint framebuffer; 150 GLuint color_renderbuffer; 151 GLuint depth_renderbuffer; 152 GLint width; 153 GLint height; 154 CAEAGLLayer *eagl_layer; 155 GLenum status; 156 GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context); 157 GstGLContextEaglPrivate *priv = context_eagl->priv; 158 UIView *window_handle = nil; 159 GstGLWindow *window = gst_gl_context_get_window (context); 160 if (window) 161 window_handle = (__bridge UIView *)((void *)gst_gl_window_get_window_handle (window)); 162 163 if (!window_handle) { 164 GST_INFO_OBJECT (context, "window handle not set yet, not updating layer"); 165 goto out; 166 } 167 168 GST_INFO_OBJECT (context, "updating layer, frame %fx%f", 169 window_handle.frame.size.width, window_handle.frame.size.height); 170 171 if (priv->eagl_layer) 172 gst_gl_context_eagl_release_layer (context); 173 174 eagl_layer = (CAEAGLLayer *)[window_handle layer]; 175 [EAGLContext setCurrentContext:GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl)]; 176 177 /* Allocate framebuffer */ 178 glGenFramebuffers (1, &framebuffer); 179 glBindFramebuffer (GL_FRAMEBUFFER, framebuffer); 180 /* Allocate color render buffer */ 181 glGenRenderbuffers (1, &color_renderbuffer); 182 glBindRenderbuffer (GL_RENDERBUFFER, color_renderbuffer); 183 [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:eagl_layer]; 184 glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 185 GL_RENDERBUFFER, color_renderbuffer); 186 /* Get renderbuffer width/height */ 187 glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, 188 &width); 189 glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, 190 &height); 191 /* allocate depth render buffer */ 192 glGenRenderbuffers (1, &depth_renderbuffer); 193 glBindRenderbuffer (GL_RENDERBUFFER, depth_renderbuffer); 194 glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, 195 height); 196 glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 197 GL_RENDERBUFFER, depth_renderbuffer); 198 199 /* check creation status */ 200 status = glCheckFramebufferStatus (GL_FRAMEBUFFER); 201 if (status != GL_FRAMEBUFFER_COMPLETE) { 202 GST_ERROR ("Failed to make complete framebuffer object %x", status); 203 goto out; 204 } 205 glBindRenderbuffer (GL_RENDERBUFFER, 0); 206 glBindFramebuffer (GL_FRAMEBUFFER, 0); 207 208 priv->eagl_layer = (__bridge_retained gpointer)eagl_layer; 209 priv->framebuffer = framebuffer; 210 priv->color_renderbuffer = color_renderbuffer; 211 priv->depth_renderbuffer = depth_renderbuffer; 212 213out: 214 if (window) 215 gst_object_unref (window); 216} 217 218static gboolean 219gst_gl_context_eagl_create_context (GstGLContext * context, GstGLAPI gl_api, 220 GstGLContext * other_context, GError ** error) 221{ 222 GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context); 223 GstGLContextEaglPrivate *priv = context_eagl->priv; 224 EAGLSharegroup *share_group; 225 226 if (other_context) { 227 EAGLContext *external_gl_context = (__bridge EAGLContext *)(void *) 228 gst_gl_context_get_gl_context (other_context); 229 share_group = [external_gl_context sharegroup]; 230 } else { 231 share_group = nil; 232 } 233 234 priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:share_group]; 235 if (!priv->eagl_context) { 236 priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:share_group]; 237 } 238 if (!priv->eagl_context) { 239 g_set_error_literal (error, GST_GL_CONTEXT_ERROR, 240 GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 241 "Failed to create OpenGL ES context"); 242 return FALSE; 243 } 244 245 priv->eagl_layer = NULL; 246 priv->framebuffer = 0; 247 priv->color_renderbuffer = 0; 248 priv->depth_renderbuffer = 0; 249 250 GST_INFO_OBJECT (context, "context created, updating layer"); 251 gst_gl_context_eagl_update_layer (context); 252 253 return TRUE; 254} 255 256static void 257gst_gl_context_eagl_destroy_context (GstGLContext * context) 258{ 259 GstGLContextEagl *context_eagl; 260 261 context_eagl = GST_GL_CONTEXT_EAGL (context); 262 263 if (!context_eagl->priv->eagl_context) 264 return; 265 266 gst_gl_context_eagl_release_layer (context); 267 268 CFRelease(context_eagl->priv->eagl_context); 269 context_eagl->priv->eagl_context = NULL; 270} 271 272static gboolean 273gst_gl_context_eagl_choose_format (GstGLContext * context, GError ** error) 274{ 275 GstGLContextEagl *context_eagl; 276 GstGLWindow *window; 277 UIView *window_handle = nil; 278 279 context_eagl = GST_GL_CONTEXT_EAGL (context); 280 window = gst_gl_context_get_window (context); 281 282 if (!window) 283 return TRUE; 284 285 if (window) 286 window_handle = (__bridge UIView *)(void *)gst_gl_window_get_window_handle (window); 287 288 if (!window_handle) { 289 gst_object_unref (window); 290 return TRUE; 291 } 292 293 CAEAGLLayer *eagl_layer; 294 NSDictionary * dict =[NSDictionary dictionaryWithObjectsAndKeys: 295 [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, 296 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; 297 298 eagl_layer = (CAEAGLLayer *)[window_handle layer]; 299 [eagl_layer setOpaque:YES]; 300 [eagl_layer setDrawableProperties:dict]; 301 302 gst_object_unref (window); 303 304 return TRUE; 305} 306 307static guintptr 308gst_gl_context_eagl_get_gl_context (GstGLContext * context) 309{ 310 return (guintptr) GST_GL_CONTEXT_EAGL (context)->priv->eagl_context; 311} 312 313void 314gst_gl_context_eagl_prepare_draw (GstGLContextEagl * context) 315{ 316 if (!context->priv->eagl_layer) 317 return; 318 319 glBindFramebuffer (GL_FRAMEBUFFER, context->priv->framebuffer); 320 glBindRenderbuffer (GL_RENDERBUFFER, context->priv->color_renderbuffer); 321} 322 323void 324gst_gl_context_eagl_finish_draw (GstGLContextEagl * context) 325{ 326 if (!context->priv->eagl_layer) 327 return; 328 329 glBindRenderbuffer (GL_RENDERBUFFER, 0); 330 glBindFramebuffer (GL_FRAMEBUFFER, 0); 331} 332 333static void 334gst_gl_context_eagl_swap_buffers (GstGLContext * context) 335{ 336 GstGLContextEagl *context_eagl; 337 338 context_eagl = GST_GL_CONTEXT_EAGL (context); 339 340 if (!context_eagl->priv->eagl_layer) 341 return; 342 343 [GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl) presentRenderbuffer:GL_RENDERBUFFER]; 344} 345 346static gboolean 347gst_gl_context_eagl_activate (GstGLContext * context, gboolean activate) 348{ 349 GstGLContextEagl *context_eagl; 350 351 context_eagl = GST_GL_CONTEXT_EAGL (context); 352 353 if (activate) { 354 EAGLContext *cur_ctx =[EAGLContext currentContext]; 355 356 if (cur_ctx == context_eagl->priv->eagl_context) { 357 GST_DEBUG ("Already attached the context to thread %p", g_thread_self ()); 358 return TRUE; 359 } 360 361 GST_DEBUG ("Attaching context to thread %p", g_thread_self ()); 362 if ([EAGLContext setCurrentContext:GS_GL_CONTEXT_EAGL_CONTEXT(context_eagl)] == NO) { 363 GST_ERROR ("Couldn't make context current"); 364 return FALSE; 365 } 366 } else { 367 GST_DEBUG ("Detaching context from thread %p", g_thread_self ()); 368 if ([EAGLContext setCurrentContext:nil] == NO) { 369 GST_ERROR ("Couldn't unbind context"); 370 return FALSE; 371 } 372 } 373 374 return TRUE; 375} 376 377static GstGLAPI 378gst_gl_context_eagl_get_gl_api (GstGLContext * context) 379{ 380 return GST_GL_API_GLES2; 381} 382 383static GstGLPlatform 384gst_gl_context_eagl_get_gl_platform (GstGLContext * context) 385{ 386 return GST_GL_PLATFORM_EAGL; 387} 388 389guintptr 390gst_gl_context_eagl_get_current_context (void) 391{ 392 return (guintptr) [EAGLContext currentContext]; 393} 394