• 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 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