]> git.sesse.net Git - vlc/blobdiff - plugins/x11/vout_xvideo.c
* CPU detection under BeOS.
[vlc] / plugins / x11 / vout_xvideo.c
index d1816f61305e45f468d1ed99bd2b88d4eb8e690d..0f2a1ad9243650943c0ec0b747861cedce05d142 100644 (file)
@@ -1,12 +1,13 @@
 /*****************************************************************************
  * vout_xvideo.c: Xvideo video output display method
  *****************************************************************************
- * Copyright (C) 1998, 1999, 2000, 2001 VideoLAN
- * $Id: vout_xvideo.c,v 1.6 2001/04/13 06:20:23 sam Exp $
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: vout_xvideo.c,v 1.40 2001/12/17 03:38:21 sam Exp $
  *
  * Authors: Shane Harper <shanegh@optusnet.com.au>
  *          Vincent Seguin <seguin@via.ecp.fr>
  *          Samuel Hocevar <sam@zoy.org>
+ *          David Kennedy <dkennedy@tinytoad.com>
  *
  * 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
 #include <sys/ipc.h>
 #endif
 
+#ifndef WIN32
+#include <netinet/in.h>                               /* BSD: struct in_addr */
+#endif
+
 #include <sys/shm.h>                                   /* shmget(), shmctl() */
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/extensions/XShm.h>
 #include <X11/extensions/Xv.h>
 #include <X11/extensions/Xvlib.h>
+#include <X11/extensions/dpms.h>
 
-#include "config.h"
 #include "common.h"
+#include "intf_msg.h"
 #include "threads.h"
 #include "mtime.h"
 #include "tests.h"
-#include "modules.h"
 
 #include "video.h"
 #include "video_output.h"
+#include "vout_common.h"
 
 #include "interface.h"
-#include "intf_msg.h"
-
 #include "netutils.h"                                 /* network_ChannelJoin */
 
-#include "main.h"
+#include "stream_control.h"                 /* needed by input_ext-intf.h... */
+#include "input_ext-intf.h"
 
-/*****************************************************************************
- * vout_sys_t: video output X11 method descriptor
- *****************************************************************************
- * This structure is part of the video output thread descriptor.
- * It describes the XVideo specific properties of an output thread.
- *****************************************************************************/
-typedef struct vout_sys_s
-{
-    /* User settings */
-#if 0
-    /* this plugin (currently) requires the SHM Ext... */
-    boolean_t           b_shm;               /* shared memory extension flag */
-#endif
+#include "modules.h"
+#include "modules_export.h"
 
-    /* Internal settings and properties */
-    Display *           p_display;                        /* display pointer */
-    int                 i_screen;                           /* screen number */
-    Window              window;                               /* root window */
-    GC                  gc;              /* graphic context instance handler */
-    int                 xv_port;
-
-    /* Display buffers and shared memory information */
-    /* Note: only 1 buffer... Xv ext does double buffering. */
-    XvImage *           p_xvimage;
-    int                 i_image_width;
-    int                 i_image_height;
-                                /* i_image_width & i_image_height reflect the
-                                 * size of the XvImage. They are used by
-                                 * vout_Display() to check if the image to be
-                                 * displayed can use the current XvImage. */
-    XShmSegmentInfo     shm_info;       /* shared memory zone information */
-
-    /* X11 generic properties */
-    Atom                wm_protocols;
-    Atom                wm_delete_window;
-
-    int                 i_window_width;              /* width of main window */
-    int                 i_window_height;            /* height of main window */
-
-
-    /* Screen saver properties */
-    int                 i_ss_timeout;                             /* timeout */
-    int                 i_ss_interval;           /* interval between changes */
-    int                 i_ss_blanking;                      /* blanking mode */
-    int                 i_ss_exposure;                      /* exposure mode */
-    
-    /* Auto-hide cursor */
-    mtime_t     i_lastmoved;
-    
-    /* Mouse pointer properties */
-    boolean_t           b_mouse;         /* is the mouse pointer displayed ? */
-
-} vout_sys_t;
+#define XVIDEO_MAX_DIRECTBUFFERS 5
+#define GUID_YUV12_PLANAR 0x32315659
 
 /*****************************************************************************
  * Local prototypes
@@ -129,28 +86,17 @@ static int  vout_Create    ( vout_thread_t * );
 static int  vout_Init      ( vout_thread_t * );
 static void vout_End       ( vout_thread_t * );
 static void vout_Destroy   ( vout_thread_t * );
-static int  vout_Manage    ( vout_thread_t * );
-static void vout_Display   ( vout_thread_t * );
-static void vout_SetPalette( vout_thread_t *, u16 *, u16 *, u16 *, u16 * );
-
-static int  XVideoCreateWindow       ( vout_thread_t * );
-static int  XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout );
-static int  XVideoCreateShmImage     ( Display* dpy, int xv_port,
-                                       XvImage **pp_xvimage,
-                                       XShmSegmentInfo *p_shm_info,
-                                       int i_width, int i_height );
-static void XVideoDestroyShmImage    ( vout_thread_t *, XvImage *,
-                                       XShmSegmentInfo * );
-static void XVideoTogglePointer      ( vout_thread_t * );
-static void XVideoEnableScreenSaver  ( vout_thread_t * );
-static void XVideoDisableScreenSaver ( vout_thread_t * );
-/*static void XVideoSetAttribute       ( vout_thread_t *, char *, float );*/
+static void vout_Display   ( vout_thread_t *, picture_t * );
+
+static int  XVideoNewPicture   ( vout_thread_t *, picture_t * );
+
+static XvImage *CreateShmImage ( Display *, int, XShmSegmentInfo *, int, int );
+static void     DestroyShmImage( Display *, XvImage *, XShmSegmentInfo * );
+
+static int  CheckForXVideo     ( Display * );
+static int  GetXVideoPort      ( Display * );
 
