1/* 2 * GStreamer 3 * Copyright (C) 2015 Julien Isorce <julien.isorce@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#include <Cocoa/Cocoa.h> 26 27#include <gst/gl/cocoa/gstgldisplay_cocoa.h> 28 29GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug); 30#define GST_CAT_DEFAULT gst_gl_display_debug 31 32G_DEFINE_TYPE (GstGLDisplayCocoa, gst_gl_display_cocoa, GST_TYPE_GL_DISPLAY); 33 34static void gst_gl_display_cocoa_finalize (GObject * object); 35static guintptr gst_gl_display_cocoa_get_handle (GstGLDisplay * display); 36 37#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 38#define NSEventMaskAny NSAnyEventMask 39#endif 40 41/* Define this if the GLib patch from 42 * https://bugzilla.gnome.org/show_bug.cgi?id=741450 43 * is used 44 */ 45#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 46 47static GstGLDisplayCocoa *singleton = NULL; 48static gint nsapp_source_id = 0; 49static GMutex nsapp_lock; 50static GCond nsapp_cond; 51 52static gboolean 53gst_gl_display_cocoa_nsapp_iteration (gpointer data) 54{ 55 NSEvent *event = nil; 56 57 if (![NSThread isMainThread]) { 58 GST_WARNING ("NSApp iteration not running in the main thread"); 59 return FALSE; 60 } 61 62 63 while ((event = ([NSApp nextEventMatchingMask:NSEventMaskAny 64 untilDate:[NSDate dateWithTimeIntervalSinceNow:0.05] 65 inMode:NSDefaultRunLoopMode dequeue:YES])) != nil) { 66 [NSApp sendEvent:event]; 67 } 68 69 return TRUE; 70} 71 72static void 73gst_gl_display_cocoa_open_and_attach_source (gpointer data) 74{ 75 if ([NSThread isMainThread]) { 76 /* The sharedApplication class method initializes 77 * the display environment and connects your program 78 * to the window server and the display server. 79 * It has to be done in the main thread. 80 */ 81 [NSApplication sharedApplication]; 82 83 GST_DEBUG ("Custom NSApp initialization done"); 84 85 nsapp_source_id = g_timeout_add (60, gst_gl_display_cocoa_nsapp_iteration, 86 NULL); 87 88 GST_DEBUG ("NSApp iteration loop attached, id %d", nsapp_source_id); 89 } 90} 91 92static gboolean 93gst_gl_display_cocoa_init_nsapp (gpointer data) 94{ 95 g_mutex_lock (&nsapp_lock); 96 97 gst_gl_display_cocoa_open_and_attach_source (data); 98 99 g_cond_signal (&nsapp_cond); 100 g_mutex_unlock (&nsapp_lock); 101 102 return FALSE; 103} 104 105static GstGLDisplayCocoa * 106gst_gl_display_cocoa_setup_nsapp (gpointer data) 107{ 108 GMainContext *context = g_main_context_default (); 109 gint delta_ms = 0; 110 111 g_mutex_lock (&nsapp_lock); 112 113 if (singleton) { 114 GST_DEBUG ("Get existing display"); 115 singleton = gst_object_ref (singleton); 116 g_mutex_unlock (&nsapp_lock); 117 return singleton; 118 } 119 120 if (NSApp != nil && !singleton) { 121 GstGLDisplayCocoa *ret = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 122 gst_object_ref_sink (ret); 123 g_mutex_unlock (&nsapp_lock); 124 return ret; 125 } 126 127 /* All application have to start with [NSApplication sharedApplication] 128 * so if NSApp is nil here let's assume this is a debugging application 129 * that runs a glib main loop. */ 130 g_assert (NSApp == nil); 131 132 GST_DEBUG ("The application has not initialized NSApp"); 133 134 if ([NSThread isMainThread]) { 135 136 GST_DEBUG ("Setting up NSApp from the main thread"); 137 if (g_main_context_is_owner (context)) { 138 GST_DEBUG ("The main thread own the context"); 139 gst_gl_display_cocoa_open_and_attach_source (data); 140 } else if (g_main_context_acquire (context)) { 141 GST_DEBUG ("The main loop should be shortly running in the main thread"); 142 gst_gl_display_cocoa_open_and_attach_source (data); 143 g_main_context_release (context); 144 } else { 145 GST_WARNING ("Main loop running in another thread"); 146 } 147 } else { 148 149 GST_DEBUG ("Setting up NSApp not from the main thread"); 150 151 if (g_main_context_is_owner (context)) { 152 GST_WARNING ("Default context not own by the main thread"); 153 delta_ms = -1; 154 } else if (g_main_context_acquire (context)) { 155 GST_DEBUG ("The main loop should be shortly running in the main thread"); 156 delta_ms = 1000; 157 g_main_context_release (context); 158 } else { 159 GST_DEBUG ("Main loop running in main thread"); 160 delta_ms = 500; 161 } 162 163 if (delta_ms > 0) { 164 gint64 end_time = g_get_monotonic_time () + delta_ms * 1000;; 165 g_idle_add_full (G_PRIORITY_HIGH, gst_gl_display_cocoa_init_nsapp, data, NULL); 166 g_cond_wait_until (&nsapp_cond, &nsapp_lock, end_time); 167 } 168 } 169 170 if (NSApp == nil) { 171 GST_ERROR ("Custom NSApp initialization failed"); 172 } else { 173 GST_DEBUG ("Create display"); 174 singleton = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 175 gst_object_ref_sink (singleton); 176 } 177 178 g_mutex_unlock (&nsapp_lock); 179 180 return singleton; 181} 182 183#endif 184 185static void 186gst_gl_display_cocoa_class_init (GstGLDisplayCocoaClass * klass) 187{ 188 GST_GL_DISPLAY_CLASS (klass)->get_handle = 189 GST_DEBUG_FUNCPTR (gst_gl_display_cocoa_get_handle); 190 191 G_OBJECT_CLASS (klass)->finalize = gst_gl_display_cocoa_finalize; 192} 193 194static void 195gst_gl_display_cocoa_init (GstGLDisplayCocoa * display_cocoa) 196{ 197 GstGLDisplay *display = (GstGLDisplay *) display_cocoa; 198 display->type = GST_GL_DISPLAY_TYPE_COCOA; 199} 200 201static void 202gst_gl_display_cocoa_finalize (GObject * object) 203{ 204#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 205 g_mutex_lock (&nsapp_lock); 206 if (singleton) { 207 GST_DEBUG ("Destroy display"); 208 singleton = NULL; 209 if (nsapp_source_id) { 210 GST_DEBUG ("Remove NSApp loop iteration, id %d", nsapp_source_id); 211 g_source_remove (nsapp_source_id); 212 } 213 nsapp_source_id = 0; 214 g_mutex_unlock (&nsapp_lock); 215 } 216 g_mutex_unlock (&nsapp_lock); 217#endif 218 219 G_OBJECT_CLASS (gst_gl_display_cocoa_parent_class)->finalize (object); 220} 221 222/** 223 * gst_gl_display_cocoa_new: 224 * 225 * Create a new #GstGLDisplayCocoa. 226 * 227 * Returns: (transfer full): a new #GstGLDisplayCocoa or %NULL 228 */ 229GstGLDisplayCocoa * 230gst_gl_display_cocoa_new (void) 231{ 232 GstGLDisplayCocoa *display; 233 234 GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay"); 235 236#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION 237 display = gst_gl_display_cocoa_setup_nsapp (NULL); 238#else 239 display = g_object_new (GST_TYPE_GL_DISPLAY_COCOA, NULL); 240 gst_object_ref_sink (display); 241#endif 242 243 return display; 244} 245 246static guintptr 247gst_gl_display_cocoa_get_handle (GstGLDisplay * display) 248{ 249 return (guintptr) NSApp; 250} 251