• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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