osxvideosink: only create the NS app thread for Cocoa once

The helper thread for Cocoa, in case no NS run loop is running,
should be started only once and shared across all the instances
running
This commit is contained in:
Andoni Morales Alastruey 2013-07-07 21:14:22 +02:00
parent 7b69f427f1
commit 213fa3af0d
2 changed files with 66 additions and 61 deletions

View file

@ -68,6 +68,12 @@ typedef struct _GstOSXVideoSinkClass GstOSXVideoSinkClass;
#define GST_TYPE_OSXVIDEOBUFFER (gst_osxvideobuffer_get_type()) #define GST_TYPE_OSXVIDEOBUFFER (gst_osxvideobuffer_get_type())
typedef enum {
GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING = 0,
GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING = 1,
GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN = 2,
} GstOSXVideoSinkRunLoopState;
/* OSXWindow stuff */ /* OSXWindow stuff */
struct _GstOSXWindow { struct _GstOSXWindow {
gint width, height; gint width, height;
@ -83,24 +89,19 @@ struct _GstOSXVideoSink {
GstOSXWindow *osxwindow; GstOSXWindow *osxwindow;
void *osxvideosinkobject; void *osxvideosinkobject;
NSView *superview; NSView *superview;
NSThread *ns_app_thread;
#ifdef RUN_NS_APP_THREAD
GMutex loop_thread_lock;
GCond loop_thread_cond;
#else
guint cocoa_timeout;
#endif
GMutex mrl_check_lock;
GCond mrl_check_cond;
gboolean mrl_check_done;
gboolean main_run_loop_running;
gboolean app_started;
gboolean keep_par; gboolean keep_par;
gboolean embed; gboolean embed;
}; };
struct _GstOSXVideoSinkClass { struct _GstOSXVideoSinkClass {
GstVideoSinkClass parent_class; GstVideoSinkClass parent_class;
GstOSXVideoSinkRunLoopState run_loop_state;
#ifdef RUN_NS_APP_THREAD
NSThread *ns_app_thread;
#else
guint cocoa_timeout;
#endif
}; };
GType gst_osx_video_sink_get_type(void); GType gst_osx_video_sink_get_type(void);

View file

@ -53,6 +53,8 @@ extern void _CFRunLoopSetCurrent(CFRunLoopRef rl);
extern pthread_t _CFMainPThread; extern pthread_t _CFMainPThread;
#endif #endif
static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory = static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -76,7 +78,10 @@ enum
}; };
static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink); static void gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink);
static GMutex _run_loop_check_mutex;
static GMutex _run_loop_mutex;
static GCond _run_loop_cond;
static GstOSXVideoSinkClass *sink_class = NULL;
static GstVideoSinkClass *parent_class = NULL; static GstVideoSinkClass *parent_class = NULL;
/* Helper to trigger calls from the main thread */ /* Helper to trigger calls from the main thread */
@ -87,7 +92,7 @@ gst_osx_video_sink_call_from_main_thread(GstOSXVideoSink *osxvideosink,
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[object performSelector:function onThread:osxvideosink->ns_app_thread [object performSelector:function onThread:sink_class->ns_app_thread
withObject:data waitUntilDone:waitUntilDone]; withObject:data waitUntilDone:waitUntilDone];
[pool release]; [pool release];
} }
@ -121,9 +126,6 @@ gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
/* check if the main run loop is running */ /* check if the main run loop is running */
gboolean is_running; gboolean is_running;
if (sink->mrl_check_done) {
return;
}
/* the easy way */ /* the easy way */
is_running = [[NSRunLoop mainRunLoop] currentMode] != nil; is_running = [[NSRunLoop mainRunLoop] currentMode] != nil;
if (is_running) { if (is_running) {
@ -137,15 +139,15 @@ gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink)
GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject; GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject;
gint64 abstime; gint64 abstime;
g_mutex_lock (&sink->mrl_check_lock); g_mutex_lock (&_run_loop_mutex);
[object performSelectorOnMainThread: [object performSelectorOnMainThread:
@selector(checkMainRunLoop) @selector(checkMainRunLoop)
withObject:nil waitUntilDone:NO]; withObject:nil waitUntilDone:NO];
/* Wait 100 ms */ /* Wait 100 ms */
abstime = g_get_monotonic_time () + 100 * 1000; abstime = g_get_monotonic_time () + 100 * 1000;
is_running = g_cond_wait_until (&sink->mrl_check_cond, is_running = g_cond_wait_until (&_run_loop_cond,
&sink->mrl_check_lock, abstime); &_run_loop_mutex, abstime);
g_mutex_unlock (&sink->mrl_check_lock); g_mutex_unlock (&_run_loop_mutex);
[pool release]; [pool release];
} }
@ -154,8 +156,12 @@ exit:
{ {
GST_DEBUG_OBJECT(sink, "The main runloop %s is running", GST_DEBUG_OBJECT(sink, "The main runloop %s is running",
is_running ? "" : " not "); is_running ? "" : " not ");
sink->main_run_loop_running = is_running; if (is_running) {
sink->mrl_check_done = TRUE; sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING;
sink_class->ns_app_thread = [NSThread mainThread];
} else {
sink_class->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_NOT_RUNNING;
}
} }
} }
@ -168,13 +174,26 @@ gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
* Since the sink needs to create it's own Cocoa window when no * Since the sink needs to create it's own Cocoa window when no
* external NSView is passed to the sink through the GstVideoOverlay API, * external NSView is passed to the sink through the GstVideoOverlay API,
* we need to run the cocoa mainloop somehow. * we need to run the cocoa mainloop somehow.
* This run loop can only be started once, by the first sink needing it
*/ */
if (!sink->main_run_loop_running) {
g_mutex_lock (&_run_loop_check_mutex);
if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN) {
gst_osx_videosink_check_main_run_loop (sink);
}
if (sink_class->run_loop_state == GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_RUNNING) {
g_mutex_unlock (&_run_loop_check_mutex);
return;
}
if (sink_class->ns_app_thread == NULL) {
#ifdef RUN_NS_APP_THREAD #ifdef RUN_NS_APP_THREAD
/* run the main runloop in a separate thread */ /* run the main runloop in a separate thread */
/* override [NSThread isMainThread] with our own implementation so that we can /* override [NSThread isMainThread] with our own implementation so that we can
* make it believe our dedicated thread is the main thread * make it believe our dedicated thread is the main thread
*/ */
Method origIsMainThread = class_getClassMethod([NSThread class], Method origIsMainThread = class_getClassMethod([NSThread class],
NSSelectorFromString(@"isMainThread")); NSSelectorFromString(@"isMainThread"));
@ -183,30 +202,31 @@ gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink )
method_exchangeImplementations(origIsMainThread, ourIsMainThread); method_exchangeImplementations(origIsMainThread, ourIsMainThread);
sink->ns_app_thread = [[NSThread alloc] sink_class->ns_app_thread = [[NSThread alloc]
initWithTarget:sink->osxvideosinkobject initWithTarget:sink->osxvideosinkobject
selector:@selector(nsAppThread) object:nil]; selector:@selector(nsAppThread) object:nil];
[sink->ns_app_thread start]; [sink_class->ns_app_thread start];
g_mutex_lock (&sink->loop_thread_lock); g_mutex_lock (&_run_loop_mutex);
while (!sink->app_started) g_cond_wait (&_run_loop_cond, &_run_loop_mutex);
g_cond_wait (&sink->loop_thread_cond, &sink->loop_thread_lock); g_mutex_unlock (&_run_loop_mutex);
g_mutex_unlock (&sink->loop_thread_lock);
#else #else
/* assume that there is a GMainLoop and iterate the main runloop from there /* assume that there is a GMainLoop and iterate the main runloop from there
*/ */
sink->cocoa_timeout = g_timeout_add (10, sink_class->cocoa_timeout = g_timeout_add (10,
(GSourceFunc) run_ns_app_loop, NULL); (GSourceFunc) run_ns_app_loop, NULL);
#endif #endif
} }
g_mutex_unlock (&_run_loop_check_mutex);
} }
static void static void
gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink) gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink)
{ {
#ifndef RUN_NS_APP_THREAD #ifndef RUN_NS_APP_THREAD
if (osxvideosink->cocoa_timeout) if (sink_class->cocoa_timeout)
g_source_remove(osxvideosink->cocoa_timeout); g_source_remove(sink_klass->cocoa_timeout);
#endif #endif
} }
@ -250,10 +270,8 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width,
GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent"); GST_INFO_OBJECT (osxvideosink, "'have-ns-view' message sent");
osxvideosink->ns_app_thread = [NSThread mainThread];
gst_osx_videosink_check_main_run_loop (osxvideosink);
gst_osx_video_sink_run_cocoa_loop (osxvideosink); gst_osx_video_sink_run_cocoa_loop (osxvideosink);
[osxwindow->gstview setMainThread:osxvideosink->ns_app_thread]; [osxwindow->gstview setMainThread:sink_class->ns_app_thread];
/* check if have-ns-view was handled and osxwindow->gstview was added to a /* check if have-ns-view was handled and osxwindow->gstview was added to a
* superview * superview
@ -408,7 +426,6 @@ gst_osx_video_sink_change_state (GstElement * element,
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_VIDEO_SINK_WIDTH (osxvideosink) = 0; GST_VIDEO_SINK_WIDTH (osxvideosink) = 0;
GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0; GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0;
osxvideosink->app_started = FALSE;
gst_osx_video_sink_osxwindow_destroy (osxvideosink); gst_osx_video_sink_osxwindow_destroy (osxvideosink);
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
@ -505,15 +522,6 @@ gst_osx_video_sink_init (GstOSXVideoSink * sink)
sink->osxwindow = NULL; sink->osxwindow = NULL;
sink->superview = NULL; sink->superview = NULL;
sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink]; sink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:sink];
#ifdef RUN_NS_APP_THREAD
g_mutex_init (&sink->loop_thread_lock);
g_cond_init (&sink->loop_thread_cond);
#endif
g_mutex_init (&sink->mrl_check_lock);
g_cond_init (&sink->mrl_check_cond);
sink->mrl_check_done = FALSE;
sink->main_run_loop_running = FALSE;
sink->app_started = FALSE;
sink->keep_par = FALSE; sink->keep_par = FALSE;
} }
@ -541,9 +549,6 @@ gst_osx_video_sink_finalize (GObject *object)
if (osxvideosink->osxvideosinkobject) if (osxvideosink->osxvideosinkobject)
[(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release]; [(GstOSXVideoSinkObject*)(osxvideosink->osxvideosinkobject) release];
g_mutex_clear (&osxvideosink->mrl_check_lock);
g_cond_clear (&osxvideosink->mrl_check_cond);
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
@ -558,8 +563,11 @@ gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass)
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass;
parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK); parent_class = g_type_class_ref (GST_TYPE_VIDEO_SINK);
sink_class = klass;
klass->run_loop_state = GST_OSX_VIDEO_SINK_RUN_LOOP_STATE_UNKNOWN;
klass->ns_app_thread = NULL;
gobject_class->set_property = gst_osx_video_sink_set_property; gobject_class->set_property = gst_osx_video_sink_set_property;
gobject_class->get_property = gst_osx_video_sink_get_property; gobject_class->get_property = gst_osx_video_sink_get_property;
@ -675,7 +683,6 @@ gst_osx_video_sink_set_window_handle (GstVideoOverlay * overlay, guintptr handle
@selector(removeFromSuperview:), (id)nil, YES); @selector(removeFromSuperview:), (id)nil, YES);
} }
[osxvideosink->superview release]; [osxvideosink->superview release];
} }
GST_INFO_OBJECT (osxvideosink, "set xwindow id 0x%lx", window_id); GST_INFO_OBJECT (osxvideosink, "set xwindow id 0x%lx", window_id);
@ -903,7 +910,6 @@ gst_osx_video_sink_get_type (void)
[osxwindow->win release]; [osxwindow->win release];
} }
} }
g_free (osxwindow); g_free (osxwindow);
} }
[pool release]; [pool release];
@ -913,7 +919,6 @@ gst_osx_video_sink_get_type (void)
-(void) nsAppThread -(void) nsAppThread
{ {
NSAutoreleasePool *pool; NSAutoreleasePool *pool;
GstOSXVideoSink *sink = osxvideosink;
/* set the main runloop as the runloop for the current thread. This has the /* set the main runloop as the runloop for the current thread. This has the
* effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
@ -931,10 +936,9 @@ gst_osx_video_sink_get_type (void)
[NSApplication sharedApplication]; [NSApplication sharedApplication];
[NSApp finishLaunching]; [NSApp finishLaunching];
g_mutex_lock (&sink->loop_thread_lock); g_mutex_lock (&_run_loop_mutex);
sink->app_started = TRUE; g_cond_signal (&_run_loop_cond);
g_cond_signal (&sink->loop_thread_cond); g_mutex_unlock (&_run_loop_mutex);
g_mutex_unlock (&sink->loop_thread_lock);
/* run the loop */ /* run the loop */
run_ns_app_loop (); run_ns_app_loop ();
@ -945,9 +949,9 @@ gst_osx_video_sink_get_type (void)
-(void) checkMainRunLoop -(void) checkMainRunLoop
{ {
g_mutex_lock (&osxvideosink->mrl_check_lock); g_mutex_lock (&_run_loop_mutex);
g_cond_signal (&osxvideosink->mrl_check_cond); g_cond_signal (&_run_loop_cond);
g_mutex_unlock (&osxvideosink->mrl_check_lock); g_mutex_unlock (&_run_loop_mutex);
} }
@end @end