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#include "gstglios_utils.h" 33 34#define GST_GL_CONTEXT_EAGL_CONTEXT(obj) \ 35 ((__bridge EAGLContext *)(obj->priv->eagl_context)) 36#define GST_GL_CONTEXT_EAGL_LAYER(obj) \ 37 ((__bridge CAEAGLLayer *)(obj->priv->eagl_layer)) 38 39#define GST_CAT_DEFAULT gst_gl_context_debug 40 41static gboolean gst_gl_context_eagl_create_context (GstGLContext * context, 42 GstGLAPI gl_api, GstGLContext * other_context, GError ** error); 43static void gst_gl_context_eagl_destroy_context (GstGLContext * context); 44static gboolean gst_gl_context_eagl_choose_format (GstGLContext * context, 45 GError ** error); 46static guintptr gst_gl_context_eagl_get_gl_context (GstGLContext * window); 47static gboolean gst_gl_context_eagl_activate (GstGLContext * context, 48 gboolean activate); 49static void gst_gl_context_eagl_swap_buffers (GstGLContext * context); 50static GstGLAPI gst_gl_context_eagl_get_gl_api (GstGLContext * context); 51static GstGLPlatform gst_gl_context_eagl_get_gl_platform (GstGLContext * 52 context); 53GstStructure *gst_gl_context_eagl_get_config (GstGLContext * context); 54 55struct _GstGLContextEaglPrivate 56{ 57 gpointer eagl_context; 58 59 /* Used if we render to a window */ 60 gpointer eagl_layer; 61 GLuint framebuffer; 62 GLuint color_renderbuffer; 63 GLuint depth_renderbuffer; 64}; 65 66G_DEFINE_TYPE_WITH_PRIVATE (GstGLContextEagl, gst_gl_context_eagl, 67 GST_TYPE_GL_CONTEXT); 68 69static void 70gst_gl_context_eagl_class_init (GstGLContextEaglClass * klass) 71{ 72 GstGLContextClass *context_class; 73 74 context_class = (GstGLContextClass *) klass; 75 76 context_class->destroy_context = 77 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_destroy_context); 78 context_class->create_context = 79 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_create_context); 80 context_class->choose_format = 81 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_choose_format); 82 context_class->get_gl_context = 83 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_context); 84 context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_activate); 85 context_class->swap_buffers = 86 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_swap_buffers); 87 context_class->get_gl_api = 88 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_api); 89 context_class->get_gl_platform = 90 GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_gl_platform); 91 context_class->get_config = GST_DEBUG_FUNCPTR (gst_gl_context_eagl_get_config); 92} 93 94static void 95gst_gl_context_eagl_init (GstGLContextEagl * context) 96{ 97 context->priv = gst_gl_context_eagl_get_instance_private (context); 98} 99 100/* Must be called in the gl thread */ 101GstGLContextEagl * 102gst_gl_context_eagl_new (GstGLDisplay * display) 103{ 104 GstGLContextEagl *context; 105 106 if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_EAGL) 107 == GST_GL_DISPLAY_TYPE_NONE) { 108 GST_INFO ("Wrong display type %u for this context type %u", display->type, 109 GST_GL_DISPLAY_TYPE_EAGL); 110 return NULL; 111 } 112 113 context = g_object_new (GST_TYPE_GL_CONTEXT_EAGL, NULL); 114 gst_object_ref_sink (context); 115 116 return context; 117} 118 119enum EAGLFormat 120{ 121 FORMAT_RGBA8 = 1, 122 FORMAT_RGB565, 123}; 124 125static GstStructure * 126layer_config_to_structure (GstGLContextEagl *eagl, CAEAGLLayer * layer) 127{ 128 GstStructure *ret; 129 NSDictionary *drawableProps = [layer drawableProperties]; 130 NSString *color_format; 131 enum EAGLFormat eagl_format = FORMAT_RGBA8; 132 133 ret = gst_structure_new (GST_GL_CONFIG_STRUCTURE_NAME, 134 GST_GL_CONFIG_STRUCTURE_SET_ARGS(PLATFORM, GstGLPlatform, GST_GL_PLATFORM_EAGL), 135 NULL); 136 137 color_format = [drawableProps objectForKey:kEAGLDrawablePropertyColorFormat]; 138 if (!color_format) 139 color_format = [layer contentsFormat]; 140 141 if (!color_format) { 142 GST_WARNING_OBJECT (eagl, "Could not retrieve color format from layer %p", layer); 143 goto failure; 144 } 145 146 if (color_format == kEAGLColorFormatRGBA8 || color_format == kCAContentsFormatRGBA8Uint) { 147 eagl_format = FORMAT_RGBA8; 148 } else if (color_format == kEAGLColorFormatRGB565) { 149 eagl_format = FORMAT_RGB565; 150 } else { 151 GST_WARNING_OBJECT (eagl, "unknown drawable format: %s", [color_format UTF8String]); 152 goto failure; 153 } 154 155 /* XXX: defaults chosen by _update_layer() */ 156 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(DEPTH_SIZE, int, 16), NULL); 157 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(STENCIL_SIZE, int, 0), NULL); 158 159 switch (eagl_format) { 160 case FORMAT_RGBA8: 161 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(RED_SIZE, int, 8), NULL); 162 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(GREEN_SIZE, int, 8), NULL); 163 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(BLUE_SIZE, int, 8), NULL); 164 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(ALPHA_SIZE, int, 8), NULL); 165 break; 166 case FORMAT_RGB565: 167 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(RED_SIZE, int, 5), NULL); 168 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(GREEN_SIZE, int, 6), NULL); 169 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(BLUE_SIZE, int, 5), NULL); 170 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(ALPHA_SIZE, int, 0), NULL); 171 break; 172 default: 173 GST_WARNING_OBJECT (eagl, "Unhandled format!"); 174 goto failure; 175 } 176 177 return ret; 178 179failure: 180 gst_structure_free (ret); 181 return NULL; 182} 183 184void 185gst_gl_context_eagl_resize (GstGLContextEagl * eagl_context) 186{ 187 int width, height; 188 189 glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->color_renderbuffer); 190 [GST_GL_CONTEXT_EAGL_CONTEXT(eagl_context) 191 renderbufferStorage:GL_RENDERBUFFER 192 fromDrawable:GST_GL_CONTEXT_EAGL_LAYER(eagl_context)]; 193 glGetRenderbufferParameteriv (GL_RENDERBUFFER, 194 GL_RENDERBUFFER_WIDTH, &width); 195 glGetRenderbufferParameteriv (GL_RENDERBUFFER, 196 GL_RENDERBUFFER_HEIGHT, &height); 197 glBindRenderbuffer (GL_RENDERBUFFER, eagl_context->priv->depth_renderbuffer); 198 glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, 199 height); 200} 201 202static void 203gst_gl_context_eagl_release_layer (GstGLContext * context) 204{ 205 GstGLContextEagl *context_eagl; 206 207 context_eagl = GST_GL_CONTEXT_EAGL (context); 208 209 if (context_eagl->priv->eagl_layer) { 210 gst_gl_context_eagl_activate (context, TRUE); 211 212 [GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl) 213 renderbufferStorage:GL_RENDERBUFFER 214 fromDrawable:nil]; 215 216 glDeleteFramebuffers (1, &context_eagl->priv->framebuffer); 217 context_eagl->priv->framebuffer = 0; 218 219 glDeleteRenderbuffers (1, &context_eagl->priv->depth_renderbuffer); 220 context_eagl->priv->depth_renderbuffer = 0; 221 glDeleteRenderbuffers (1, &context_eagl->priv->color_renderbuffer); 222 context_eagl->priv->color_renderbuffer = 0; 223 224 CFRelease (context_eagl->priv->eagl_layer); 225 context_eagl->priv->eagl_layer = NULL; 226 gst_gl_context_eagl_activate (context, FALSE); 227 } 228} 229 230void 231gst_gl_context_eagl_update_layer (GstGLContext * context, gpointer layer) 232{ 233 GLuint framebuffer; 234 GLuint color_renderbuffer; 235 GLuint depth_renderbuffer; 236 GLint width; 237 GLint height; 238 CAEAGLLayer *eagl_layer; 239 GLenum status; 240 GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context); 241 GstGLContextEaglPrivate *priv = context_eagl->priv; 242 GstGLWindow *window = gst_gl_context_get_window (context); 243 GstStructure *fmt; 244 245 if (!layer || !gst_gl_window_get_window_handle (window)) { 246 GST_INFO_OBJECT (context, "window handle not set yet, not updating layer"); 247 goto out; 248 } 249 250 if (priv->eagl_layer) 251 gst_gl_context_eagl_release_layer (context); 252 253 eagl_layer = (__bridge CAEAGLLayer *) layer; 254 [EAGLContext setCurrentContext:GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl)]; 255 256 /* Allocate framebuffer */ 257 glGenFramebuffers (1, &framebuffer); 258 glBindFramebuffer (GL_FRAMEBUFFER, framebuffer); 259 /* Allocate color render buffer */ 260 glGenRenderbuffers (1, &color_renderbuffer); 261 glBindRenderbuffer (GL_RENDERBUFFER, color_renderbuffer); 262 [GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl) renderbufferStorage: GL_RENDERBUFFER fromDrawable:eagl_layer]; 263 glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 264 GL_RENDERBUFFER, color_renderbuffer); 265 /* Get renderbuffer width/height */ 266 glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, 267 &width); 268 glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, 269 &height); 270 /* allocate depth render buffer */ 271 glGenRenderbuffers (1, &depth_renderbuffer); 272 glBindRenderbuffer (GL_RENDERBUFFER, depth_renderbuffer); 273 glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, 274 height); 275 glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 276 GL_RENDERBUFFER, depth_renderbuffer); 277 278 /* check creation status */ 279 status = glCheckFramebufferStatus (GL_FRAMEBUFFER); 280 if (status != GL_FRAMEBUFFER_COMPLETE) { 281 GST_ERROR ("Failed to make complete framebuffer object %x", status); 282 goto out; 283 } 284 glBindRenderbuffer (GL_RENDERBUFFER, 0); 285 glBindFramebuffer (GL_FRAMEBUFFER, 0); 286 287 priv->eagl_layer = (__bridge_retained gpointer) eagl_layer; 288 priv->framebuffer = framebuffer; 289 priv->color_renderbuffer = color_renderbuffer; 290 priv->depth_renderbuffer = depth_renderbuffer; 291 292 fmt = layer_config_to_structure (context_eagl, eagl_layer); 293 if (fmt) { 294 GST_DEBUG_OBJECT (context_eagl, "chosen config %" GST_PTR_FORMAT, fmt); 295 gst_structure_free (fmt); 296 } 297 298out: 299 if (window) 300 gst_object_unref (window); 301 if (layer) 302 CFRelease (layer); 303} 304 305static gboolean 306gst_gl_context_eagl_create_context (GstGLContext * context, GstGLAPI gl_api, 307 GstGLContext * other_context, GError ** error) 308{ 309 GstGLContextEagl *context_eagl = GST_GL_CONTEXT_EAGL (context); 310 GstGLContextEaglPrivate *priv = context_eagl->priv; 311 GstGLWindow *window; 312 GstGLWindowEagl *window_eagl; 313 gpointer layer; 314 EAGLSharegroup *share_group; 315 316 if (other_context) { 317 EAGLContext *external_gl_context = (__bridge EAGLContext *)(void *) 318 gst_gl_context_get_gl_context (other_context); 319 share_group = [external_gl_context sharegroup]; 320 } else { 321 share_group = nil; 322 } 323 324 priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:share_group]; 325 if (!priv->eagl_context) { 326 priv->eagl_context = (__bridge_retained gpointer)[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:share_group]; 327 } 328 if (!priv->eagl_context) { 329 g_set_error_literal (error, GST_GL_CONTEXT_ERROR, 330 GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 331 "Failed to create OpenGL ES context"); 332 return FALSE; 333 } 334 335 priv->eagl_layer = NULL; 336 priv->framebuffer = 0; 337 priv->color_renderbuffer = 0; 338 priv->depth_renderbuffer = 0; 339 340 GST_INFO_OBJECT (context, "context created, updating layer"); 341 window = gst_gl_context_get_window (context); 342 if (!window) { 343 g_set_error_literal (error, GST_GL_CONTEXT_ERROR, 344 GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 345 "No window to render into"); 346 return FALSE; 347 } 348 349 window_eagl = GST_GL_WINDOW_EAGL (window); 350 layer = gst_gl_window_eagl_get_layer (window_eagl); 351 gst_gl_context_eagl_update_layer (context, layer); 352 353 gst_object_unref (window); 354 355 return TRUE; 356} 357 358static void 359gst_gl_context_eagl_destroy_context (GstGLContext * context) 360{ 361 GstGLContextEagl *context_eagl; 362 363 context_eagl = GST_GL_CONTEXT_EAGL (context); 364 365 if (!context_eagl->priv->eagl_context) 366 return; 367 368 gst_gl_context_eagl_release_layer (context); 369 370 CFRelease(context_eagl->priv->eagl_context); 371 context_eagl->priv->eagl_context = NULL; 372} 373 374static gboolean 375gst_gl_context_eagl_choose_format (GstGLContext * context, GError ** error) 376{ 377 return TRUE; 378} 379 380static guintptr 381gst_gl_context_eagl_get_gl_context (GstGLContext * context) 382{ 383 return (guintptr) GST_GL_CONTEXT_EAGL (context)->priv->eagl_context; 384} 385 386void 387gst_gl_context_eagl_prepare_draw (GstGLContextEagl * context) 388{ 389 if (!context->priv->eagl_layer) 390 return; 391 392 glBindFramebuffer (GL_FRAMEBUFFER, context->priv->framebuffer); 393 glBindRenderbuffer (GL_RENDERBUFFER, context->priv->color_renderbuffer); 394} 395 396void 397gst_gl_context_eagl_finish_draw (GstGLContextEagl * context) 398{ 399 if (!context->priv->eagl_layer) 400 return; 401 402 glBindRenderbuffer (GL_RENDERBUFFER, 0); 403 glBindFramebuffer (GL_FRAMEBUFFER, 0); 404} 405 406static void 407gst_gl_context_eagl_swap_buffers (GstGLContext * context) 408{ 409 GstGLContextEagl *context_eagl; 410 411 context_eagl = GST_GL_CONTEXT_EAGL (context); 412 413 if (!context_eagl->priv->eagl_layer) 414 return; 415 416 [GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl) presentRenderbuffer:GL_RENDERBUFFER]; 417} 418 419static gboolean 420gst_gl_context_eagl_activate (GstGLContext * context, gboolean activate) 421{ 422 GstGLContextEagl *context_eagl; 423 424 context_eagl = GST_GL_CONTEXT_EAGL (context); 425 426 if (activate) { 427 EAGLContext *cur_ctx =[EAGLContext currentContext]; 428 429 if (cur_ctx == context_eagl->priv->eagl_context) { 430 GST_DEBUG ("Already attached the context to thread %p", g_thread_self ()); 431 return TRUE; 432 } 433 434 GST_DEBUG ("Attaching context to thread %p", g_thread_self ()); 435 if ([EAGLContext setCurrentContext:GST_GL_CONTEXT_EAGL_CONTEXT(context_eagl)] == NO) { 436 GST_ERROR ("Couldn't make context current"); 437 return FALSE; 438 } 439 } else { 440 GST_DEBUG ("Detaching context from thread %p", g_thread_self ()); 441 if ([EAGLContext setCurrentContext:nil] == NO) { 442 GST_ERROR ("Couldn't unbind context"); 443 return FALSE; 444 } 445 } 446 447 return TRUE; 448} 449 450static GstGLAPI 451gst_gl_context_eagl_get_gl_api (GstGLContext * context) 452{ 453 return GST_GL_API_GLES2; 454} 455 456static GstGLPlatform 457gst_gl_context_eagl_get_gl_platform (GstGLContext * context) 458{ 459 return GST_GL_PLATFORM_EAGL; 460} 461 462guintptr 463gst_gl_context_eagl_get_current_context (void) 464{ 465 return (guintptr) [EAGLContext currentContext]; 466} 467 468GstStructure * 469gst_gl_context_eagl_get_config (GstGLContext * context) 470{ 471 GstGLContextEagl *eagl = GST_GL_CONTEXT_EAGL (context); 472 473 if (!eagl->priv->eagl_layer) 474 return NULL; 475 476 return layer_config_to_structure (eagl, (__bridge CAEAGLLayer *) eagl->priv->eagl_layer); 477} 478