From 213fa3af0d5cea0faf358586c6fd5e8cb7d8314f Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Sun, 7 Jul 2013 21:14:22 +0200 Subject: [PATCH] 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 --- sys/osxvideo/osxvideosink.h | 25 ++++----- sys/osxvideo/osxvideosink.m | 102 +++++++++++++++++++----------------- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/sys/osxvideo/osxvideosink.h b/sys/osxvideo/osxvideosink.h index cf0410f644..2517bb37f2 100644 --- a/sys/osxvideo/osxvideosink.h +++ b/sys/osxvideo/osxvideosink.h @@ -68,6 +68,12 @@ typedef struct _GstOSXVideoSinkClass GstOSXVideoSinkClass; #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 */ struct _GstOSXWindow { gint width, height; @@ -83,24 +89,19 @@ struct _GstOSXVideoSink { GstOSXWindow *osxwindow; void *osxvideosinkobject; 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 embed; }; struct _GstOSXVideoSinkClass { 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); diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m index c75c681d9d..8c172e5383 100644 --- a/sys/osxvideo/osxvideosink.m +++ b/sys/osxvideo/osxvideosink.m @@ -53,6 +53,8 @@ extern void _CFRunLoopSetCurrent(CFRunLoopRef rl); extern pthread_t _CFMainPThread; #endif + + static GstStaticPadTemplate gst_osx_video_sink_sink_template_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -76,7 +78,10 @@ enum }; 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; /* 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]; - [object performSelector:function onThread:osxvideosink->ns_app_thread + [object performSelector:function onThread:sink_class->ns_app_thread withObject:data waitUntilDone:waitUntilDone]; [pool release]; } @@ -121,9 +126,6 @@ gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink) /* check if the main run loop is running */ gboolean is_running; - if (sink->mrl_check_done) { - return; - } /* the easy way */ is_running = [[NSRunLoop mainRunLoop] currentMode] != nil; if (is_running) { @@ -137,15 +139,15 @@ gst_osx_videosink_check_main_run_loop (GstOSXVideoSink *sink) GstOSXVideoSinkObject * object = (GstOSXVideoSinkObject *) sink->osxvideosinkobject; gint64 abstime; - g_mutex_lock (&sink->mrl_check_lock); + g_mutex_lock (&_run_loop_mutex); [object performSelectorOnMainThread: @selector(checkMainRunLoop) withObject:nil waitUntilDone:NO]; /* Wait 100 ms */ abstime = g_get_monotonic_time () + 100 * 1000; - is_running = g_cond_wait_until (&sink->mrl_check_cond, - &sink->mrl_check_lock, abstime); - g_mutex_unlock (&sink->mrl_check_lock); + is_running = g_cond_wait_until (&_run_loop_cond, + &_run_loop_mutex, abstime); + g_mutex_unlock (&_run_loop_mutex); [pool release]; } @@ -154,8 +156,12 @@ exit: { GST_DEBUG_OBJECT(sink, "The main runloop %s is running", is_running ? "" : " not "); - sink->main_run_loop_running = is_running; - sink->mrl_check_done = TRUE; + if (is_running) { + 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 * external NSView is passed to the sink through the GstVideoOverlay API, * 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 /* run the main runloop in a separate thread */ /* 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], NSSelectorFromString(@"isMainThread")); @@ -183,30 +202,31 @@ gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * sink ) method_exchangeImplementations(origIsMainThread, ourIsMainThread); - sink->ns_app_thread = [[NSThread alloc] + sink_class->ns_app_thread = [[NSThread alloc] initWithTarget:sink->osxvideosinkobject selector:@selector(nsAppThread) object:nil]; - [sink->ns_app_thread start]; + [sink_class->ns_app_thread start]; - g_mutex_lock (&sink->loop_thread_lock); - while (!sink->app_started) - g_cond_wait (&sink->loop_thread_cond, &sink->loop_thread_lock); - g_mutex_unlock (&sink->loop_thread_lock); + g_mutex_lock (&_run_loop_mutex); + g_cond_wait (&_run_loop_cond, &_run_loop_mutex); + g_mutex_unlock (&_run_loop_mutex); #else /* 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); #endif } + + g_mutex_unlock (&_run_loop_check_mutex); } static void gst_osx_video_sink_stop_cocoa_loop (GstOSXVideoSink * osxvideosink) { #ifndef RUN_NS_APP_THREAD - if (osxvideosink->cocoa_timeout) - g_source_remove(osxvideosink->cocoa_timeout); + if (sink_class->cocoa_timeout) + g_source_remove(sink_klass->cocoa_timeout); #endif } @@ -250,10 +270,8 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width, 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); - [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 * superview @@ -408,7 +426,6 @@ gst_osx_video_sink_change_state (GstElement * element, case GST_STATE_CHANGE_PAUSED_TO_READY: GST_VIDEO_SINK_WIDTH (osxvideosink) = 0; GST_VIDEO_SINK_HEIGHT (osxvideosink) = 0; - osxvideosink->app_started = FALSE; gst_osx_video_sink_osxwindow_destroy (osxvideosink); break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -505,15 +522,6 @@ gst_osx_video_sink_init (GstOSXVideoSink * sink) sink->osxwindow = NULL; sink->superview = NULL; 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; } @@ -541,9 +549,6 @@ gst_osx_video_sink_finalize (GObject *object) if (osxvideosink->osxvideosinkobject) [(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); } @@ -558,8 +563,11 @@ gst_osx_video_sink_class_init (GstOSXVideoSinkClass * klass) gstelement_class = (GstElementClass *) klass; gstbasesink_class = (GstBaseSinkClass *) klass; - 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->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); } [osxvideosink->superview release]; - } 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]; } } - g_free (osxwindow); } [pool release]; @@ -913,7 +919,6 @@ gst_osx_video_sink_get_type (void) -(void) nsAppThread { NSAutoreleasePool *pool; - GstOSXVideoSink *sink = osxvideosink; /* set the main runloop as the runloop for the current thread. This has the * effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue @@ -931,10 +936,9 @@ gst_osx_video_sink_get_type (void) [NSApplication sharedApplication]; [NSApp finishLaunching]; - g_mutex_lock (&sink->loop_thread_lock); - sink->app_started = TRUE; - g_cond_signal (&sink->loop_thread_cond); - g_mutex_unlock (&sink->loop_thread_lock); + g_mutex_lock (&_run_loop_mutex); + g_cond_signal (&_run_loop_cond); + g_mutex_unlock (&_run_loop_mutex); /* run the loop */ run_ns_app_loop (); @@ -945,9 +949,9 @@ gst_osx_video_sink_get_type (void) -(void) checkMainRunLoop { - g_mutex_lock (&osxvideosink->mrl_check_lock); - g_cond_signal (&osxvideosink->mrl_check_cond); - g_mutex_unlock (&osxvideosink->mrl_check_lock); + g_mutex_lock (&_run_loop_mutex); + g_cond_signal (&_run_loop_cond); + g_mutex_unlock (&_run_loop_mutex); } @end