]> git.sesse.net Git - vlc/blobdiff - modules/video_output/opengllayer.m
Do not leak the IPC helper - might fix #1598
[vlc] / modules / video_output / opengllayer.m
index 0b67ad7ae5dfefe04508684f906a1ad754bb147d..aed86020f95703c612c0da1ab5d84059afe960f9 100644 (file)
  *****************************************************************************/
 #include <errno.h>                                                 /* ENOMEM */
 
-#include <vlc/vlc.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
 #include <vlc_vout.h>
 
 #import <QuartzCore/QuartzCore.h>
@@ -66,6 +71,9 @@
 #   define GL_CLAMP_TO_EDGE 0x812F
 #endif
 
+@interface VLCVideoView : NSObject
+- (void)addVoutLayer:(CALayer *)layer;
+@end
 
 /*****************************************************************************
  * Vout interface
@@ -85,13 +93,13 @@ vlc_module_begin();
     set_shortname( "OpenGLLayer" );
     set_category( CAT_VIDEO );
     set_subcategory( SUBCAT_VIDEO_VOUT );
-    set_description( _("Core Animation OpenGL Layer (Mac OS X)") );
+    set_description( N_("Core Animation OpenGL Layer (Mac OS X)") );
     set_capability( "video output", 20 );
     add_shortcut( "opengllayer" );
     set_callbacks( CreateVout, DestroyVout );
 vlc_module_end();
 
-@interface VLCVideoLayer : CAOpenGLLayer {
+@interface VLCVoutLayer : CAOpenGLLayer {
     vout_thread_t * p_vout;
 }
 + (id)layerWithVout:(vout_thread_t*)_p_vout; 
@@ -109,14 +117,16 @@ struct vout_sys_t
 
     uint8_t    *pp_buffer[2]; /* one last rendered, one to be rendered */
     int         i_index;
-    vlc_bool_t  b_frame_available;
+    bool  b_frame_available;
+    
+    CGLContextObj glContext;
 
     int         i_tex_width;
     int         i_tex_height;
     GLuint      p_textures[2];
 
     NSAutoreleasePool *autorealease_pool;
-    VLCVideoLayer * o_layer;
+    VLCVoutLayer * o_layer;
     id          o_cocoa_container;
 };
 
@@ -143,7 +153,6 @@ static int CreateVout( vlc_object_t *p_this )
     msg_Dbg( p_vout, "Texture size: %dx%d", p_sys->i_tex_width,
              p_sys->i_tex_height );
 
-
     p_vout->pf_init = Init;
     p_vout->pf_end = End;
     p_vout->pf_manage = Manage;
@@ -200,11 +209,11 @@ static int Init( vout_thread_t *p_vout )
             return VLC_EGENERIC;
         }
     }
-    p_sys->b_frame_available = VLC_FALSE;
+    p_sys->b_frame_available = false;
     p_sys->i_index = 0;
-    
+
     p_vout->p_picture[0].i_planes = 1;
-    p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[0];
+    p_vout->p_picture[0].p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
     p_vout->p_picture[0].p->i_lines = p_vout->output.i_height;
     p_vout->p_picture[0].p->i_visible_lines = p_vout->output.i_height;
     p_vout->p_picture[0].p->i_pixel_pitch = i_pixel_pitch;
@@ -221,7 +230,7 @@ static int Init( vout_thread_t *p_vout )
     I_OUTPUTPICTURES = 1;
     p_sys->autorealease_pool = [[NSAutoreleasePool alloc] init];
 
