1/* 2 * GStreamer 3 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.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 "gstglcontext_cocoa.h" 32#include "gstgl_cocoa_private.h" 33 34static gboolean gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api, 35 GstGLContext * other_context, GError **error); 36static void gst_gl_context_cocoa_destroy_context (GstGLContext *context); 37static guintptr gst_gl_context_cocoa_get_gl_context (GstGLContext * window); 38static gboolean gst_gl_context_cocoa_activate (GstGLContext * context, gboolean activate); 39static GstGLAPI gst_gl_context_cocoa_get_gl_api (GstGLContext * context); 40static GstGLPlatform gst_gl_context_cocoa_get_gl_platform (GstGLContext * context); 41static void gst_gl_context_cocoa_swap_buffers (GstGLContext * context); 42static GstStructure * gst_gl_context_cocoa_get_config (GstGLContext * context); 43 44GST_DEBUG_CATEGORY_STATIC (gst_gl_context_cocoa_debug); 45#define GST_CAT_DEFAULT gst_gl_context_cocoa_debug 46 47G_DEFINE_TYPE_WITH_CODE (GstGLContextCocoa, gst_gl_context_cocoa, 48 GST_TYPE_GL_CONTEXT, 49 G_ADD_PRIVATE (GstGLContextCocoa) 50 GST_DEBUG_CATEGORY_INIT (gst_gl_context_cocoa_debug, "glcontext_cocoa", 0, "Cocoa GL Context"); ); 51 52static void 53gst_gl_context_cocoa_class_init (GstGLContextCocoaClass * klass) 54{ 55 GstGLContextClass *context_class = (GstGLContextClass *) klass; 56 57 context_class->swap_buffers = 58 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_swap_buffers); 59 context_class->destroy_context = 60 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_destroy_context); 61 context_class->create_context = 62 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_create_context); 63 context_class->get_gl_context = 64 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_context); 65 context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_activate); 66 context_class->get_gl_api = 67 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_api); 68 context_class->get_gl_platform = 69 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_platform); 70 context_class->get_config = 71 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_config); 72} 73 74static void 75gst_gl_context_cocoa_init (GstGLContextCocoa * context) 76{ 77 context->priv = gst_gl_context_cocoa_get_instance_private (context); 78} 79 80/* Must be called in the gl thread */ 81GstGLContextCocoa * 82gst_gl_context_cocoa_new (GstGLDisplay * display) 83{ 84 GstGLContextCocoa *context; 85 86 if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_COCOA) == 0) 87 /* we require an cocoa display to create CGL contexts */ 88 return NULL; 89 90 context = g_object_new (GST_TYPE_GL_CONTEXT_COCOA, NULL); 91 gst_object_ref_sink (context); 92 93 return context; 94} 95 96struct pixel_attr 97{ 98 CGLPixelFormatAttribute attr; 99 const gchar *attr_name; 100}; 101 102static struct pixel_attr pixel_attrs[] = { 103 {kCGLPFAAllRenderers, "All Renderers"}, 104 {kCGLPFADoubleBuffer, "Double Buffered"}, 105 {kCGLPFAAuxBuffers, "Aux Buffers"}, 106 {kCGLPFAColorSize, "Color Size"}, 107 {kCGLPFAAlphaSize, "Alpha Size"}, 108 {kCGLPFADepthSize, "Depth Size"}, 109 {kCGLPFAStencilSize, "Stencil Size"}, 110 {kCGLPFAAccumSize, "Accum Size"}, 111 {kCGLPFAMinimumPolicy, "Minimum Policy"}, 112 {kCGLPFAMaximumPolicy, "Maximum Policy"}, 113 {kCGLPFASampleBuffers, "Sample Buffers"}, 114 {kCGLPFASamples, "Samples"}, 115 {kCGLPFAAuxDepthStencil, "Aux Depth Stencil"}, 116 {kCGLPFAColorFloat, "Color Float"}, 117 {kCGLPFAMultisample, "Multisample"}, 118 {kCGLPFASupersample, "Supersample"}, 119 {kCGLPFARendererID, "Renderer ID"}, 120 {kCGLPFANoRecovery, "No Recovery"}, 121 {kCGLPFAAccelerated, "Accelerated"}, 122 {kCGLPFAClosestPolicy, "Closest Policy"}, 123 {kCGLPFABackingStore, "Backing Store"}, 124 {kCGLPFADisplayMask, "Display Mask"}, 125 {kCGLPFAAllowOfflineRenderers, "Allow Offline Renderers"}, 126 {kCGLPFAAcceleratedCompute, "Accelerated Compute"}, 127 {kCGLPFAOpenGLProfile, "OpenGL Profile"}, 128 {kCGLPFAVirtualScreenCount, "Virtual Screen Count"}, 129#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 130 {kCGLPFAStereo, "Stereo"}, 131#endif 132#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 133 {kCGLPFACompliant, "Compliant"}, 134 {kCGLPFARemotePBuffer, "Remote PBuffer"}, 135 {kCGLPFASingleRenderer, "Single Renderer"}, 136 {kCGLPFAWindow, "Window"}, 137#endif 138#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 139// {kCGLPFAOffScreen, "Off Screen"}, 140// {kCGLPFAPBuffer, "PBuffer"}, 141#endif 142#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 143// {kCGLPFAFullScreen, "Full Screen"}, 144#endif 145#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 146// {kCGLPFAMPSafe, "MP Safe"}, 147// {kCGLPFAMultiScreen, "Multi Screen"}, 148// {kCGLPFARobust, "Robust"}, 149#endif 150}; 151 152void 153gst_gl_context_cocoa_dump_pixel_format (CGLPixelFormatObj fmt) 154{ 155 int i; 156 157 for (i = 0; i < G_N_ELEMENTS (pixel_attrs); i++) { 158 gint val; 159 CGLError ret = CGLDescribePixelFormat (fmt, 0, pixel_attrs[i].attr, &val); 160 161 if (ret != kCGLNoError) { 162 GST_WARNING ("failed to get pixel format %p attribute %s", fmt, pixel_attrs[i].attr_name); 163 } else { 164 GST_DEBUG ("Pixel format %p attr %s = %i", fmt, pixel_attrs[i].attr_name, 165 val); 166 } 167 } 168} 169 170CGLPixelFormatObj 171gst_gl_context_cocoa_get_pixel_format (GstGLContextCocoa *context) 172{ 173 return context->priv->pixel_format; 174} 175 176static GstStructure * 177cgl_pixel_format_to_structure (CGLPixelFormatObj fmt) 178{ 179 GstStructure *ret; 180 int val, alpha; 181 182 ret = gst_structure_new (GST_GL_CONFIG_STRUCTURE_NAME, 183 GST_GL_CONFIG_STRUCTURE_SET_ARGS(PLATFORM, GstGLPlatform, GST_GL_PLATFORM_CGL), 184 NULL); 185 186 if (CGLDescribePixelFormat (fmt, 0, kCGLPFAAlphaSize, &alpha) != kCGLNoError) 187 goto failure; 188 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(ALPHA_SIZE, int, alpha), NULL); 189 190 if (CGLDescribePixelFormat (fmt, 0, kCGLPFADepthSize, &val) != kCGLNoError) 191 goto failure; 192 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(DEPTH_SIZE, int, val), NULL); 193 194 if (CGLDescribePixelFormat (fmt, 0, kCGLPFAStencilSize, &val) != kCGLNoError) 195 goto failure; 196 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(STENCIL_SIZE, int, val), NULL); 197 198 if (CGLDescribePixelFormat (fmt, 0, kCGLPFAColorSize, &val) != kCGLNoError) 199 goto failure; 200 val -= alpha; 201 if (val % 3 == 0) { 202 /* XXX: assumes that bits are evenly distributed */ 203 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(RED_SIZE, int, val / 3), NULL); 204 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(GREEN_SIZE, int, val / 3), NULL); 205 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(BLUE_SIZE, int, val / 3), NULL); 206 } else { 207 GST_WARNING ("Don't know how to split a color size of %u into R,G,B values", 208 val); 209 goto failure; 210 } 211 212 if (CGLDescribePixelFormat (fmt, 0, kCGLPFASamples, &val) != kCGLNoError) 213 goto failure; 214 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(SAMPLES, int, val), NULL); 215 216 if (CGLDescribePixelFormat (fmt, 0, kCGLPFASampleBuffers, &val) != kCGLNoError) 217 goto failure; 218 gst_structure_set (ret, GST_GL_CONFIG_STRUCTURE_SET_ARGS(SAMPLE_BUFFERS, int, val), NULL); 219 220 return ret; 221 222failure: 223 gst_structure_free (ret); 224 return NULL; 225} 226 227static gboolean 228gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api, 229 GstGLContext *other_context, GError **error) 230{ 231 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 232 GstGLContextCocoaPrivate *priv = context_cocoa->priv; 233 GstGLWindow *window = gst_gl_context_get_window (context); 234 GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window); 235 GstGLAPI context_api = GST_GL_API_NONE; 236 const GLint swapInterval = 1; 237 CGLPixelFormatObj fmt = NULL; 238 CGLContextObj glContext; 239 CGLPixelFormatAttribute attribs[] = { 240 kCGLPFADoubleBuffer, 241 kCGLPFAAccumSize, 32, 242 0 243 }; 244 CGLError ret; 245 gint pix_fmt_i = 0; 246 gint npix; 247 248 if ((gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3)) == GST_GL_API_NONE) { 249 g_set_error (error, GST_GL_CONTEXT_ERROR, 250 GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 251 "The CGL backend only supports GL and GL3"); 252 goto error; 253 } 254 255 priv->gl_context = nil; 256 if (other_context) 257 priv->external_gl_context = (CGLContextObj) gst_gl_context_get_gl_context (other_context); 258 else 259 priv->external_gl_context = NULL; 260 261 if (priv->external_gl_context) { 262 gint profile; 263 264 fmt = CGLGetPixelFormat (priv->external_gl_context); 265 266#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 267 /* core profile is only available in >= 10.7 */ 268 if (kCGLNoError == CGLDescribePixelFormat (fmt, 0, kCGLPFAOpenGLProfile, 269 &profile)) { 270 if (profile == kCGLOGLPVersion_3_2_Core) { 271 context_api = GST_GL_API_OPENGL3; 272 } else { 273 context_api =GST_GL_API_OPENGL; 274 } 275 } 276#else 277 context_api = GST_GL_API_OPENGL; 278#endif 279 } 280 281 if (!fmt) { 282#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 283 if (gl_api & GST_GL_API_OPENGL3) { 284 attribs[pix_fmt_i++] = kCGLPFAOpenGLProfile; 285 attribs[pix_fmt_i++] = (int) kCGLOGLPVersion_3_2_Core; 286 context_api = GST_GL_API_OPENGL3; 287 } else { 288 context_api = GST_GL_API_OPENGL; 289 } 290#else 291 context_api = GST_GL_API_OPENGL; 292#endif 293 294 attribs[pix_fmt_i++] = 0; 295 296 ret = CGLChoosePixelFormat (attribs, &fmt, &npix); 297 if (ret != kCGLNoError) { 298 g_set_error (error, GST_GL_CONTEXT_ERROR, 299 GST_GL_CONTEXT_ERROR_WRONG_CONFIG, "cannot choose a pixel format: %s", CGLErrorString (ret)); 300 goto error; 301 } 302 } 303 304 gst_gl_context_cocoa_dump_pixel_format (fmt); 305 306 ret = CGLCreateContext (fmt, priv->external_gl_context, &glContext); 307 if (ret != kCGLNoError) { 308 g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 309 "failed to create context: %s", CGLErrorString (ret)); 310 goto error; 311 } 312 313 context_cocoa->priv->pixel_format = fmt; 314 context_cocoa->priv->gl_context = glContext; 315 316 _invoke_on_main ((GstGLWindowCB) gst_gl_window_cocoa_create_window, 317 gst_object_ref (window_cocoa), (GDestroyNotify) gst_object_unref); 318 319 if (!context_cocoa->priv->gl_context) { 320 goto error; 321 } 322 323 GST_INFO_OBJECT (context, "GL context created: %p", context_cocoa->priv->gl_context); 324 325 CGLSetCurrentContext (context_cocoa->priv->gl_context); 326 327 /* Back and front buffers are swapped only during the vertical retrace of the monitor. 328 * Discarded if you configured your driver to Never-use-V-Sync. 329 */ 330 CGLSetParameter (context_cocoa->priv->gl_context, kCGLCPSwapInterval, &swapInterval); 331 332 context_cocoa->priv->context_api = context_api; 333 334 if (window) 335 gst_object_unref (window); 336 337 return TRUE; 338 339error: 340 { 341 if (window) 342 gst_object_unref (window); 343 return FALSE; 344 } 345} 346 347static void 348gst_gl_context_cocoa_swap_buffers (GstGLContext * context) 349{ 350} 351 352static void 353gst_gl_context_cocoa_destroy_context (GstGLContext *context) 354{ 355 /* FIXME: Need to release context and other things? */ 356} 357 358static guintptr 359gst_gl_context_cocoa_get_gl_context (GstGLContext * context) 360{ 361 return (guintptr) GST_GL_CONTEXT_COCOA (context)->priv->gl_context; 362} 363 364static gboolean 365gst_gl_context_cocoa_activate (GstGLContext * context, gboolean activate) 366{ 367 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 368 gpointer context_handle = activate ? context_cocoa->priv->gl_context : NULL; 369 370 return kCGLNoError == CGLSetCurrentContext (context_handle); 371} 372 373static GstGLAPI 374gst_gl_context_cocoa_get_gl_api (GstGLContext * context) 375{ 376 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 377 378 if (context_cocoa->priv->gl_context) 379 return context_cocoa->priv->context_api; 380 381 return GST_GL_API_OPENGL | GST_GL_API_OPENGL3; 382} 383 384static GstGLPlatform 385gst_gl_context_cocoa_get_gl_platform (GstGLContext * context) 386{ 387 return GST_GL_PLATFORM_CGL; 388} 389 390guintptr 391gst_gl_context_cocoa_get_current_context (void) 392{ 393 return (guintptr) CGLGetCurrentContext (); 394} 395 396static GstStructure * 397gst_gl_context_cocoa_get_config (GstGLContext * context) 398{ 399 GstGLContextCocoa *cocoa = GST_GL_CONTEXT_COCOA (context); 400 401 g_return_val_if_fail (cocoa->priv->pixel_format, NULL); 402 403 return cgl_pixel_format_to_structure (cocoa->priv->pixel_format); 404} 405