From 045440b4833a9d6e05d9181bfbfef87aa6a85041 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 14 Mar 2007 16:30:19 +0000 Subject: sys/osxvideo/: Fix leaks when running a NSApp. Original commit message from CVS: * sys/osxvideo/cocoawindow.h: * sys/osxvideo/cocoawindow.m: * sys/osxvideo/osxvideosink.h: * sys/osxvideo/osxvideosink.m: Fix leaks when running a NSApp. Accept any kind of resolutions. Works in fullscreen. Can maximize. Only thing left before being able to move this to -good is documentation and embedded window support. --- sys/osxvideo/cocoawindow.h | 1 + sys/osxvideo/cocoawindow.m | 91 +++++++++++++---------- sys/osxvideo/osxvideosink.h | 12 ++- sys/osxvideo/osxvideosink.m | 176 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 225 insertions(+), 55 deletions(-) (limited to 'sys') diff --git a/sys/osxvideo/cocoawindow.h b/sys/osxvideo/cocoawindow.h index f479c995..340bd160 100644 --- a/sys/osxvideo/cocoawindow.h +++ b/sys/osxvideo/cocoawindow.h @@ -43,6 +43,7 @@ struct _GstOSXImage; int width, height; BOOL fullscreen; NSOpenGLContext* fullScreenContext; + NSOpenGLContext* actualContext; } - (void) drawQuad; - (void) drawRect: (NSRect) rect; diff --git a/sys/osxvideo/cocoawindow.m b/sys/osxvideo/cocoawindow.m index e191de45..9b5e7b31 100644 --- a/sys/osxvideo/cocoawindow.m +++ b/sys/osxvideo/cocoawindow.m @@ -41,6 +41,7 @@ @ implementation GstOSXVideoSinkWindow +/* The object has to be released */ - (id) initWithContentRect: (NSRect) rect styleMask: (unsigned int) styleMask backing: (NSBackingStoreType) bufferingType @@ -84,7 +85,7 @@ - (void) sendEvent:(NSEvent *) event { BOOL taken = NO; - GST_LOG ("event %p type:%d", event,[event type]); + GST_DEBUG ("event %p type:%d", event,[event type]); if ([event type] == NSKeyDown) { } @@ -128,14 +129,15 @@ self = [super initWithFrame: frame pixelFormat:fmt]; - [[self openGLContext] makeCurrentContext]; - [[self openGLContext] update]; + actualContext = [self openGLContext]; + [actualContext makeCurrentContext]; + [actualContext update]; /* Black background */ glClearColor (0.0, 0.0, 0.0, 0.0); pi_texture = 0; - data = g_malloc (2 * 320 * 240); + data = nil; width = frame.size.width; height = frame.size.height; @@ -154,7 +156,7 @@ return; } - [[self openGLContext] makeCurrentContext]; + [actualContext makeCurrentContext]; bounds = [self bounds]; @@ -163,25 +165,28 @@ } - (void) initTextures { - NSOpenGLContext *currentContext; - if (fullscreen) - currentContext = fullScreenContext; - else - currentContext =[self openGLContext]; - - [currentContext makeCurrentContext]; + [actualContext makeCurrentContext]; /* Free previous texture if any */ - if (initDone) { + if (pi_texture) { glDeleteTextures (1, &pi_texture); } + + if (data) { + data = g_realloc (data, width * height * sizeof(short)); // short or 3byte? + } else { + data = g_malloc0(width * height * sizeof(short)); + } /* Create textures */ glGenTextures (1, &pi_texture); glEnable (GL_TEXTURE_RECTANGLE_EXT); glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glPixelStorei (GL_UNPACK_ROW_LENGTH, width); + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); /* Use VRAM texturing */ @@ -192,7 +197,6 @@ our buffer */ glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); - /* Linear interpolation */ glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -203,36 +207,34 @@ GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ?? glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, - width, height, 0, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); + width, height, 0, + GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); initDone = 1; } - (void) reloadTexture { - NSOpenGLContext *currentContext; - if (!initDone) { return; } GST_LOG ("Reloading Texture"); - if (fullscreen) - currentContext = fullScreenContext; - else - currentContext =[self openGLContext]; - [currentContext makeCurrentContext]; + [actualContext makeCurrentContext]; glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); + glPixelStorei (GL_UNPACK_ROW_LENGTH, width); /* glTexSubImage2D is faster than glTexImage2D http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/ TextureRange/MainOpenGLView.m.htm */ - glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, width, height, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME + glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, + width, height, + GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME } - (void) cleanUp { @@ -260,15 +262,9 @@ } - (void) drawRect:(NSRect) rect { - NSOpenGLContext *currentContext; long params[] = { 1 }; - if (fullscreen) { - currentContext = fullScreenContext; - } else { - currentContext =[self openGLContext]; - } - [currentContext makeCurrentContext]; + [actualContext makeCurrentContext]; CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params); @@ -276,7 +272,7 @@ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (!initDone) { - [[self openGLContext] flushBuffer]; + [actualContext flushBuffer]; return; } @@ -284,7 +280,7 @@ glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME [self drawQuad]; /* Draw */ - [currentContext flushBuffer]; + [actualContext flushBuffer]; } - (void) displayTexture { @@ -337,6 +333,8 @@ return; } + actualContext = fullScreenContext; + /* Capture display, switch to fullscreen */ if (CGCaptureAllDisplays () != CGDisplayNoErr) { GST_WARNING ("CGCaptureAllDisplays() failed"); @@ -353,7 +351,13 @@ } else if (fullscreen && !flag) { // fullscreen now and needs to go back to normal initDone = NO; + + actualContext = [self openGLContext]; + [NSOpenGLContext clearCurrentContext]; + [fullScreenContext clearDrawable]; + [fullScreenContext release]; + fullScreenContext = nil; CGReleaseAllDisplays (); @@ -373,11 +377,24 @@ width = w; height = h; - // FIXME : so, do we free, or don't we ? - //if (data) g_free(data); +// if (data) g_free(data); - data = g_malloc0 (2 * w * h); - [self reloadTexture]; +// data = g_malloc0 (2 * w * h); + [self initTextures]; } +- (void) dealloc { + GST_LOG ("dealloc called"); + if (data) g_free(data); + + if (fullScreenContext) { + [NSOpenGLContext clearCurrentContext]; + [fullScreenContext clearDrawable]; + [fullScreenContext release]; + if (actualContext == fullScreenContext) actualContext = nil; + fullScreenContext = nil; + } + + [super dealloc]; +} @end diff --git a/sys/osxvideo/osxvideosink.h b/sys/osxvideo/osxvideosink.h index 2c144f5e..d7fba966 100644 --- a/sys/osxvideo/osxvideosink.h +++ b/sys/osxvideo/osxvideosink.h @@ -65,12 +65,12 @@ struct _GstOSXWindow { gboolean internal; GstOSXVideoSinkWindow* win; GstGLView* gstview; + NSAutoreleasePool *pool; }; struct _GstOSXVideoSink { /* Our element stuff */ GstVideoSink videosink; - GstOSXWindow *osxwindow; gint fps_n; @@ -92,6 +92,16 @@ struct _GstOSXVideoSinkClass { GType gst_osx_video_sink_get_type(void); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 +@interface NSApplication(AppleMenu) +- (void)setAppleMenu:(NSMenu *)menu; +@end +#endif + +@interface GstAppDelegate : NSObject +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +@end + G_END_DECLS #endif /* __GST_OSX_VIDEO_SINK_H__ */ diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m index 0122c4cb..a521d068 100644 --- a/sys/osxvideo/osxvideosink.m +++ b/sys/osxvideo/osxvideosink.m @@ -27,7 +27,7 @@ /* Object header */ #include "osxvideosink.h" - +#include #import "cocoawindow.h" /* Debugging category */ @@ -51,9 +51,31 @@ GST_STATIC_PAD_TEMPLATE ("sink", "framerate = (fraction) [ 0, MAX ], " "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], " +#ifdef G_BYTE_ORDER == G_BIG_ENDIAN + "format = (fourcc) YUY2") +#else "format = (fourcc) UYVY") +#endif ); +// much of the following cocoa NSApp code comes from libsdl and libcaca +@implementation NSApplication(Gst) +- (void)setRunning +{ + _running = 1; +} +@end + +@implementation GstAppDelegate : NSObject +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + // destroy stuff here! + GST_DEBUG("Kill me please!"); + return NSTerminateNow; +} +@end + + enum { ARG_0, @@ -64,18 +86,113 @@ enum static GstVideoSinkClass *parent_class = NULL; + /* cocoa event loop - needed if not run in own app */ -/* FIXME : currently disabled since a huge memory leak happens if it is run. */ -gpointer +gint cocoa_event_loop (GstOSXVideoSink * vsink) { - GST_DEBUG_OBJECT (vsink, "About to start cocoa event loop"); + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; + + if ([NSApp isRunning]) { + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode dequeue:YES ]; + if ( event != nil ) { + switch ([event type]) { + default: //XXX Feed me please + [NSApp sendEvent:event]; + break; + } + } + } + + [pool release]; - [NSApp run]; + return TRUE; +} - GST_DEBUG_OBJECT (vsink, "Cocoa event loop ended"); +static NSString * +GetApplicationName(void) +{ + NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} - return NULL; +static void +CreateApplicationMenus(void) +{ + NSString *appName; + NSString *title; + NSMenu *appleMenu; + NSMenu *windowMenu; + NSMenuItem *menuItem; + + /* Create the main menu bar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + + /* Create the application menu */ + appName = GetApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/""]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/""]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + [appleMenu release]; + + + /* Create the window menu */ + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@/*"m"*/""]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + [windowMenu release]; } /* This function handles osx window creation */ @@ -84,6 +201,7 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width, gint height) { NSRect rect; + GstOSXWindow *osxwindow = NULL; g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), NULL); @@ -93,28 +211,50 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width, osxwindow->width = width; osxwindow->height = height; osxwindow->internal = TRUE; + osxwindow->pool = [[NSAutoreleasePool alloc] init]; if (osxvideosink->embed == FALSE) { - NSAutoreleasePool *pool; + ProcessSerialNumber psn; + unsigned int mask = NSTitledWindowMask | + NSClosableWindowMask | + NSResizableWindowMask | + NSTexturedBackgroundWindowMask | + NSMiniaturizableWindowMask; rect.origin.x = 100.0; rect.origin.y = 100.0; rect.size.width = (float) osxwindow->width; rect.size.height = (float) osxwindow->height; - pool =[[NSAutoreleasePool alloc] init]; + if (!GetCurrentProcess(&psn)) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } [NSApplication sharedApplication]; - osxwindow->win =[[GstOSXVideoSinkWindow alloc] initWithContentRect: rect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing: NSBackingStoreBuffered defer: NO screen:nil]; + + osxwindow->win =[[GstOSXVideoSinkWindow alloc] + initWithContentRect: rect + styleMask: mask + backing: NSBackingStoreBuffered + defer: NO + screen: nil]; + [osxwindow->win autorelease]; + [NSApplication sharedApplication]; [osxwindow->win makeKeyAndOrderFront:NSApp]; osxwindow->gstview =[osxwindow->win gstView]; + [osxwindow->gstview autorelease]; if (osxvideosink->fullscreen) [osxwindow->gstview setFullScreen:YES]; - [pool release]; + CreateApplicationMenus(); + + [NSApp finishLaunching]; + [NSApp setDelegate:[[GstAppDelegate alloc] init]]; - /* Start Cocoa event loop */ -// g_thread_create ((GThreadFunc) cocoa_event_loop, osxvideosink, FALSE, NULL); + [NSApp setRunning]; + // insert event dispatch in the glib main loop + g_idle_add ((GSourceFunc) cocoa_event_loop, osxvideosink); } else { /* Needs to be embedded */ @@ -123,6 +263,7 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width, rect.size.width = (float) osxwindow->width; rect.size.height = (float) osxwindow->height; osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect]; + [osxwindow->gstview autorelease]; /* send signal FIXME: need to send a bus message */ /*g_signal_emit (G_OBJECT(osxvideosink), @@ -140,6 +281,8 @@ gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink, g_return_if_fail (osxwindow != NULL); g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink)); + [osxwindow->pool release]; + g_free (osxwindow); } @@ -149,7 +292,7 @@ gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink, GstOSXWindow * osxwindow, guint width, guint height) { NSSize size; - + NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init]; g_return_if_fail (osxwindow != NULL); g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink)); @@ -159,7 +302,8 @@ gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink, size.width = width; size.height = height; /* Call relevant cocoa function to resize window */ -[osxwindow->win setContentSize:size]; + [osxwindow->win setContentSize:size]; + [subPool release]; } static void @@ -350,10 +494,8 @@ static void gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink) { - osxvideosink->osxwindow = NULL; - osxvideosink->fps_n = 0; osxvideosink->fps_d = 0; -- cgit v1.2.1