-static int  XVideoCheckForXv         ( Display * );
-static int  XVideoGetPort            ( Display * );
-static void XVideoOutputCoords       ( const picture_t *, const boolean_t,
-                                       const int, const int,
-                                       int *, int *, int *, int * );
+/*static void XVideoSetAttribute       ( vout_thread_t *, char *, float );*/
 
 /*****************************************************************************
  * Functions exported as capabilities. They are declared as static so that
@@ -163,9 +109,9 @@ void _M( vout_getfunctions )( function_list_t * p_function_list )
     p_function_list->functions.vout.pf_init       = vout_Init;
     p_function_list->functions.vout.pf_end        = vout_End;
     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
-    p_function_list->functions.vout.pf_manage     = vout_Manage;
+    p_function_list->functions.vout.pf_manage     = _M( vout_Manage );
     p_function_list->functions.vout.pf_display    = vout_Display;
-    p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
+    p_function_list->functions.vout.pf_setpalette = NULL;
 }
 
 /*****************************************************************************
@@ -176,24 +122,55 @@ void _M( vout_getfunctions )( function_list_t * p_function_list )
  *****************************************************************************/
 static int vout_Probe( probedata_t *p_data )
 {
+    Display *p_display;                                   /* display pointer */
+    char    *psz_display;
+
+    /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
+    psz_display = XDisplayName( main_GetPszVariable(VOUT_DISPLAY_VAR, NULL) );
+    p_display = XOpenDisplay( psz_display );
+    if( p_display == NULL )                                         /* error */
+    {
+        intf_WarnMsg( 3, "vout: Xvideo cannot open display %s", psz_display );
+        intf_WarnMsg( 3, "vout: Xvideo not supported" );
+        return( 0 );
+    }
+
+    if( !CheckForXVideo( p_display ) )
+    {
+        intf_WarnMsg( 3, "vout: Xvideo not supported" );
+        XCloseDisplay( p_display );
+        return( 0 );
+    }
+
+    if( GetXVideoPort( p_display ) < 0 )
+    {
+        intf_WarnMsg( 3, "vout: Xvideo not supported" );
+        XCloseDisplay( p_display );
+        return( 0 );
+    }
+
+    /* Clean-up everyting */
+    XCloseDisplay( p_display );
+
     if( TestMethod( VOUT_METHOD_VAR, "xvideo" ) )
     {
         return( 999 );
     }
 
-    return( 90 );
+    return( 150 );
 }
 
 /*****************************************************************************
  * vout_Create: allocate XVideo video thread output method
  *****************************************************************************
- * This function allocate and initialize a XVideo vout method. It uses some of
+ * This function allocates and initialize a XVideo vout method. It uses some of
  * the vout properties to choose the window size, and change them according to
  * the actual properties of the display.
  *****************************************************************************/
 static int vout_Create( vout_thread_t *p_vout )
 {
     char *psz_display;
+    XColor cursor_color;
 
     /* Allocate structure */
     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
@@ -215,7 +192,7 @@ static int vout_Create( vout_thread_t *p_vout )
     }
     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
 
-    if( !XVideoCheckForXv( p_vout->p_sys->p_display ) )
+    if( !CheckForXVideo( p_vout->p_sys->p_display ) )
     {
         intf_ErrMsg( "vout error: no XVideo extension" );
         XCloseDisplay( p_vout->p_sys->p_display );
@@ -223,19 +200,51 @@ static int vout_Create( vout_thread_t *p_vout )
         return( 1 );
     }
 
+    /* Check we have access to a video port */
+    if( (p_vout->p_sys->i_xvport = GetXVideoPort(p_vout->p_sys->p_display)) <0 )
+    {
+        intf_ErrMsg( "vout error: cannot get XVideo port" );
+        XCloseDisplay( p_vout->p_sys->p_display );
+        free( p_vout->p_sys );
+        return 1;
+    }
+    intf_DbgMsg( "Using xv port %d" , p_vout->p_sys->xv_port );
+
+    /* Create blank cursor (for mouse cursor autohiding) */
+    p_vout->p_sys->b_mouse_pointer_visible = 1;
+    p_vout->p_sys->cursor_pixmap = XCreatePixmap( p_vout->p_sys->p_display,
+                                                  DefaultRootWindow(
+                                                     p_vout->p_sys->p_display),
+                                                  1, 1, 1 );
+
+    XParseColor( p_vout->p_sys->p_display,
+                 XCreateColormap( p_vout->p_sys->p_display,
+                                  DefaultRootWindow(
+                                                    p_vout->p_sys->p_display ),
+                                  DefaultVisual(
+                                                p_vout->p_sys->p_display,
+                                                p_vout->p_sys->i_screen ),
+                                  AllocNone ),
+                 "black", &cursor_color );
+
+    p_vout->p_sys->blank_cursor = XCreatePixmapCursor(
+                                      p_vout->p_sys->p_display,
+                                      p_vout->p_sys->cursor_pixmap,
+                                      p_vout->p_sys->cursor_pixmap,
+                                      &cursor_color,
+                                      &cursor_color, 1, 1 );
+
     /* Spawn base window - this window will include the video output window,
      * but also command buttons, subtitles and other indicators */
-    if( XVideoCreateWindow( p_vout ) )
+    if( _M( XCommonCreateWindow ) ( p_vout ) )
     {
-        intf_ErrMsg( "vout error: cannot create XVideo window" );
+        intf_ErrMsg( "vout error: no suitable Xvideo image input port" );
+        _M( XCommonDestroyWindow ) ( p_vout );
         XCloseDisplay( p_vout->p_sys->p_display );
         free( p_vout->p_sys );
         return( 1 );
     }
 
-    if( (p_vout->p_sys->xv_port = XVideoGetPort( p_vout->p_sys->p_display ))<0 )
-        return 1;
-
 #if 0
     /* XXX The brightness and contrast values should be read from environment
      * XXX variables... */
@@ -243,10 +252,8 @@ static int vout_Create( vout_thread_t *p_vout )
     XVideoSetAttribute( p_vout, "XV_CONTRAST",   0.5 );
 #endif
 
-    p_vout->p_sys->b_mouse = 1;
-
     /* Disable screen saver and return */
-    XVideoDisableScreenSaver( p_vout );
+    _M( XCommonDisableScreenSaver ) ( p_vout );
 
     return( 0 );
 }
@@ -256,773 +263,503 @@ static int vout_Create( vout_thread_t *p_vout )
  *****************************************************************************/
 static int vout_Init( vout_thread_t *p_vout )
 {
-#ifdef SYS_DARWIN1_3
-    /* FIXME : As of 2001-03-16, XFree4 for MacOS X does not support Xshm. */
-    p_vout->p_sys->b_shm = 0;
-#endif
-    p_vout->b_need_render = 0;
-    p_vout->p_sys->i_image_width = p_vout->p_sys->i_image_height = 0;
-
-    return( 0 );
-}
-
-/*****************************************************************************
- * vout_End: terminate XVideo video thread output method
- *****************************************************************************
- * Destroy the XvImage. It is called at the end of the thread, but also each
- * time the image is resized.
- *****************************************************************************/
-static void vout_End( vout_thread_t *p_vout )
-{
-    XVideoDestroyShmImage( p_vout, p_vout->p_sys->p_xvimage,
-                           &p_vout->p_sys->shm_info );
-}
-
-/*****************************************************************************
- * vout_Destroy: destroy XVideo video thread output method
- *****************************************************************************
- * Terminate an output method created by vout_CreateOutputMethod
- *****************************************************************************/
-static void vout_Destroy( vout_thread_t *p_vout )
-{
-    /* Enable screen saver */
-    XVideoEnableScreenSaver( p_vout );
+    int i_index;
+    picture_t *p_pic;
 
-    /* Destroy window */
-    XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
-    XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
-    XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
+    I_OUTPUTPICTURES = 0;
 
-    XCloseDisplay( p_vout->p_sys->p_display );
+    /* Initialize the output structure */
+    switch( p_vout->render.i_chroma )
+    {
+        case YUV_420_PICTURE:
+            p_vout->output.i_chroma = p_vout->render.i_chroma;
+            p_vout->output.i_width  = p_vout->render.i_width;
+            p_vout->output.i_height = p_vout->render.i_height;
+            p_vout->output.i_aspect = p_vout->render.i_aspect;
+            break;
 
-    /* Destroy structure */
-    free( p_vout->p_sys );
-}
+        default:
+            return( 0 );
+    }
 
