diff --git a/Makefile b/Makefile index 2de75fb..fd4e503 100644 --- a/Makefile +++ b/Makefile @@ -590,6 +590,7 @@ SRCS_MPLAYER-$(TDFXVID) += libvo/vo_tdfx_vid.c SRCS_MPLAYER-$(TGA) += libvo/vo_tga.c SRCS_MPLAYER-$(V4L2) += libvo/vo_v4l2.c SRCS_MPLAYER-$(V4L2) += libao2/ao_v4l2.c +SRCS_MPLAYER-$(VAAPI) += libvo/vo_vaapi.c SRCS_MPLAYER-$(VDPAU) += libvo/vo_vdpau.c SRCS_MPLAYER-$(VESA) += libvo/gtf.c libvo/vo_vesa.c libvo/vesa_lvo.c @@ -643,6 +644,7 @@ SRCS_MPLAYER = command.c \ libao2/audio_out.c \ libvo/aspect.c \ libvo/geometry.c \ + libvo/stats.c \ libvo/old_vo_wrapper.c \ libvo/spuenc.c \ libvo/video_out.c \ libvo/vo_mpegpes.c \ diff --git a/cfg-common.h b/cfg-common.h index c6dfef0..a280890 100644 --- a/cfg-common.h +++ b/cfg-common.h @@ -521,6 +521,7 @@ const m_option_t common_opts[] = { {"vfm", &video_fm_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL}, {"ac", &audio_codec_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL}, {"vc", &video_codec_list, CONF_TYPE_STRING_LIST, 0, 0, 0, NULL}, + {"va", &video_hwaccel_name, CONF_TYPE_STRING, 0, 0, 0, NULL}, // postprocessing: #ifdef CONFIG_FFMPEG diff --git a/codec-cfg.c b/codec-cfg.c index 4db74c5..2f160fb 100644 --- a/codec-cfg.c +++ b/codec-cfg.c @@ -224,6 +224,13 @@ static const struct { {"VDPAU_VC1",IMGFMT_VDPAU_VC1}, {"VDPAU_MPEG4",IMGFMT_VDPAU_MPEG4}, + {"VAAPI_MPEG2", IMGFMT_VAAPI_MPEG2}, + {"VAAPI_MPEG4", IMGFMT_VAAPI_MPEG4}, + {"VAAPI_H263", IMGFMT_VAAPI_H263}, + {"VAAPI_H264", IMGFMT_VAAPI_H264}, + {"VAAPI_WMV3", IMGFMT_VAAPI_WMV3}, + {"VAAPI_VC1", IMGFMT_VAAPI_VC1}, + {NULL, 0} }; diff --git a/configure b/configure index 1e0f7fc..45e4574 100755 --- a/configure +++ b/configure @@ -401,8 +401,10 @@ Video output: --enable-dvb enable DVB video output [autodetect] --enable-mga enable mga_vid video output [autodetect] --enable-xmga enable mga_vid X11 video output [autodetect] + --enable-xrender enable Xrender video output [autodetect] --enable-xv enable Xv video output [autodetect] --enable-xvmc enable XvMC acceleration [disable] + --enable-vaapi enable VA-API acceleration [disable] --enable-vdpau enable VDPAU acceleration [autodetect] --enable-vm enable XF86VidMode support [autodetect] --enable-xinerama enable Xinerama support [autodetect] @@ -506,6 +506,7 @@ Use these options if autodetection fails: --extra-libs-mplayer=FLAGS extra linker flags for MPlayer --with-xvmclib=NAME adapter-specific library name (e.g. XvMCNVIDIA) + --with-vaapi-prefix=PATH prefix to VA-API include and lib directories --with-freetype-config=PATH path to freetype-config --with-sdl-config=PATH path to sdl*-config --with-dvdnav-config=PATH path to dvdnav-config @@ -555,11 +555,15 @@ _prefix="/usr/local" ffmpeg=auto ffmpeg_internals=no _mplayer=yes +_vaapi=auto +_vaapi_glx=no +_libgtop=auto _x11=auto _xshape=auto _xss=auto _dga1=auto _dga2=auto +_xrender=auto _xv=auto _xvmc=no #auto when complete _vdpau=auto @@ -855,6 +863,9 @@ for ac_option do _xvmclib=$(echo $ac_option | cut -d '=' -f 2) ;; + --with-vaapi-prefix=*) + _vaapiprefix=$(echo $ac_option | cut -d '=' -f 2) + ;; --with-sdl-config=*) _sdlconfig=$(echo $ac_option | cut -d '=' -f 2) ;; @@ -970,10 +981,14 @@ for ac_option do --disable-xshape) _xshape=no ;; --enable-xss) _xss=yes ;; --disable-xss) _xss=no ;; + --enable-xrender) _xrender=yes ;; + --disable-xrender) _xrender=no ;; --enable-xv) _xv=yes ;; --disable-xv) _xv=no ;; --enable-xvmc) _xvmc=yes ;; --disable-xvmc) _xvmc=no ;; + --enable-vaapi) _vaapi=yes ;; + --disable-vaapi) _vaapi=no ;; --enable-vdpau) _vdpau=yes ;; --disable-vdpau) _vdpau=no ;; --enable-sdl) _sdl=yes ;; @@ -1068,6 +1083,8 @@ for ac_option do --disable-mad) _mad=no ;; --enable-libcdio) _libcdio=yes ;; --disable-libcdio) _libcdio=no ;; + --enable-libgtop) _libgtop=yes ;; + --disable-libgtop) _libgtop=no ;; --enable-libvorbis) _libvorbis=yes ;; --disable-libvorbis) _libvorbis=no ;; --enable-speex) _speex=yes ;; @@ -3895,6 +3912,28 @@ fi echores "$_gettimeofday" +echocheck "clock_gettime()" +cat > $TMPC << EOF +#include +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 < +#include +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 +#include +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 +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 < +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 < +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 +#include +#include +#include +#include +#include + +#if CONFIG_LIBGTOP +#include +#include +#include +#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 + +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 + +#if CONFIG_GL +#include "gl_common.h" +#include +#include +#endif + +#include +#include +#include +#include +#if CONFIG_VAAPI_GLX +#include +#endif + +#if CONFIG_XRENDER +#include +#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 ", + "" +}; + +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 ""; +} + +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 ""; +} + +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): + */ + 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; +}