-    [VLCVideoLayer performSelectorOnMainThread:@selector(autoinitInVout:)
+    [VLCVoutLayer performSelectorOnMainThread:@selector(autoinitInVout:)
                              withObject:[NSValue valueWithPointer:p_vout]
                              waitUntilDone:YES];
 
@@ -235,17 +244,17 @@ static void End( vout_thread_t *p_vout )
 {
     vout_sys_t *p_sys = p_vout->p_sys;
 
-    p_vout->p_sys->b_frame_available = 0;
+    p_vout->p_sys->b_frame_available = false;
 
-    [p_sys->o_layer performSelectorOnMainThread:@selector(removeFromSuperlayer)
-                    withObject:nil waitUntilDone:YES];
+    [p_vout->p_sys->o_cocoa_container performSelectorOnMainThread:@selector(removeVoutLayer:) withObject:p_vout->p_sys->o_layer waitUntilDone:YES];
+
+    // Should be done automatically
     [p_sys->o_layer release];
     [p_sys->autorealease_pool release];
 
     /* Free the texture buffer*/
-    glDeleteTextures( 2, p_sys->p_textures );
-    if( p_sys->pp_buffer[0] ) free( p_sys->pp_buffer[0] );
-    if( p_sys->pp_buffer[1] ) free( p_sys->pp_buffer[1] );
+    free( p_sys->pp_buffer[0] );
+    free( p_sys->pp_buffer[1] );
 }
 
 /*****************************************************************************
@@ -267,7 +276,9 @@ static void DestroyVout( vlc_object_t *p_this )
  * a non null value if an error occurred.
  *****************************************************************************/
 static int Manage( vout_thread_t *p_vout )
-{    
+{
+    vout_sys_t *p_sys = p_vout->p_sys;
+
     return VLC_SUCCESS;
 }
 
@@ -278,16 +289,37 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic )
 {
     vout_sys_t *p_sys = p_vout->p_sys;
 
-    /* Switch buffers */
-    int p_new_index;
-    @synchronized(p_sys->o_layer)
+    @synchronized( p_sys->o_layer ) /* Make sure the p_sys->glContext isn't edited */
     {
-        p_new_index = (p_sys->i_index + 1) & 1;
+        if( p_sys->glContext )
+        {
+            CGLLockContext(p_sys->glContext);
+            CGLSetCurrentContext(p_sys->glContext);
+            int i_new_index;
+            i_new_index = ( p_sys->i_index + 1 ) & 1;
+
+
+            /* Update the texture */
+            glBindTexture( VLCGL_TARGET, p_sys->p_textures[i_new_index] );
+            glTexSubImage2D( VLCGL_TARGET, 0, 0, 0,
+                         p_vout->fmt_out.i_width,
+                         p_vout->fmt_out.i_height,
+                         VLCGL_FORMAT, VLCGL_TYPE, p_sys->pp_buffer[i_new_index] );
+
+            /* Bind to the previous texture for drawing */
+            glBindTexture( VLCGL_TARGET, p_sys->p_textures[p_sys->i_index] );
+
+            /* Switch buffers */
+            p_sys->i_index = i_new_index;
+            p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
+            CGLUnlockContext(p_sys->glContext);
+            
+            p_sys->b_frame_available = true;
+        }
     }
 
     /* Give a buffer where the image will be rendered */
-    p_pic->p->p_pixels = p_sys->pp_buffer[p_new_index];
-
+    p_pic->p->p_pixels = p_sys->pp_buffer[p_sys->i_index];
 }
 
 /*****************************************************************************
@@ -296,14 +328,9 @@ static void Render( vout_thread_t *p_vout, picture_t *p_pic )
 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
 {
     vout_sys_t *p_sys = p_vout->p_sys;
-
-    /* The frame is ready, give its number so the o_layer can display it */
-    @synchronized(p_sys->o_layer)
-    {
-        p_sys->i_index = (p_sys->i_index + 1) & 1; /* Indicate the layer should use that index */
-    }
-
-    p_sys->b_frame_available = 1;
+    
+    [p_sys->o_layer performSelectorOnMainThread:@selector(display)
+                    withObject:nil waitUntilDone:YES];
 }
 
 /*****************************************************************************
@@ -352,18 +379,19 @@ static int InitTextures( vout_thread_t *p_vout )
 
         glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
 
-#ifdef __DISABLED_FOR_NOW__
+        /* Note: It seems that we can't bypass those, and even
+         * disabled they are used. They are the cause of the flickering */
+
         /* Tell the driver not to make a copy of the texture but to use
            our buffer */
         glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
         glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
-#endif
 
         /* Use AGP texturing */
-        glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
-                         GL_STORAGE_SHARED_APPLE );
+        glTexParameteri( VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE );
+
         /* Call glTexImage2D only once, and use glTexSubImage2D later */
-        glTexImage2D( VLCGL_TARGET, 0, 3, p_sys->i_tex_width,
+        glTexImage2D( VLCGL_TARGET, 0, 4, p_sys->i_tex_width,
                       p_sys->i_tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
                       p_sys->pp_buffer[i_index] );
     }
@@ -372,9 +400,9 @@ static int InitTextures( vout_thread_t *p_vout )
 }
 
 /*****************************************************************************
- * @implementation VLCVideoLayer
+ * @implementation VLCVoutLayer
  */