-/*****************************************************************************
- * vout_Manage: handle X11 events
- *****************************************************************************
- * This function should be called regularly by video output thread. It manages
- * X11 events and allows window resizing. It returns a non null value on
- * error.
- *
- * XXX  Should "factor-out" common code in this and the "same" fn in the x11
- * XXX  plugin!
- *****************************************************************************/
-static int vout_Manage( vout_thread_t *p_vout )
-{
-    XEvent      xevent;                                         /* X11 event */
-    char        i_key;                                    /* ISO Latin-1 key */
-    KeySym      x_key_symbol;
-
-    /* Handle X11 events: ConfigureNotify events are parsed to know if the
-     * output window's size changed, MapNotify and UnmapNotify to know if the
-     * window is mapped (and if the display is useful), and ClientMessages
-     * to intercept window destruction requests */
-    while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                              StructureNotifyMask | KeyPressMask |
-                              ButtonPressMask | ButtonReleaseMask | 
-                              PointerMotionMask, &xevent )
-           == True )
+    /* Try to initialize up to XVIDEO_MAX_DIRECTBUFFERS direct buffers */
+    while( I_OUTPUTPICTURES < XVIDEO_MAX_DIRECTBUFFERS )
     {
-        /* ConfigureNotify event: prepare  */
-        if( (xevent.type == ConfigureNotify)
-            /*&& ((xevent.xconfigure.width != p_vout->p_sys->i_window_width)
-                || (xevent.xconfigure.height != p_vout->p_sys->i_window_height))*/ )
-        {
-            /* Update dimensions */
-            p_vout->p_sys->i_window_width = xevent.xconfigure.width;
-            p_vout->p_sys->i_window_height = xevent.xconfigure.height;
-        }
-        /* MapNotify event: change window status and disable screen saver */
-        else if( xevent.type == MapNotify)
-        {
-            if( (p_vout != NULL) && !p_vout->b_active )
-            {
-                XVideoDisableScreenSaver( p_vout );
-                p_vout->b_active = 1;
-            }
-        }
-        /* UnmapNotify event: change window status and enable screen saver */
-        else if( xevent.type == UnmapNotify )
-        {
-            if( (p_vout != NULL) && p_vout->b_active )
-            {
-                XVideoEnableScreenSaver( p_vout );
-                p_vout->b_active = 0;
-            }
-        }
-        /* Keyboard event */
-        else if( xevent.type == KeyPress )
+        p_pic = NULL;
+
+        /* Find an empty picture slot */
+        for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
         {
-            /* We may have keys like F1 trough F12, ESC ... */
-            x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
-                                             xevent.xkey.keycode, 0 );
-            switch( x_key_symbol )
+            if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
             {
-                 case XK_Escape:
-                     p_main->p_intf->b_die = 1;
-                     break;
-                 case XK_Menu:
-                     p_main->p_intf->b_menu_change = 1;
-                     break;
-                 default:
-                     /* "Normal Keys"
-                      * The reason why I use this instead of XK_0 is that 
-                      * with XLookupString, we don't have to care about
-                      * keymaps. */
-
-                    if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
-                    {
-                        switch( i_key )
-                        {
-                        case 'q':
-                        case 'Q':
-                            p_main->p_intf->b_die = 1;
-                            break;
-                        case '0':
-                            network_ChannelJoin( 0 );
-                            break;
-                        case '1':
-                            network_ChannelJoin( 1 );
-                            break;
-                        case '2':
-                            network_ChannelJoin( 2 );
-                            break;
-                        case '3':
-                            network_ChannelJoin( 3 );
-                            break;
-                        case '4':
-                            network_ChannelJoin( 4 );
-                            break;
-                        case '5':
-                            network_ChannelJoin( 5 );
-                            break;
-                        case '6':
-                            network_ChannelJoin( 6 );
-                            break;
-                        case '7':
-                            network_ChannelJoin( 7 );
-                            break;
-                        case '8':
-                            network_ChannelJoin( 8 );
-                            break;
-                        case '9':
-                            network_ChannelJoin( 9 );
-                            break;
-                        default:
-                            if( intf_ProcessKey( p_main->p_intf, 
-                                                 (char )i_key ) )
-                            {
-                               intf_DbgMsg( "unhandled key '%c' (%i)", 
-                                            (char)i_key, i_key );
-                            }
-                            break;
-                        }
-                    }
+                p_pic = p_vout->p_picture + i_index;
                 break;
             }
         }
-        /* Mouse click */
-        else if( xevent.type == ButtonPress )
-        {
-            switch( ((XButtonEvent *)&xevent)->button )
-            {
-                case Button1:
-                    /* in this part we will eventually manage
-                     * clicks for DVD navigation for instance */
-                    break;
-            }
-        }
-        /* Mouse release */
-        else if( xevent.type == ButtonRelease )
-        {
-            switch( ((XButtonEvent *)&xevent)->button )
-            {
-                case Button3:
-                    /* FIXME: need locking ! */
-                    p_main->p_intf->b_menu_change = 1;
-                    break;
-            }
-        }
-        /* Mouse move */
-        else if( xevent.type == MotionNotify )
-        {
-            p_vout->p_sys->i_lastmoved = mdate();
-            if( ! p_vout->p_sys->b_mouse )
-            {
-                XVideoTogglePointer( p_vout ); 
-            }
-        }
-        
-#ifdef DEBUG
-        /* Other event */
-        else
-        {
-            intf_DbgMsg( "%p -> unhandled event type %d received",
-                         p_vout, xevent.type );
-        }
-#endif
-    }
 
-    /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
-     * are handled - according to the man pages, the format is always 32
-     * in this case */
-    while( XCheckTypedEvent( p_vout->p_sys->p_display,
-                             ClientMessage, &xevent ) )
-    {
-        if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
-            && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
+        /* Allocate the picture */
+        if( XVideoNewPicture( p_vout, p_pic ) )
         {
-            p_main->p_intf->b_die = 1;
-        }
-        else
-        {
-            intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
+            break;
         }
-    }
 
