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())
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);

View file

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