-@implementation VLCVideoLayer
+@implementation VLCVoutLayer
 
 /*****************************************************************************
  * autoinitInVout: Called from the video thread to create a layer.
@@ -384,38 +412,35 @@ static int InitTextures( vout_thread_t *p_vout )
 + (void)autoinitInVout:(NSValue*)arg
 {
     vout_thread_t * p_vout = [arg pointerValue];
-    p_vout->p_sys->o_layer = [[VLCVideoLayer layerWithVout: p_vout] retain];
-    [p_vout->p_sys->o_cocoa_container addVoutSubview:p_vout->p_sys->o_layer];
-}
-
-- (void)setVout:(vout_thread_t*)_p_vout 
-{
-    p_vout = _p_vout;
+    p_vout->p_sys->o_layer = [[VLCVoutLayer layerWithVout:p_vout] retain];
+    [p_vout->p_sys->o_cocoa_container addVoutLayer:p_vout->p_sys->o_layer];
 }
 
 + (id)layerWithVout:(vout_thread_t*)_p_vout 
 {
-    VLCVideoLayer* me = [super layer];
+    VLCVoutLayer* me = [[[self alloc] init] autorelease];
     if( me )
     {
-        me.asynchronous = YES;
-        [me setVout: _p_vout];
+        me->p_vout = _p_vout;
+        me.asynchronous = NO;
+        me.bounds = CGRectMake( 0.0, 0.0, 
+                                (float)_p_vout->fmt_in.i_visible_width * _p_vout->fmt_in.i_sar_num,
+                                (float)_p_vout->fmt_in.i_visible_height * _p_vout->fmt_in.i_sar_den );
     }
     return me;
 }
 
 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
 {
-    /* Only draw the frame when if have a frame that was previously rendered */
-    return p_vout->p_sys->b_frame_available;
+    /* Only draw the frame if we have a frame that was previously rendered */
+       return p_vout->p_sys->b_frame_available; // Flag is cleared by drawInCGLContext:pixelFormat:forLayerTime:displayTime:
 }
 
 - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
 {
-    /* Init */
     CGLLockContext( glContext );
     CGLSetCurrentContext( glContext );
-    
+
     float f_width, f_height, f_x, f_y;
 
     f_x = (float)p_vout->fmt_out.i_x_offset;
@@ -425,17 +450,6 @@ static int InitTextures( vout_thread_t *p_vout )
     f_height = (float)p_vout->fmt_out.i_y_offset +
                (float)p_vout->fmt_out.i_visible_height;
 
-    @synchronized(self)
-    {
-        glBindTexture( VLCGL_TARGET, p_vout->p_sys->p_textures[p_vout->p_sys->i_index] );
-
-        glTexSubImage2D( VLCGL_TARGET, 0, 0, 0,
-                     p_vout->fmt_out.i_width,
-                     p_vout->fmt_out.i_height,
-                     VLCGL_FORMAT, VLCGL_TYPE, p_vout->p_sys->pp_buffer[p_vout->p_sys->i_index] );
-
-     }
-    
     glClear( GL_COLOR_BUFFER_BIT );
 
     glEnable( VLCGL_TARGET );
@@ -445,33 +459,12 @@ static int InitTextures( vout_thread_t *p_vout )
     glTexCoord2f( f_width, f_height ); glVertex2f( 1.0, -1.0 );
     glTexCoord2f( f_x, f_height ); glVertex2f( -1.0, -1.0 );
     glEnd();
-    
+
     glDisable( VLCGL_TARGET );
 
     glFlush();
-    CGLUnlockContext( glContext );
-}
 
-- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
-{
-       GLuint attribs[] = 
-       {
-               NSOpenGLPFANoRecovery,
-               NSOpenGLPFAWindow,
-               NSOpenGLPFAAccelerated,
-               NSOpenGLPFADoubleBuffer,
-               NSOpenGLPFAColorSize, 24,
-               NSOpenGLPFAAlphaSize, 8,
-               NSOpenGLPFADepthSize, 24,
-               NSOpenGLPFAStencilSize, 8,
-               NSOpenGLPFAAccumSize, 0,
-               0
-       };
-
-       NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; 
-    
-
-    return [fmt CGLPixelFormatObj];
+    CGLUnlockContext( glContext );
 }
 
 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
@@ -479,17 +472,47 @@ static int InitTextures( vout_thread_t *p_vout )
     CGLContextObj context = [super copyCGLContextForPixelFormat:pixelFormat];
 
     CGLLockContext( context );
+
     CGLSetCurrentContext( context );
+
+    /* Swap buffers only during the vertical retrace of the monitor.
+    http://developer.apple.com/documentation/GraphicsImaging/
+    Conceptual/OpenGL/chap5/chapter_5_section_44.html */
+
+    GLint params = 1;
+    CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
+                     &params );
+
     InitTextures( p_vout );
 
-    glDisable(GL_BLEND);
-    glDisable(GL_DEPTH_TEST);
-    glDepthMask(GL_FALSE);
-    glDisable(GL_CULL_FACE);
+    glDisable( GL_BLEND );
+    glDisable( GL_DEPTH_TEST );
+    glDepthMask( GL_FALSE );
+    glDisable( GL_CULL_FACE) ;
     glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
     glClear( GL_COLOR_BUFFER_BIT );
 
     CGLUnlockContext( context );
+    @synchronized( self )
+    {
+        p_vout->p_sys->glContext = context;
+    }
+
     return context;
 }
+
+- (void)releaseCGLContext:(CGLContextObj)glContext
+{
+    @synchronized( self )
+    {
+        p_vout->p_sys->glContext = nil;
+    }
+
+    CGLLockContext( glContext );
+    CGLSetCurrentContext( glContext );
+
+    glDeleteTextures( 2, p_vout->p_sys->p_textures );
+
+    CGLUnlockContext( glContext );
+}
 @end