-    if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE))
-    {
-        /* FIXME: clear flags ?? */
-    }
+        p_pic->i_status        = DESTROYED_PICTURE;
+        p_pic->i_type          = DIRECT_PICTURE;
 
-    /*
-     * Size change
-     */
-    if( p_vout->i_changes & VOUT_SIZE_CHANGE )
-    {
-        intf_DbgMsg( "vout: resizing window" );
-        p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
-        /* Noting to do here...
-         * vout_Display() detects size changes of the image to be displayed and
-         * re-creates the XvImage.*/
-        intf_Msg( "vout: video display resized (%dx%d)",
-                  p_vout->i_width, p_vout->i_height );
+        p_pic->i_left_margin   =
+        p_pic->i_right_margin  =
+        p_pic->i_top_margin    =
+        p_pic->i_bottom_margin = 0;
+
+        PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
+
+        I_OUTPUTPICTURES++;
     }
 
-    /* Autohide Cursor */
-    if( p_vout->p_sys->b_mouse &&
-        mdate() - p_vout->p_sys->i_lastmoved > 2000000 )
-            XVideoTogglePointer( p_vout ); 
-    
-    return 0;
+    return( 0 );
 }
 
 /*****************************************************************************
- * XVideoUpdateImgSizeIfRequired 
+ * vout_End: terminate XVideo video thread output method
  *****************************************************************************
- * This function checks to see if the image to be displayed is of a different
- * size to the last image displayed. If so, the old shm block must be
- * destroyed and a new one created.
- * Note: the "image size" is the size of the image to be passed to the Xv
- * extension (which is probably different to the size of the output window).
+ * Destroy the XvImage. It is called at the end of the thread, but also each
+ * time the image is resized.
  *****************************************************************************/
-static int XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout )
+static void vout_End( vout_thread_t *p_vout )
 {
-    int i_img_width         = p_vout->p_rendered_pic->i_width;
-    int i_img_height        = p_vout->p_rendered_pic->i_height;
+    int i_index;
 
-    if( p_vout->p_sys->i_image_width != i_img_width
-            || p_vout->p_sys->i_image_height != i_img_height )
+    /* Free the direct buffers we allocated */
+    for( i_index = I_OUTPUTPICTURES ; i_index ; )
     {
-        p_vout->p_sys->i_image_width  = i_img_width;
-        p_vout->p_sys->i_image_height = i_img_height;
-
-        /* Destroy XvImage to change its size */
-        vout_End( p_vout );
-            /* Note: vout_End does nothing if no XvImage to destroy. */
-
-        /* Create XvImage using XShm extension */
-        if( XVideoCreateShmImage( p_vout->p_sys->p_display,
-                                  p_vout->p_sys->xv_port,
-                                  &p_vout->p_sys->p_xvimage,
-                                  &p_vout->p_sys->shm_info,
-                                  i_img_width, i_img_height ) )
-        {
-            intf_Msg( "vout: failed to create xvimage." );
-            p_vout->p_sys->i_image_width = 0;
-            return( 1 );
-        }
-
-        /* Set bytes per line and initialize buffers */
-        p_vout->i_bytes_per_line =
-            (p_vout->p_sys->p_xvimage->data_size) /
-            (p_vout->p_sys->p_xvimage->height);
-
-        /* vout_SetBuffers( p_vout, p_vout->p_sys->p_xvimage->data ); */
+        i_index--;
+        DestroyShmImage( p_vout->p_sys->p_display,
+                         PP_OUTPUTPICTURE[ i_index ]->p_sys->p_xvimage,
+                         &PP_OUTPUTPICTURE[ i_index ]->p_sys->shminfo );
+        free( PP_OUTPUTPICTURE[ i_index ]->p_sys );
     }
-    return( 0 );
 }
 
 /*****************************************************************************
- * vout_Display: displays previously rendered output
+ * vout_Destroy: destroy XVideo video thread output method
  *****************************************************************************
- * This function sends the currently rendered image to X11 server.
- * (The Xv extension takes care of "double-buffering".)
+ * Terminate an output method created by vout_Create
  *****************************************************************************/
-static void vout_Display( vout_thread_t *p_vout )
+static void vout_Destroy( vout_thread_t *p_vout )
 {
-    boolean_t b_draw = 1;
-    int i_size = p_vout->p_rendered_pic->i_width *
-                   p_vout->p_rendered_pic->i_height;
-
-    if( XVideoUpdateImgSizeIfRequired( p_vout ) )
-        return;
-
-    switch( p_vout->p_rendered_pic->i_type )
+    /* Restore cursor if it was blanked */
+    if( !p_vout->p_sys->b_mouse_pointer_visible )
     {
-    case YUV_422_PICTURE:
-        intf_ErrMsg( "vout error: YUV_422_PICTURE not (yet) supported" );
-        b_draw = 0;
-        break;
-
-    case YUV_444_PICTURE:
-        intf_ErrMsg( "vout error: YUV_444_PICTURE not (yet) supported" );
-        b_draw = 0;
-        break;
-
-    case YUV_420_PICTURE:
-        memcpy( p_vout->p_sys->p_xvimage->data,
-                p_vout->p_rendered_pic->p_y, i_size );
-        memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ),
-                p_vout->p_rendered_pic->p_v, i_size / 4 );
-        memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ) + ( i_size / 4 ),
-                p_vout->p_rendered_pic->p_u, i_size / 4 );
-        break;
+        _M( XCommonToggleMousePointer ) ( p_vout );
     }
 
-    if( b_draw )
-    {
-        int     i_dummy,
-                i_window_width = p_vout->p_sys->i_window_width,
-                i_window_height = p_vout->p_sys->i_window_height,
-                i_dest_width, i_dest_height, i_dest_x, i_dest_y;
-        Window  window;
+    /* Destroy blank cursor pixmap */
+    XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
 
-#if 1
-        /* If I change the line above to "#if 0" I find on resizing the window
-         * that blue rectangles (used to specify where part of the YUV overlay
-         * used to be drawn) may remain around the edge of the video output. */
-        XGetGeometry( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                      &window, &i_dummy, &i_dummy,
-                      &i_window_width, &i_window_height, &i_dummy, &i_dummy );
-#endif
+    _M( XCommonEnableScreenSaver ) ( p_vout );
+    _M( XCommonDestroyWindow ) ( p_vout );
+    XCloseDisplay( p_vout->p_sys->p_display );
 
-        XVideoOutputCoords( p_vout->p_rendered_pic, p_vout->b_scale,
-                            i_window_width, i_window_height,
-                            &i_dest_x, &i_dest_y,
-                            &i_dest_width, &i_dest_height);
-  
-        XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->xv_port,
-                       p_vout->p_sys->window, p_vout->p_sys->gc,
-                       p_vout->p_sys->p_xvimage,
-                       0 /*src_x*/, 0 /*src_y*/,
-                       p_vout->p_rendered_pic->i_width,
-                       p_vout->p_rendered_pic->i_height,
-                       i_dest_x, i_dest_y, i_dest_width, i_dest_height,
-                       True );
-    }
+    /* Destroy structure */
+    free( p_vout->p_sys );
 }
 
