rendered paste bodydiff --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;
+}