All pastes #2046603 Raw Edit

Someone

public text v1 · immutable
#2046603 ·published 2011-04-15 00:12 UTC
rendered paste body
diff --git a/Makefile b/Makefile
index 2de75fb..fd4e503 100644
--- a/Makefile
+++ b/Makefile
@@ -590,6 +590,7 @@ SRCS_MPLAYER-$(TDFXVID)       += libvo/vo_tdfx_vid.c
 SRCS_MPLAYER-$(TGA)           += libvo/vo_tga.c
 SRCS_MPLAYER-$(V4L2)          += libvo/vo_v4l2.c
 SRCS_MPLAYER-$(V4L2)          += libao2/ao_v4l2.c
+SRCS_MPLAYER-$(VAAPI)         += libvo/vo_vaapi.c
 SRCS_MPLAYER-$(VDPAU)         += libvo/vo_vdpau.c
 SRCS_MPLAYER-$(VESA)          += libvo/gtf.c libvo/vo_vesa.c libvo/vesa_lvo.c
 
@@ -643,6 +644,7 @@ SRCS_MPLAYER = command.c \
                libao2/audio_out.c \
                libvo/aspect.c \
                libvo/geometry.c \
+               libvo/stats.c \
                libvo/old_vo_wrapper.c \
                libvo/spuenc.c \
                libvo/video_out.c \
                libvo/vo_mpegpes.c \
diff --git a/cfg-common.h b/cfg-common.h
index c6dfef0..a280890 100644
--- a/cfg-common.h
+++ b/cfg-common.h
@@ -521,6 +521,7 @@ const m_option_t common_opts[] = {
     {"vfm", &video_fm_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
     {"ac", &audio_codec_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
     {"vc", &video_codec_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL},
+    {"va", &video_hwaccel_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
 
     // postprocessing:
 #ifdef CONFIG_FFMPEG
diff --git a/codec-cfg.c b/codec-cfg.c
index 4db74c5..2f160fb 100644
--- a/codec-cfg.c
+++ b/codec-cfg.c
@@ -224,6 +224,13 @@ static const struct {
     {"VDPAU_VC1",IMGFMT_VDPAU_VC1},
     {"VDPAU_MPEG4",IMGFMT_VDPAU_MPEG4},
 
+    {"VAAPI_MPEG2", IMGFMT_VAAPI_MPEG2},
+    {"VAAPI_MPEG4", IMGFMT_VAAPI_MPEG4},
+    {"VAAPI_H263",  IMGFMT_VAAPI_H263},
+    {"VAAPI_H264",  IMGFMT_VAAPI_H264},
+    {"VAAPI_WMV3",  IMGFMT_VAAPI_WMV3},
+    {"VAAPI_VC1",   IMGFMT_VAAPI_VC1},
+
     {NULL,    0}
 };
 
diff --git a/configure b/configure
index 1e0f7fc..45e4574 100755
--- a/configure
+++ b/configure
@@ -401,8 +401,10 @@ Video output:
   --enable-dvb             enable DVB video output [autodetect]
   --enable-mga             enable mga_vid video output [autodetect]
   --enable-xmga            enable mga_vid X11 video output [autodetect]
+  --enable-xrender         enable Xrender video output [autodetect]
   --enable-xv              enable Xv video output [autodetect]
   --enable-xvmc            enable XvMC acceleration [disable]
+  --enable-vaapi           enable VA-API acceleration [disable]
   --enable-vdpau           enable VDPAU acceleration [autodetect]
   --enable-vm              enable XF86VidMode support [autodetect]
   --enable-xinerama        enable Xinerama support [autodetect]
@@ -506,6 +506,7 @@ Use these options if autodetection fails:
   --extra-libs-mplayer=FLAGS  extra linker flags for MPlayer
   --with-xvmclib=NAME         adapter-specific library name (e.g. XvMCNVIDIA)
 
+  --with-vaapi-prefix=PATH    prefix to VA-API include and lib directories
   --with-freetype-config=PATH path to freetype-config
   --with-sdl-config=PATH      path to sdl*-config
   --with-dvdnav-config=PATH   path to dvdnav-config
@@ -555,11 +555,15 @@ _prefix="/usr/local"
 ffmpeg=auto
 ffmpeg_internals=no
 _mplayer=yes
+_vaapi=auto
+_vaapi_glx=no
+_libgtop=auto
 _x11=auto
 _xshape=auto
 _xss=auto
 _dga1=auto
 _dga2=auto
+_xrender=auto
 _xv=auto
 _xvmc=no  #auto when complete
 _vdpau=auto
@@ -855,6 +863,9 @@ for ac_option do
     _xvmclib=$(echo $ac_option | cut -d '=' -f 2)
     ;;
 
+  --with-vaapi-prefix=*)
+    _vaapiprefix=$(echo $ac_option | cut -d '=' -f 2)
+    ;;
   --with-sdl-config=*)
     _sdlconfig=$(echo $ac_option | cut -d '=' -f 2)
     ;;
@@ -970,10 +981,14 @@ for ac_option do
   --disable-xshape)     _xshape=no      ;;
   --enable-xss)         _xss=yes        ;;
   --disable-xss)        _xss=no         ;;
+  --enable-xrender)     _xrender=yes    ;;
+  --disable-xrender)    _xrender=no     ;;
   --enable-xv)          _xv=yes         ;;
   --disable-xv)         _xv=no          ;;
   --enable-xvmc)        _xvmc=yes       ;;
   --disable-xvmc)       _xvmc=no        ;;
+  --enable-vaapi)       _vaapi=yes      ;;
+  --disable-vaapi)      _vaapi=no       ;;
   --enable-vdpau)       _vdpau=yes      ;;
   --disable-vdpau)      _vdpau=no       ;;
   --enable-sdl)         _sdl=yes        ;;
@@ -1068,6 +1083,8 @@ for ac_option do
   --disable-mad)        _mad=no         ;;
   --enable-libcdio)     _libcdio=yes    ;;
   --disable-libcdio)    _libcdio=no     ;;
+  --enable-libgtop)     _libgtop=yes    ;;
+  --disable-libgtop)    _libgtop=no     ;;
   --enable-libvorbis)   _libvorbis=yes  ;;
   --disable-libvorbis)  _libvorbis=no   ;;
   --enable-speex)       _speex=yes      ;;
@@ -3895,6 +3912,28 @@ fi
 echores "$_gettimeofday"
 
 
+echocheck "clock_gettime()"
+cat > $TMPC << EOF
+#include <time.h>
+int main(void) {
+    struct timespec t;
+    clock_gettime(CLOCK_REALTIME, &t);
+    return 0;
+}
+EOF
+_clock_gettime=no
+cc_check -lrt && _clock_gettime=yes
+if test "$_clock_gettime" = yes ; then
+  def_clock_gettime='#define HAVE_CLOCK_GETTIME 1'
+  extra_ldflags="$extra_ldflags -lrt"
+  _need_clock_gettime=no
+else
+  def_clock_gettime='#undef HAVE_CLOCK_GETTIME'
+  _need_clock_gettime=yes
+fi
+echores "$_clock_gettime"
+
+
 echocheck "glob()"
 _glob=no
 statement_check glob.h 'glob("filename", 0, 0, 0)' && _glob=yes
@@ -4265,7 +4304,7 @@ else
   novomodules="x11 $novomodules"
   res_comment="check if the dev(el) packages are installed"
   # disable stuff that depends on X
-  _xv=no ; _xvmc=no ; _xinerama=no ; _vm=no ; _xf86keysym=no ; _vdpau=no
+  _xv=no ; _xvmc=no ; _xinerama=no ; _vm=no ; _xf86keysym=no ; _vaapi=no ; _vdpau=no
 fi
 echores "$_x11"
 
@@ -4312,6 +4351,30 @@ else
 fi
 
 
+echocheck "Xrender"
+if test "$_xrender" = auto ; then
+  cat > $TMPC <<EOF
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+int main(void) {
+  (void) XRenderCreatePicture(0, 0, 0, 0, 0);
+  return 0; }
+EOF
+  _xrender=no
+  cc_check -lXrender && _xrender=yes
+fi
+
+if test "$_xrender" = yes ; then
+  def_xrender='#define CONFIG_XRENDER 1'
+  libs_mplayer="$libs_mplayer -lXrender"
+  vomodules="xrender $vomodules"
+else
+  def_xrender='#undef CONFIG_XRENDER'
+  novomodules="xrender $novomodules"
+fi
+echores "$_xrender"
+
+
 echocheck "Xv"
 if test "$_xv" = auto ; then
   _xv=no
@@ -4791,6 +4854,31 @@ echores "$_corevideo"
 fi #if darwin
 
 
+echocheck "libgtop"
+if test "$_libgtop" = auto ; then
+  _libgtop=no
+  if $_pkg_config --exists 'libgtop-2.0' ; then
+
+cat > $TMPC << EOF
+#include <glibtop/cpu.h>
+#include <glibtop/proctime.h>
+int main(void) { return 0; }
+EOF
+cc_check $($_pkg_config --libs --cflags libgtop-2.0) && tmp_run && _libgtop=yes
+
+  fi
+fi
+echores "$_libgtop"
+
+if test "$_libgtop" = yes ; then
+  def_libgtop='#define CONFIG_LIBGTOP 1'
+  libs_mplayer="$libs_mplayer $($_pkg_config --libs libgtop-2.0)"
+  extra_cflags="$extra_cflags $($_pkg_config --cflags libgtop-2.0)"
+else
+  def_libgtop='#define CONFIG_LIBGTOP 0'
+fi
+
+
 echocheck "PNG support"
 if test "$_png" = auto ; then
   _png=no
@@ -5152,6 +5240,23 @@ else
 fi
 echores "$_gl"
 
+echocheck "OpenGL utilities (GLU)"
+_glu=no
+if test "$_gl" = yes; then
+  cat > $TMPC << EOF
+#include <GL/glu.h>
+int main(void) {
+  gluPerspective(0.0, 0.0, 0.0, 0.0);
+  return 0;
+}
+EOF
+  cc_check -lGLU && _glu=yes
+fi
+if test "$_glu" = yes; then
+  libs_mplayer="$libs_mplayer -lGLU"
+fi
+echores "$_glu"
+
 
 echocheck "MatrixView"
 if test "$_gl" = no ; then
@@ -5719,6 +5824,57 @@ echores "$_dart"
 fi #if os2
 
 
+#########################
+# HARDWARE ACCELERATORS #
+#########################
+
+echocheck "VA-API"
+if test -n "$_vaapiprefix"; then
+  _vaapiinc="-I$_vaapiprefix/include"
+  _vaapilib="-L$_vaapiprefix/lib"
+fi
+if test "$_vaapi" = yes -o "$_vaapi" = auto; then
+  _vaapi=no
+  cat > $TMPC <<EOF
+#include <va/va_x11.h>
+int main(void) { (void) vaGetDisplay(0); return 0; }
+EOF
+  cc_check $_vaapiinc $_vaapilib -lva-x11 && _vaapi=yes
+fi
+
+if test "$_vaapi" = yes ; then
+  def_vaapi='#define CONFIG_VAAPI 1'
+  extra_cflags="$extra_cflags $_vaapiinc"
+  libs_mencoder="$libs_mencoder $_vaapilib -lva"
+  libs_mplayer="$libs_mplayer $_vaapilib -lva -lva-x11"
+  vomodules="vaapi $vomodules"
+else
+  def_vaapi='#define CONFIG_VAAPI 0'
+  novomodules="vaapi $novomodules"
+  libavhwaccels=`echo $libavhwaccels | sed -e "s/\(MPEG[124]\|H26[34]\|WMV3\|VC1\)_VAAPI_HWACCEL//g"`
+fi
+echores "$_vaapi"
+
+echocheck "VA-API (with GLX support)"
+if test "$_vaapi" = yes; then
+  _vaapi_glx=no
+  if test "$_gl" = "yes" -a "$_glu" = yes; then
+    cat > $TMPC <<EOF
+#include <va/va_glx.h>
+int main(void) { (void) vaGetDisplayGLX(0); return 0; }
+EOF
+    cc_check $_vaapiinc $_vaapilib -lva-glx && _vaapi_glx=yes
+  fi
+fi
+if test "$_vaapi_glx" = yes; then
+  def_vaapi_glx='#define CONFIG_VAAPI_GLX 1'
+  libs_mplayer="$libs_mplayer -lva-glx"
+else
+  def_vaapi_glx='#define CONFIG_VAAPI_GLX 0'
+fi
+echores "$_vaapi_glx"
+
+
 # set default CD/DVD devices
 if win32 || os2 ; then
   default_cdrom_device="D:"
@@ -6836,6 +6836,7 @@ TV_V4L1 = $_tv_v4l1
 TV_V4L2 = $_tv_v4l2
 UNRAR_EXEC = $_unrar_exec
 V4L2 = $_v4l2
+VAAPI = $_vaapi
 VCD = $_vcd
 VDPAU = $_vdpau
 VESA = $_vesa
@@ -8140,7 +8297,9 @@ CONFIG_MLIB     = $_mlib
 YASM       = $_yasm
 YASMFLAGS  = $YASMFLAGS
 
+CONFIG_VAAPI    = $_vaapi
 CONFIG_VDPAU    = $_vdpau
+CONFIG_XRENDER  = $_xrender
 CONFIG_XVMC     = $_xvmc
 CONFIG_ZLIB     = $_zlib
 
@@ -8254,6 +8413,7 @@ $def_winsock2_h
 
 
 /* system functions */
+$def_clock_gettime
 $def_gethostbyname2
 $def_gettimeofday
 $def_glob
@@ -6995,6 +6995,7 @@ $def_extern_asm
 $def_extern_prefix
 $def_iconv
 $def_kstat
+$def_libgtop
 $def_macosx_bundle
 $def_macosx_finder
 $def_named_asm_args
@@ -7197,6 +7197,8 @@ $def_tdfxfb
 $def_tdfxvid
 $def_tga
 $def_v4l2
+$def_vaapi
+$def_vaapi_glx
 $def_vdpau
 $def_vesa
 $def_vm
@@ -8538,6 +8701,7 @@ $def_xf86keysym
 $def_xinerama
 $def_xmga
 $def_xss
+$def_xrender
 $def_xv
 $def_xvmc
 $def_xvr100
diff --git a/etc/codecs.conf b/etc/codecs.conf
index d353bb5..e39a926 100644
--- a/etc/codecs.conf
+++ b/etc/codecs.conf
@@ -212,6 +212,7 @@ videocodec ffmpeg2
   fourcc slif ; SoftLab MPEG-2 I-frames Codec
   driver ffmpeg
   dll "mpeg2video"
+  out VAAPI_MPEG2
   out YV12,I420,IYUV
   out 422P,444P
 
@@ -941,6 +942,7 @@ videocodec ffwmv3
   fourcc WMV3,wmv3
   driver ffmpeg
   dll wmv3
+  out VAAPI_WMV3
   out YV12,I420,IYUV
 
 videocodec ffwmvp
@@ -967,6 +969,7 @@ videocodec ffvc1
   fourcc vc-1,VC-1
   driver ffmpeg
   dll vc1
+  out VAAPI_VC1
   out YV12,I420,IYUV
 
 videocodec ffvc1vdpau
@@ -989,6 +992,7 @@ videocodec ffh264
   format 0x10000005
   driver ffmpeg
   dll h264
+  out VAAPI_H264
   out YV12,I420,IYUV
 
 videocodec ffh264vdpau
@@ -1058,6 +1062,7 @@ videocodec ffodivx
   fourcc SIPP ; Samsung SHR-6040
   driver ffmpeg
   dll mpeg4 ;opendivx
+  out VAAPI_MPEG4
   out YV12,I420,IYUV
 
 videocodec ffodivxvdpau
@@ -1646,6 +1651,7 @@ videocodec ffh263
   fourcc VX1K     ; Agora Labs VX1000S H263
   driver ffmpeg
   dll h263
+  out VAAPI_H263
   out YV12,I420,IYUV
 
 videocodec ffzygo
diff --git a/fmt-conversion.c b/fmt-conversion.c
index 9e88100..bd32ce8 100644
--- a/fmt-conversion.c
+++ b/fmt-conversion.c
@@ -18,12 +18,14 @@
 
 #include "mp_msg.h"
 #include "libavutil/avutil.h"
+#include "libavcodec/avcodec.h"
 #include "libmpcodecs/img_format.h"
 #include "fmt-conversion.h"
 
 static const struct {
     int fmt;
     enum PixelFormat pix_fmt;
+    enum CodecID codec_id;
 } conversion_map[] = {
     {IMGFMT_ARGB, PIX_FMT_ARGB},
     {IMGFMT_BGRA, PIX_FMT_BGRA},
@@ -95,6 +97,17 @@ static const struct {
     {IMGFMT_VDPAU_WMV3,      PIX_FMT_VDPAU_WMV3},
     {IMGFMT_VDPAU_VC1,       PIX_FMT_VDPAU_VC1},
     {IMGFMT_VDPAU_MPEG4,     PIX_FMT_VDPAU_MPEG4},
+
+    /* VA-API formats */
+    {IMGFMT_VAAPI_MPEG2,     PIX_FMT_VAAPI_VLD,  CODEC_ID_MPEG2VIDEO},
+    {IMGFMT_VAAPI_MPEG2_IDCT,PIX_FMT_VAAPI_IDCT, CODEC_ID_MPEG2VIDEO},
+    {IMGFMT_VAAPI_MPEG2_MOCO,PIX_FMT_VAAPI_MOCO, CODEC_ID_MPEG2VIDEO},
+    {IMGFMT_VAAPI_MPEG4,     PIX_FMT_VAAPI_VLD,  CODEC_ID_MPEG4},
+    {IMGFMT_VAAPI_H263,      PIX_FMT_VAAPI_VLD,  CODEC_ID_H263},
+    {IMGFMT_VAAPI_H264,      PIX_FMT_VAAPI_VLD,  CODEC_ID_H264},
+    {IMGFMT_VAAPI_WMV3,      PIX_FMT_VAAPI_VLD,  CODEC_ID_WMV3},
+    {IMGFMT_VAAPI_VC1,       PIX_FMT_VAAPI_VLD,  CODEC_ID_VC1},
+
     {0, PIX_FMT_NONE}
 };
 
@@ -111,12 +124,14 @@ enum PixelFormat imgfmt2pixfmt(int fmt)
     return pix_fmt;
 }
 