-static void vout_SetPalette( p_vout_thread_t p_vout,
-                             u16 *red, u16 *green, u16 *blue, u16 *transp )
+/*****************************************************************************
+ * vout_Display: displays previously rendered output
+ *****************************************************************************
+ * This function sends the currently rendered image to X11 server.
+ * (The Xv extension takes care of "double-buffering".)
+ *****************************************************************************/
+static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
 {
-    return;
+    int i_width, i_height, i_x, i_y;
+
+    vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
+                       &i_x, &i_y, &i_width, &i_height );
+
+    XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->i_xvport,
+                   p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
+                   p_pic->p_sys->p_xvimage, 0 /*src_x*/, 0 /*src_y*/,
+                   p_vout->output.i_width, p_vout->output.i_height,
+                   0 /*dest_x*/, 0 /*dest_y*/, i_width, i_height,
+                   True );
+
+    XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
+                   i_width, i_height );
+
+    XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
+                 i_x, i_y );
 }
 
 /* following functions are local */
 
 /*****************************************************************************
- * XVideoCheckForXv: check for the XVideo extension
+ * CheckForXVideo: check for the XVideo extension
  *****************************************************************************/
-static int XVideoCheckForXv( Display *dpy )
+static int CheckForXVideo( Display *p_display )
 {
     unsigned int i;
 
-    switch( XvQueryExtension( dpy, &i, &i, &i, &i, &i ) )
+    switch( XvQueryExtension( p_display, &i, &i, &i, &i, &i ) )
     {
         case Success:
             return( 1 );
 
         case XvBadExtension:
-            intf_ErrMsg( "vout error: XvBadExtension" );
+            intf_WarnMsg( 3, "vout error: XvBadExtension" );
             return( 0 );
 
         case XvBadAlloc:
-            intf_ErrMsg( "vout error: XvBadAlloc" );
+            intf_WarnMsg( 3, "vout error: XvBadAlloc" );
             return( 0 );
 
         default:
-            intf_ErrMsg( "vout error: XvQueryExtension failed" );
+            intf_WarnMsg( 3, "vout error: XvQueryExtension failed" );
             return( 0 );
     }
 }
 
 /*****************************************************************************
- * XVideoCreateWindow: open and set-up XVideo main window
+ * XVideoNewPicture: allocate a picture
+ *****************************************************************************
+ * Returns 0 on success, -1 otherwise
  *****************************************************************************/
-static int XVideoCreateWindow( vout_thread_t *p_vout )
+static int XVideoNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
 {
-    XSizeHints              xsize_hints;
-    XSetWindowAttributes    xwindow_attributes;
-    XGCValues               xgcvalues;
-    XEvent                  xevent;
-    boolean_t               b_expose;
-    boolean_t               b_configure_notify;
-    boolean_t               b_map_notify;
-
-    /* Set main window's size */
-    p_vout->p_sys->i_window_width =  main_GetIntVariable( VOUT_WIDTH_VAR,
-                                                   VOUT_WIDTH_DEFAULT );
-    p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
-                                                   VOUT_HEIGHT_DEFAULT );
-
-    /* Prepare window manager hints and properties */
-    xsize_hints.base_width          = p_vout->p_sys->i_window_width;
-    xsize_hints.base_height         = p_vout->p_sys->i_window_height;
-    xsize_hints.flags               = PSize;
-    p_vout->p_sys->wm_protocols     = XInternAtom( p_vout->p_sys->p_display,
-                                                   "WM_PROTOCOLS", True );
-    p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
-                                                   "WM_DELETE_WINDOW", True );
-
-    /* Prepare window attributes */
-    xwindow_attributes.backing_store = Always;       /* save the hidden part */
-    xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
-                                                      p_vout->p_sys->i_screen );
-
-    xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
-
-    /* Create the window and set hints - the window must receive ConfigureNotify
-     * events, and, until it is displayed, Expose and MapNotify events. */
-    p_vout->p_sys->window =
-            XCreateWindow( p_vout->p_sys->p_display,
-                           DefaultRootWindow( p_vout->p_sys->p_display ),
-                           0, 0,
-                           p_vout->p_sys->i_window_width,
-                           p_vout->p_sys->i_window_height, 1,
-                           0, InputOutput, 0,
-                           CWBackingStore | CWBackPixel | CWEventMask,
-                           &xwindow_attributes );
-
-    /* Set window manager hints and properties: size hints, command,
-     * window's name, and accepted protocols */
-    XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                       &xsize_hints );
-    XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                 p_main->ppsz_argv, p_main->i_argc );
-    XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                VOUT_TITLE " (XVideo output)" );
-
-    if( (p_vout->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
-        || (p_vout->p_sys->wm_delete_window == None)
-        || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                             &p_vout->p_sys->wm_delete_window, 1 ) )
-    {
-        /* WM_DELETE_WINDOW is not supported by window manager */
-        intf_Msg( "vout error: missing or bad window manager" );
-    }
+    int i_width  = p_vout->output.i_width;
+    int i_height = p_vout->output.i_height;
 
-    /* Creation of a graphic context that doesn't generate a GraphicsExpose
-     * event when using functions like XCopyArea */
-    xgcvalues.graphics_exposures = False;
-    p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
-                                   p_vout->p_sys->window,
-                                   GCGraphicsExposures, &xgcvalues);
-
-    /* Send orders to server, and wait until window is displayed - three
-     * events must be received: a MapNotify event, an Expose event allowing
-     * drawing in the window, and a ConfigureNotify to get the window
-     * dimensions. Once those events have been received, only ConfigureNotify
-     * events need to be received. */
-    b_expose = 0;
-    b_configure_notify = 0;
-    b_map_notify = 0;
-    XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
-    do
+    switch( p_vout->output.i_chroma )
     {
-        XNextEvent( p_vout->p_sys->p_display, &xevent);
-        if( (xevent.type == Expose)
-            && (xevent.xexpose.window == p_vout->p_sys->window) )
-        {
-            b_expose = 1;
-        }
-        else if( (xevent.type == MapNotify)
-                 && (xevent.xmap.window == p_vout->p_sys->window) )
-        {
-            b_map_notify = 1;
-        }
-        else if( (xevent.type == ConfigureNotify)
-                 && (xevent.xconfigure.window == p_vout->p_sys->window) )
-        {
-            b_configure_notify = 1;
-            p_vout->p_sys->i_window_width = xevent.xconfigure.width;
-            p_vout->p_sys->i_window_height = xevent.xconfigure.height;
-        }
-    } while( !( b_expose && b_configure_notify && b_map_notify ) );
+        case YUV_420_PICTURE:
+            /* We know this chroma, allocate a buffer which will be used
+             * directly by the decoder */
+            p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
 
