mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
osxvideosink: add code to optionally run the cocoa main runloop in a separate thread
Add a little hack to run the cocoa main runloop from a separate thread _when_ the main runloop is not being run (which means that the app doesn't use cocoa). Runloops are thread specific, so the hack boils down to getting the runloop for the main thread and setting it as the runloop for our dedicated thread.
This commit is contained in:
parent
e3cba62b7e
commit
718837d914
2 changed files with 93 additions and 12 deletions
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <objc/runtime.h>
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
#include <QuickTime/QuickTime.h>
|
#include <QuickTime/QuickTime.h>
|
||||||
|
@ -72,10 +73,14 @@ struct _GstOSXVideoSink {
|
||||||
/* Our element stuff */
|
/* Our element stuff */
|
||||||
GstVideoSink videosink;
|
GstVideoSink videosink;
|
||||||
GstOSXWindow *osxwindow;
|
GstOSXWindow *osxwindow;
|
||||||
gboolean app_started;
|
|
||||||
void *osxvideosinkobject;
|
void *osxvideosinkobject;
|
||||||
NSView *superview;
|
NSView *superview;
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
GThread *ns_app_thread;
|
||||||
|
#else
|
||||||
guint cocoa_timeout;
|
guint cocoa_timeout;
|
||||||
|
gboolean app_started;
|
||||||
|
#endif
|
||||||
gboolean keep_par;
|
gboolean keep_par;
|
||||||
gboolean embed;
|
gboolean embed;
|
||||||
};
|
};
|
||||||
|
@ -118,6 +123,9 @@ GType gst_osx_video_sink_get_type(void);
|
||||||
GstOSXVideoSink *osxvideosink;
|
GstOSXVideoSink *osxvideosink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
+ (BOOL) isMainThread;
|
||||||
|
#endif
|
||||||
-(id) initWithSink: (GstOSXVideoSink *) sink;
|
-(id) initWithSink: (GstOSXVideoSink *) sink;
|
||||||
-(void) createInternalWindow;
|
-(void) createInternalWindow;
|
||||||
-(void) resize;
|
-(void) resize;
|
||||||
|
|
|
@ -47,6 +47,11 @@
|
||||||
GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
|
GST_DEBUG_CATEGORY (gst_debug_osx_video_sink);
|
||||||
#define GST_CAT_DEFAULT gst_debug_osx_video_sink
|
#define GST_CAT_DEFAULT gst_debug_osx_video_sink
|
||||||
|
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
extern void _CFRunLoopSetCurrent(CFRunLoopRef rl);
|
||||||
|
extern pthread_t _CFMainPThread;
|
||||||
|
#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,
|
||||||
|
@ -87,44 +92,100 @@ gst_osx_video_sink_call_from_main_thread(NSObject * object, SEL function,
|
||||||
|
|
||||||
/* Poll for cocoa events */
|
/* Poll for cocoa events */
|
||||||
static void
|
static void
|
||||||
cocoa_poll_events (void) {
|
run_ns_app_loop (void) {
|
||||||
NSEvent *event;
|
NSEvent *event;
|
||||||
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
|
||||||
|
NSDate *pollTime = nil;
|
||||||
|
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
/* when running the loop in a thread we want to sleep as long as possible */
|
||||||
|
pollTime = [NSDate distantFuture];
|
||||||
|
#else
|
||||||
|
pollTime = [NSDate distantPast];
|
||||||
|
#endif
|
||||||
|
|
||||||
do {
|
do {
|
||||||
event =[NSApp nextEventMatchingMask: NSAnyEventMask untilDate:
|
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:pollTime
|
||||||
[NSDate distantPast] inMode:
|
inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||||
NSDefaultRunLoopMode dequeue:YES];
|
|
||||||
[NSApp sendEvent:event];
|
[NSApp sendEvent:event];
|
||||||
}
|
}
|
||||||
while (event != nil);
|
while (event != nil);
|
||||||
[pool release];
|
[pool release];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
static gpointer
|
||||||
|
ns_app_loop_thread (gpointer data)
|
||||||
|
{
|
||||||
|
NSAutoreleasePool *pool;
|
||||||
|
|
||||||
|
/* set the main runloop as the runloop for the current thread. This has the
|
||||||
|
* effect that calling NSApp nextEventMatchingMask:untilDate:inMode:dequeue
|
||||||
|
* runs the main runloop.
|
||||||
|
*/
|
||||||
|
_CFRunLoopSetCurrent(CFRunLoopGetMain());
|
||||||
|
|
||||||
|
/* this is needed to make IsMainThread checks in core foundation work from the
|
||||||
|
* current thread
|
||||||
|
*/
|
||||||
|
_CFMainPThread = pthread_self();
|
||||||
|
|
||||||
|
pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
|
[NSApplication sharedApplication];
|
||||||
|
[NSApp finishLaunching];
|
||||||
|
|
||||||
|
/* run the loop */
|
||||||
|
run_ns_app_loop ();
|
||||||
|
|
||||||
|
[pool release];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * osxvideosink )
|
gst_osx_video_sink_run_cocoa_loop (GstOSXVideoSink * osxvideosink )
|
||||||
{
|
{
|
||||||
/* Cocoa applications require a main run loop running to dispatch UI
|
/* Cocoa applications require a main runloop running to dispatch UI
|
||||||
* events and process deferred calls to the main thread through
|
* events and process deferred calls to the main thread through
|
||||||
* perfermSelectorOnMainThread.
|
* perfermSelectorOnMainThread.
|
||||||
* 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 GstXOverlay API,
|
* external NSView is passed to the sink through the GstXOverlay API,
|
||||||
* we need to replace cocoa's main run loop with a poller.
|
* we need to run the cocoa mainloop somehow.
|
||||||
* We are also assuming that when the main run loop is not running
|
|
||||||
* (nor native Cocoa, nor GTK, nor QT), there is at least glib's main
|
|
||||||
* loop running.
|
|
||||||
*/
|
*/
|
||||||
if ([[NSRunLoop mainRunLoop] currentMode] == nil) {
|
if ([[NSRunLoop mainRunLoop] currentMode] == nil) {
|
||||||
osxvideosink->cocoa_timeout = g_timeout_add (10,
|
#ifdef RUN_NS_APP_THREAD
|
||||||
(GSourceFunc) cocoa_poll_events, NULL);
|
/* 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
|
||||||
|
*/
|
||||||
|
Method origIsMainThread = class_getClassMethod([NSThread class],
|
||||||
|
NSSelectorFromString(@"isMainThread"));
|
||||||
|
Method ourIsMainThread = class_getClassMethod([GstOSXVideoSinkObject class],
|
||||||
|
NSSelectorFromString(@"isMainThread"));
|
||||||
|
|
||||||
|
method_exchangeImplementations(origIsMainThread, ourIsMainThread);
|
||||||
|
|
||||||
|
osxvideosink->ns_app_thread = g_thread_new ("GstNSAppThread",
|
||||||
|
ns_app_loop_thread, NULL);
|
||||||
|
#else
|
||||||
|
/* assume that there is a GMainLoop and iterate the main runloop from there
|
||||||
|
*/
|
||||||
|
osxvideosink->cocoa_timeout = g_timeout_add (10,
|
||||||
|
(GSourceFunc) run_ns_app_loop, NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
if (osxvideosink->cocoa_timeout)
|
if (osxvideosink->cocoa_timeout)
|
||||||
g_source_remove(osxvideosink->cocoa_timeout);
|
g_source_remove(osxvideosink->cocoa_timeout);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function handles osx window creation */
|
/* This function handles osx window creation */
|
||||||
|
@ -416,7 +477,9 @@ gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink)
|
||||||
osxvideosink->superview = NULL;
|
osxvideosink->superview = NULL;
|
||||||
osxvideosink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc]
|
osxvideosink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc]
|
||||||
initWithSink:osxvideosink];
|
initWithSink:osxvideosink];
|
||||||
|
#ifndef RUN_NS_APP_THREAD
|
||||||
osxvideosink->app_started = FALSE;
|
osxvideosink->app_started = FALSE;
|
||||||
|
#endif
|
||||||
osxvideosink->keep_par = FALSE;
|
osxvideosink->keep_par = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,11 +779,13 @@ gst_osx_video_sink_get_type (void)
|
||||||
rect.size.width = (float) osxwindow->width;
|
rect.size.width = (float) osxwindow->width;
|
||||||
rect.size.height = (float) osxwindow->height;
|
rect.size.height = (float) osxwindow->height;
|
||||||
|
|
||||||
|
#ifndef RUN_NS_APP_THREAD
|
||||||
if (!osxvideosink->app_started) {
|
if (!osxvideosink->app_started) {
|
||||||
[NSApplication sharedApplication];
|
[NSApplication sharedApplication];
|
||||||
[NSApp finishLaunching];
|
[NSApp finishLaunching];
|
||||||
osxvideosink->app_started = TRUE;
|
osxvideosink->app_started = TRUE;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!GetCurrentProcess(&psn)) {
|
if (!GetCurrentProcess(&psn)) {
|
||||||
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
||||||
|
@ -741,6 +806,14 @@ gst_osx_video_sink_get_type (void)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RUN_NS_APP_THREAD
|
||||||
|
+ (BOOL) isMainThread
|
||||||
|
{
|
||||||
|
/* FIXME: ideally we should return YES only for ->ns_app_thread here */
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (void) resize
|
- (void) resize
|
||||||
{
|
{
|
||||||
GstOSXWindow *osxwindow = osxvideosink->osxwindow;
|
GstOSXWindow *osxwindow = osxvideosink->osxwindow;
|
||||||
|
|
Loading…
Reference in a new issue