-int pixfmt2imgfmt(enum PixelFormat pix_fmt)
+int pixfmt2imgfmt(enum PixelFormat pix_fmt, int codec_id)
 {
     int i;
     int fmt;
     for (i = 0; conversion_map[i].pix_fmt != PIX_FMT_NONE; i++)
-        if (conversion_map[i].pix_fmt == pix_fmt)
+        if (conversion_map[i].pix_fmt == pix_fmt &&
+            (conversion_map[i].codec_id == 0 ||
+             conversion_map[i].codec_id == codec_id))
             break;
     fmt = conversion_map[i].fmt;
     if (!fmt)
diff --git a/fmt-conversion.h b/fmt-conversion.h
index 9e133a8..962ca4e 100644
--- a/fmt-conversion.h	2011-03-22 16:20:04.000000000 +0100
+++ b/fmt-conversion.h	2011-04-06 21:00:34.561717643 +0200
@@ -23,6 +23,6 @@
 #include "libavutil/avutil.h"
 
 enum PixelFormat imgfmt2pixfmt(int fmt);
-int pixfmt2imgfmt(enum PixelFormat pix_fmt);
+int pixfmt2imgfmt(enum PixelFormat pix_fmt, int codec_id);
 
 #endif /* MPLAYER_FMT_CONVERSION_H */
diff --git a/libmpcodecs/dec_video.c b/libmpcodecs/dec_video.c
index 95249c5..172864b 100644
--- a/libmpcodecs/dec_video.c
+++ b/libmpcodecs/dec_video.c
@@ -53,7 +53,45 @@
 int field_dominance = -1;
 
 int divx_quality = 0;
+char *video_hwaccel_name=NULL;
 
+int get_video_hwaccel(void)
+{
+    static int video_hwaccel = -1;
+    if (video_hwaccel < 0) {
+        video_hwaccel = HWACCEL_NONE;
+        if (video_hwaccel_name) {
+            if (!strcmp(video_hwaccel_name,"xvmc"))
+                video_hwaccel = HWACCEL_XVMC;
+            else if (!strcmp(video_hwaccel_name,"vaapi"))
+                video_hwaccel = HWACCEL_VAAPI;
+            else if (!strcmp(video_hwaccel_name,"vdpau"))
+                video_hwaccel = HWACCEL_VDPAU;
+        }
+    }
+    return video_hwaccel;
+}
+
+const char *get_video_hwaccel_name(int hwaccel)
+{
+    switch (hwaccel) {
+    case HWACCEL_XVMC:  return "XvMC";
+    case HWACCEL_VAAPI: return "VA API";
+    case HWACCEL_VDPAU: return "VDPAU";
+    }
+    return NULL;
+}
+
+const char *get_video_hwaccel_short_name(int hwaccel)
+{
+    switch (hwaccel) {
+    case HWACCEL_XVMC:  return "xvmc";
+    case HWACCEL_VAAPI: return "vaapi";
+    case HWACCEL_VDPAU: return "vdpau";
+    }
+    return NULL;
+}
+
 int get_video_quality_max(sh_video_t *sh_video)
 {
     vf_instance_t *vf = sh_video->vfilter;
diff --git a/libmpcodecs/dec_video.h b/libmpcodecs/dec_video.h
index 4be4205..1a51635 100644
--- a/libmpcodecs/dec_video.h
+++ b/libmpcodecs/dec_video.h
@@ -21,6 +21,14 @@
 
 #include "libmpdemux/stheader.h"
 
+enum {
+  HWACCEL_NONE = 0,
+  HWACCEL_XVMC,
+  HWACCEL_VAAPI,
+  HWACCEL_VDPAU,
+  HWACCEL_COUNT
+};
+
 struct osd_state;
 
 // dec_video.c:
@@ -41,6 +49,11 @@ int set_rectangle(sh_video_t *sh_video, int param, int value);
 void resync_video_stream(sh_video_t *sh_video);
 int get_current_video_decoder_lag(sh_video_t *sh_video);
 
+int get_video_hwaccel(void);
+const char *get_video_hwaccel_name(int hwaccel);
+const char *get_video_hwaccel_short_name(int hwaccel);
+
 extern int divx_quality;
+extern char *video_hwaccel_name;
 
 #endif /* MPLAYER_DEC_VIDEO_H */
diff --git a/libmpcodecs/img_format.c b/libmpcodecs/img_format.c
index df47ce0..c3db07b 100644
--- a/libmpcodecs/img_format.c
+++ b/libmpcodecs/img_format.c
@@ -96,6 +96,14 @@ const char *vo_format_name(int format)
 	case IMGFMT_ZRMJPEGIB: return "Zoran MJPEG bottom field first";
 	case IMGFMT_XVMC_MOCO_MPEG2: return "MPEG1/2 Motion Compensation";
 	case IMGFMT_XVMC_IDCT_MPEG2: return "MPEG1/2 Motion Compensation and IDCT";
+	case IMGFMT_VAAPI_MPEG2: return "MPEG-2 VA-API Acceleration";
+	case IMGFMT_VAAPI_MPEG2_IDCT: return "MPEG-2 VA-API Acceleration (Motion Compensation and IDCT)";
+	case IMGFMT_VAAPI_MPEG2_MOCO: return "MPEG-2 VA-API Acceleration (Motion Compensation)";
+	case IMGFMT_VAAPI_MPEG4: return "MPEG-4 VA-API Acceleration";
+	case IMGFMT_VAAPI_H263:  return "H.263 VA-API Acceleration";
+	case IMGFMT_VAAPI_H264:  return "H.264 VA-API Acceleration";
+	case IMGFMT_VAAPI_WMV3:  return "WMV3 VA-API Acceleration";
+	case IMGFMT_VAAPI_VC1:   return "VC-1 VA-API Acceleration";
 	case IMGFMT_VDPAU_MPEG1: return "MPEG1 VDPAU acceleration";
 	case IMGFMT_VDPAU_MPEG2: return "MPEG2 VDPAU acceleration";
 	case IMGFMT_VDPAU_H264: return "H.264 VDPAU acceleration";
diff --git a/libmpcodecs/img_format.h b/libmpcodecs/img_format.h
index c95ed4d..403690e 100644
--- a/libmpcodecs/img_format.h
+++ b/libmpcodecs/img_format.h
@@ -182,6 +182,26 @@
 #define IMGFMT_XVMC_MOCO_MPEG2 (IMGFMT_XVMC|0x02)
 #define IMGFMT_XVMC_IDCT_MPEG2 (IMGFMT_XVMC|0x82)
 
+/* VA-API Formats */
+
+#define IMGFMT_VAAPI               0x56410000 /* 'VA'00 */
+#define IMGFMT_VAAPI_MASK          0xFFFF0000
+#define IMGFMT_IS_VAAPI(fmt)       (((fmt) & IMGFMT_VAAPI_MASK) == IMGFMT_VAAPI)
+#define IMGFMT_VAAPI_CODEC_MASK    0x000000F0
+#define IMGFMT_VAAPI_CODEC(fmt)    ((fmt) & IMGFMT_VAAPI_CODEC_MASK)
+#define IMGFMT_VAAPI_CODEC_MPEG2   (0x10)
+#define IMGFMT_VAAPI_CODEC_MPEG4   (0x20)
+#define IMGFMT_VAAPI_CODEC_H264    (0x30)
+#define IMGFMT_VAAPI_CODEC_VC1     (0x40)
+#define IMGFMT_VAAPI_MPEG2         (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2)
+#define IMGFMT_VAAPI_MPEG2_IDCT    (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|1)
+#define IMGFMT_VAAPI_MPEG2_MOCO    (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|2)
+#define IMGFMT_VAAPI_MPEG4         (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4)
+#define IMGFMT_VAAPI_H263          (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4|1)
+#define IMGFMT_VAAPI_H264          (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_H264)
+#define IMGFMT_VAAPI_VC1           (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1)
+#define IMGFMT_VAAPI_WMV3          (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1|1)
+
 // VDPAU specific format.
 #define IMGFMT_VDPAU               0x1DC80000
 #define IMGFMT_VDPAU_MASK          0xFFFF0000
@@ -193,7 +213,9 @@
 #define IMGFMT_VDPAU_VC1           (IMGFMT_VDPAU|0x05)
 #define IMGFMT_VDPAU_MPEG4         (IMGFMT_VDPAU|0x06)
 
-#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_XVMC(fmt))
+#define IMGFMT_IS_HWACCEL(fmt)     (IMGFMT_IS_VAAPI(fmt) || \
+                                    IMGFMT_IS_VDPAU(fmt) || \
+                                    IMGFMT_IS_XVMC(fmt))
 
 typedef struct {
     void* data;
diff --git a/libmpcodecs/vd.c b/libmpcodecs/vd.c
index bdec830..63a7df8 100644
--- a/libmpcodecs/vd.c
+++ b/libmpcodecs/vd.c
@@ -403,3 +403,12 @@ void mpcodecs_draw_slice(sh_video_t *sh, unsigned char **src, int *stride,
     if (vf->draw_slice)
         vf->draw_slice(vf, src, stride, w, h, x, y);
 }
+
+void *mpcodecs_get_hwaccel_context(sh_video_t *sh)
+{
+    void *ctx = NULL;
+    struct vf_instance *vf = sh->vfilter;
+    if (vf->control(vf, VFCTRL_GET_HWACCEL_CONTEXT, &ctx) == CONTROL_TRUE)
+        return ctx;
+    return NULL;
+}
diff --git a/libmpcodecs/vd.h b/libmpcodecs/vd.h
index 8f7c238..e655d60 100644
--- a/libmpcodecs/vd.h
+++ b/libmpcodecs/vd.h
@@ -64,6 +64,7 @@ extern const m_option_t xvid_dec_opts[];
 int mpcodecs_config_vo(sh_video_t *sh, int w, int h, unsigned int preferred_outfmt);
 mp_image_t* mpcodecs_get_image(sh_video_t *sh, int mp_imgtype, int mp_imgflag, int w, int h);
 void mpcodecs_draw_slice(sh_video_t *sh, unsigned char** src, int* stride, int w,int h, int x, int y);
+void *mpcodecs_get_hwaccel_context(sh_video_t *sh);
 
 #define VDFLAGS_DROPFRAME 3
 
diff --git a/libmpcodecs/vd_ffmpeg.c b/libmpcodecs/vd_ffmpeg.c
index 7c68a20..609ac41 100644
--- a/libmpcodecs/vd_ffmpeg.c
+++ b/libmpcodecs/vd_ffmpeg.c
@@ -33,6 +33,7 @@
 
 #include "vd_internal.h"
 #include "vd_ffmpeg.h"
+#include "dec_video.h"
 
 static const vd_info_t info = {
     "FFmpeg's libavcodec codec family",
@@ -232,7 +233,7 @@ static void set_format_params(struct AVCodecContext *avctx, enum PixelFormat fmt
     int imgfmt;
     if (fmt == PIX_FMT_NONE)
         return;
-    imgfmt = pixfmt2imgfmt(fmt);
+    imgfmt = pixfmt2imgfmt(fmt, avctx->codec_id);
     if (IMGFMT_IS_HWACCEL(imgfmt)) {
         sh_video_t *sh     = avctx->opaque;
         vd_ffmpeg_ctx *ctx = sh->context;
@@ -295,6 +296,12 @@ static int init(sh_video_t *sh){
     avctx->codec_type = CODEC_TYPE_VIDEO;
     avctx->codec_id = lavc_codec->id;
 
+#if CONFIG_VAAPI
+    if(get_video_hwaccel() == HWACCEL_VAAPI){
+        mp_msg(MSGT_DECVIDEO, MSGL_INFO, MSGTR_MPCODECS_VAAPIAcceleratedCodec);
+        avctx->get_format = get_format;
+    }
+#endif /* CONFIG_VAAPI */
 #if CONFIG_VDPAU
     if(lavc_codec->capabilities & CODEC_CAP_HWACCEL_VDPAU){
         avctx->get_format = get_format;
@@ -487,7 +494,7 @@ static void draw_slice(struct AVCodecContext *s,
                         const AVFrame *src, int offset[4],
                         int y, int type, int height){
     sh_video_t *sh = s->opaque;
-    uint8_t *source[MP_MAX_PLANES]= {src->data[0] + offset[0], src->data[1] + offset[1], src->data[2] + offset[2]};
+    uint8_t *source[MP_MAX_PLANES]= {src->data[0] + offset[0], src->data[1] + offset[1], src->data[2] + offset[2], src->data[3] + offset[3]};
     int strides[MP_MAX_PLANES] = {src->linesize[0], src->linesize[1], src->linesize[2]};
 #if 0
     int start=0, i;
@@ -571,9 +578,10 @@ static int init_vo(sh_video_t *sh, enum PixelFormat pix_fmt){
         sh->disp_w = width;
         sh->disp_h = height;
         ctx->pix_fmt = pix_fmt;
-        ctx->best_csp = pixfmt2imgfmt(pix_fmt);
+        ctx->best_csp = pixfmt2imgfmt(pix_fmt, avctx->codec_id);
         if (!mpcodecs_config_vo(sh, sh->disp_w, sh->disp_h, ctx->best_csp))
             return -1;
+        avctx->hwaccel_context = mpcodecs_get_hwaccel_context(sh);
         ctx->vo_initialized = 1;
     }
     return 0;
@@ -972,24 +980,62 @@ static mp_image_t *decode(sh_video_t *sh, void *data, int len, int flags){
     return mpi;
 }
 
-#if CONFIG_XVMC || CONFIG_VDPAU
+#if CONFIG_XVMC || CONFIG_VAAPI || CONFIG_VDPAU
+static inline int is_hwaccel_format(int imgfmt)
+{
+    switch (get_video_hwaccel()) {
+    case HWACCEL_VAAPI: return IMGFMT_IS_VAAPI(imgfmt) != 0;
+    case HWACCEL_VDPAU: return IMGFMT_IS_VDPAU(imgfmt) != 0;
+    case HWACCEL_XVMC:  return IMGFMT_IS_XVMC(imgfmt)  != 0;
+    }
+    return 0;
+}
+
+static int query_format(sh_video_t *sh, int fmt)
+{
+    vd_ffmpeg_ctx * const ctx = sh->context;
+    AVCodecContext * const avctx = ctx->avctx;
+    int r, width, height;
+    /* XXX: some codecs have not initialized width and height yet at
+       this point, so we are faking the dimensions so that init_vo()
+       doesn't fail because of 0x0 size */
+    if ((width = avctx->width) == 0)
+        avctx->width = 64;
+    if ((height = avctx->height) == 0)
+        avctx->height = 64;
+    r = init_vo(sh, fmt);
+    avctx->width = width;
+    avctx->height = height;
+    return r;
+}
+
 static enum PixelFormat get_format(struct AVCodecContext *avctx,
-                                    const enum PixelFormat *fmt){
-    enum PixelFormat selected_format;
+                                   const enum PixelFormat *fmt){
+    enum PixelFormat selected_format = PIX_FMT_NONE;
     int imgfmt;
     sh_video_t *sh = avctx->opaque;
-    int i;
-
-    for(i=0;fmt[i]!=PIX_FMT_NONE;i++){
-        imgfmt = pixfmt2imgfmt(fmt[i]);
-        if(!IMGFMT_IS_HWACCEL(imgfmt)) continue;
-        mp_msg(MSGT_DECVIDEO, MSGL_INFO, MSGTR_MPCODECS_TryingPixfmt, i);
-        if(init_vo(sh, fmt[i]) >= 0) {
-            break;
+    int i, try_hwaccel;
+
+    for (try_hwaccel = 1; try_hwaccel >= 0; --try_hwaccel) {
+        for (i = 0; fmt[i] != PIX_FMT_NONE; i++) {
+            imgfmt = pixfmt2imgfmt(fmt[i], avctx->codec_id);
+            if ((try_hwaccel ^ is_hwaccel_format(imgfmt)) != 0)
+                continue;
+            mp_msg(MSGT_DECVIDEO, MSGL_INFO, MSGTR_MPCODECS_TryingPixfmt, i);
+            if (query_format(sh, fmt[i]) >= 0) {
+                if (try_hwaccel) {
+                    /* don't allow format conversion for HW acceleration */
+                    if (sh->codec->outfmt[sh->outfmtidx] != imgfmt)
+                        continue;
+                }
+                selected_format = fmt[i];
+                break;
+            }
         }
+        if (selected_format != PIX_FMT_NONE)
+            break;
     }
-    selected_format = fmt[i];
     set_format_params(avctx, selected_format);
     return selected_format;
 }
-#endif /* CONFIG_XVMC || CONFIG_VDPAU */
+#endif /* CONFIG_XVMC || CONFIG_VAAPI || CONFIG_VDPAU */
diff --git a/libmpcodecs/vf.h b/libmpcodecs/vf.h
index 3619e69..fd869b7 100644
--- a/libmpcodecs/vf.h
+++ b/libmpcodecs/vf.h
@@ -111,6 +111,7 @@ typedef struct vf_seteq_s
 #define VFCTRL_REDRAW_OSD  21 /* Change user-visible OSD immediately */
 #define VFCTRL_SET_YUV_COLORSPACE 22
 #define VFCTRL_GET_YUV_COLORSPACE 23
+#define VFCTRL_GET_HWACCEL_CONTEXT 24 /* Get HW accelerator context */
 
 #include "vfcap.h"
 
diff --git a/libmpcodecs/vf_vo.c b/libmpcodecs/vf_vo.c
index b598214..da9e930 100644
--- a/libmpcodecs/vf_vo.c
+++ b/libmpcodecs/vf_vo.c
@@ -138,6 +138,12 @@ static int control(struct vf_instance *vf, int request, void* data)
         return vo_control(video_out, VOCTRL_DRAW_EOSD, &images) == VO_TRUE;
     }
 #endif
+    case VFCTRL_GET_HWACCEL_CONTEXT:
+    {
+        if(!video_out) return CONTROL_FALSE; // vo not configured?
+        return(video_out->control(VOCTRL_GET_HWACCEL_CONTEXT, data)
+               == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
+    }
     }
     return CONTROL_UNKNOWN;
 }
diff --git a/libvo/gl_common.c b/libvo/gl_common.c
index 116cebb..8f344f6 100644
--- a/libvo/gl_common.c
+++ b/libvo/gl_common.c
@@ -141,6 +141,11 @@ void* (GLAPIENTRY *mpglAllocateMemoryMESA)(void *, int, size_t, float, float, fl
 void (GLAPIENTRY *mpglFreeMemoryMESA)(void *, int, void *);
 /** \} */ // end of glextfunctions group
 
+void (GLAPIENTRY *mpglXBindTexImage)(Display *, GLXDrawable, int, const int *);
+void (GLAPIENTRY *mpglXReleaseTexImage)(Display *, GLXDrawable, int);
+GLXPixmap (GLAPIENTRY *mpglXCreatePixmap)(Display *, GLXFBConfig, Pixmap, const int *);
+void (GLAPIENTRY *mpglXDestroyPixmap)(Display *, GLXPixmap);
+
 //! \defgroup glgeneral OpenGL general helper functions
 
 //! \defgroup glcontext OpenGL context management helper functions
@@ -470,6 +475,10 @@ static const extfunc_desc_t extfuncs[] = {
   {&mpglTexImage3D, NULL, {"glTexImage3D", NULL}},
   {&mpglAllocateMemoryMESA, "GLX_MESA_allocate_memory", {"glXAllocateMemoryMESA", NULL}},
   {&mpglFreeMemoryMESA, "GLX_MESA_allocate_memory", {"glXFreeMemoryMESA", NULL}},
+  {&mpglXBindTexImage, "GLX_EXT_texture_from_pixmap", {"glXBindTexImageEXT", NULL}},
+  {&mpglXReleaseTexImage, "GLX_EXT_texture_from_pixmap", {"glXReleaseTexImageEXT", NULL}},
+  {&mpglXCreatePixmap, "GLX_EXT_texture_from_pixmap", {"glXCreatePixmap", NULL}},
+  {&mpglXDestroyPixmap, "GLX_EXT_texture_from_pixmap", {"glXDestroyPixmap", NULL}},
   {NULL}
 };
 
diff --git a/libvo/gl_common.h b/libvo/gl_common.h
index d44e96f..fd8677b 100644
--- a/libvo/gl_common.h
+++ b/libvo/gl_common.h
@@ -510,4 +510,9 @@ extern void (GLAPIENTRY *mpglTexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei
 extern void* (GLAPIENTRY *mpglAllocateMemoryMESA)(void *, int, size_t, float, float, float);
 extern void (GLAPIENTRY *mpglFreeMemoryMESA)(void *, int, void *);
 
+extern void (GLAPIENTRY *mpglXBindTexImage)(Display *, GLXDrawable, int, const int *);
+extern void (GLAPIENTRY *mpglXReleaseTexImage)(Display *, GLXDrawable, int);
+extern GLXPixmap (GLAPIENTRY *mpglXCreatePixmap)(Display *, GLXFBConfig, Pixmap, const int *);
+extern void (GLAPIENTRY *mpglXDestroyPixmap)(Display *, GLXPixmap);
+
 #endif /* MPLAYER_GL_COMMON_H */
diff --git a/libvo/stats.c b/libvo/stats.c
new file mode 100644
index 0000000..c4ff0ca
--- /dev/null
+++ b/libvo/stats.c
@@ -0,0 +1,217 @@
+#include "config.h"
+#include "stats.h"
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+#include <inttypes.h>
+
+#if CONFIG_LIBGTOP
+#include <glibtop/cpu.h>
+#include <glibtop/proctime.h>
+#include <glibtop/procstate.h>
+#endif
+
+// Process statistics
+struct proc_stats {
+    uint64_t utime;
+    uint64_t stime;
+    uint64_t cutime;
+    uint64_t cstime;
+    uint64_t frequency;
+    uint64_t cpu_time;
+    uint64_t start_time;
+    uint64_t current_time;
+};
+
+// Get current process stats
+static int get_proc_stats(struct proc_stats *pstats);
+
+void stats_init(void)
+{
+#if CONFIG_LIBGTOP
+    glibtop_init();
+#endif
+}
+
+void stats_exit(void)
+{
+#if CONFIG_LIBGTOP
+    glibtop_close();
+#endif
+}
+
+// Get CPU frequency
+unsigned int get_cpu_frequency(void)
+{
+    unsigned int freq = 0;
+#if defined __linux__
+    {
+        FILE *proc_file = fopen("/proc/cpuinfo", "r");
+        if (proc_file) {
+            char line[256];
+            char *old_locale = setlocale(LC_NUMERIC, NULL);
+            setlocale(LC_NUMERIC, "C");
+            while(fgets(line, sizeof(line), proc_file)) {
+                float f;
+                int len = strlen(line);
+                if (len == 0)
+                    continue;
+                line[len - 1] = 0;
+                if (sscanf(line, "cpu MHz : %f", &f) == 1)
+                    freq = (unsigned int)f;
+            }
+            setlocale(LC_NUMERIC, old_locale);
+            fclose(proc_file);
+        }
+    }
+#endif
+    return freq;
+}
+
+// Get CPU usage in percent
+static float get_cpu_usage_1(void)
+{
+    static struct proc_stats prev_stats;
+    struct proc_stats curr_stats;
+    uint64_t prev_proc_time = 0, curr_proc_time = 0;
+    float pcpu = 0.0f;
+
+    if (get_proc_stats(&curr_stats) == 0) {
+        prev_proc_time += prev_stats.utime;
+        prev_proc_time += prev_stats.stime;
+        prev_proc_time += prev_stats.cutime;
+        prev_proc_time += prev_stats.cstime;
+        curr_proc_time += curr_stats.utime;
+        curr_proc_time += curr_stats.stime;
+        curr_proc_time += curr_stats.cutime;
+        curr_proc_time += curr_stats.cstime;
+        if (prev_stats.start_time > 0)
+            pcpu = 100.0 * ((float)(curr_proc_time - prev_proc_time) /
+                            (float)(curr_stats.cpu_time - prev_stats.cpu_time));
+        prev_stats = curr_stats;
+    }
+    return pcpu;
+}
+
+float get_cpu_usage(enum CpuUsageType type)
+{
+    static float pcpu_total = 0.0;
+    static unsigned int n_samples;
+    float pcpu;
+
+    pcpu        = get_cpu_usage_1();
+    pcpu_total += pcpu / 100.0;
+    ++n_samples;
+
+    if (type == CPU_USAGE_AVERAGE)
+        pcpu = 100.0 * (pcpu_total / n_samples);
+    return pcpu;
+}
+
+// For ELF executable, notes are pushed before environment and args
+static int find_elf_note(unsigned long match, unsigned long *pval)
+{
+    unsigned long *ep = (unsigned long *)__environ;
+    while (*ep++);
+    for (; *ep != 0; ep += 2) {
+        if (ep[0] == match) {
+            *pval = ep[1];
+            return 0;
+        }
+    }
+    return -1;
+}
+
+#ifndef AT_CLKTCK
+#define AT_CLKTCK 17
+#endif
+
+// Get current process stats
+int get_proc_stats(struct proc_stats *pstats)
+{
+    int error = -1;
+    char line[256], *str, *end;
+    char vc;
+    int vi;
+    unsigned long vul;
+    unsigned long long vull;
+    float vf;
+#if defined __linux__
+    {
+        FILE *proc_file = fopen("/proc/self/stat", "r");
+        if (proc_file) {
+            if (fgets(line, sizeof(line), proc_file)) {
+                unsigned long utime, stime, cutime, cstime, start_time;
+                str = strrchr(line, ')');
+                if (str && sscanf(str + 2,
+                                  "%c "
+                                  "%d %d %d %d %d "
+                                  "%lu %lu %lu %lu %lu %lu %lu "
+                                  "%ld %ld %ld %ld %ld %ld "
+                                  "%lu %lu ",
+                                  &vc,
+                                  &vi, &vi, &vi, &vi, &vi,
+                                  &vul, &vul, &vul, &vul, &vul, &utime, &stime,
+                                  &cutime, &cstime, &vul, &vul, &vul, &vul,
+                                  &start_time, &vul) == 21) {
+                    pstats->utime      = utime;
+                    pstats->stime      = stime;
+                    pstats->cutime     = cutime;
+                    pstats->cstime     = cstime;
+                    pstats->start_time = start_time;
+                    error = 0;
+                }
+            }
+            fclose(proc_file);
+        }
+        if (error)
+            return error;
+        error = -1;
+
+        if (find_elf_note(AT_CLKTCK, &vul) == 0) {
+            pstats->frequency = vul;
+            error = 0;
+        }
+        if (error)
+            return error;
+        error = -1;
+
+        proc_file = fopen("/proc/uptime", "r");
+        if (proc_file) {
+            if (fgets(line, sizeof(line), proc_file)) {
+                char *old_locale = setlocale(LC_NUMERIC, NULL);
+                setlocale(LC_NUMERIC, "C");
+                if (sscanf(line, "%f", &vf) == 1) {
+                    pstats->cpu_time = (uint64_t)(vf * (float)pstats->frequency);
+                    error = 0;
+                }
+                setlocale(LC_NUMERIC, old_locale);
+            }
+            fclose(proc_file);
+        }
+    }
+#elif CONFIG_LIBGTOP
+    {
+        glibtop_cpu cpu;
+        glibtop_proc_time proc_time;
+        glibtop_proc_state proc_state;
+
+        glibtop_get_cpu(&cpu);
+        glibtop_get_proc_state(&proc_state, getpid());
+        pstats->cpu_time   = cpu.xcpu_total[proc_state.processor];
+
+        glibtop_get_proc_time(&proc_time, getpid());
+        pstats->utime      = proc_time.utime;
+        pstats->stime      = proc_time.stime;
+        pstats->cutime     = proc_time.cutime;
+        pstats->cstime     = proc_time.cstime;
+        pstats->start_time = proc_time.start_time;
+        pstats->frequency  = proc_time.frequency;
+
+        error = 0;
+    }
+#endif
+    return error;
+}
diff --git a/libvo/stats.h b/libvo/stats.h
new file mode 100644
index 0000000..62e7412
--- /dev/null
+++ b/libvo/stats.h
@@ -0,0 +1,21 @@
+#ifndef MPLAYER_STATS_H
+#define MPLAYER_STATS_H
+
+#include <stdint.h>
+
+void stats_init(void);
+void stats_exit(void);
+
+/// CPU usage model
+enum CpuUsageType {
+    CPU_USAGE_QUANTUM = 1, ///< CPU usage since the last call to cpu_get_usage()
+    CPU_USAGE_AVERAGE      ///< CPU usage average'd since program start
+};
+
+/// Get CPU frequency
+unsigned int get_cpu_frequency(void);
+
+/// Get CPU usage in percent
+float get_cpu_usage(enum CpuUsageType type);
+
+#endif /* MPLAYER_STATS_H */
diff --git a/libvo/video_out.c b/libvo/video_out.c
index 83f2a24..d22d214 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -94,6 +94,7 @@ extern const vo_functions_t video_out_mga;
 extern struct vo_driver video_out_x11;
 extern struct vo_driver video_out_xover;
 extern struct vo_driver video_out_xvmc;
+extern struct vo_driver video_out_vaapi;
 extern struct vo_driver video_out_vdpau;
 extern struct vo_driver video_out_xv;
 extern struct vo_driver video_out_gl_nosw;
@@ -291,6 +292,9 @@ const vo_functions_t* const video_out_drivers[] =
 #ifdef CONFIG_MD5SUM
         &video_out_md5sum,
 #endif
+#if CONFIG_VAAPI
+        &video_out_vaapi,
+#endif
         NULL
 };
 
diff --git a/libvo/video_out.h b/libvo/video_out.h
index 5fde30d..755da3a 100644
--- a/libvo/video_out.h
+++ b/libvo/video_out.h
@@ -90,6 +90,10 @@ typedef struct mp_eosd_res {
 #define VOCTRL_SET_YUV_COLORSPACE 33
 #define VOCTRL_GET_YUV_COLORSPACE 34
 
+// Return current HW acceleration context
+// void *get_hwaccel_context(void);
+#define VOCTRL_GET_HWACCEL_CONTEXT 35
+
 // Vo can be used by xover
 #define VOCTRL_XOVERLAY_SUPPORT 22
 
diff --git a/libvo/vo_vaapi.c b/libvo/vo_vaapi.c
new file mode 100644
index 0000000..9d46485
--- /dev/null
+++ b/libvo/vo_vaapi.c
@@ -0,0 +1,2737 @@
+/*
+ * VA API output module
+ *
+ * Copyright (C) 2008-2009 Splitted-Desktop Systems
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+#include "subopt-helper.h"
+#include "video_out.h"
+#include "video_out_internal.h"
+#include "fastmemcpy.h"
+#include "sub/sub.h"
+#include "sub/eosd.h"
+#include "x11_common.h"
+#include "libavutil/common.h"
+#include "libavcodec/vaapi.h"
+#include "gui/interface.h"
+#include "stats.h"
+#include <stdarg.h>
+
+#if CONFIG_GL
+#include "gl_common.h"
+#include <GL/glu.h>
+#include <GL/glx.h>
+#endif
+
+#include <assert.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <va/va_x11.h>
+#if CONFIG_VAAPI_GLX
+#include <va/va_glx.h>
+#endif
+
+#if CONFIG_XRENDER
+#include <X11/extensions/Xrender.h>
+#endif
+
+/* Compatibility glue with upstream libva */
+#ifndef VA_SDS_VERSION
+#define VA_SDS_VERSION          0
+#endif
+
+/* Compatibility glue with VA-API >= 0.30 */
+#ifndef VA_INVALID_ID
+#define VA_INVALID_ID           0xffffffff
+#endif
+#ifndef VA_FOURCC
+#define VA_FOURCC(ch0, ch1, ch2, ch3)           \
+    ((uint32_t)(uint8_t)(ch0) |                 \
+     ((uint32_t)(uint8_t)(ch1) << 8) |          \
+     ((uint32_t)(uint8_t)(ch2) << 16) |         \
+     ((uint32_t)(uint8_t)(ch3) << 24 ))
+#endif
+#if defined VA_SRC_BT601 && defined VA_SRC_BT709
+# define USE_VAAPI_COLORSPACE 1
+#else
+# define USE_VAAPI_COLORSPACE 0
+#endif
+
+/* Defined to 1 if VA/GLX 'bind' API is available */
+#define USE_VAAPI_GLX_BIND                                \
+    (VA_MAJOR_VERSION == 0 &&                             \
+     ((VA_MINOR_VERSION == 30 &&                          \
+       VA_MICRO_VERSION == 4 && VA_SDS_VERSION >= 5) ||   \
+      (VA_MINOR_VERSION == 31 &&                          \
+       VA_MICRO_VERSION == 0 && VA_SDS_VERSION >= 1 && VA_SDS_VERSION < 5)))
+
+/* Compatibility glue with VA-API >= 0.31 */
+#if defined VA_CHECK_VERSION
+#if VA_CHECK_VERSION(0,31,0)
+#define vaPutImage2             vaPutImage
+#define vaAssociateSubpicture2  vaAssociateSubpicture
+#endif
+#endif
+
+/* Compatibility glue with VA-API >= 0.31.1 */
+#ifndef VA_SRC_SMPTE_240
+#define VA_SRC_SMPTE_240        0x00000040
+#endif
+#if defined VA_FILTER_SCALING_MASK
+# define USE_VAAPI_SCALING 1
+#else
+# define USE_VAAPI_SCALING 0
+#endif
+
+static vo_info_t info = {
+    "VA API with X11",
+    "vaapi",
+    "Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>",
+    ""
+};
+
+const LIBVO_EXTERN(vaapi)
+
+/* Numbers of video surfaces */
+#define MAX_OUTPUT_SURFACES       2 /* Maintain synchronisation points in flip_page() */
+#define MAX_VIDEO_SURFACES       21 /* Maintain free surfaces in a queue (use least-recently-used) */
+#define NUM_VIDEO_SURFACES_MPEG2  3 /* 1 decode frame, up to  2 references */
+#define NUM_VIDEO_SURFACES_MPEG4  3 /* 1 decode frame, up to  2 references */
+#define NUM_VIDEO_SURFACES_H264  21 /* 1 decode frame, up to 20 references */
+#define NUM_VIDEO_SURFACES_VC1    3 /* 1 decode frame, up to  2 references */
+
+static void ensure_osd(void);
+static int reset_xrender_specific(void);
+
+typedef void (*draw_alpha_func)(int x0, int y0, int w, int h,
+                                unsigned char *src, unsigned char *srca,
+                                int stride);
+
+typedef void (*eosd_draw_alpha_func)(unsigned char *src,
+                                     int src_w, int src_h, int src_stride,
+                                     int dst_x, int dst_y,
+                                     uint32_t color);
+
+struct vaapi_surface {
+    VASurfaceID id;
+    VAImage     image;
+    int         is_bound; /* Flag: image bound to the surface? */
+};
+
+struct vaapi_equalizer {
+    VADisplayAttribute brightness;
+    VADisplayAttribute contrast;
+    VADisplayAttribute hue;
+    VADisplayAttribute saturation;
+};
+
+static int                      g_is_visible;
+static int                      g_is_paused;
+static uint32_t                 g_image_width;
+static uint32_t                 g_image_height;
+static uint32_t                 g_image_format;
+static uint32_t                 g_image_fields;
+static Pixmap                   g_image_pixmap;
+static struct vo_rect           g_output_rect;
+static struct vaapi_surface    *g_output_surfaces[MAX_OUTPUT_SURFACES];
+static unsigned int             g_output_surface;
+static int                      g_deint;
+static int                      g_deint_type;
+static int                      g_colorspace;
+static unsigned int             g_scaling;
+
+static int                      gl_enabled;
+static int                      gl_use_tfp;
+#if CONFIG_GL
+static MPGLContext              gl_context;
+static int                      gl_binding;
+static int                      gl_reflect;
+static int                      gl_finish;
+static GLuint                   gl_texture;
+static GLuint                   gl_font_base;
+static Pixmap                   gl_pixmap;
+static int                      gl_visual_attr[] = {
+    GLX_RGBA,
+    GLX_RED_SIZE, 1,
+    GLX_GREEN_SIZE, 1,
+    GLX_BLUE_SIZE, 1,
+    GLX_DOUBLEBUFFER,
+    GL_NONE
+};
+#endif
+
+#if CONFIG_VAAPI_GLX
+static void                    *gl_surface;
+#endif
+
+static int                      xr_enabled;
+#if CONFIG_XRENDER
+static Picture                  xr_video_picture;
+static Picture                  xr_window_picture;
+#endif
+
+static struct vaapi_context    *va_context;
+static VAProfile               *va_profiles;
+static int                      va_num_profiles;
+static VAEntrypoint            *va_entrypoints;
+static int                      va_num_entrypoints;
+static VASurfaceID             *va_surface_ids;
+static int                      va_num_surfaces;
+static struct vaapi_surface   **va_free_surfaces;
+static int                      va_free_surfaces_head_index;
+static int                      va_free_surfaces_tail_index;
+static VAImageFormat           *va_image_formats;
+static int                      va_num_image_formats;
+static VAImageFormat           *va_subpic_formats;
+static unsigned int            *va_subpic_flags;
+static int                      va_num_subpic_formats;
+static VAImage                  va_osd_image;
+static uint8_t                 *va_osd_image_data;
+static VASubpictureID           va_osd_subpicture;
+static int                      va_osd_associated;
+static draw_alpha_func          va_osd_draw_alpha;
+static uint8_t                 *va_osd_palette;
+static struct vaapi_equalizer   va_equalizer;
+static VAImage                  va_eosd_image;
+static uint8_t                 *va_eosd_image_data;
+static VASubpictureID           va_eosd_subpicture;
+static int                      va_eosd_associated;
+static eosd_draw_alpha_func     va_eosd_draw_alpha;
+
+///< Flag: direct surface mapping: use mpi->number to select free VA surface?
+static int                      va_dm;
+
+///< Flag: gather run-time statistics (CPU usage, frequency)
+static int                      cpu_stats;
+static unsigned int             cpu_frequency;
+static float                    cpu_usage;
+
+// X error trap
+static int x11_error_code = 0;
+static int (*old_error_handler)(Display *, XErrorEvent *);
+
+static int error_handler(Display *dpy, XErrorEvent *error)
+{
+    x11_error_code = error->error_code;
+    return 0;
+}
+
+static void x11_trap_errors(void)
+{
+    x11_error_code    = 0;
+    old_error_handler = XSetErrorHandler(error_handler);
+}
+
+static int x11_untrap_errors(void)
+{
+    XSetErrorHandler(old_error_handler);
+    return x11_error_code;
+}
+
+static int check_status(VAStatus status, const char *msg)
+{
+    if (status != VA_STATUS_SUCCESS) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] %s: %s\n", msg, vaErrorStr(status));
+        return 0;
+    }
+    return 1;
+}
+
+static const char *string_of_VAImageFormat(VAImageFormat *imgfmt)
+{
+    static char str[5];
+    str[0] = imgfmt->fourcc;
+    str[1] = imgfmt->fourcc >> 8;
+    str[2] = imgfmt->fourcc >> 16;
+    str[3] = imgfmt->fourcc >> 24;
+    str[4] = '\0';
+    return str;
+}
+
+static const char *string_of_VAProfile(VAProfile profile)
+{
+    switch (profile) {
+#define PROFILE(profile) \
+        case VAProfile##profile: return "VAProfile" #profile
+        PROFILE(MPEG2Simple);
+        PROFILE(MPEG2Main);
+        PROFILE(MPEG4Simple);
+        PROFILE(MPEG4AdvancedSimple);
+        PROFILE(MPEG4Main);
+        PROFILE(H264Baseline);
+        PROFILE(H264Main);
+        PROFILE(H264High);
+        PROFILE(VC1Simple);
+        PROFILE(VC1Main);
+        PROFILE(VC1Advanced);
+#undef PROFILE
+    }
+    return "<unknown>";
+}
+
+static const char *string_of_VAEntrypoint(VAEntrypoint entrypoint)
+{
+    switch (entrypoint) {
+#define ENTRYPOINT(entrypoint) \
+        case VAEntrypoint##entrypoint: return "VAEntrypoint" #entrypoint
+        ENTRYPOINT(VLD);
+        ENTRYPOINT(IZZ);
+        ENTRYPOINT(IDCT);
+        ENTRYPOINT(MoComp);
+        ENTRYPOINT(Deblocking);
+#undef ENTRYPOINT
+    }
+    return "<unknown>";
+}
+
+static int has_profile(VAProfile profile)
+{
+    if (va_profiles && va_num_profiles > 0) {
+        int i;
+        for (i = 0; i < va_num_profiles; i++) {
+            if (va_profiles[i] == profile)
+                return 1;
+        }
+    }
+    return 0;
+}
+
+static int VAProfile_from_imgfmt(uint32_t format)
+{
+    static const int mpeg2_profiles[] =
+        { VAProfileMPEG2Main, VAProfileMPEG2Simple, -1 };
+    static const int mpeg4_profiles[] =
+        { VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple, VAProfileMPEG4Simple, -1 };
+    static const int h264_profiles[] =
+        { VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline, -1 };
+    static const int wmv3_profiles[] =
+        { VAProfileVC1Main, VAProfileVC1Simple, -1 };
+    static const int vc1_profiles[] =
+        { VAProfileVC1Advanced, -1 };
+
+    const int *profiles = NULL;
+    switch (IMGFMT_VAAPI_CODEC(format)) {
+    case IMGFMT_VAAPI_CODEC_MPEG2:
+        profiles = mpeg2_profiles;
+        break;
+    case IMGFMT_VAAPI_CODEC_MPEG4:
+        profiles = mpeg4_profiles;
+        break;
+    case IMGFMT_VAAPI_CODEC_H264:
+        profiles = h264_profiles;
+        break;
+    case IMGFMT_VAAPI_CODEC_VC1:
+        switch (format) {
+        case IMGFMT_VAAPI_WMV3:
+            profiles = wmv3_profiles;
+            break;
+        case IMGFMT_VAAPI_VC1:
+            profiles = vc1_profiles;
+            break;
+        }
+        break;
+    }
+
+    if (profiles) {
+        for (int i = 0; profiles[i] != -1; i++) {
+            if (has_profile(profiles[i]))
+                return profiles[i];
+        }
+    }
+    return -1;
+}
+
+static int has_entrypoint(VAEntrypoint entrypoint)
+{
+    if (va_entrypoints && va_num_entrypoints > 0) {
+        int i;
+        for (i = 0; i < va_num_entrypoints; i++) {
+            if (va_entrypoints[i] == entrypoint)
+                return 1;
+        }
+    }
+    return 0;
+}
+
+static int VAEntrypoint_from_imgfmt(uint32_t format)
+{
+    int entrypoint = 0;
+    switch (format) {
+    case IMGFMT_VAAPI_MPEG2:
+    case IMGFMT_VAAPI_MPEG4:
+    case IMGFMT_VAAPI_H263:
+    case IMGFMT_VAAPI_H264:
+    case IMGFMT_VAAPI_WMV3:
+    case IMGFMT_VAAPI_VC1:
+        entrypoint = VAEntrypointVLD;
+        break;
+    case IMGFMT_VAAPI_MPEG2_IDCT:
+        entrypoint = VAEntrypointIDCT;
+        break;
+    case IMGFMT_VAAPI_MPEG2_MOCO:
+        entrypoint = VAEntrypointMoComp;
+        break;
+    }
+
+    if (entrypoint)
+        return has_entrypoint(entrypoint);
+
+    return -1;
+}
+
+static VAImageFormat *find_image_format(uint32_t fourcc)
+{
+    if (va_image_formats && va_num_image_formats > 0) {
+        int i;
+        for (i = 0; i < va_num_image_formats; i++) {
+            if (va_image_formats[i].fourcc == fourcc)
+                return &va_image_formats[i];
+        }
+    }
+    return NULL;
+}
+
+static VAImageFormat *VAImageFormat_from_imgfmt(uint32_t format)
+{
+    uint32_t fourcc = 0;
+
+    switch (format) {
+    case IMGFMT_NV12: fourcc = VA_FOURCC('N','V','1','2'); break;
+    case IMGFMT_YV12: fourcc = VA_FOURCC('Y','V','1','2'); break;
+    case IMGFMT_I420: fourcc = VA_FOURCC('I','4','2','0'); break;
+    case IMGFMT_IYUV: fourcc = VA_FOURCC('I','Y','U','V'); break;
+    }
+
+    if (fourcc)
+        return find_image_format(fourcc);
+
+    return NULL;
+}
+
+static struct vaapi_surface *alloc_vaapi_surface(unsigned int width,
+                                                 unsigned int height,
+                                                 unsigned int format)
+{
+    struct vaapi_surface *surface = NULL;
+    struct vaapi_surface **surfaces;
+    VASurfaceID *surface_ids;
+    VAStatus status;
+
+    surface = calloc(1, sizeof(*surface));
+    if (!surface)
+        goto error;
+
+    surfaces = realloc(va_free_surfaces,
+                       (1 + va_num_surfaces) * sizeof(surfaces[0]));
+    if (!surfaces)
+        goto error;
+
+    surface_ids = realloc(va_surface_ids,
+                          (1 + va_num_surfaces) * sizeof(surface_ids[0]));
+    if (!surface_ids)
+        goto error;
+
+    status = vaCreateSurfaces(va_context->display, width, height, format,
+                              1, &surface->id);
+    if (!check_status(status, "vaCreateSurfaces()"))
+        goto error;
+
+    va_surface_ids                    = surface_ids;
+    va_surface_ids[va_num_surfaces]   = surface->id;
+    va_free_surfaces                  = surfaces;
+    va_free_surfaces[va_num_surfaces] = surface;
+    surface->image.image_id           = VA_INVALID_ID;
+    surface->image.buf                = VA_INVALID_ID;
+    ++va_num_surfaces;
+    return surface;
+error:
+    free(surface);
+    return NULL;
+}
+
+static void resize(void)
+{
+    struct vo_rect src;
+
+    calc_src_dst_rects(g_image_width, g_image_height,
+                       &src, &g_output_rect, NULL, NULL);
+
+    ensure_osd();
+
+    vo_x11_clearwindow(mDisplay, vo_window);
+
+#if CONFIG_GL
+#define FOVY     60.0f
+#define ASPECT   1.0f
+#define Z_NEAR   0.1f
+#define Z_FAR    100.0f
+#define Z_CAMERA 0.869f
+
+    if (gl_enabled) {
+        glViewport(0, 0, vo_dwidth, vo_dheight);
+        glMatrixMode(GL_PROJECTION);
+        glLoadIdentity();
+        gluPerspective(FOVY, ASPECT, Z_NEAR, Z_FAR);
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+
+        glTranslatef(-0.5f, -0.5f, -Z_CAMERA);
+        glScalef(1.0f / (GLfloat)vo_dwidth,
+                 -1.0f / (GLfloat)vo_dheight,
+                 1.0f / (GLfloat)vo_dwidth);
+        glTranslatef(0.0f, -1.0f * (GLfloat)vo_dheight, 0.0f);
+    }
+#endif
+
+#if CONFIG_XRENDER
+    if (xr_enabled)
+        reset_xrender_specific();
+#endif
+
+    if (g_is_visible)
+        flip_page();
+}
+
+#if CONFIG_GL
+static int gl_build_font(void)
+{
+    XFontStruct *fi;
+
+    gl_font_base = glGenLists(96);
+
+    fi = XLoadQueryFont(mDisplay, "-adobe-helvetica-medium-r-normal--16-*-*-*-p-*-iso8859-1" );
+    if (!fi) {
+        fi = XLoadQueryFont(mDisplay, "fixed");
+        if (!fi)
+            return -1;
+    }
+
+    glXUseXFont(fi->fid, 32, 96, gl_font_base);
+    XFreeFont(mDisplay, fi);
+    return 0;
+}
+
+static void gl_printf(const char *format, ...)
+{
+    va_list args;
+    char *text;
+    int textlen;
+
+    va_start(args, format);
+    textlen = vsnprintf(NULL, 0, format, args);
+    va_end(args);
+
+    text = malloc(textlen + 1);
+    if (!text)
+        return;
+
+    va_start(args, format);
+    vsprintf(text, format, args);
+    va_end(args);
+
+    glPushAttrib(GL_LIST_BIT);
+    glListBase(gl_font_base - 32);
+    glCallLists(textlen, GL_UNSIGNED_BYTE, text);
+    glPopAttrib();
+    free(text);
+}
+
+static void gl_draw_rectangle(int x, int y, int w, int h, unsigned int rgba)
+{
+    glColor4f((GLfloat)((rgba >> 24) & 0xff) / 255.0,
+              (GLfloat)((rgba >> 16) & 0xff) / 255.0,
+              (GLfloat)((rgba >> 8) & 0xff) / 255.0,
+              (GLfloat)(rgba & 0xff) / 255.0);
+
+    glTranslatef((GLfloat)x, (GLfloat)y, 0.0f);
+    glBegin(GL_QUADS);
+    {
+        glVertex2i(0, 0);
+        glVertex2i(w, 0);
+        glVertex2i(w, h);
+        glVertex2i(0, h);
+    }
+    glEnd();
+}
+#endif
+
+#if CONFIG_XRENDER
+static int init_xrender(void)
+{
+    int dummy;
+
+    return XRenderQueryExtension(mDisplay, &dummy, &dummy);
+}
+
+static void uninit_xrender(void)
+{
+}
+#endif
+
+static inline unsigned char *get_osd_image_data(int x0, int y0)
+{
+    return (va_osd_image_data +
+            va_osd_image.offsets[0] +
+            va_osd_image.pitches[0] * y0 +
+            x0 * ((va_osd_image.format.bits_per_pixel + 7) / 8));
+}
+
+static void draw_alpha_rgb32(int x0, int y0, int w, int h,
+                             unsigned char *src, unsigned char *srca,
+                             int stride)
+{
+    int x, y;
+    const unsigned int dststride = va_osd_image.pitches[0];
+    unsigned char *dst = get_osd_image_data(x0, y0);
+
+    for (y = 0; y < h; y++, dst += dststride, src += stride, srca += stride)
+        for (x = 0; x < w; x++) {
+            const unsigned char c = src[x];
+            dst[4*x + 0] = c;
+            dst[4*x + 1] = c;
+            dst[4*x + 2] = c;
+            dst[4*x + 3] = -srca[x];
+        }
+}
+
+static void draw_alpha_IA44(int x0, int y0, int w, int h,
+                            unsigned char *src, unsigned char *srca,
+                            int stride)
+{
+    int x, y;
+    const unsigned int dststride = va_osd_image.pitches[0];
+    unsigned char *dst = get_osd_image_data(x0, y0);
+
+    for (y = 0; y < h; y++, dst += dststride)
+        for (x = 0; x < w; x++)
+            dst[x] = (src[y*stride + x] >> 4) | (-srca[y*stride + x] & 0xf0);
+}
+
+static void draw_alpha_AI44(int x0, int y0, int w, int h,
+                            unsigned char *src, unsigned char *srca,
+                            int stride)
+{
+    int x, y;
+    const unsigned int dststride = va_osd_image.pitches[0];
+    unsigned char *dst = get_osd_image_data(x0, y0);
+
+    for (y = 0; y < h; y++, dst += dststride)
+        for (x = 0; x < w; x++)
+            dst[x] = (src[y*stride + x] & 0xf0) | (-srca[y*stride + x] >> 4);
+}
+
+static void draw_alpha_IA88(int x0, int y0, int w, int h,
+                            unsigned char *src, unsigned char *srca,
+                            int stride)
+{
+    int x, y;
+    const unsigned int dststride = va_osd_image.pitches[0];
+    unsigned char *dst = get_osd_image_data(x0, y0);
+
+    for (y = 0; y < h; y++, dst += dststride)
+        for (x = 0; x < w; x++) {
+            dst[2*x + 0] =  src [y*stride + x];
+            dst[2*x + 1] = -srca[y*stride + x];
+        }
+}
+
+static void draw_alpha_AI88(int x0, int y0, int w, int h,
+                            unsigned char *src, unsigned char *srca,
+                            int stride)
+{
+    int x, y;
+    const unsigned int dststride = va_osd_image.pitches[0];
+    unsigned char *dst = get_osd_image_data(x0, y0);
+
+    for (y = 0; y < h; y++, dst += dststride)
+        for (x = 0; x < w; x++) {
+            dst[2*x + 0] = -srca[y*stride + x];
+            dst[2*x + 1] =  src [y*stride + x];
+        }
+}
+
+///< List of subpicture formats in preferred order
+static const struct {
+    uint32_t format;
+    draw_alpha_func draw_alpha;
+}
+va_osd_info[] = {
+    { VA_FOURCC('I','A','4','4'), draw_alpha_IA44  },
+    { VA_FOURCC('A','I','4','4'), draw_alpha_AI44  },
+    { VA_FOURCC('I','A','8','8'), draw_alpha_IA88  },
+    { VA_FOURCC('A','I','8','8'), draw_alpha_AI88  },
+    { VA_FOURCC('B','G','R','A'), draw_alpha_rgb32 },
+    { VA_FOURCC('R','G','B','A'), draw_alpha_rgb32 },
+    { 0, NULL }
+};
+
+static uint8_t *gen_osd_palette(const VAImage *image)
+{
+    uint8_t *palette;
+    int i, is_rgb;
+    int r_idx = -1, g_idx = -1, b_idx = -1;
+    int y_idx = -1, u_idx = -1, v_idx = -1;
+    int i_idx = -1, a_idx = -1;
+
+    if (image->num_palette_entries < 1)
+        return NULL;
+
+    palette = malloc(image->num_palette_entries * image->entry_bytes);
+    if (!palette)
+        return NULL;
+
+    for (i = 0; i < image->entry_bytes; i++) {
+        switch (image->component_order[i]) {
+        case 'R': r_idx = i; is_rgb = 1; break;
+        case 'G': g_idx = i; is_rgb = 1; break;
+        case 'B': b_idx = i; is_rgb = 1; break;
+        case 'Y': y_idx = i; is_rgb = 0; break;
+        case 'U': u_idx = i; is_rgb = 0; break;
+        case 'V': v_idx = i; is_rgb = 0; break;
+        case 'I': i_idx = i; break;
+        case 'A': a_idx = i; break;
+        }
+    }
+
+    if (r_idx != -1 && g_idx != -1 && b_idx != -1) {      /* RGB format */
+        for (i = 0; i < image->num_palette_entries; i++) {
+            const int n = i * image->entry_bytes;
+            palette[n + r_idx] = i * 0xff / (image->num_palette_entries - 1);
+            palette[n + g_idx] = i * 0xff / (image->num_palette_entries - 1);
+            palette[n + b_idx] = i * 0xff / (image->num_palette_entries - 1);
+        }
+    }
+    else if (y_idx != -1 && u_idx != -1 && v_idx != -1) { /* YUV format */
+        for (i = 0; i < image->num_palette_entries; i++) {
+            const int n = i * image->entry_bytes;
+            palette[n + y_idx] = i * 0xff / (image->num_palette_entries - 1);
+            palette[n + u_idx] = 0x80;
+            palette[n + v_idx] = 0x80;
+        }
+    }
+    else if (i_idx != -1 && a_idx != -1) {/* AYUV format (GMA500 "psb" bug) */
+        for (i = 0; i < image->num_palette_entries; i++) {
+            const int n = i * image->entry_bytes;
+            palette[n + 0] = 0x80;
+            palette[n + 1] = 0x80;
+            palette[n + 2] = 16 + i * 220 / (image->num_palette_entries - 1);
+            palette[n + 3] = 0;
+        }
+    }
+    else {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not set up subpicture palette\n");
+        free(palette);
+        palette = NULL;
+    }
+    return palette;
+}
+
+static void disable_osd(void)
+{
+    if (!va_osd_associated)
+        return;
+
+    vaDeassociateSubpicture(va_context->display,
+                            va_osd_subpicture,
+                            va_surface_ids, va_num_surfaces);
+
+    va_osd_associated = 0;
+}
+
+static int enable_osd(void)
+{
+    VAStatus status;
+
+    disable_osd();
+
+    status = vaAssociateSubpicture2(va_context->display,
+                                    va_osd_subpicture,
+                                    va_surface_ids, va_num_surfaces,
+                                    0, 0,
+                                    va_osd_image.width, va_osd_image.height,
+                                    0, 0,
+                                    g_image_width, g_image_height,
+                                    0);
+    if (!check_status(status, "vaAssociateSubpicture()"))
+        return -1;
+
+    va_osd_associated = 1;
+    return 0;
+}
+
+static void destroy_osd(void)
+{
+    disable_osd();
+
+    if (va_osd_subpicture != VA_INVALID_ID) {
+        vaDestroySubpicture(va_context->display, va_osd_subpicture);
+        va_osd_subpicture = VA_INVALID_ID;
+    }
+
+    if (va_osd_image.image_id != VA_INVALID_ID) {
+        vaDestroyImage(va_context->display, va_osd_image.image_id);
+        va_osd_image.image_id = VA_INVALID_ID;
+        va_osd_image.width    = 0;
+        va_osd_image.height   = 0;
+    }
+}
+
+static void create_osd(void)
+{
+    VAStatus status;
+    int i, j;
+
+    for (i = 0; va_osd_info[i].format; i++) {
+        for (j = 0; j < va_num_subpic_formats; j++)
+            if (va_subpic_formats[j].fourcc == va_osd_info[i].format)
+                break;
+        if (j < va_num_subpic_formats &&
+            vaCreateImage(va_context->display, &va_subpic_formats[j],
+                          g_output_rect.width, g_output_rect.height,
+                          &va_osd_image) == VA_STATUS_SUCCESS) {
+            va_osd_palette = gen_osd_palette(&va_osd_image);
+            if (((!va_osd_image.num_palette_entries) ^ (!va_osd_palette)) == 0)
+                break;
+            vaDestroyImage(va_context->display, va_osd_image.image_id);
+            va_osd_image.image_id = VA_INVALID_ID;
+        }
+    }
+
+    if (va_osd_info[i].format &&
+        vaCreateSubpicture(va_context->display, va_osd_image.image_id,
+                           &va_osd_subpicture) == VA_STATUS_SUCCESS) {
+        va_osd_draw_alpha = va_osd_info[i].draw_alpha;
+        if (va_osd_palette) {
+            status = vaSetImagePalette(va_context->display,
+                                       va_osd_image.image_id, va_osd_palette);
+            check_status(status, "vaSetImagePalette()");
+        }
+        mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using %s surface for OSD\n",
+               string_of_VAImageFormat(&va_osd_image.format));
+    }
+}
+
+static void ensure_osd(void)
+{
+    if (g_output_rect.width  == va_osd_image.width &&
+        g_output_rect.height == va_osd_image.height)
+        return;
+
+    destroy_osd();
+    create_osd();
+}
+
+static inline unsigned char *get_eosd_image_data(int x0, int y0)
+{
+    return (va_eosd_image_data +
+            va_eosd_image.offsets[0] +
+            va_eosd_image.pitches[0] * y0 +
+            x0 * ((va_eosd_image.format.bits_per_pixel + 7) / 8));
+}
+
+static void eosd_draw_alpha_bgra(unsigned char *src,
+                                 int src_w, int src_h, int src_stride,
+                                 int dst_x, int dst_y,
+                                 uint32_t color)
+{
+    int x, y;
+    const unsigned int dst_stride = va_eosd_image.pitches[0];
+    unsigned char *dst = get_eosd_image_data(dst_x, dst_y);
+    const unsigned int r = (color >> 24) & 0xff;
+    const unsigned int g = (color >> 16) & 0xff;
+    const unsigned int b = (color >>  8) & 0xff;
+    const unsigned int a = 0xff - (color & 0xff);
+
+    for (y = 0; y < src_h; y++, dst += dst_stride, src += src_stride)
+        for (x = 0; x < src_w; x++) {
+            const unsigned int v = src[x];
+            dst[4*x + 0] = (b * v + dst[4*x + 0] * (0xff - v)) / 255;
+            dst[4*x + 1] = (g * v + dst[4*x + 1] * (0xff - v)) / 255;
+            dst[4*x + 2] = (r * v + dst[4*x + 2] * (0xff - v)) / 255;
+            dst[4*x + 3] = (a * v + dst[4*x + 3] * (0xff - v)) / 255;
+        }
+}
+
+static void eosd_draw_alpha_rgba(unsigned char *src,
+                                 int src_w, int src_h, int src_stride,
+                                 int dst_x, int dst_y,
+                                 uint32_t color)
+{
+    int x, y;
+    const unsigned int dst_stride = va_eosd_image.pitches[0];
+    unsigned char *dst = get_eosd_image_data(dst_x, dst_y);
+    const unsigned int r = (color >> 24) & 0xff;
+    const unsigned int g = (color >> 16) & 0xff;
+    const unsigned int b = (color >>  8) & 0xff;
+    const unsigned int a = 0xff - (color & 0xff);
+
+    for (y = 0; y < src_h; y++, dst += dst_stride, src += src_stride)
+        for (x = 0; x < src_w; x++) {
+            const unsigned int v = src[x];
+            dst[4*x + 0] = (r * v + dst[4*x + 0] * (0xff - v)) / 255;
+            dst[4*x + 1] = (g * v + dst[4*x + 1] * (0xff - v)) / 255;
+            dst[4*x + 2] = (b * v + dst[4*x + 2] * (0xff - v)) / 255;
+            dst[4*x + 3] = (a * v + dst[4*x + 3] * (0xff - v)) / 255;
+        }
+}
+
+static void disable_eosd(void)
+{
+    if (!va_eosd_associated)
+        return;
+
+    vaDeassociateSubpicture(va_context->display,
+                            va_eosd_subpicture,
+                            va_surface_ids, va_num_surfaces);
+
+    va_eosd_associated = 0;
+}
+
+static int enable_eosd(void)
+{
+    VAStatus status;
+
+    if (va_eosd_associated)
+        return 0;
+
+    status = vaAssociateSubpicture2(va_context->display,
+                                    va_eosd_subpicture,
+                                    va_surface_ids, va_num_surfaces,
+                                    0, 0, g_image_width, g_image_height,
+                                    0, 0, g_image_width, g_image_height,
+                                    0);
+    if (!check_status(status, "vaAssociateSubpicture()"))
+        return -1;
+
+    va_eosd_associated = 1;
+    return 0;
+}
+
+///< List of subpicture formats in preferred order
+static const struct {
+    uint32_t format;
+    eosd_draw_alpha_func draw_alpha;
+}
+va_eosd_info[] = {
+    { VA_FOURCC('B','G','R','A'), eosd_draw_alpha_bgra },
+    { VA_FOURCC('R','G','B','A'), eosd_draw_alpha_rgba },
+    { 0, NULL }
+};
+
+static int is_direct_mapping_init(void)
+{
+    VADisplayAttribute attr;
+    VAStatus status;
+
+    if (va_dm < 2)
+        return va_dm;
+
+    /* If the driver doesn't make a copy of the VA surface for
+       display, then we have to retain it until it's no longer the
+       visible surface. In other words, if the driver is using
+       DirectSurface mode, we don't want to decode the new surface
+       into the previous one that was used for display. */
+    attr.type  = VADisplayAttribDirectSurface;
+    attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
+
+    status = vaGetDisplayAttributes(va_context->display, &attr, 1);
+    if (status == VA_STATUS_SUCCESS)
+        return !attr.value;
+    return 0;
+}
+
+static inline int is_direct_mapping(void)
+{
+    static int dm = -1;
+    if (dm < 0) {
+        dm = is_direct_mapping_init();
+        if (dm)
+            mp_msg(MSGT_VO, MSGL_INFO,
+                   "[vo_vaapi] Using 1:1 VA surface mapping\n");
+    }
+    return dm;
+}
+
+static int int_012(int *n)
+{
+    return *n >= 0 && *n <= 2;
+}
+
+#if USE_VAAPI_SCALING
+static strarg_t g_scaling_arg = { 0, NULL };
+
+static int test_scaling_arg(void *arg)
+{
+    strarg_t * const strarg = arg;
+
+    return (strargcmp(strarg, "default") == 0 ||
+            strargcmp(strarg, "fast") == 0 ||
+            strargcmp(strarg, "hq") == 0);
+}
+
+static void setup_scaling(const char *scaling)
+{
+    if (strcmp(scaling, "default") == 0)
+        g_scaling = VA_FILTER_SCALING_DEFAULT;
+    else if (strcmp(scaling, "fast") == 0)
+        g_scaling = VA_FILTER_SCALING_FAST;
+    else if (strcmp(scaling, "hq") == 0)
+        g_scaling = VA_FILTER_SCALING_HQ;
+    else if (strcmp(scaling, "nla") == 0)
+        g_scaling = VA_FILTER_SCALING_NL_ANAMORPHIC;
+}
+#endif
+
+static const opt_t subopts[] = {
+    { "dm",          OPT_ARG_INT,  &va_dm,        (opt_test_f)int_012 },
+    { "stats",       OPT_ARG_BOOL, &cpu_stats,    NULL },
+    { "deint",       OPT_ARG_INT,  &g_deint,      (opt_test_f)int_012 },
+#if USE_VAAPI_COLORSPACE
+    { "colorspace",  OPT_ARG_INT,  &g_colorspace, (opt_test_f)int_012 },
+#endif
+#if USE_VAAPI_SCALING
+    { "scaling",     OPT_ARG_STR,  &g_scaling_arg, test_scaling_arg },
+#endif
+#if CONFIG_GL
+    { "gl",          OPT_ARG_BOOL, &gl_enabled,   NULL },
+    { "glfinish",    OPT_ARG_BOOL, &gl_finish,    NULL },
+#if USE_VAAPI_GLX_BIND
+    { "bind",        OPT_ARG_BOOL, &gl_binding,   NULL },
+#endif
+    { "reflect",     OPT_ARG_BOOL, &gl_reflect,   NULL },
+    { "tfp",         OPT_ARG_BOOL, &gl_use_tfp,   NULL },
+#endif
+#if CONFIG_XRENDER
+    { "xrender",     OPT_ARG_BOOL, &xr_enabled,   NULL },
+#endif
+    { NULL, }
+};
+
+static int preinit(const char *arg)
+{
+    VADisplayAttribute *display_attrs;
+    VAStatus status;
+    int va_major_version, va_minor_version;
+    int i, max_image_formats, max_subpic_formats, max_profiles;
+    int num_display_attrs, max_display_attrs;
+
+    va_dm = 2;
+    g_deint = 0;
+    g_deint_type = 2;
+    g_colorspace = 1;
+    g_scaling = 0;
+    if (subopt_parse(arg, subopts) != 0) {
+        mp_msg(MSGT_VO, MSGL_FATAL,
+               "\n-vo vaapi command line help:\n"
+               "Example: mplayer -vo vaapi:gl\n"
+               "\nOptions:\n"
+               "  dm\n"
+               "    0: use least-recently-used VA surface\n"
+               "    1: identify VA surface with MPI index\n"
+               "    2: auto-detect use of direct surface mapping (default)\n"
+               "  deint (all modes > 0 respect -field-dominance)\n"
+               "    0: no deinterlacing (default)\n"
+               "    1: only show first field\n"
+               "    2: bob deinterlacing\n"
+#if USE_VAAPI_COLORSPACE
+               "  colorspace\n"
+               "    0: guess based on video resolution\n"
+               "    1: ITU-R BT.601 (default)\n"
+               "    2: ITU-R BT.709\n"
+               "    3: SMPTE-240M\n"
+#endif
+#if USE_VAAPI_SCALING
+               "  scaling\n"
+               "    default: use implementation default (default)\n"
+               "    fast:    use fast scaling, but possibly with less quality\n"
+               "    hq:      use high-quality scaling, but possibly slower\n"
+               "    nla:     use non-linear anamorphic scaling\n"
+#endif
+#if CONFIG_GL
+               "  gl\n"
+               "    Enable OpenGL rendering\n"
+               "  glfinish\n"
+               "    Call glFinish() before swapping buffers\n"
+               "  tfp\n"
+               "    Use GLX texture-from-pixmap instead of VA/GLX extensions\n"
+#if USE_VAAPI_GLX_BIND
+               "  bind\n"
+               "    Use VA surface binding instead of copy\n"
+#endif
+               "  reflect\n"
+               "    Enable OpenGL reflection effects\n"
+#endif
+#if CONFIG_XRENDER
+               "  xrender\n"
+               "    Enable Xrender rendering, thus vaPutSurface() to a Pixmap\n"
+#endif
+               "\n" );
+        return -1;
+    }
+    if (gl_enabled && xr_enabled) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] User requested both Xrender and OpenGL rendering\n");
+        return -1;
+    }
+    if (g_deint)
+        g_deint_type = g_deint;
+#if CONFIG_GL
+    if (gl_enabled)
+        mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using OpenGL rendering%s\n",
+               gl_reflect ? ", with reflection effects" : "");
+#endif
+#if CONFIG_XRENDER
+    if (xr_enabled)
+        mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using Xrender rendering\n");
+#endif
+#if USE_VAAPI_SCALING
+    if (g_scaling_arg.str) {
+        mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using '%s' scaling\n", g_scaling_arg.str);
+        setup_scaling(g_scaling_arg.str);
+    }
+#endif
+
+    stats_init();
+
+#if CONFIG_GL
+    if (gl_enabled && !init_mpglcontext(&gl_context, GLTYPE_X11))
+        return -1;
+    else
+#endif
+    if (!vo_init())
+        return -1;
+#if CONFIG_XRENDER
+    if (xr_enabled && !init_xrender())
+        return -1;
+#endif
+
+    va_context = calloc(1, sizeof(*va_context));
+    if (!va_context)
+        return -1;
+
+#if CONFIG_VAAPI_GLX
+    if (gl_enabled)
+        va_context->display = vaGetDisplayGLX(mDisplay);
+    else
+#endif
+        va_context->display = vaGetDisplay(mDisplay);
+    if (!va_context->display)
+        return -1;
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): VA display %p\n", va_context->display);
+
+    status = vaInitialize(va_context->display, &va_major_version, &va_minor_version);
+    if (!check_status(status, "vaInitialize()"))
+        return -1;
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): VA API version %d.%d\n",
+           va_major_version, va_minor_version);
+
+    max_image_formats = vaMaxNumImageFormats(va_context->display);
+    va_image_formats = calloc(max_image_formats, sizeof(*va_image_formats));
+    if (!va_image_formats)
+        return -1;
+    status = vaQueryImageFormats(va_context->display, va_image_formats, &va_num_image_formats);
+    if (!check_status(status, "vaQueryImageFormats()"))
+        return -1;
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d image formats available\n",
+           va_num_image_formats);
+    for (i = 0; i < va_num_image_formats; i++)
+        mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAImageFormat(&va_image_formats[i]));
+
+    max_subpic_formats = vaMaxNumSubpictureFormats(va_context->display);
+    va_subpic_formats = calloc(max_subpic_formats, sizeof(*va_subpic_formats));
+    if (!va_subpic_formats)
+        return -1;
+    va_subpic_flags = calloc(max_subpic_formats, sizeof(*va_subpic_flags));
+    if (!va_subpic_flags)
+        return -1;
+    status = vaQuerySubpictureFormats(va_context->display, va_subpic_formats, va_subpic_flags, &va_num_subpic_formats);
+    if (!check_status(status, "vaQuerySubpictureFormats()"))
+        va_num_subpic_formats = 0; /* XXX: don't error out for IEGD */
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d subpicture formats available\n",
+           va_num_subpic_formats);
+    for (i = 0; i < va_num_subpic_formats; i++)
+        mp_msg(MSGT_VO, MSGL_DBG2, "  %s, flags 0x%x\n", string_of_VAImageFormat(&va_subpic_formats[i]), va_subpic_flags[i]);
+
+    max_profiles = vaMaxNumProfiles(va_context->display);
+    va_profiles = calloc(max_profiles, sizeof(*va_profiles));
+    if (!va_profiles)
+        return -1;
+    status = vaQueryConfigProfiles(va_context->display, va_profiles, &va_num_profiles);
+    if (!check_status(status, "vaQueryConfigProfiles()"))
+        return -1;
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d profiles available\n",
+           va_num_profiles);
+    for (i = 0; i < va_num_profiles; i++)
+        mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAProfile(va_profiles[i]));
+
+    va_osd_subpicture      = VA_INVALID_ID;
+    va_osd_image.image_id  = VA_INVALID_ID;
+    va_eosd_subpicture     = VA_INVALID_ID;
+    va_eosd_image.image_id = VA_INVALID_ID;
+
+    max_display_attrs = vaMaxNumDisplayAttributes(va_context->display);
+    display_attrs = calloc(max_display_attrs, sizeof(*display_attrs));
+    if (display_attrs) {
+        num_display_attrs = 0;
+        status = vaQueryDisplayAttributes(va_context->display,
+                                          display_attrs, &num_display_attrs);
+        if (check_status(status, "vaQueryDisplayAttributes()")) {
+            for (i = 0; i < num_display_attrs; i++) {
+                VADisplayAttribute *attr;
+                switch (display_attrs[i].type) {
+                case VADisplayAttribBrightness:
+                    attr = &va_equalizer.brightness;
+                    break;
+                case VADisplayAttribContrast:
+                    attr = &va_equalizer.contrast;
+                    break;
+                case VADisplayAttribHue:
+                    attr = &va_equalizer.hue;
+                    break;
+                case VADisplayAttribSaturation:
+                    attr = &va_equalizer.saturation;
+                    break;
+                default:
+                    attr = NULL;
+                    break;
+                }
+                if (attr)
+                    *attr = display_attrs[i];
+            }
+        }
+        free(display_attrs);
+    }
+    return 0;
+}
+
+static void free_video_specific(void)
+{
+    int i;
+
+#if CONFIG_VAAPI_GLX
+    if (gl_surface) {
+        VAStatus status;
+        status = vaDestroySurfaceGLX(va_context->display, gl_surface);
+        check_status(status, "vaDestroySurfaceGLX()");
+        gl_surface = NULL;
+    }
+#endif
+
+    if (va_context && va_context->context_id) {
+        vaDestroyContext(va_context->display, va_context->context_id);
+        va_context->context_id = 0;
+    }
+
+    if (va_free_surfaces) {
+        for (i = 0; i < va_num_surfaces; i++) {
+            if (!va_free_surfaces[i])
+                continue;
+            if (va_free_surfaces[i]->image.image_id != VA_INVALID_ID) {
+                vaDestroyImage(va_context->display,
+                               va_free_surfaces[i]->image.image_id);
+                va_free_surfaces[i]->image.image_id = VA_INVALID_ID;
+            }
+            free(va_free_surfaces[i]);
+            va_free_surfaces[i] = NULL;
+        }
+        free(va_free_surfaces);
+        va_free_surfaces = NULL;
+        va_free_surfaces_head_index = 0;
+        va_free_surfaces_tail_index = 0;
+    }
+
+    g_output_surface = 0;
+    memset(g_output_surfaces, 0, sizeof(g_output_surfaces));
+
+    if (va_osd_palette) {
+        free(va_osd_palette);
+        va_osd_palette = NULL;
+    }
+
+    disable_eosd();
+    disable_osd();
+
+    if (va_eosd_subpicture != VA_INVALID_ID) {
+        vaDestroySubpicture(va_context->display, va_eosd_subpicture);
+        va_eosd_subpicture = VA_INVALID_ID;
+    }
+
+    if (va_eosd_image.image_id != VA_INVALID_ID) {
+        vaDestroyImage(va_context->display, va_eosd_image.image_id);
+        va_eosd_image.image_id = VA_INVALID_ID;
+    }
+
+    destroy_osd();
+
+    if (va_surface_ids) {
+        vaDestroySurfaces(va_context->display, va_surface_ids, va_num_surfaces);
+        free(va_surface_ids);
+        va_surface_ids = NULL;
+        va_num_surfaces = 0;
+    }
+
+    if (va_context && va_context->config_id) {
+        vaDestroyConfig(va_context->display, va_context->config_id);
+        va_context->config_id = 0;
+    }
+
+    if (va_entrypoints) {
+        free(va_entrypoints);
+        va_entrypoints = NULL;
+    }
+
+#if CONFIG_GL
+    if (gl_pixmap) {
+        x11_trap_errors();
+        mpglXDestroyPixmap(mDisplay, gl_pixmap);
+        XSync(mDisplay, False);
+        x11_untrap_errors();
+        gl_pixmap = None;
+    }
+
+    if (g_image_pixmap) {
+        XFreePixmap(mDisplay, g_image_pixmap);
+        g_image_pixmap = None;
+    }
+
+    if (gl_texture) {
+        glDeleteTextures(1, &gl_texture);
+        gl_texture = GL_NONE;
+    }
+#endif
+
+#if CONFIG_XRENDER
+    if (xr_window_picture) {
+        XRenderFreePicture(mDisplay, xr_window_picture);
+        xr_window_picture = None;
+    }
+#endif
+
+    g_is_visible = 0;
+}
+
+static void uninit(void)
+{
+    if (!vo_config_count)
+        return;
+
+    free_video_specific();
+
+    if (va_profiles) {
+        free(va_profiles);
+        va_profiles = NULL;
+    }
+
+    if (va_subpic_flags) {
+        free(va_subpic_flags);
+        va_subpic_flags = NULL;
+    }
+
+    if (va_subpic_formats) {
+        free(va_subpic_formats);
+        va_subpic_formats = NULL;
+    }
+
+    if (va_image_formats) {
+        free(va_image_formats);
+        va_image_formats = NULL;
+    }
+
+    if (va_context && va_context->display) {
+        vaTerminate(va_context->display);
+        va_context->display = NULL;
+    }
+
+    if (va_context) {
+        free(va_context);
+        va_context = NULL;
+    }
+
+#ifdef CONFIG_XF86VM
+    vo_vm_close();
+#endif
+#if CONFIG_XRENDER
+    if (xr_enabled)
+        uninit_xrender();
+#endif
+#if CONFIG_GL
+    if (gl_enabled)
+        uninit_mpglcontext(&gl_context);
+    else
+#endif
+    vo_x11_uninit();
+
+    stats_exit();
+}
+
+static int config_x11(uint32_t width, uint32_t height,
+                      uint32_t display_width, uint32_t display_height,
+                      uint32_t flags, char *title)
+{
+    Colormap cmap;
+    XVisualInfo visualInfo;
+    XVisualInfo *vi;
+    XSetWindowAttributes xswa;
+    unsigned long xswa_mask;
+    XWindowAttributes wattr;
+    int depth;
+
+#ifdef CONFIG_GUI
+    if (use_gui)
+        guiGetEvent(guiSetShVideo, 0);  // the GUI will set up / resize our window
+    else
+#endif
+    {
+#ifdef CONFIG_XF86VM
+        if (flags & VOFLAG_MODESWITCHING)
+            vo_vm_switch();
+#endif
+        XGetWindowAttributes(mDisplay, DefaultRootWindow(mDisplay), &wattr);
+        depth = wattr.depth;
+        if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
+            depth = 24;
+        XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, &visualInfo);
+
+#if CONFIG_GL
+        if (gl_enabled) {
+            vi = glXChooseVisual(mDisplay, mScreen, gl_visual_attr);
+            if (!vi)
+                return -1;
+            cmap = XCreateColormap(mDisplay, mRootWin, vi->visual, AllocNone);
+            if (cmap == None)
+                return -1;
+        }
+        else
+#endif
+        {
+            vi = &visualInfo;
+            XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, vi);
+            cmap = CopyFromParent;
+        }
+
+        vo_x11_create_vo_window(vi,
+                                vo_dx, vo_dy, display_width, display_height,
+                                flags, cmap, "vaapi", title);
+
+        if (vi != &visualInfo)
+            XFree(vi);
+
+        xswa_mask             = CWBorderPixel | CWBackPixel;
+        xswa.border_pixel     = 0;
+        xswa.background_pixel = 0;
+        XChangeWindowAttributes(mDisplay, vo_window, xswa_mask, &xswa);
+
+#ifdef CONFIG_XF86VM
+        if (flags & VOFLAG_MODESWITCHING) {
+            /* Grab the mouse pointer in our window */
+            if (vo_grabpointer)
+                XGrabPointer(mDisplay, vo_window, True, 0,
+                             GrabModeAsync, GrabModeAsync,
+                             vo_window, None, CurrentTime);
+            XSetInputFocus(mDisplay, vo_window, RevertToNone, CurrentTime);
+        }
+#endif
+    }
+    return 0;
+}
+
+#if CONFIG_GL
+static GLXFBConfig *get_fbconfig_for_depth(int depth)
+{
+    GLXFBConfig *fbconfigs, *ret = NULL;
+    int          n_elements, i, found;
+    int          db, stencil, alpha, mipmap, rgba, value;
+
+    static GLXFBConfig *cached_config = NULL;
+    static int          have_cached_config = 0;
+
+    if (have_cached_config)
+        return cached_config;
+
+    fbconfigs = glXGetFBConfigs(mDisplay, mScreen, &n_elements);
+
+    db      = SHRT_MAX;
+    stencil = SHRT_MAX;
+    mipmap  = 0;
+    rgba    = 0;
+
+    found = n_elements;
+
+    for (i = 0; i < n_elements; i++) {
+        XVisualInfo *vi;
+        int          visual_depth;
+
+        vi = glXGetVisualFromFBConfig(mDisplay, fbconfigs[i]);
+        if (!vi)
+            continue;
+
+        visual_depth = vi->depth;
+        XFree(vi);
+
+        if (visual_depth != depth)
+            continue;
+
+        glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_ALPHA_SIZE, &alpha);
+        glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_BUFFER_SIZE, &value);
+        if (value != depth && (value - alpha) != depth)
+            continue;
+
+        value = 0;
+        if (depth == 32) {
+            glXGetFBConfigAttrib(mDisplay, fbconfigs[i],
+                                 GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
+            if (value)
+                rgba = 1;
+        }
+
+        if (!value) {
+            if (rgba)
+                continue;
+
+            glXGetFBConfigAttrib(mDisplay, fbconfigs[i],
+                                 GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
+            if (!value)
+                continue;
+        }
+
+        glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_DOUBLEBUFFER, &value);
+        if (value > db)
+            continue;
+        db = value;
+
+        glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_STENCIL_SIZE, &value);
+        if (value > stencil)
+            continue;
+        stencil = value;
+
+        found = i;
+    }
+
+    if (found != n_elements) {
+        ret = malloc(sizeof(*ret));
+        *ret = fbconfigs[found];
+    }
+
+    if (n_elements)
+        XFree(fbconfigs);
+
+    have_cached_config = 1;
+    cached_config = ret;
+    return ret;
+}
+
+static int config_tfp(unsigned int width, unsigned int height)
+{
+    GLXFBConfig *fbconfig;
+    int attribs[7], i = 0;
+    const int depth = 24;
+
+    if (!mpglXBindTexImage || !mpglXReleaseTexImage) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] No GLX texture-from-pixmap extension available\n");
+        return -1;
+    }
+
+    if (depth != 24 && depth != 32)
+        return -1;
+
+    g_image_pixmap = XCreatePixmap(mDisplay, vo_window, width, height, depth);
+    if (!g_image_pixmap) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not create X11 pixmap\n");
+        return -1;
+    }
+
+    fbconfig = get_fbconfig_for_depth(depth);
+    if (!fbconfig) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not find an FBConfig for 32-bit pixmap\n");
+        return -1;
+    }
+
+    attribs[i++] = GLX_TEXTURE_TARGET_EXT;
+    attribs[i++] = GLX_TEXTURE_2D_EXT;
+    attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
+    if (depth == 24)
+        attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT;
+    else if (depth == 32)
+        attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
+    attribs[i++] = GLX_MIPMAP_TEXTURE_EXT;
+    attribs[i++] = GL_FALSE;
+    attribs[i++] = None;
+
+    x11_trap_errors();
+    gl_pixmap = mpglXCreatePixmap(mDisplay, *fbconfig, g_image_pixmap, attribs);
+    XSync(mDisplay, False);
+    if (x11_untrap_errors()) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not create GLX pixmap\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int config_glx(unsigned int width, unsigned int height)
+{
+    if (gl_context.setGlWindow(&gl_context) == SET_WINDOW_FAILED)
+        return -1;
+
+    glDisable(GL_DEPTH_TEST);
+    glDepthMask(GL_FALSE);
+    glDisable(GL_CULL_FACE);
+    glEnable(GL_TEXTURE_2D);
+    glDrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT);
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    /* Create TFP resources */
+    if (gl_use_tfp && config_tfp(width, height) == 0)
+        mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using GLX texture-from-pixmap extension\n");
+    else
+        gl_use_tfp = 0;
+
+    /* Create OpenGL texture */
+    /* XXX: assume GL_ARB_texture_non_power_of_two is available */
+    glEnable(GL_TEXTURE_2D);
+    glGenTextures(1, &gl_texture);
+    mpglBindTexture(GL_TEXTURE_2D, gl_texture);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    if (!gl_use_tfp) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
+                     GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+    }
+    mpglBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+
+    glClearColor(0.0, 0.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    if (gl_build_font() < 0)
+        return -1;
+    return 0;
+}
+#endif
+
+#if CONFIG_XRENDER
+static XRenderPictFormat *get_xrender_argb32_format(void)
+{
+    static XRenderPictFormat *pictformat = NULL;
+    XRenderPictFormat templ;
+
+    const unsigned long mask =
+        PictFormatType      |
+        PictFormatDepth     |
+        PictFormatRed       |
+        PictFormatRedMask   |
+        PictFormatGreen     |
+        PictFormatGreenMask |
+        PictFormatBlue      |
+        PictFormatBlueMask  |
+        PictFormatAlphaMask;
+
+    if (pictformat)
+        return pictformat;
+
+    /* First, look for a 32-bit format which ignores the alpha component */
+    templ.depth            = 32;
+    templ.type             = PictTypeDirect;
+    templ.direct.red       = 16;
+    templ.direct.green     = 8;
+    templ.direct.blue      = 0;
+    templ.direct.redMask   = 0xff;
+    templ.direct.greenMask = 0xff;
+    templ.direct.blueMask  = 0xff;
+    templ.direct.alphaMask = 0;
+
+    pictformat = XRenderFindFormat(mDisplay, mask, &templ, 0);
+
+    if (!pictformat) {
+        /* Not all X servers support xRGB32 formats. However, the
+         * XRENDER spec says that they must support an ARGB32 format,
+         * so we can always return that.
+         */
+        pictformat = XRenderFindStandardFormat(mDisplay, PictStandardARGB32);
+        if (!pictformat)
+            mp_msg(MSGT_VO, MSGL_ERR, "XRENDER ARGB32 format not supported\n");
+    }
+    return pictformat;
+}
+
+static int create_xrender_specific(void)
+{
+    XRenderPictFormat *pictformat;
+
+    if (g_output_rect.width == 0 && g_output_rect.height == 0)
+        return 0;
+
+    g_image_pixmap = XCreatePixmap(mDisplay, vo_window, g_output_rect.width,
+                                   g_output_rect.height, 32);
+    if (!g_image_pixmap) {
+        mp_msg(MSGT_VO, MSGL_ERR, "Could not create video pixmap\n");
+        return -1;
+    }
+
+    pictformat = get_xrender_argb32_format();
+    if (!pictformat)
+        return -1;
+    xr_video_picture = XRenderCreatePicture(mDisplay, g_image_pixmap,
+                                            pictformat, 0, NULL);
+    if (!xr_video_picture) {
+        mp_msg(MSGT_VO, MSGL_ERR, "Could not create XRENDER backing picture for Pixmap\n");
+        return -1;
+    }
+    return 0;
+}
+
+static void free_xrender_specific(void)
+{
+    if (xr_video_picture) {
+        XRenderFreePicture(mDisplay, xr_video_picture);
+        xr_video_picture = None;
+    }
+
+    if (g_image_pixmap) {
+        XFreePixmap(mDisplay, g_image_pixmap);
+        g_image_pixmap = None;
+    }
+}
+
+static int reset_xrender_specific(void)
+{
+    free_xrender_specific();
+    return create_xrender_specific();
+}
+
+/* XXX: create a Pixmap as large as the display rect */
+static int config_xrender(unsigned int width, unsigned int height)
+{
+    XWindowAttributes wattr;
+    XRenderPictFormat *pictformat;
+
+    XGetWindowAttributes(mDisplay, vo_window, &wattr);
+    pictformat = XRenderFindVisualFormat(mDisplay, wattr.visual);
+    if (!pictformat) {
+        mp_msg(MSGT_VO, MSGL_ERR, "XRENDER does not support Window visual\n");
+        return -1;
+    }
+
+    xr_window_picture = XRenderCreatePicture(mDisplay, vo_window, pictformat,
+                                             0, NULL);
+    if (!xr_window_picture) {
+        mp_msg(MSGT_VO, MSGL_ERR, "Could not create XRENDER backing picture for Window\n");
+        return -1;
+    }
+    return reset_xrender_specific();
+}
+#endif
+
+static int config_vaapi(uint32_t width, uint32_t height, uint32_t format)
+{
+    VAConfigAttrib attrib;
+    VAStatus status;
+    int i, j, profile, entrypoint, max_entrypoints, num_surfaces;
+
+    /* Create video surfaces */
+    if (!IMGFMT_IS_VAAPI(format))
+        num_surfaces = MAX_OUTPUT_SURFACES;
+    else {
+        switch (IMGFMT_VAAPI_CODEC(format)) {
+        case IMGFMT_VAAPI_CODEC_MPEG2:
+            num_surfaces = NUM_VIDEO_SURFACES_MPEG2;
+            break;
+        case IMGFMT_VAAPI_CODEC_MPEG4:
+            num_surfaces = NUM_VIDEO_SURFACES_MPEG4;
+            break;
+        case IMGFMT_VAAPI_CODEC_H264:
+            num_surfaces = NUM_VIDEO_SURFACES_H264;
+            break;
+        case IMGFMT_VAAPI_CODEC_VC1:
+            num_surfaces = NUM_VIDEO_SURFACES_VC1;
+            break;
+        default:
+            num_surfaces = 0;
+            break;
+        }
+        if (num_surfaces == 0)
+            return -1;
+        if (!is_direct_mapping())
+            num_surfaces = FFMIN(2 * num_surfaces, MAX_VIDEO_SURFACES);
+    }
+    for (i = 0; i < num_surfaces; i++) {
+        struct vaapi_surface *surface;
+        surface = alloc_vaapi_surface(width, height, VA_RT_FORMAT_YUV420);
+        if (!surface)
+            return -1;
+    }
+    assert(va_num_surfaces == num_surfaces);
+
+#if CONFIG_VAAPI_GLX
+    /* Create GLX surfaces */
+    if (gl_enabled && !gl_use_tfp) {
+        status = vaCreateSurfaceGLX(va_context->display,
+                                    GL_TEXTURE_2D, gl_texture,
+                                    &gl_surface);
+        if (!check_status(status, "vaCreateSurfaceGLX()"))
+            return -1;
+    }
+#endif
+
+    /* Create OSD data */
+    va_osd_draw_alpha     = NULL;
+    va_osd_image.image_id = VA_INVALID_ID;
+    va_osd_image.buf      = VA_INVALID_ID;
+    va_osd_subpicture     = VA_INVALID_ID;
+    ensure_osd();
+
+    /* Create EOSD data */
+    va_eosd_draw_alpha     = NULL;
+    va_eosd_image.image_id = VA_INVALID_ID;
+    va_eosd_image.buf      = VA_INVALID_ID;
+    va_eosd_subpicture     = VA_INVALID_ID;
+    for (i = 0; va_eosd_info[i].format; i++) {
+        for (j = 0; j < va_num_subpic_formats; j++)
+            if (va_subpic_formats[j].fourcc == va_eosd_info[i].format)
+                break;
+        if (j < va_num_subpic_formats &&
+            vaCreateImage(va_context->display, &va_subpic_formats[j],
+                          width, height, &va_eosd_image) == VA_STATUS_SUCCESS)
+            break;
+    }
+    if (va_eosd_info[i].format &&
+        vaCreateSubpicture(va_context->display, va_eosd_image.image_id,
+                           &va_eosd_subpicture) == VA_STATUS_SUCCESS) {
+        va_eosd_draw_alpha = va_eosd_info[i].draw_alpha;
+        mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using %s surface for EOSD\n",
+               string_of_VAImageFormat(&va_eosd_image.format));
+    }
+
+    /* Allocate VA images */
+    if (!IMGFMT_IS_VAAPI(format)) {
+        VAImageFormat *image_format = VAImageFormat_from_imgfmt(format);
+        if (!image_format)
+            return -1;
+        for (i = 0; i < va_num_surfaces; i++) {
+            struct vaapi_surface * const s = va_free_surfaces[i];
+            s->is_bound = 0;
+            status = vaDeriveImage(va_context->display, s->id, &s->image);
+            if (status == VA_STATUS_SUCCESS) {
+                /* vaDeriveImage() is supported, check format */
+                if (s->image.format.fourcc != image_format->fourcc) {
+                    vaDestroyImage(va_context->display, s->image.image_id);
+                    return -1;
+                }
+                if (s->image.width == width && s->image.height == height) {
+                    s->is_bound = 1;
+                    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using vaDeriveImage()\n");
+                }
+                else {
+                    vaDestroyImage(va_context->display, s->image.image_id);
+                    status = VA_STATUS_ERROR_OPERATION_FAILED;
+                }
+
+            }
+            if (status != VA_STATUS_SUCCESS) {
+                status = vaCreateImage(va_context->display, image_format,
+                                       width, height, &s->image);
+                if (!check_status(status, "vaCreateImage()"))
+                    return -1;
+            }
+        }
+        return 0;
+    }
+
+    /* Check profile */
+    profile = VAProfile_from_imgfmt(format);
+    if (profile < 0)
+        return -1;
+
+    /* Check entry-point (only VLD for now) */
+    max_entrypoints = vaMaxNumEntrypoints(va_context->display);
+    va_entrypoints = calloc(max_entrypoints, sizeof(*va_entrypoints));
+    if (!va_entrypoints)
+        return -1;
+
+    status = vaQueryConfigEntrypoints(va_context->display, profile,
+                                      va_entrypoints, &va_num_entrypoints);
+    if (!check_status(status, "vaQueryConfigEntrypoints()"))
+        return -1;
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] config_vaapi(%s): %d entrypoints available\n",
+           string_of_VAProfile(profile), va_num_entrypoints);
+    for (i = 0; i < va_num_entrypoints; i++)
+        mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAEntrypoint(va_entrypoints[i]));
+
+    entrypoint = VAEntrypoint_from_imgfmt(format);
+    if (entrypoint != VAEntrypointVLD)
+        return -1;
+
+    /* Check chroma format (only 4:2:0 for now) */
+    attrib.type = VAConfigAttribRTFormat;
+    status = vaGetConfigAttributes(va_context->display, profile, entrypoint, &attrib, 1);
+    if (!check_status(status, "vaGetConfigAttributes()"))
+        return -1;
+    if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
+        return -1;
+
+    /* Create a configuration for the decode pipeline */
+    status = vaCreateConfig(va_context->display, profile, entrypoint, &attrib, 1, &va_context->config_id);
+    if (!check_status(status, "vaCreateConfig()"))
+        return -1;
+
+    /* Create a context for the decode pipeline */
+    status = vaCreateContext(va_context->display, va_context->config_id,
+                             width, height, VA_PROGRESSIVE,
+                             va_surface_ids, va_num_surfaces,
+                             &va_context->context_id);
+    if (!check_status(status, "vaCreateContext()"))
+        return -1;
+    return 0;
+}
+
+static int config(uint32_t width, uint32_t height,
+                  uint32_t display_width, uint32_t display_height,
+                  uint32_t flags, char *title, uint32_t format)
+{
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] config(): size %dx%d, display size %dx%d, flags %x, title '%s', format %x (%s)\n",
+           width, height, display_width, display_height, flags, title, format, vo_format_name(format));
+
+    free_video_specific();
+
+    if (config_x11(width, height, display_width, display_height, flags, title) < 0)
+        return -1;
+
+#if CONFIG_GL
+    if (gl_enabled && config_glx(width, height) < 0)
+        return -1;
+#endif
+
+#if CONFIG_XRENDER
+    if (xr_enabled && config_xrender(width, height) < 0)
+        return -1;
+#endif
+
+    if (config_vaapi(width, height, format) < 0)
+        return -1;
+
+    g_is_visible   = 0;
+    g_is_paused    = 0;
+    g_image_width  = width;
+    g_image_height = height;
+    g_image_format = format;
+    resize();
+    return 0;
+}
+
+static int query_format(uint32_t format)
+{
+    const int default_caps = (VFCAP_CSP_SUPPORTED |
+                              VFCAP_CSP_SUPPORTED_BY_HW |
+                              VFCAP_HWSCALE_UP |
+                              VFCAP_HWSCALE_DOWN |
+                              VFCAP_OSD |
+                              VFCAP_EOSD);
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] query_format(): format %x (%s)\n",
+           format, vo_format_name(format));
+
+    switch (format) {
+    case IMGFMT_VAAPI_MPEG2:
+    case IMGFMT_VAAPI_MPEG4:
+    case IMGFMT_VAAPI_H263:
+    case IMGFMT_VAAPI_H264:
+    case IMGFMT_VAAPI_WMV3:
+    case IMGFMT_VAAPI_VC1:
+        return default_caps | VOCAP_NOSLICES;
+    case IMGFMT_NV12:
+    case IMGFMT_YV12:
+    case IMGFMT_I420:
+    case IMGFMT_IYUV:
+        if (VAImageFormat_from_imgfmt(format))
+            return default_caps;
+        break;
+    }
+    return 0;
+}
+
+static inline int get_field_flags(int i)
+{
+    return (g_deint && (g_image_fields & MP_IMGFIELD_INTERLACED) ?
+            (((!!(g_image_fields & MP_IMGFIELD_TOP_FIRST)) ^ i) == 0 ?
+             VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE);
+}
+
+static inline int get_colorspace_flags(void)
+{
+    int csp = 0;
+#if USE_VAAPI_COLORSPACE
+    switch (g_colorspace) {
+    case 0:
+        csp = ((g_image_width >= 1280 || g_image_height > 576) ?
+               VA_SRC_BT709 : VA_SRC_BT601);
+        break;
+    case 1:
+        csp = VA_SRC_BT601;
+        break;
+    case 2:
+        csp = VA_SRC_BT709;
+        break;
+    case 3:
+        csp = VA_SRC_SMPTE_240;
+        break;
+    default:
+        assert(0);
+        break;
+    }
+#endif
+    return csp;
+}
+
+static void put_surface_x11(struct vaapi_surface *surface)
+{
+    VAStatus status;
+    int i;
+
+    for (i = 0; i <= !!(g_deint > 1); i++) {
+        const unsigned int flags = (get_field_flags(i) |
+                                    get_colorspace_flags() |
+                                    g_scaling);
+        status = vaPutSurface(va_context->display,
+                              surface->id,
+                              vo_window,
+                              0, 0, g_image_width, g_image_height,
+                              g_output_rect.left,
+                              g_output_rect.top,
+                              g_output_rect.width,
+                              g_output_rect.height,
+                              NULL, 0,
+                              flags);
+        if (!check_status(status, "vaPutSurface()"))
+            return;
+    }
+}
+
+#if CONFIG_GL
+static void put_surface_glx(struct vaapi_surface *surface)
+{
+    VAStatus status;
+    int i;
+
+    if (gl_use_tfp) {
+        for (i = 0; i <= !!(g_deint > 1); i++) {
+            const unsigned int flags = (get_field_flags(i) |
+                                        get_colorspace_flags() |
+                                        g_scaling);
+            status = vaPutSurface(va_context->display,
+                                  surface->id,
+                                  g_image_pixmap,
+                                  0, 0, g_image_width, g_image_height,
+                                  0, 0, g_image_width, g_image_height,
+                                  NULL, 0,
+                                  flags);
+            if (!check_status(status, "vaPutSurface()"))
+                return;
+        }
+        g_output_surfaces[g_output_surface] = surface;
+        return;
+    }
+
+#if CONFIG_VAAPI_GLX
+    if (gl_binding) {
+#if USE_VAAPI_GLX_BIND
+        for (i = 0; i <= !!(g_deint > 1); i++) {
+            const unsigned int flags = (get_field_flags(i) |
+                                        get_colorspace_flags() |
+                                        g_scaling);
+            status = vaAssociateSurfaceGLX(va_context->display,
+                                           gl_surface,
+                                           surface->id,
+                                           flags);
+            if (!check_status(status, "vaAssociateSurfaceGLX()"))
+                return;
+        }
+#else
+        mp_msg(MSGT_VO, MSGL_WARN, "vaAssociateSurfaceGLX() is not implemented\n");
+        gl_binding = 0;
+#endif
+    }
+
+    if (!gl_binding) {
+        for (i = 0; i <= !!(g_deint > 1); i++) {
+            const unsigned int flags = (get_field_flags(i) |
+                                        get_colorspace_flags() |
+                                        g_scaling);
+            status = vaCopySurfaceGLX(va_context->display,
+                                      gl_surface,
+                                      surface->id,
+                                      flags);
+
+            if (status == VA_STATUS_ERROR_UNIMPLEMENTED) {
+                mp_msg(MSGT_VO, MSGL_WARN,
+                       "[vo_vaapi] vaCopySurfaceGLX() is not implemented\n");
+                gl_binding = 1;
+            }
+            else {
+                if (!check_status(status, "vaCopySurfaceGLX()"))
+                    return;
+            }
+        }
+    }
+#endif
+    g_output_surfaces[g_output_surface] = surface;
+}
+
+static int glx_bind_texture(void)
+{
+    glEnable(GL_TEXTURE_2D);
+    mpglBindTexture(GL_TEXTURE_2D, gl_texture);
+
+    if (gl_use_tfp) {
+        x11_trap_errors();
+        mpglXBindTexImage(mDisplay, gl_pixmap, GLX_FRONT_LEFT_EXT, NULL);
+        XSync(mDisplay, False);
+        if (x11_untrap_errors())
+            mp_msg(MSGT_VO, MSGL_WARN, "[vo_vaapi] Update bind_tex_image failed\n");
+    }
+
+#if USE_VAAPI_GLX_BIND
+    if (gl_binding) {
+        VAStatus status;
+        status = vaBeginRenderSurfaceGLX(va_context->display, gl_surface);
+        if (!check_status(status, "vaBeginRenderSurfaceGLX()"))
+            return -1;
+    }
+#endif
+    return 0;
+}
+
+static int glx_unbind_texture(void)
+{
+    if (gl_use_tfp) {
+        x11_trap_errors();
+        mpglXReleaseTexImage(mDisplay, gl_pixmap, GLX_FRONT_LEFT_EXT);
+        if (x11_untrap_errors())
+            mp_msg(MSGT_VO, MSGL_WARN, "[vo_vaapi] Failed to release?\n");
+    }
+
+#if USE_VAAPI_GLX_BIND
+    if (gl_binding) {
+        VAStatus status;
+        status = vaEndRenderSurfaceGLX(va_context->display, gl_surface);
+        if (!check_status(status, "vaEndRenderSurfaceGLX()"))
+            return -1;
+    }
+#endif
+
+    mpglBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_TEXTURE_2D);
+    return 0;
+}
+
+static void render_background(void)
+{
+    /* Original code from Mirco Muller (MacSlow):
+       <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
+    GLfloat fStartX = 0.0f;
+    GLfloat fStartY = 0.0f;
+    GLfloat fWidth  = (GLfloat)vo_dwidth;
+    GLfloat fHeight = (GLfloat)vo_dheight;
+
+    glBegin(GL_QUADS);
+    {
+        /* top third, darker grey to white */
+        glColor3f(0.85f, 0.85f, 0.85f);
+        glVertex3f(fStartX, fStartY, 0.0f);
+        glColor3f(0.85f, 0.85f, 0.85f);
+        glVertex3f(fStartX + fWidth, fStartY, 0.0f);
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
+
+        /* middle third, just plain white */
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
+        glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
+        glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
+        glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
+
+        /* bottom third, white to lighter grey */
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
+        glColor3f(0.62f, 0.66f, 0.69f);
+        glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
+        glColor3f(0.62f, 0.66f, 0.69f);
+        glVertex3f(fStartX, fStartY + fHeight, 0.0f);
+    }
+    glEnd();
+}
+
+static void render_frame(void)
+{
+    struct vo_rect * const r = &g_output_rect;
+
+    if (glx_bind_texture() < 0)
+        return;
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    glBegin(GL_QUADS);
+    {
+        glTexCoord2f(0.0f, 0.0f); glVertex2i(r->left, r->top);
+        glTexCoord2f(0.0f, 1.0f); glVertex2i(r->left, r->bottom);
+        glTexCoord2f(1.0f, 1.0f); glVertex2i(r->right, r->bottom);
+        glTexCoord2f(1.0f, 0.0f); glVertex2i(r->right, r->top);
+    }
+    glEnd();
+    if (glx_unbind_texture() < 0)
+        return;
+}
+
+static void render_reflection(void)
+{
+    struct vo_rect * const r = &g_output_rect;
+    const unsigned int rh  = g_output_rect.height / 5;
+    GLfloat ry = 1.0f - (GLfloat)rh / (GLfloat)r->height;
+
+    if (glx_bind_texture() < 0)
+        return;
+    glBegin(GL_QUADS);
+    {
+        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+        glTexCoord2f(0.0f, 1.0f); glVertex2i(r->left, r->top);
+        glTexCoord2f(1.0f, 1.0f); glVertex2i(r->right, r->top);
+
+        glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
+        glTexCoord2f(1.0f, ry); glVertex2i(r->right, r->top + rh);
+        glTexCoord2f(0.0f, ry); glVertex2i(r->left, r->top + rh);
+    }
+    glEnd();
+    if (glx_unbind_texture() < 0)
+        return;
+}
+
+static void flip_page_glx(void)
+{
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    if (gl_reflect) {
+        render_background();
+
+        glPushMatrix();
+        glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
+        glTranslatef(50.0f, 0.0f, 0.0f);
+    }
+
+    render_frame();
+
+    if (gl_reflect) {
+        glPushMatrix();
+        glTranslatef(0.0, (GLfloat)g_output_rect.height + 5.0f, 0.0f);
+        render_reflection();
+        glPopMatrix();
+        glPopMatrix();
+    }
+
+    if (cpu_stats) {
+        gl_draw_rectangle(0, 0, vo_dwidth, 32, 0x000000ff);
+        glColor3f(1.0f, 1.0f, 1.0f);
+        glRasterPos2i(16, 20);
+        gl_printf("MPlayer: %.1f%% of CPU @ %u MHz", cpu_usage, cpu_frequency);
+    }
+
+    if (gl_finish)
+        mpglFinish();
+    gl_context.swapGlBuffers(&gl_context);
+
+    if (vo_fs) /* avoid flickering borders in fullscreen mode */
+        glClear(GL_COLOR_BUFFER_BIT);
+}
+#endif
+
+#if CONFIG_XRENDER
+static void put_surface_xrender(struct vaapi_surface *surface)
+{
+    VAStatus status;
+    int i;
+
+    for (i = 0; i <= !!(g_deint > 1); i++) {
+        const unsigned int flags = (get_field_flags(i) |
+                                    get_colorspace_flags() |
+                                    g_scaling);
+        status = vaPutSurface(va_context->display,
+                              surface->id,
+                              g_image_pixmap,
+                              0, 0, g_image_width, g_image_height,
+                              0, 0, g_output_rect.width, g_output_rect.height,
+                              NULL, 0,
+                              flags);
+        if (!check_status(status, "vaPutSurface()"))
+            return;
+        XRenderComposite(mDisplay,
+                         PictOpSrc, xr_video_picture, 0, xr_window_picture,
+                         0, 0,
+                         0, 0,
+                         g_output_rect.left, g_output_rect.top,
+                         g_output_rect.width, g_output_rect.height);
+    }
+}
+#endif
+
+static void put_surface(struct vaapi_surface *surface)
+{
+    if (!surface || surface->id == VA_INVALID_SURFACE)
+        return;
+
+#if CONFIG_GL
+    if (gl_enabled)
+        put_surface_glx(surface);
+    else
+#endif
+#if CONFIG_XRENDER
+    if (xr_enabled)
+        put_surface_xrender(surface);
+    else
+#endif
+        put_surface_x11(surface);
+}
+
+static int draw_slice(uint8_t * image[], int stride[],
+                      int w, int h, int x, int y)
+{
+    struct vaapi_surface * const surface = va_free_surfaces[g_output_surface];
+    VAImage * const va_image = &surface->image;
+    VAStatus status;
+    uint8_t *image_data = NULL;
+    uint8_t *dst[3] = { 0, };
+    unsigned int dst_stride[3];
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] draw_slice(): location (%d,%d), size %dx%d\n", x, y, w, h);
+
+    status = vaMapBuffer(va_context->display, va_image->buf,
+                         (void *)&image_data);
+    if (!check_status(status, "vaMapBuffer()"))
+        return VO_FALSE;
+
+    dst_stride[0] = va_image->pitches[0];
+    dst[0] = image_data + va_image->offsets[0] + y * dst_stride[0] + x;
+
+    memcpy_pic(dst[0], image[0], w, h, dst_stride[0], stride[0]);
+
+    x /= 2;
+    y /= 2;
+    w /= 2;
+    h /= 2;
+
+    if (g_image_format == IMGFMT_YV12) {
+        /* MPlayer's YV12 is actually I420, so swap U/V components */
+        dst_stride[1] = va_image->pitches[2];
+        dst[1] = image_data + va_image->offsets[2] + y * dst_stride[1] + x;
+        dst_stride[2] = va_image->pitches[1];
+        dst[2] = image_data + va_image->offsets[1] + y * dst_stride[2] + x;
+    }
+    else {
+        if (image[1]) {
+            dst_stride[1] = va_image->pitches[1];
+            dst[1] = image_data + va_image->offsets[1] + y * dst_stride[1] + x;
+        }
+        if (image[2]) {
+            dst_stride[2] = va_image->pitches[2];
+            dst[2] = image_data + va_image->offsets[2] + y * dst_stride[2] + x;
+        }
+    }
+
+    if (image[1]) /* RGBA only has a single plane */
+        memcpy_pic(dst[1], image[1], w, h, dst_stride[1], stride[1]);
+
+    if (image[2]) /* NV12 only has two planes */
+        memcpy_pic(dst[2], image[2], w, h, dst_stride[2], stride[2]);
+
+    status = vaUnmapBuffer(va_context->display, surface->image.buf);
+    if (!check_status(status, "vaUnmapBuffer()"))
+        return VO_FALSE;
+
+    return VO_TRUE;
+}
+
+static int draw_frame(uint8_t * src[])
+{
+    mp_msg(MSGT_VO,MSGL_INFO, MSGTR_LIBVO_X11_DrawFrameCalled);
+
+    return -1;
+}
+
+static void draw_osd(void)
+{
+    VAStatus status;
+    const int osd_width  = va_osd_image.width;
+    const int osd_height = va_osd_image.height;
+
+    ensure_osd();
+    if (va_osd_image.image_id == VA_INVALID_ID)
+        return;
+
+    if (!va_osd_draw_alpha)
+        return;
+
+    if (!vo_update_osd(osd_width, osd_height))
+        return;
+
+    if (!vo_osd_check_range_update(0, 0, osd_width, osd_height)) {
+        disable_osd();
+        return;
+    }
+
+    status = vaMapBuffer(va_context->display, va_osd_image.buf,
+                         (void *)&va_osd_image_data);
+    if (!check_status(status, "vaMapBuffer()"))
+        return;
+
+    memset(va_osd_image_data, 0, va_osd_image.data_size);
+
+    vo_draw_text(osd_width, osd_height, va_osd_draw_alpha);
+
+    status = vaUnmapBuffer(va_context->display, va_osd_image.buf);
+    if (!check_status(status, "vaUnmapBuffer()"))
+        return;
+    va_osd_image_data = NULL;
+
+    enable_osd();
+}
+
+static void draw_eosd(struct mp_eosd_image_list *imgs)
+{
+    struct mp_eosd_image *img = eosd_image_first(imgs);
+    struct mp_eosd_image *i;
+    VAStatus status;
+
+    if (!va_eosd_draw_alpha)
+        return;
+
+    // Nothing changed, no need to redraw
+    if (imgs->changed == 0)
+        return;
+
+    // There's nothing to render!
+    if (!img) {
+        disable_eosd();
+        return;
+    }
+
+    if (imgs->changed == 1)
+        goto eosd_skip_upload;
+
+    status = vaMapBuffer(va_context->display, va_eosd_image.buf,
+                         (void *)&va_eosd_image_data);
+    if (!check_status(status, "vaMapBuffer()"))
+        return;
+
+    memset(va_eosd_image_data, 0, va_eosd_image.data_size);
+
+    for (i = img; i; i = i->next)
+        va_eosd_draw_alpha(i->bitmap, i->w, i->h, i->stride,
+                           i->dst_x, i->dst_y, i->color);
+
+    status = vaUnmapBuffer(va_context->display, va_eosd_image.buf);
+    if (!check_status(status, "vaUnmapBuffer()"))
+        return;
+    va_eosd_image_data = NULL;
+
+eosd_skip_upload:
+    enable_eosd();
+}
+
+static void flip_page(void)
+{
+    struct vaapi_surface *surface;
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] flip_page()\n");
+
+    surface = g_output_surfaces[g_output_surface];
+    if (!surface)
+        return;
+
+    put_surface(surface);
+    g_output_surface = (g_output_surface + 1) % MAX_OUTPUT_SURFACES;
+    g_is_visible     = 1;
+
+#if CONFIG_GL
+    if (gl_enabled)
+        flip_page_glx();
+#endif
+}
+
+static struct vaapi_surface *get_surface(mp_image_t *mpi)
+{
+    struct vaapi_surface *surface;
+
+    if (mpi->type == MP_IMGTYPE_NUMBERED && is_direct_mapping()) {
+        assert(mpi->number < va_num_surfaces);
+        surface = va_free_surfaces[mpi->number];
+        return surface;
+    }
+
+    /* Push current surface to a free slot */
+    if (mpi->priv) {
+        assert(!va_free_surfaces[va_free_surfaces_tail_index]);
+        va_free_surfaces[va_free_surfaces_tail_index] = mpi->priv;
+        va_free_surfaces_tail_index = (va_free_surfaces_tail_index + 1) % va_num_surfaces;
+    }
+
+    /* Pop the least recently used free surface */
+    assert(va_free_surfaces[va_free_surfaces_head_index]);
+    surface = va_free_surfaces[va_free_surfaces_head_index];
+    va_free_surfaces[va_free_surfaces_head_index] = NULL;
+    va_free_surfaces_head_index = (va_free_surfaces_head_index + 1) % va_num_surfaces;
+    return surface;
+}
+
+static uint32_t get_image(mp_image_t *mpi)
+{
+    struct vaapi_surface *surface;
+
+    if (mpi->type != MP_IMGTYPE_NUMBERED)
+        return VO_FALSE;
+
+    if (!IMGFMT_IS_VAAPI(g_image_format))
+        return VO_FALSE;
+
+    surface = get_surface(mpi);
+    if (!surface)
+        return VO_FALSE;
+
+    mpi->flags |= MP_IMGFLAG_DIRECT;
+    mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = mpi->stride[3] = 0;
+    mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = mpi->planes[3] = NULL;
+    mpi->planes[0] = (char *)surface;
+    mpi->planes[3] = (char *)(uintptr_t)surface->id;
+    mpi->num_planes = 1;
+    mpi->priv = surface;
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] get_image(): surface 0x%08x\n", surface->id);
+
+    return VO_TRUE;
+}
+
+static int put_image(mp_image_t *mpi, struct vaapi_surface *surface)
+{
+    VAStatus status;
+
+    if ((mpi->flags & (MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV)) != (MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV))
+        return VO_FALSE;
+
+    if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
+        if (!draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0))
+            return VO_FALSE;
+    }
+
+    if (!surface->is_bound) {
+        status = vaPutImage2(va_context->display,
+                             surface->id,
+                             surface->image.image_id,
+                             mpi->x, mpi->y, mpi->w, mpi->h,
+                             mpi->x, mpi->y, mpi->w, mpi->h);
+        if (!check_status(status, "vaPutImage()"))
+            return VO_FALSE;
+    }
+
+    return VO_TRUE;
+}
+
+static uint32_t draw_image(mp_image_t *mpi)
+{
+    struct vaapi_surface *surface = (struct vaapi_surface *)mpi->priv;
+
+    g_image_fields = mpi->fields;
+
+    if (!IMGFMT_IS_VAAPI(mpi->imgfmt)) {
+        /* XXX: no direct rendering in non-accelerated mode */
+        surface = va_free_surfaces[g_output_surface];
+        if (!put_image(mpi, surface))
+            return VO_FALSE;
+    }
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] draw_image(): surface 0x%08x\n", surface->id);
+
+    g_output_surfaces[g_output_surface] = surface;
+
+    if (cpu_stats) {
+        static uint64_t ticks;
+        if ((ticks++ % 30) == 0) {
+            cpu_frequency = get_cpu_frequency();
+            cpu_usage = get_cpu_usage(CPU_USAGE_QUANTUM);
+        }
+    }
+    return VO_TRUE;
+}
+
+static void check_events(void)
+{
+    int events = vo_x11_check_events(mDisplay);
+
+    if (events & VO_EVENT_RESIZE)
+        resize();
+
+    if ((events & (VO_EVENT_EXPOSE|VO_EVENT_RESIZE)) && g_is_paused) {
+        /* Redraw the last visible buffer  */
+        if (g_is_visible) {
+            struct vaapi_surface *surface = g_output_surfaces[g_output_surface];
+            if (surface)
+                put_surface(surface);
+        }
+    }
+}
+
+static VADisplayAttribute *get_display_attribute(const char *name)
+{
+    VADisplayAttribute *attr;
+    if (!strcasecmp(name, "brightness"))
+        attr = &va_equalizer.brightness;
+    else if (!strcasecmp(name, "contrast"))
+        attr = &va_equalizer.contrast;
+    else if (!strcasecmp(name, "saturation"))
+        attr = &va_equalizer.saturation;
+    else if (!strcasecmp(name, "hue"))
+        attr = &va_equalizer.hue;
+    else
+        attr = NULL;
+    return attr;
+}
+
+static int get_equalizer(const char *name, int *value)
+{
+    VADisplayAttribute * const attr = get_display_attribute(name);
+    int r;
+
+    if (!attr || !(attr->flags & VA_DISPLAY_ATTRIB_GETTABLE))
+        return VO_NOTIMPL;
+
+    /* normalize to -100 .. 100 range */
+    r = attr->max_value - attr->min_value;
+    if (r == 0)
+        return VO_NOTIMPL;
+    *value = ((attr->value - attr->min_value) * 200) / r - 100;
+    return VO_TRUE;
+}
+
+static int set_equalizer(const char *name, int value)
+{
+    VADisplayAttribute * const attr = get_display_attribute(name);
+    VAStatus status;
+    int r;
+
+    if (!attr || !(attr->flags & VA_DISPLAY_ATTRIB_SETTABLE))
+        return VO_NOTIMPL;
+
+    /* normalize to attribute value range */
+    r = attr->max_value - attr->min_value;
+    if (r == 0)
+        return VO_NOTIMPL;
+    attr->value = ((value + 100) * r) / 200 + attr->min_value;
+
+    status = vaSetDisplayAttributes(va_context->display, attr, 1);
+    if (!check_status(status, "vaSetDisplayAttributes()"))
+        return VO_FALSE;
+    return VO_TRUE;
+}
+
+static int control(uint32_t request, void *data, ...)
+{
+    switch (request) {
+    case VOCTRL_GET_DEINTERLACE:
+        *(int*)data = g_deint;
+        return VO_TRUE;
+    case VOCTRL_SET_DEINTERLACE:
+        g_deint = *(int*)data;
+        if (g_deint)
+            g_deint = g_deint_type;
+        return VO_TRUE;
+    case VOCTRL_PAUSE:
+        return (g_is_paused = 1);
+    case VOCTRL_RESUME:
+        return (g_is_paused = 0);
+    case VOCTRL_QUERY_FORMAT:
+        return query_format(*((uint32_t *)data));
+    case VOCTRL_GET_IMAGE:
+        return get_image(data);
+    case VOCTRL_DRAW_IMAGE:
+        return draw_image(data);
+    case VOCTRL_GUISUPPORT:
+        return VO_TRUE;
+    case VOCTRL_BORDER:
+        vo_x11_border();
+        resize();
+        return VO_TRUE;
+    case VOCTRL_FULLSCREEN:
+        vo_x11_fullscreen();
+        resize();
+        return VO_TRUE;
+    case VOCTRL_SET_EQUALIZER: {
+        va_list ap;
+        int value;
+
+        va_start(ap, data);
+        value = va_arg(ap, int);
+
+        va_end(ap);
+        return set_equalizer(data, value);
+    }
+    case VOCTRL_GET_EQUALIZER: {
+        va_list ap;
+        int *value;
+
+        va_start(ap, data);
+        value = va_arg(ap, int *);
+
+        va_end(ap);
+        return get_equalizer(data, value);
+    }
+    case VOCTRL_ONTOP:
+        vo_x11_ontop();
+        return VO_TRUE;
+    case VOCTRL_UPDATE_SCREENINFO:
+        update_xinerama_info();
+        return VO_TRUE;
+    case VOCTRL_GET_PANSCAN:
+        return VO_TRUE;
+    case VOCTRL_SET_PANSCAN:
+        resize();
+        return VO_TRUE;
+    case VOCTRL_GET_HWACCEL_CONTEXT:
+        *((void **)data) = va_context;
+        return VO_TRUE;
+    case VOCTRL_DRAW_EOSD:
+        if (!data)
+            return VO_FALSE;
+        draw_eosd(data);
+        return VO_TRUE;
+    case VOCTRL_GET_EOSD_RES: {
+        struct mp_eosd_settings *r = data;
+        r->mt = r->mb = r->ml = r->mr = 0;
+        r->srcw = g_image_width;
+        r->srch = g_image_height;
+        r->w    = g_image_width;
+        r->h    = g_image_height;
+        return VO_TRUE;
+    }
+    }
+    return VO_NOTIMPL;
+}