-    XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
-                  StructureNotifyMask | KeyPressMask |
-                  ButtonPressMask | ButtonReleaseMask | 
-                  PointerMotionMask );
+            if( p_pic->p_sys == NULL )
+            {
+                return -1;
+            }
 
-    /* At this stage, the window is open, displayed, and ready to
-     * receive data */
-    return( 0 );
+            /* Create XvImage using XShm extension */
+            p_pic->p_sys->p_xvimage =
+                CreateShmImage( p_vout->p_sys->p_display,
+                                p_vout->p_sys->i_xvport,
+                                &p_pic->p_sys->shminfo,
+                                p_vout->output.i_width,
+                                p_vout->output.i_height );
+            if( p_pic->p_sys->p_xvimage == NULL )
+            {
+                free( p_pic->p_sys );
+                return -1;
+            }
+
+
+            /* Precalculate some values */
+            p_pic->i_size         = i_width * i_height;
+            p_pic->i_chroma_width = i_width / 2;
+            p_pic->i_chroma_size  = i_height * ( i_width / 2 );
+
+            /* FIXME: try to get the right i_bytes value from p_xvimage */
+            p_pic->planes[Y_PLANE].p_data  = p_pic->p_sys->p_xvimage->data;
+            p_pic->planes[Y_PLANE].i_bytes = p_pic->i_size * sizeof(u8);
+            p_pic->planes[Y_PLANE].i_line_bytes = i_width * sizeof(u8);
+
+            p_pic->planes[U_PLANE].p_data  = (u8*)p_pic->p_sys->p_xvimage->data
+                                               + p_pic->i_size * 5 / 4;
+            p_pic->planes[U_PLANE].i_bytes = p_pic->i_size * sizeof(u8) / 4;
+            p_pic->planes[U_PLANE].i_line_bytes = p_pic->i_chroma_width
+                                                   * sizeof(u8);
+
+            p_pic->planes[V_PLANE].p_data  = (u8*)p_pic->p_sys->p_xvimage->data
+                                               + p_pic->i_size;
+            p_pic->planes[V_PLANE].i_bytes = p_pic->i_size * sizeof(u8) / 4;
+            p_pic->planes[V_PLANE].i_line_bytes = p_pic->i_chroma_width
+                                                   * sizeof(u8);
+
+            p_pic->i_planes = 3;
+
+            return 0;
+
+        default:
+            /* Unknown chroma, tell the guy to get lost */
+            p_pic->i_planes = 0;
+
+            return -1;
+    }
 }
 
 /*****************************************************************************
- * XVideoCreateShmImage: create an XvImage using shared memory extension
+ * CreateShmImage: create an XvImage using shared memory extension
  *****************************************************************************
  * Prepare an XvImage for display function.
  * The order of the operations respects the recommandations of the mit-shm
  * document by J.Corbet and K.Packard. Most of the parameters were copied from
  * there.
  *****************************************************************************/
-static int XVideoCreateShmImage( Display* dpy, int xv_port,
-                                    XvImage **pp_xvimage,
-                                    XShmSegmentInfo *p_shm_info,
-                                    int i_width, int i_height )
+static XvImage *CreateShmImage( Display* p_display, int i_xvport,
+                                XShmSegmentInfo *p_shminfo,
+                                int i_width, int i_height )
 {
-    #define GUID_YUV12_PLANAR 0x32315659
+    XvImage *p_xvimage;
 
-    *pp_xvimage = XvShmCreateImage( dpy, xv_port,
-                                    GUID_YUV12_PLANAR, 0,
-                                    i_width, i_height,
-                                    p_shm_info );
+    p_xvimage = XvShmCreateImage( p_display, i_xvport,
+                                  GUID_YUV12_PLANAR, 0,
+                                  i_width, i_height,
+                                  p_shminfo );
+    if( p_xvimage == NULL )
+    {
+        intf_ErrMsg( "vout error: XvShmCreateImage failed." );
+        return( NULL );
+    }
 
-    p_shm_info->shmid    = shmget( IPC_PRIVATE, (*pp_xvimage)->data_size,
-                                   IPC_CREAT | 0777 );
-    p_shm_info->shmaddr  = (*pp_xvimage)->data = shmat( p_shm_info->shmid,
-                                                        0, 0 );
-    p_shm_info->readOnly = False;
+    p_shminfo->shmid = shmget( IPC_PRIVATE, p_xvimage->data_size,
+                               IPC_CREAT | 0776 );
+    if( p_shminfo->shmid < 0 ) /* error */
+    {
+        intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
+                     strerror( errno ) );
+        return( NULL );
+    }
 
-    shmctl( p_shm_info->shmid, IPC_RMID, 0 ); /* XXX */
+    p_shminfo->shmaddr = p_xvimage->data = shmat( p_shminfo->shmid, 0, 0 );
+    p_shminfo->readOnly = False;
 
-    if( !XShmAttach( dpy, p_shm_info ) )
+    if( !XShmAttach( p_display, p_shminfo ) )
     {
         intf_ErrMsg( "vout error: XShmAttach failed" );
-        return( -1 );
+        shmctl( p_shminfo->shmid, IPC_RMID, 0 );
+        shmdt( p_shminfo->shmaddr );
+        return( NULL );
     }
 
     /* Send image to X server. This instruction is required, since having
      * built a Shm XImage and not using it causes an error on XCloseDisplay */
-    XFlush( dpy );
+    XSync( p_display, False );
 
-    return( 0 );
+#if 1
+    /* Mark the shm segment to be removed when there are no more
+     * attachements, so it is automatic on process exit or after shmdt */
+    shmctl( p_shminfo->shmid, IPC_RMID, 0 );
+#endif
+
+    return( p_xvimage );
 }
 
 /*****************************************************************************
- * XVideoDestroyShmImage
+ * DestroyShmImage
  *****************************************************************************
  * Destroy XImage AND associated data. Detach shared memory segment from
  * server and process, then free it. If pointer is NULL, the image won't be
- * destroyed (see vout_ManageOutputMethod())
+ * destroyed (see vout_Manage())
  *****************************************************************************/
