X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fmacosx.m;h=17f697d4b1e4769bac40a0602765bf4d01c47641;hb=adc35019701701eec7a6f91128103fc462ce3b3b;hp=7fc4450f12a36efb0e1722ca8f56e22de30f7f59;hpb=76e8da0ff1691aad46f89810712c6eda6e548ee8;p=vlc diff --git a/modules/video_output/macosx.m b/modules/video_output/macosx.m index 7fc4450f12..17f697d4b1 100644 --- a/modules/video_output/macosx.m +++ b/modules/video_output/macosx.m @@ -1,7 +1,7 @@ /***************************************************************************** * voutgl.m: MacOS X OpenGL provider ***************************************************************************** - * Copyright (C) 2001-2009 the VideoLAN team + * Copyright (C) 2001-2011 the VideoLAN team * $Id$ * * Authors: Colin Delacroix @@ -12,6 +12,7 @@ * Benjamin Pracht * Damien Fouilleul * Pierre d'Herbemont + * Felix Paul Kühne * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,9 +43,17 @@ #include #include #include -#include +#include +#include #include "opengl.h" +#ifndef MAC_OS_X_VERSION_10_7 +enum { + NSApplicationPresentationFullScreen = (1 << 10), + NSApplicationPresentationAutoHideToolbar = (1 << 11) +}; +#endif + /** * Forward declarations */ @@ -52,13 +61,13 @@ static int Open(vlc_object_t *); static void Close(vlc_object_t *); static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count); -static void PictureRender(vout_display_t *vd, picture_t *pic); -static void PictureDisplay(vout_display_t *vd, picture_t *pic); +static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture); +static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture); static int Control (vout_display_t *vd, int query, va_list ap); -static int OpenglLock(vout_opengl_t *gl); -static void OpenglUnlock(vout_opengl_t *gl); -static void OpenglSwap(vout_opengl_t *gl); +static int OpenglLock(vlc_gl_t *gl); +static void OpenglUnlock(vlc_gl_t *gl); +static void OpenglSwap(vlc_gl_t *gl); /** * Module declaration @@ -72,8 +81,7 @@ vlc_module_begin () set_capability("vout display", 300) set_callbacks(Open, Close) - add_shortcut("macosx") - add_shortcut("vout_macosx") + add_shortcut("macosx", "vout_macosx") vlc_module_end () /** @@ -87,19 +95,22 @@ vlc_module_end () @interface VLCOpenGLVideoView : NSOpenGLView { vout_display_t *vd; + BOOL _hasPendingReshape; } - (void)setVoutDisplay:(vout_display_t *)vd; +- (void)setVoutFlushing:(BOOL)flushing; @end - + struct vout_display_sys_t { VLCOpenGLVideoView *glView; id container; - vout_opengl_t gl; - vout_display_opengl_t vgl; - + vout_window_t *embed; + vlc_gl_t gl; + vout_display_opengl_t *vgl; + picture_pool_t *pool; picture_t *current; bool has_first_frame; @@ -114,16 +125,46 @@ static int Open(vlc_object_t *this) if (!sys) return VLC_ENOMEM; + if( !CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay ) ) + { + msg_Err( this, "no OpenGL hardware acceleration found, video output will fail" ); + dialog_Fatal( this, _("Video output is not supported"), _("Your Mac lacks Quartz Extreme acceleration, which is required for video output.") ); + return VLC_EGENERIC; + } + else + msg_Dbg( this, "Quartz Extreme acceleration is active" ); + vd->sys = sys; sys->pool = NULL; sys->gl.sys = NULL; - + sys->embed = NULL; + /* Get the drawable object */ id container = var_CreateGetAddress(vd, "drawable-nsobject"); - if (!container) + if (container) { - msg_Dbg(vd, "No drawable-nsobject, passing over."); - goto error; + vout_display_DeleteWindow(vd, NULL); + } + else + { + vout_window_cfg_t wnd_cfg; + + memset (&wnd_cfg, 0, sizeof (wnd_cfg)); + wnd_cfg.type = VOUT_WINDOW_TYPE_NSOBJECT; + wnd_cfg.x = var_InheritInteger (vd, "video-x"); + wnd_cfg.y = var_InheritInteger (vd, "video-y"); + wnd_cfg.width = vd->cfg->display.width; + wnd_cfg.height = vd->cfg->display.height; + + sys->embed = vout_display_NewWindow (vd, &wnd_cfg); + if (sys->embed) + container = sys->embed->handle.nsobject; + + if (!container) + { + msg_Dbg(vd, "No drawable-nsobject nor vout_window_t found, passing over."); + goto error; + } } /* This will be released in Close(), on @@ -132,8 +173,8 @@ static int Open(vlc_object_t *this) /* Get our main view*/ nsPool = [[NSAutoreleasePool alloc] init]; - sys->glView = [[VLCOpenGLVideoView alloc] init]; + [VLCOpenGLVideoView performSelectorOnMainThread:@selector(getNewView:) withObject:[NSValue valueWithPointer:&sys->glView] waitUntilDone:YES]; if (!sys->glView) goto error; @@ -142,7 +183,21 @@ static int Open(vlc_object_t *this) /* We don't wait, that means that we'll have to be careful about releasing * container. * That's why we'll release on main thread in Close(). */ - [(id)container performSelectorOnMainThread:@selector(addVoutSubview:) withObject:sys->glView waitUntilDone:NO]; + if ([(id)container respondsToSelector:@selector(addVoutSubview:)]) + [(id)container performSelectorOnMainThread:@selector(addVoutSubview:) withObject:sys->glView waitUntilDone:NO]; + else if ([container isKindOfClass:[NSView class]]) + { + NSView *parentView = container; + [parentView performSelectorOnMainThread:@selector(addSubview:) withObject:sys->glView waitUntilDone:NO]; + [sys->glView performSelectorOnMainThread:@selector(setFrameWithValue:) withObject:[NSValue valueWithRect:[parentView bounds]] waitUntilDone:NO]; + } + else + { + msg_Err(vd, "Invalid drawable-nsobject object. drawable-nsobject must either be an NSView or comply to the @protocol VLCOpenGLVideoViewEmbedding."); + goto error; + } + + [nsPool release]; nsPool = nil; @@ -150,9 +205,13 @@ static int Open(vlc_object_t *this) sys->gl.lock = OpenglLock; sys->gl.unlock = OpenglUnlock; sys->gl.swap = OpenglSwap; + sys->gl.getProcAddress = NULL; sys->gl.sys = sys; - - if (vout_display_opengl_Init(&sys->vgl, &vd->fmt, &sys->gl)) + const vlc_fourcc_t *subpicture_chromas; + video_format_t fmt = vd->fmt; + + sys->vgl = vout_display_opengl_New(&vd->fmt, &subpicture_chromas, &sys->gl); + if (!sys->vgl) { sys->gl.sys = NULL; goto error; @@ -161,10 +220,12 @@ static int Open(vlc_object_t *this) /* */ vout_display_info_t info = vd->info; info.has_pictures_invalid = false; - + info.has_event_thread = true; + info.subpicture_chromas = subpicture_chromas; + /* Setup vout_display_t once everything is fine */ vd->info = info; - + vd->pool = Pool; vd->prepare = PictureRender; vd->display = PictureDisplay; @@ -190,17 +251,22 @@ void Close(vlc_object_t *this) [sys->glView setVoutDisplay:nil]; var_Destroy(vd, "drawable-nsobject"); - /* This will retain sys->glView */ - [(id)sys->container performSelectorOnMainThread:@selector(removeVoutSubview:) withObject:sys->glView waitUntilDone:NO]; + if ([(id)sys->container respondsToSelector:@selector(removeVoutSubview:)]) + { + /* This will retain sys->glView */ + [(id)sys->container performSelectorOnMainThread:@selector(removeVoutSubview:) withObject:sys->glView waitUntilDone:NO]; + } /* release on main thread as explained in Open() */ [(id)sys->container performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; [sys->glView performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:NO]; - + [sys->glView release]; if (sys->gl.sys != NULL) - vout_display_opengl_Clean(&sys->vgl); + vout_display_opengl_Delete(sys->vgl); + if (sys->embed) + vout_display_DeleteWindow(vd, sys->embed); free (sys); } @@ -211,70 +277,127 @@ void Close(vlc_object_t *this) static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count) { vout_display_sys_t *sys = vd->sys; - VLC_UNUSED(requested_count); if (!sys->pool) - sys->pool = vout_display_opengl_GetPool (&sys->vgl); + sys->pool = vout_display_opengl_GetPool (sys->vgl, requested_count); assert(sys->pool); return sys->pool; } -static void PictureRender(vout_display_t *vd, picture_t *pic) +static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; - - vout_display_opengl_Prepare( &sys->vgl, pic ); + + vout_display_opengl_Prepare( sys->vgl, pic, subpicture ); } -static void PictureDisplay(vout_display_t *vd, picture_t *pic) +static void PictureDisplay(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; - vout_display_opengl_Display(&sys->vgl, &vd->fmt ); + [sys->glView setVoutFlushing:YES]; + vout_display_opengl_Display(sys->vgl, &vd->fmt ); + [sys->glView setVoutFlushing:NO]; picture_Release (pic); sys->has_first_frame = true; + (void)subpicture; } static int Control (vout_display_t *vd, int query, va_list ap) { vout_display_sys_t *sys = vd->sys; - + switch (query) { - case VOUT_DISPLAY_CHANGE_FULLSCREEN: + case VOUT_DISPLAY_CHANGE_FULLSCREEN: + { + /* todo */ + return VLC_EGENERIC; + } case VOUT_DISPLAY_CHANGE_WINDOW_STATE: - case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: + { + unsigned state = va_arg (ap, unsigned); + if( (state & VOUT_WINDOW_STATE_ABOVE) != 0) + [[sys->glView window] setLevel: NSStatusWindowLevel]; + else + [[sys->glView window] setLevel: NSNormalWindowLevel]; + return VLC_SUCCESS; + } case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: + { + [[sys->glView window] performSelectorOnMainThread:@selector(zoom:) withObject: nil waitUntilDone:NO]; + return VLC_SUCCESS; + } + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: case VOUT_DISPLAY_CHANGE_ZOOM: case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: case VOUT_DISPLAY_CHANGE_SOURCE_CROP: { - /* todo */ - return VLC_EGENERIC; + NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init]; + NSPoint topleftbase; + NSPoint topleftscreen; + NSRect new_frame; + const vout_display_cfg_t *cfg; + + id o_window = [sys->glView window]; + if (!o_window) + return VLC_SUCCESS; // this is okay, since the event will occur again when we have a window + NSRect windowFrame = [o_window frame]; + NSRect glViewFrame = [sys->glView frame]; + NSSize windowMinSize = [o_window minSize]; + + topleftbase.x = 0; + topleftbase.y = windowFrame.size.height; + topleftscreen = [o_window convertBaseToScreen: topleftbase]; + cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *); + int i_width = cfg->display.width; + int i_height = cfg->display.height; + + /* Calculate the window's new size, if it is larger than our minimal size */ + if (i_width < windowMinSize.width) + i_width = windowMinSize.width; + if (i_height < windowMinSize.height) + i_height = windowMinSize.height; + + if( i_height != glViewFrame.size.height || i_width != glViewFrame.size.width ) + { + new_frame.size.width = windowFrame.size.width - glViewFrame.size.width + i_width; + new_frame.size.height = windowFrame.size.height - glViewFrame.size.height + i_height; + + new_frame.origin.x = topleftscreen.x; + new_frame.origin.y = topleftscreen.y - new_frame.size.height; + + [sys->glView performSelectorOnMainThread:@selector(setWindowFrameWithValue:) withObject:[NSValue valueWithRect:new_frame] waitUntilDone:NO]; + } + [o_pool release]; + return VLC_SUCCESS; } + case VOUT_DISPLAY_HIDE_MOUSE: + { + [NSCursor setHiddenUntilMouseMoves: YES]; return VLC_SUCCESS; - + } + case VOUT_DISPLAY_GET_OPENGL: { - vout_opengl_t **gl = va_arg (ap, vout_opengl_t **); + vlc_gl_t **gl = va_arg (ap, vlc_gl_t **); *gl = &sys->gl; return VLC_SUCCESS; } - + case VOUT_DISPLAY_RESET_PICTURES: assert (0); default: msg_Err (vd, "Unknown request in Mac OS X vout display"); return VLC_EGENERIC; } - printf("query %d\n", query); } /***************************************************************************** * vout opengl callbacks *****************************************************************************/ -static int OpenglLock(vout_opengl_t *gl) +static int OpenglLock(vlc_gl_t *gl) { vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys; NSOpenGLContext *context = [sys->glView openGLContext]; @@ -284,16 +407,16 @@ static int OpenglLock(vout_opengl_t *gl) [context makeCurrentContext]; return 0; } - return 1; + return 1; } -static void OpenglUnlock(vout_opengl_t *gl) +static void OpenglUnlock(vlc_gl_t *gl) { vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys; CGLUnlockContext([[sys->glView openGLContext] CGLContextObj]); } -static void OpenglSwap(vout_opengl_t *gl) +static void OpenglSwap(vlc_gl_t *gl) { vout_display_sys_t *sys = (vout_display_sys_t *)gl->sys; [[sys->glView openGLContext] flushBuffer]; @@ -306,12 +429,19 @@ static void OpenglSwap(vout_opengl_t *gl) #define VLCAssertMainThread() assert([[NSThread currentThread] isMainThread]) ++ (void)getNewView:(NSValue *)value +{ + id *ret = [value pointerValue]; + *ret = [[self alloc] init]; +} + /** * Gets called by the Open() method. - * (Non main thread). */ - (id)init { + VLCAssertMainThread(); + /* Warning - this may be called on non main thread */ NSOpenGLPixelFormatAttribute attribs[] = @@ -325,13 +455,15 @@ static void OpenglSwap(vout_opengl_t *gl) NSOpenGLPFAWindow, 0 }; - - NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; - + + NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + if (!fmt) return nil; self = [super initWithFrame:NSMakeRect(0,0,10,10) pixelFormat:fmt]; + [fmt release]; + if (!self) return nil; @@ -341,9 +473,32 @@ static void OpenglSwap(vout_opengl_t *gl) GLint params[] = { 1 }; CGLSetParameter([[self openGLContext] CGLContextObj], kCGLCPSwapInterval, params); + [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; return self; } +/** + * Gets called by the Open() method. + */ +- (void)setFrameWithValue:(NSValue *)value +{ + [self setFrame:[value rectValue]]; +} + +/** + * Gets called by Control() to make sure that we're performing on the main thread + */ +- (void)setWindowFrameWithValue:(NSValue *)value +{ + if (!(NSAppKitVersionNumber >= 1115.2 && [NSApp currentSystemPresentationOptions] == NSApplicationPresentationFullScreen)) + { + NSRect frame = [value rectValue]; + if (frame.origin.x <= 0.0 && frame.origin.y <= 0.0) + [[self window] center]; + [[self window] setFrame:frame display:YES animate: YES]; + } +} + /** * Gets called by the Close and Open methods. * (Non main thread). @@ -355,13 +510,44 @@ static void OpenglSwap(vout_opengl_t *gl) } } + +/** + * Gets called when the vout will aquire the lock and flush. + * (Non main thread). + */ +- (void)setVoutFlushing:(BOOL)flushing +{ + if (!flushing) + return; + @synchronized(self) { + _hasPendingReshape = NO; + } +} + +/** + * Can -drawRect skip rendering?. + */ +- (BOOL)canSkipRendering +{ + VLCAssertMainThread(); + + @synchronized(self) { + BOOL hasFirstFrame = vd && vd->sys->has_first_frame; + return !_hasPendingReshape && hasFirstFrame; + } +} + + /** * Local method that locks the gl context. */ - (BOOL)lockgl { VLCAssertMainThread(); - CGLError err = CGLLockContext([[self openGLContext] CGLContextObj]); + NSOpenGLContext *context = [self openGLContext]; + CGLError err = CGLLockContext([context CGLContextObj]); + if (err == kCGLNoError) + [context makeCurrentContext]; return err == kCGLNoError; } @@ -382,17 +568,22 @@ static void OpenglSwap(vout_opengl_t *gl) { VLCAssertMainThread(); + // We may have taken some times to take the opengl Lock. + // Check here to see if we can just skip the frame as well. + if ([self canSkipRendering]) + return; + + BOOL hasFirstFrame; @synchronized(self) { // vd can be accessed from multiple threads - if (vd && vd->sys->has_first_frame) - { - // This will lock gl. - vout_display_opengl_Display( &vd->sys->vgl, &vd->source ); - } - else { - glClear(GL_COLOR_BUFFER_BIT); - } - - } + hasFirstFrame = vd && vd->sys->has_first_frame; + } + + if (hasFirstFrame) { + // This will lock gl. + vout_display_opengl_Display( vd->sys->vgl, &vd->source ); + } + else + glClear(GL_COLOR_BUFFER_BIT); } /** @@ -401,7 +592,7 @@ static void OpenglSwap(vout_opengl_t *gl) - (void)reshape { VLCAssertMainThread(); - + NSRect bounds = [self bounds]; CGFloat height = bounds.size.height; @@ -413,10 +604,10 @@ static void OpenglSwap(vout_opengl_t *gl) if (vd) { CGFloat videoHeight = vd->source.i_visible_height; CGFloat videoWidth = vd->source.i_visible_width; - + GLint sarNum = vd->source.i_sar_num; GLint sarDen = vd->source.i_sar_den; - + if (height * videoWidth * sarNum < width * videoHeight * sarDen) { x = (height * videoWidth * sarNum) / (videoHeight * sarDen); @@ -426,16 +617,25 @@ static void OpenglSwap(vout_opengl_t *gl) { x = width; y = (width * videoHeight * sarDen) / (videoWidth * sarNum); - } + } } } - - [self lockgl]; - glClearColor(0, 0, 0, 1); - glViewport((width - x) / 2, (height - y) / 2, x, y); - [self unlockgl]; - [super reshape]; + if ([self lockgl]) { + glViewport((width - x) / 2, (height - y) / 2, x, y); + + @synchronized(self) { + // This may be cleared before -drawRect is being called, + // in this case we'll skip the rendering. + // This will save us for rendering two frames (or more) for nothing + // (one by the vout, one (or more) by drawRect) + _hasPendingReshape = YES; + } + + [self unlockgl]; + + [super reshape]; + } } /** @@ -460,6 +660,10 @@ static void OpenglSwap(vout_opengl_t *gl) - (void)drawRect:(NSRect) rect { VLCAssertMainThread(); + + if ([self canSkipRendering]) + return; + BOOL success = [self lockgl]; if (!success) return; @@ -469,8 +673,24 @@ static void OpenglSwap(vout_opengl_t *gl) [self unlockgl]; } +- (void)renewGState +{ + NSWindow *window = [self window]; + + // Remove flashes with splitter view. + if ([window respondsToSelector:@selector(disableScreenUpdatesUntilFlush)]) + [window disableScreenUpdatesUntilFlush]; + + [super renewGState]; +} + - (BOOL)mouseDownCanMoveWindow { return YES; } + +- (BOOL)isOpaque +{ + return YES; +} @end