-static void XVideoDestroyShmImage( vout_thread_t *p_vout, XvImage *p_xvimage,
-                                   XShmSegmentInfo *p_shm_info )
+static void DestroyShmImage( Display *p_display, XvImage *p_xvimage,
+                             XShmSegmentInfo *p_shminfo )
 {
-    /* If pointer is NULL, do nothing */
-    if( p_xvimage == NULL )
-    {
-        return;
-    }
+    /* Detach from server */
+    XShmDetach( p_display, p_shminfo );
+    XSync( p_display, False );
 
-    XShmDetach( p_vout->p_sys->p_display, p_shm_info );/* detach from server */
 #if 0
-    XDestroyImage( p_ximage ); /* XXX */
+    XDestroyImage( p_xvimage ); /* XXX */
 #endif
 
-    if( shmdt( p_shm_info->shmaddr ) )  /* detach shared memory from process */
-    {                                   /* also automatic freeing...         */
+    XFree( p_xvimage );
+
+    if( shmdt( p_shminfo->shmaddr ) )   /* detach shared memory from process */
+    {
         intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
                      strerror(errno) );
     }
 }
 
 /*****************************************************************************
- * XVideoEnableScreenSaver: enable screen saver
+ * GetXVideoPort: get YUV12 port
  *****************************************************************************
- * This function enable the screen saver on a display after it had been
- * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
- * know wether the screen saver can be activated or not: if n successive calls
- * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
- * will be required before the screen saver could effectively be activated.
+ * 
  *****************************************************************************/
-void XVideoEnableScreenSaver( vout_thread_t *p_vout )
+static int GetXVideoPort( Display *dpy )
 {
-    intf_DbgMsg( "intf: enabling screen saver" );
-    XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
-                     p_vout->p_sys->i_ss_interval,
-                     p_vout->p_sys->i_ss_blanking,
-                     p_vout->p_sys->i_ss_exposure );
-}
+    XvAdaptorInfo *p_adaptor;
+    int i_adaptor, i_num_adaptors, i_requested_adaptor;
+    int i_selected_port;
 
-/*****************************************************************************
- * XVideoDisableScreenSaver: disable screen saver
- *****************************************************************************
- * See XEnableScreenSaver
- *****************************************************************************/
-void XVideoDisableScreenSaver( vout_thread_t *p_vout )
-{
-    /* Save screen saver informations */
-    XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
-                     &p_vout->p_sys->i_ss_interval,
-                     &p_vout->p_sys->i_ss_blanking,
-                     &p_vout->p_sys->i_ss_exposure );
-
-    /* Disable screen saver */
-    intf_DbgMsg( "intf: disabling screen saver" );
-    XSetScreenSaver( p_vout->p_sys->p_display, 0,
-                     p_vout->p_sys->i_ss_interval,
-                     p_vout->p_sys->i_ss_blanking,
-                     p_vout->p_sys->i_ss_exposure );
-}
+    switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
+                             &i_num_adaptors, &p_adaptor ) )
+    {
+        case Success:
+            break;
 
-/*****************************************************************************
- * XVideoTogglePointer: hide or show the mouse pointer
- *****************************************************************************
- * This function hides the X pointer if it is visible by putting it at
- * coordinates (32,32) and setting the pointer sprite to a blank one. To
- * show it again, we disable the sprite and restore the original coordinates.
- *****************************************************************************/
-void XVideoTogglePointer( vout_thread_t *p_vout )
-{
-    static Cursor cursor;
-    static boolean_t b_cursor = 0;
+        case XvBadExtension:
+            intf_WarnMsg( 3, "vout error: XvBadExtension for XvQueryAdaptors" );
+            return( -1 );
 
-    if( p_vout->p_sys->b_mouse )
-    {
-        p_vout->p_sys->b_mouse = 0;
+        case XvBadAlloc:
+            intf_WarnMsg( 3, "vout error: XvBadAlloc for XvQueryAdaptors" );
+            return( -1 );
 
-        if( !b_cursor )
-        {
-            XColor color;
-            Pixmap blank = XCreatePixmap( p_vout->p_sys->p_display,
-                               DefaultRootWindow(p_vout->p_sys->p_display),
-                               1, 1, 1 );
-
-            XParseColor( p_vout->p_sys->p_display,
-                         XCreateColormap( p_vout->p_sys->p_display,
-                                          DefaultRootWindow(
-                                                  p_vout->p_sys->p_display ),
-                                          DefaultVisual(
-                                                  p_vout->p_sys->p_display,
-                                                  p_vout->p_sys->i_screen ),
-                                          AllocNone ),
-                         "black", &color );
-
-            cursor = XCreatePixmapCursor( p_vout->p_sys->p_display,
-                           blank, blank, &color, &color, 1, 1 );
-
-            b_cursor = 1;
-        }
-        XDefineCursor( p_vout->p_sys->p_display,
-                       p_vout->p_sys->window, cursor );
+        default:
+            intf_WarnMsg( 3, "vout error: XvQueryAdaptors failed" );
+            return( -1 );
     }
-    else
-    {
-        p_vout->p_sys->b_mouse = 1;
 
-        XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
-    }
-}
+    i_selected_port = -1;
+    i_requested_adaptor = main_GetIntVariable( VOUT_XVADAPTOR_VAR, -1 );
 
-/* This based on some code in SetBufferPicture... At the moment it's only
- * used by the xvideo plugin, but others may want to use it. */
-static void XVideoOutputCoords( const picture_t *p_pic, const boolean_t scale,
-                                const int win_w, const int win_h,
-                                int *dx, int *dy, int *w, int *h)
-{
-    if( !scale )
+    /* No special xv port has been requested so try all of them */
+    for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
     {
-        *w = p_pic->i_width; *h = p_pic->i_height;
-    }
-    else
-    {
-        *w = win_w;
-        switch( p_pic->i_aspect_ratio )
+        XvImageFormatValues *p_formats;
+        int i_format, i_num_formats;
+        int i_port;
+
+        /* If we requested an adaptor and it's not this one, we aren't
+         * interested */
+        if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
+       {
+            continue;
+       }
+
+       /* If the adaptor doesn't have the required properties, skip it */
+        if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
+            !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
         {
-        case AR_3_4_PICTURE:        *h = win_w * 3 / 4;      break;
-        case AR_16_9_PICTURE:       *h = win_w * 9 / 16;     break;
-        case AR_221_1_PICTURE:      *h = win_w * 100 / 221;  break;
-        case AR_SQUARE_PICTURE:
-                default:            *h = win_w; break;
-        }
+            continue;
+       }
+
+        /* Check that port supports YUV12 planar format... */
+        p_formats = XvListImageFormats( dpy, p_adaptor[i_adaptor].base_id,
+                                        &i_num_formats );
 
-        if( *h > win_h )
+        for( i_format = 0; i_format < i_num_formats; i_format++ )
         {
-            *h = win_h;
-            switch( p_pic->i_aspect_ratio )
+            XvEncodingInfo  *p_enc;
+            int             i_enc, i_num_encodings;
+            XvAttribute     *p_attr;
+            int             i_attr, i_num_attributes;
+
+            /* If this is not the format we want, forget it */
+            if( p_formats[ i_format ].id != GUID_YUV12_PLANAR )
             {
-            case AR_3_4_PICTURE:    *w = win_h * 4 / 3;      break;
-            case AR_16_9_PICTURE:   *w = win_h * 16 / 9;     break;
-            case AR_221_1_PICTURE:  *w = win_h * 221 / 100;  break;
-            case AR_SQUARE_PICTURE:
-                    default:        *w = win_h; break;
+                continue;
             }
-        }
-    }
 
-    /* Set picture position */
-    *dx = (win_w - *w) / 2;
-    *dy = (win_h - *h) / 2;
-}
+            /* Look for the first available port supporting this format */
+            for( i_port = p_adaptor[i_adaptor].base_id;
+                 ( i_port < p_adaptor[i_adaptor].base_id
+                             + p_adaptor[i_adaptor].num_ports )
+                   && ( i_selected_port == -1 );
+                 i_port++ )
+            {
+                if( XvGrabPort( dpy, i_port, CurrentTime ) == Success )
+                {
+                    i_selected_port = i_port;
+                }
+            }
 
+            /* If no free port was found, forget it */
+            if( i_selected_port == -1 )
+            {
+                continue;
+            }
 
-static int XVideoGetPort( Display *dpy )
-{
-    int            i, i_adaptors;
-    XvAdaptorInfo *adaptor_info;
+            /* If we found a port, print information about it */
+            intf_WarnMsg( 3, "vout: GetXVideoPort found adaptor %i, port %i",
+                             i_adaptor, i_selected_port );
+            intf_WarnMsg( 3, "  image format 0x%x (%4.4s) %s supported",
+                             p_formats[ i_format ].id,
+                             (char *)&p_formats[ i_format ].id,
+                             ( p_formats[ i_format ].format
+                                == XvPacked ) ? "packed" : "planar" );
 
-    switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
-                             &i_adaptors, &adaptor_info ) )
-    {
-        case Success:
-            break;
+            intf_WarnMsg( 4, " encoding list:" );
 
-        case XvBadExtension:
-            intf_ErrMsg( "vout error: XvBadExtension for XvQueryAdaptors" );
-            return( -1 );
+            if( XvQueryEncodings( dpy, i_selected_port,
+                                  &i_num_encodings, &p_enc )
+                 != Success )
+            {
+                intf_WarnMsg( 4, "  XvQueryEncodings failed" );
+                continue;
+            }
 
-        case XvBadAlloc:
-            intf_ErrMsg( "vout error: XvBadAlloc for XvQueryAdaptors" );
-            return( -1 );
+            for( i_enc = 0; i_enc < i_num_encodings; i_enc++ )
+            {
+                intf_WarnMsg( 4, "  id=%ld, name=%s, size=%ldx%ld,"
+                                 " numerator=%d, denominator=%d",
+                              p_enc[i_enc].encoding_id, p_enc[i_enc].name,
+                              p_enc[i_enc].width, p_enc[i_enc].height,
+                              p_enc[i_enc].rate.numerator,
+                              p_enc[i_enc].rate.denominator );
+            }
 
-        default:
-            intf_ErrMsg( "vout error: XvQueryAdaptors failed" );
-            return( -1 );
-    }
+            if( p_enc != NULL )
+            {
+                XvFreeEncodingInfo( p_enc );
+            }
 
-    for( i=0; i < i_adaptors; ++i )
-        if( ( adaptor_info[ i ].type & XvInputMask ) &&
-            ( adaptor_info[ i ].type & XvImageMask ) )
+            intf_WarnMsg( 4, " attribute list:" );
+            p_attr = XvQueryPortAttributes( dpy, i_selected_port,
+                                            &i_num_attributes );
+            for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
             {
-                return adaptor_info[ i ].base_id;
+                intf_WarnMsg( 4,
+                      "  name=%s, flags=[%s%s ], min=%i, max=%i",
+                      p_attr[i_attr].name,
+                      (p_attr[i_attr].flags & XvGettable) ? " get" : "",
+                      (p_attr[i_attr].flags & XvSettable) ? " set" : "",
+                      p_attr[i_attr].min_value, p_attr[i_attr].max_value );
             }
 
-    intf_ErrMsg( "vout error: didn't find an Xvideo image input port." );
-    return( -1 );
-}
+            if( p_attr != NULL )
+            {
+                XFree( p_attr );
+            }
+        }
 
+        if( p_formats != NULL )
+        {
+            XFree( p_formats );
+        }
+
+    }
+
+    if( i_num_adaptors > 0 )
+    {
+        XvFreeAdaptorInfo( p_adaptor );
+    }
+
+    if( i_selected_port == -1 )
+    {
+        if( i_requested_adaptor == -1 )
+        {
+            intf_WarnMsg( 3, "vout: no free XVideo port found for YV12" );
+        }
+        else
+        {
+            intf_WarnMsg( 3, "vout: XVideo adaptor %i does not have a free "
+                             "XVideo port for YV12", i_requested_adaptor );
+        }
+    }
+
+    return( i_selected_port );
+}
 
 #if 0
 /*****************************************************************************
  * XVideoSetAttribute
  *****************************************************************************
  * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
- * XV_CONTRAST. "value" should be in the range of 0 to 1.
+ * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
  *****************************************************************************/
 static void XVideoSetAttribute( vout_thread_t *p_vout,
                                 char *attr_name, float f_value )
 {
     int             i_attrib;
     XvAttribute    *p_attrib;
-    Display        *p_dpy   = p_vout->p_sys->p_display;
-    int             xv_port = p_vout->p_sys->xv_port;
+    Display        *p_display = p_vout->p_sys->p_display;
+    int             i_xvport  = p_vout->p_sys->i_xvport;
 
-    p_attrib = XvQueryPortAttributes( p_dpy, xv_port, &i_attrib );
+    p_attrib = XvQueryPortAttributes( p_display, i_xvport, &i_attrib );
 
     do
     {
@@ -1034,11 +771,15 @@ static void XVideoSetAttribute( vout_thread_t *p_vout,
                                     - p_attrib[ i_attrib ].min_value + 1 )
                         + p_attrib[ i_attrib ].min_value;
 
-            XvSetPortAttribute( p_dpy, xv_port,
-                            XInternAtom( p_dpy, attr_name, False ), i_sv );
-            return;
+            XvSetPortAttribute( p_display, i_xvport,
+                            XInternAtom( p_display, attr_name, False ), i_sv );
+            break;
         }
 
     } while( i_attrib > 0 );
+
+    if( p_attrib )
+        XFree( p_attrib );
 }
 #endif
+