Web lists-archives.com

[MPlayer-dev-eng] VAAPI/X11 video output driver





Hi,

See patch attached: apply to current trunk mplayer, configure, make
mplayer, run with -vo vaapi on anything using recent Intel graphics (or
something else with VAAPI, maybe).

This is very much in a "works for me" state.  If it's useful to anyone
else then they can use it as-is, and if it or some derivative is
sensible to integrate into mplayer then that would be nice.  There are
probably lots of edge cases I haven't considered which crash it.  It's
also quite possible that I have broken other things with these changes
(especially VDPAU and XvMC in the libavcodec driver).


Features:
  * Plays H.264, H.265, WMV3, VC1 and MPEG2 streams on Intel Quick Sync.
  * Fully accelerated, with no extra redundant memcpy of image data
(looking at you, VLC).
  * No ffmpeg changes required - uses the hwaccel interface.
  * Interface controls all work normally (keyboard input, resize,
fullscreen).


Deficiencies:
  * It can only output to an X11 window (maybe it should be called
vaapi_x11).  OpenGL output probably wouldn't be hard to add to the
existing code; alternatively there could separately be a filter which
comes after a decoder to convert frames to a format usable in other places.
  * Tearing is visible.  There is probably some nice X11 way to fix this
by drawing to multiple drawables and swapping.
  * Choosing video formats can do strange things.  If you try to play an
MPEG4 stream on Intel (not supported by Quick Sync), it gets stuck.
  * It can't play in non-accelerated mode (software-decoded planes).
This could straightforwardly be rectified (just memcpy data into a
surface), but it doesn't have any obvious value because other outputs
already do this properly.
  * No OSD support.  Unclear how to do it nicely (map the surface and
draw directly on it; do something clever with transparency?).
  * No deinterlacing or other filter features.
  * Cleanup on errors is incomplete (will leak memory and possibly do
other nasty things).


Tested configurations (on Debian Stretch, libva 0.38):
  * 7th generation Intel graphics (Haswell 4500U): H.264, WMV3, VC1.
  * 8th generation Intel graphics (Braswell N3700): as 7th, plus H.265
(using the GPU hybrid decode, which works completely transparently).

I have no reason to believe that 9th generation (Skylake) will not work;
similarly older versions (with correspondingly less capability).  I
don't know about other (non-Quick Sync) VAAPI drivers.

I tried resolutions up to 4K.  The Haswell was perfectly happy playing
eight simultaneous 30Mbps 4K25 H.264 streams with downscale to 720p,
needing about 20% CPU to do it.

VP8 and VP9 (present in 8th and 9th generation Intel graphics
respectively) are not supported because they are not yet implemented in
ffmpeg.  If they were and matched the existing drivers, I imagine they
would just work after updating a few lists.


Some questions:
  * How is the video format choice meant to work?  I feel like I am
rejecting everything offered which isn't VAAPI hwaccel from libavcodec,
but nasty things still happen if you try to play an unsupported stream
(it doesn't give up).
  * How is information meant be passed back from libavcodec to the vo
driver?  This patch makes a nasty global to release frames allocated by
the output once they are finished with (no longer needed for
referencing).  Also it would be nice to know the number of reference
frames the stream requires so that we don't waste so much memory.
  * Same question in the other direction.  There is another nasty global
for passing the hwaccel context into libavcodec.
  * What libvo controls (VOCTRL_*) and related support are regarded as
required for a new VO driver?


Thanks,

- Mark

Index: Makefile
===================================================================
--- Makefile	(revision 37558)
+++ Makefile	(working copy)
@@ -556,6 +556,7 @@
 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                        \
Index: codec-cfg.c
===================================================================
--- codec-cfg.c	(revision 37558)
+++ codec-cfg.c	(working copy)
@@ -258,6 +258,13 @@
     {"VDPAU_MPEG4", IMGFMT_VDPAU_MPEG4},
     {"VDPAU_HEVC",  IMGFMT_VDPAU_HEVC},
 
+    {"VAAPI_MPEG2", IMGFMT_VAAPI_MPEG2},
+    {"VAAPI_H264",  IMGFMT_VAAPI_H264},
+    {"VAAPI_WMV3",  IMGFMT_VAAPI_WMV3},
+    {"VAAPI_VC1",   IMGFMT_VAAPI_VC1},
+    {"VAAPI_MPEG4", IMGFMT_VAAPI_MPEG4},
+    {"VAAPI_HEVC",  IMGFMT_VAAPI_HEVC},
+
     {NULL,    0}
 };
 
Index: configure
===================================================================
--- configure	(revision 37558)
+++ configure	(working copy)
@@ -487,6 +487,7 @@
   --enable-xvmc            enable XvMC acceleration [disable]
   --enable-vda             enable VDA acceleration [autodetect]
   --enable-vdpau           enable VDPAU acceleration [autodetect]
+  --enable-vaapi           enable VAAPI acceleration [autodetect]
   --enable-vm              enable XF86VidMode support [autodetect]
   --enable-xinerama        enable Xinerama support [autodetect]
   --enable-x11             enable X11 video output [autodetect]
@@ -677,6 +678,7 @@
 _xvmc=no  #auto when complete
 _vda=auto
 _vdpau=auto
+_vaapi=auto
 _sdl=auto
 _kva=auto
 _direct3d=auto
@@ -1026,6 +1028,8 @@
   --disable-vda)        _vda=no         ;;
   --enable-vdpau)       _vdpau=yes      ;;
   --disable-vdpau)      _vdpau=no       ;;
+  --enable-vaapi)       _vaapi=yes      ;;
+  --disable-vaapi)      _vaapi=no       ;;
   --enable-sdl)         _sdl=yes        ;;
   --disable-sdl)        _sdl=no         ;;
   --enable-kva)         _kva=yes        ;;
@@ -4764,6 +4768,26 @@
 echores "$_vdpau"
 
 
+echocheck "VAAPI"
+if test "$_vaapi" = auto && test "$_x11" = yes ; then
+  _vaapi=no
+  if test "$_dl" = yes ; then
+    statement_check va/va_x11.h 'vaGetDisplay(0)' -lva -lva-x11 && _vaapi=yes
+  fi
+fi
+if test "$_vaapi" = yes ; then
+  def_vaapi='#define CONFIG_VAAPI 1'
+  libs_mplayer="$libs_mplayer -lva -lva-x11"
+  vomodules="vaapi $vomodules"
+  libavhwaccels="$libavhwaccels H263_VAAPI_HWACCEL H264_VAAPI_HWACCEL HEVC_VAAPI_HWACCEL MPEG2_VAAPI_HWACCEL MPEG4_VAAPI_HWACCEL VC1_VAAPI_HWACCEL WMV3_VAAPI_HWACCEL"
+else
+  def_vaapi='#define CONFIG_VAAPI 0'
+  novomodules="vaapi $novomodules"
+  libavdecoders=$(filter_out_component decoder '[A-Z0-9]*_VAAPI')
+fi
+echores "$_vaapi"
+
+
 echocheck "Xinerama"
 if test "$_xinerama" = auto && test "$_x11" = yes ; then
   _xinerama=no
@@ -8520,6 +8544,7 @@
 VCD = $_vcd
 VDA = $_vda
 VDPAU = $_vdpau
+VAAPI = $_vaapi
 VESA = $_vesa
 VIDIX = $_vidix
 VIDIX_PCIDB = $_vidix_pcidb_val
@@ -8697,6 +8722,7 @@
 CONFIG_VF_LAVFI = $_vf_lavfi
 CONFIG_VDA      = $_vda
 CONFIG_VDPAU    = $_vdpau
+CONFIG_VAAPI    = $_vaapi
 CONFIG_XVMC     = $_xvmc
 CONFIG_ZLIB     = $_zlib
 
@@ -9084,6 +9110,7 @@
 $def_tga
 $def_v4l2
 $def_vdpau
+$def_vaapi
 $def_vesa
 $def_vidix
 $def_vidix_drv_cyberblade
@@ -9244,7 +9271,6 @@
 #define CONFIG_SWSCALE_ALPHA 1
 #define SWS_MAX_FILTER_SIZE 256
 #define CONFIG_QSV 0
-#define CONFIG_VAAPI 0
 
 #define HAVE_ALIGNED_STACK 1
 #define HAVE_AS_DN_DIRECTIVE 1
Index: etc/codecs.conf
===================================================================
--- etc/codecs.conf	(revision 37558)
+++ etc/codecs.conf	(working copy)
@@ -408,6 +408,7 @@
   out 422P,444P
   out IDCT_MPEG2
   out MOCO_MPEG2
+  out VAAPI_MPEG2
 
 ; for backward compatibility
 videocodec ffmpeg12
@@ -1252,6 +1253,7 @@
   driver ffmpeg
   dll wmv3
   out YV12,I420,IYUV
+  out VAAPI_WMV3
 
 videocodec ffwvp2
   info "FFmpeg WVP2"
@@ -1293,6 +1295,7 @@
   driver ffmpeg
   dll vc1
   out YV12,I420,IYUV
+  out VAAPI_VC1
 
 videocodec ffvc1vdpau
   info "FFmpeg WVC1 (VDPAU)"
@@ -1347,6 +1350,7 @@
   out YV12,420P9,420P10,420P12
   out 422P,422P9,422P10,444P12
   out 444P,444P9,444P10,444P12
+  out VAAPI_HEVC
 
 videocodec ffhevcvdpau
   info "FFmpeg HEVC / H.265 (VDPAU)"
@@ -1382,6 +1386,7 @@
   out 422P,422P9,422P10,444P12,444P14
   out 444P,444P9,444P10,444P12,444P14
   out GBR24P,GBR12P,GBR14P
+  out VAAPI_H264
 
 videocodec ffh264vdpau
   info "FFmpeg H.264 (VDPAU)"
@@ -1509,6 +1514,7 @@
   driver ffmpeg
   dll mpeg4 ;opendivx
   out YV12,I420,IYUV
+  out VAAPI_MPEG4
 
 videocodec ffodivxvdpau
   info "FFmpeg MPEG-4,DIVX-4/5 (VDPAU)"
Index: fmt-conversion.c
===================================================================
--- fmt-conversion.c	(revision 37558)
+++ fmt-conversion.c	(working copy)
@@ -145,6 +145,7 @@
     int i;
     enum AVPixelFormat pix_fmt;
     if (IMGFMT_IS_VDPAU(fmt)) return AV_PIX_FMT_VDPAU;
+    if (IMGFMT_IS_VAAPI(fmt)) return AV_PIX_FMT_VAAPI;
     for (i = 0; conversion_map[i].fmt; i++)
         if (conversion_map[i].fmt == fmt)
             break;
Index: libmpcodecs/img_format.c
===================================================================
--- libmpcodecs/img_format.c	(revision 37558)
+++ libmpcodecs/img_format.c	(working copy)
@@ -143,6 +143,12 @@
     case IMGFMT_VDPAU_WMV3:      return "WMV3 VDPAU acceleration";
     case IMGFMT_VDPAU_VC1:       return "VC1 VDPAU acceleration";
     case IMGFMT_VDPAU_HEVC:      return "HEVC VDPAU acceleration";
+    case IMGFMT_VAAPI_MPEG2:     return "MPEG2 VAAPI acceleration";
+    case IMGFMT_VAAPI_H264:      return "H.264 VAAPI acceleration";
+    case IMGFMT_VAAPI_MPEG4:     return "MPEG-4 Part 2 VAAPI acceleration";
+    case IMGFMT_VAAPI_WMV3:      return "WMV3 VAAPI acceleration";
+    case IMGFMT_VAAPI_VC1:       return "VC1 VAAPI acceleration";
+    case IMGFMT_VAAPI_HEVC:      return "HEVC VAAPI acceleration";
     }
     snprintf(unknown_format,20,"Unknown 0x%04x",format);
     return unknown_format;
Index: libmpcodecs/img_format.h
===================================================================
--- libmpcodecs/img_format.h	(revision 37558)
+++ libmpcodecs/img_format.h	(working copy)
@@ -295,8 +295,20 @@
 #define IMGFMT_VDPAU_MPEG4         (IMGFMT_VDPAU|0x06)
 #define IMGFMT_VDPAU_HEVC          (IMGFMT_VDPAU|0x07)
 
-#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_XVMC(fmt))
+#define IMGFMT_VAAPI               0x1DC90000
+#define IMGFMT_VAAPI_MASK          0xFFFF0000
+#define IMGFMT_IS_VAAPI(fmt)       (((fmt) & IMGFMT_VAAPI_MASK) == IMGFMT_VAAPI)
+// (Or values match the VAProfile in libva, in case that's ever useful.)
+#define IMGFMT_VAAPI_MPEG2         (IMGFMT_VAAPI | 1)
+#define IMGFMT_VAAPI_MPEG4         (IMGFMT_VAAPI | 3)
+#define IMGFMT_VAAPI_H264          (IMGFMT_VAAPI | 7)
+#define IMGFMT_VAAPI_WMV3          (IMGFMT_VAAPI | 9)
+#define IMGFMT_VAAPI_VC1           (IMGFMT_VAAPI | 10)
+#define IMGFMT_VAAPI_HEVC          (IMGFMT_VAAPI | 17)
 
+#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_XVMC(fmt) || \
+                                IMGFMT_IS_VAAPI(fmt))
+
 typedef struct {
     void* data;
     int size;
Index: libmpcodecs/vd_ffmpeg.c
===================================================================
--- libmpcodecs/vd_ffmpeg.c	(revision 37558)
+++ libmpcodecs/vd_ffmpeg.c	(working copy)
@@ -40,6 +40,9 @@
 #if CONFIG_VDPAU
 #include "libavcodec/vdpau.h"
 #endif
+#if CONFIG_VAAPI
+#include "libavcodec/vaapi.h"
+#endif
 #include "libavutil/pixdesc.h"
 
 static const vd_info_t info = {
@@ -91,7 +94,11 @@
     int b_count;
     AVRational last_sample_aspect_ratio;
     int palette_sent;
-    int use_vdpau;
+    enum {
+        HWACCEL_MODE_NONE,
+        HWACCEL_MODE_VDPAU,
+        HWACCEL_MODE_VAAPI,
+    } hwaccel_mode;
 } vd_ffmpeg_ctx;
 
 #include "m_option.h"
@@ -241,6 +248,15 @@
         case AV_CODEC_ID_VC1:        return IMGFMT_VDPAU_VC1;
         case AV_CODEC_ID_HEVC:       return IMGFMT_VDPAU_HEVC;
         }
+    if (fmt == AV_PIX_FMT_VAAPI)
+        switch (cid) {
+        case AV_CODEC_ID_H264:       return IMGFMT_VAAPI_H264;
+        case AV_CODEC_ID_MPEG2VIDEO: return IMGFMT_VAAPI_MPEG2;
+        case AV_CODEC_ID_MPEG4:      return IMGFMT_VAAPI_MPEG4;
+        case AV_CODEC_ID_WMV3:       return IMGFMT_VAAPI_WMV3;
+        case AV_CODEC_ID_VC1:        return IMGFMT_VAAPI_VC1;
+        case AV_CODEC_ID_HEVC:       return IMGFMT_VAAPI_HEVC;
+        }
     return pixfmt2imgfmt(fmt);
 }
 
@@ -289,30 +305,55 @@
     int imgfmt;
     if (fmt == AV_PIX_FMT_NONE)
         return;
-    ctx->use_vdpau = fmt == AV_PIX_FMT_VDPAU;
     imgfmt = pixfmt2imgfmt2(fmt, avctx->codec_id);
+    switch(fmt) {
 #if CONFIG_VDPAU
-    if (!ctx->use_vdpau) {
+    case AV_PIX_FMT_VDPAU:
+        ctx->hwaccel_mode = HWACCEL_MODE_VDPAU;
+        {
+            AVVDPAUContext *vdpc = avctx->hwaccel_context;
+            if (!vdpc)
+                avctx->hwaccel_context = vdpc = av_alloc_vdpaucontext();
+            vdpc->render2 = vdpau_render_wrapper;
+        }
+        break;
+#endif
+#if CONFIG_VAAPI
+    case AV_PIX_FMT_VAAPI:
+        ctx->hwaccel_mode = HWACCEL_MODE_VAAPI;
+        {
+            struct vaapi_context *vctx = avctx->hwaccel_context;
+            if(!vctx) {
+                vctx = calloc(1, sizeof(struct vaapi_context));
+
+                extern void vo_vaapi_set_hwaccel_context(struct vaapi_context *hwaccel);
+                vo_vaapi_set_hwaccel_context(vctx);
+
+                avctx->hwaccel_context = vctx;
+            }
+        }
+        break;
+#endif
+    default:
+        ctx->hwaccel_mode = HWACCEL_MODE_NONE;
         av_freep(&avctx->hwaccel_context);
-    } else {
-        AVVDPAUContext *vdpc = avctx->hwaccel_context;
-        if (!vdpc)
-            avctx->hwaccel_context = vdpc = av_alloc_vdpaucontext();
-        vdpc->render2 = vdpau_render_wrapper;
+        break;
     }
-#endif
     if (IMGFMT_IS_HWACCEL(imgfmt)) {
         ctx->do_dr1    = 1;
         ctx->nonref_dr = 0;
         avctx->get_buffer2 = get_buffer2;
-        mp_msg(MSGT_DECVIDEO, MSGL_V, IMGFMT_IS_XVMC(imgfmt) ?
-               MSGTR_MPCODECS_XVMCAcceleratedMPEG2 :
-               "[VD_FFMPEG] VDPAU accelerated decoding\n");
-        if (ctx->use_vdpau) {
+        if (ctx->hwaccel_mode == HWACCEL_MODE_VDPAU ||
+            ctx->hwaccel_mode == HWACCEL_MODE_VAAPI) {
+            mp_msg(MSGT_DECVIDEO, MSGL_V,
+                   ctx->hwaccel_mode == HWACCEL_MODE_VDPAU ?
+                   "[VD_FFMPEG] VDPAU accelerated decoding\n" :
+                   "[VD_FFMPEG] VAAPI accelerated decoding\n");
             avctx->draw_horiz_band = NULL;
             avctx->slice_flags = 0;
             ctx->do_slices = 0;
         } else {
+            mp_msg(MSGT_DECVIDEO, MSGL_V, MSGTR_MPCODECS_XVMCAcceleratedMPEG2);
             avctx->draw_horiz_band = draw_slice;
             avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD;
             ctx->do_slices = 1;
@@ -547,8 +588,8 @@
         mp_msg(MSGT_DECVIDEO, MSGL_FATAL, "BUG in FFmpeg, draw_slice called with NULL pointer!\n");
         return;
     }
-    if (mpi && ctx->use_vdpau) {
-        mp_msg(MSGT_DECVIDEO, MSGL_FATAL, "BUG in FFmpeg, draw_slice called for VDPAU!\n");
+    if (mpi && ctx->hwaccel_mode != HWACCEL_MODE_NONE) {
+        mp_msg(MSGT_DECVIDEO, MSGL_FATAL, "BUG in FFmpeg, draw_slice called in hwaccel mode!\n");
         return;
     }
     if (height < 0)
@@ -730,28 +771,41 @@
         avctx->draw_horiz_band= draw_slice;
     } else
         avctx->draw_horiz_band= NULL;
+    switch(ctx->hwaccel_mode) {
 #if CONFIG_VDPAU
-    if (ctx->use_vdpau) {
-        VdpVideoSurface surface = (VdpVideoSurface)mpi->priv;
-        avctx->draw_horiz_band= NULL;
-        mpi->planes[3] = surface;
-    }
+    case HWACCEL_MODE_VDPAU:
+        {
+            VdpVideoSurface surface = (VdpVideoSurface)mpi->priv;
+            avctx->draw_horiz_band= NULL;
+            mpi->planes[3] = surface;
+        }
+        break;
 #endif
+#if CONFIG_VAAPI
+    case HWACCEL_MODE_VAAPI:
+        {
+            // This is the VASurfaceID.
+            mpi->planes[3] = mpi->priv;
+        }
+        break;
+#endif
 #if CONFIG_XVMC
-    if(IMGFMT_IS_XVMC(mpi->imgfmt)) {
-        struct xvmc_pix_fmt *render = mpi->priv; //same as data[2]
-        if(!(mpi->flags & MP_IMGFLAG_DIRECT)) {
-            mp_msg(MSGT_DECVIDEO, MSGL_ERR, MSGTR_MPCODECS_OnlyBuffersAllocatedByVoXvmcAllowed);
-            assert(0);
-            return -1;//!!fixme check error conditions in ffmpeg
+    case HWACCEL_MODE_XVMC:
+        {
+            struct xvmc_pix_fmt *render = mpi->priv; //same as data[2]
+            if(!(mpi->flags & MP_IMGFLAG_DIRECT)) {
+                mp_msg(MSGT_DECVIDEO, MSGL_ERR, MSGTR_MPCODECS_OnlyBuffersAllocatedByVoXvmcAllowed);
+                assert(0);
+                return -1;//!!fixme check error conditions in ffmpeg
+            }
+            if(mp_msg_test(MSGT_DECVIDEO, MSGL_DBG5))
+                mp_msg(MSGT_DECVIDEO, MSGL_DBG5, "vd_ffmpeg::get_buffer (xvmc render=%p)\n", render);
+            assert(render != 0);
+            assert(render->xvmc_id == AV_XVMC_ID);
+            avctx->draw_horiz_band= draw_slice;
         }
-        if(mp_msg_test(MSGT_DECVIDEO, MSGL_DBG5))
-            mp_msg(MSGT_DECVIDEO, MSGL_DBG5, "vd_ffmpeg::get_buffer (xvmc render=%p)\n", render);
-        assert(render != 0);
-        assert(render->xvmc_id == AV_XVMC_ID);
-        avctx->draw_horiz_band= draw_slice;
+#endif
     }
-#endif
 
     pic->data[0]= mpi->planes[0];
     pic->data[1]= mpi->planes[1];
@@ -815,6 +869,14 @@
     sh_video_t *sh = avctx->opaque;
     vd_ffmpeg_ctx *ctx = sh->context;
     int i;
+
+#if CONFIG_VAAPI
+    if(ctx->hwaccel_mode == HWACCEL_MODE_VAAPI) {
+        extern vo_vaapi_release_image(mp_image_t *mpi);
+        vo_vaapi_release_image(mpi);
+    }
+#endif
+
     if (pic->opaque == NULL) {
         mpcodec_default_release_buffer(avctx, pic);
         return;
Index: libvo/video_out.c
===================================================================
--- libvo/video_out.c	(revision 37558)
+++ libvo/video_out.c	(working copy)
@@ -102,6 +102,7 @@
 extern const vo_functions_t video_out_x11;
 extern const vo_functions_t video_out_xvmc;
 extern const vo_functions_t video_out_vdpau;
+extern const vo_functions_t video_out_vaapi;
 extern const vo_functions_t video_out_xv;
 extern const vo_functions_t video_out_gl_nosw;
 extern const vo_functions_t video_out_gl;
@@ -198,6 +199,9 @@
 #if CONFIG_VDPAU
         &video_out_vdpau,
 #endif
+#if CONFIG_VAAPI
+        &video_out_vaapi,
+#endif
 #ifdef CONFIG_XV
         &video_out_xv,
 #endif
Index: libvo/vo_vaapi.c
===================================================================
--- libvo/vo_vaapi.c	(nonexistent)
+++ libvo/vo_vaapi.c	(working copy)
@@ -0,0 +1,522 @@
+/*
+ * VAAPI/X11 video output driver
+ *
+ * Copyright (C) 2015 Mark Thompson <mrt@xxxxxxxxx>
+ *
+ * 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 <va/va.h>
+#include <va/va_x11.h>
+
+#include "libavcodec/vaapi.h"
+
+#include "mp_msg.h"
+#include "subopt-helper.h"
+
+#include "x11_common.h"
+#include "video_out.h"
+
+#define NO_DRAW_FRAME
+#define NO_DRAW_SLICE
+#include "video_out_internal.h"
+
+static vo_info_t info = {
+    "VAAPI/X11",
+    "vaapi",
+    "Mark Thompson <mrt@xxxxxxxxx>",
+    "VAAPI decode and output in X11 (usable with Intel Quick Sync Video)",
+};
+
+#include "libavcodec/vaapi.h"
+
+const LIBVO_EXTERN(vaapi)
+
+// This needs to be at least the number of reference frames required by the
+// stream, plus two for the current and next output surfaces?
+#define DEFAULT_SURFACES 18
+#define MAX_SURFACES 64
+
+typedef struct {
+    VADisplay *display;
+
+    VAConfigID config_id;
+    VAContextID context_id;
+
+    int surface_count;
+    VASurfaceID surface_ids[MAX_SURFACES];
+    int surface_refs[MAX_SURFACES];
+
+    int current_draw_index, next_draw_index;
+
+    uint32_t video_format;
+    uint32_t video_width, video_height;
+
+    int window_width, window_height;
+
+    struct vaapi_context *hwaccel;
+} vo_vaapi_context;
+
+static vo_vaapi_context static_context;
+
+// Hack - this needs to feed in to libavcodec in vd_ffmpeg.c.
+void vo_vaapi_set_hwaccel_context(struct vaapi_context *hwaccel)
+{
+    vo_vaapi_context *ctx = &static_context;
+    if(ctx->display) {
+        // Fill it in immediately.
+        hwaccel->display    = ctx->display;
+        hwaccel->config_id  = ctx->config_id;
+        hwaccel->context_id = ctx->context_id;
+    } else {
+        // We will fill it when libva is initialised.
+    }
+    ctx->hwaccel = hwaccel;
+}
+
+static void draw_osd(void)
+{
+    static int osd_warned = 0;
+    if(!osd_warned) {
+        mp_msg(MSGT_VO, MSGL_WARN, "[vaapi] OSD drawing is not supported.\n");
+        osd_warned = 1;
+    }
+}
+
+static void resize(void)
+{
+    vo_vaapi_context *ctx = &static_context;
+
+    if(ctx->window_width != vo_dwidth || ctx->window_height != vo_dheight) {
+        ctx->window_width  = vo_dwidth;
+        ctx->window_height = vo_dheight;
+
+        // Also redraw the current frame (if there is one) using the new sizing.
+        if(ctx->current_draw_index >= 0) {
+            VAStatus vas;
+            vas = vaPutSurface(ctx->display,
+                               ctx->surface_ids[ctx->current_draw_index],
+                               vo_window,
+                               0, 0, ctx->video_width, ctx->video_height,
+                               0, 0, ctx->window_width, ctx->window_height,
+                               0, 0, 0);
+            if(vas != VA_STATUS_SUCCESS) {
+                mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to redraw surface: "
+                       "%d (%s).\n", vas, vaErrorStr(vas));
+            }
+        }
+    }
+}
+
+static void check_events(void)
+{
+    int events = vo_x11_check_events(mDisplay);
+
+    if(events & VO_EVENT_RESIZE) {
+        mp_msg(MSGT_VO, MSGL_V, "[vaapi] resize event.\n");
+        resize();
+    }
+
+    // Should we care about any other events?
+}
+
+static void flip_page(void)
+{
+    vo_vaapi_context *ctx = &static_context;
+    VAStatus vas;
+    int current, next;
+
+    current = ctx->current_draw_index;
+    next = ctx->next_draw_index;
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] flip page (%d -> %d).\n",
+           current, next);
+
+    vas = vaPutSurface(ctx->display, ctx->surface_ids[next],
+                       vo_window,
+                       0, 0, ctx->video_width, ctx->video_height,
+                       0, 0, ctx->window_width, ctx->window_height,
+                       0, 0, 0);
+    if(vas != VA_STATUS_SUCCESS) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to draw surface: %d (%s).\n",
+               vas, vaErrorStr(vas));
+    }
+
+    if(current >= 0) {
+        if(ctx->surface_refs[current] == 0) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] current draw surface %#x (%d) "
+                   "has no reference?\n", ctx->surface_ids[current], current);
+        } else {
+            --ctx->surface_refs[current];
+        }
+    }
+
+    ctx->current_draw_index = next;
+    ctx->next_draw_index = -1;
+}
+
+static int draw_image(mp_image_t *mpi)
+{
+    vo_vaapi_context *ctx = &static_context;
+    VASurfaceID surface;
+    int i;
+
+    surface = (VASurfaceID)(uintptr_t)mpi->priv;
+    for(i = 0; i < ctx->surface_count; i++) {
+        if(ctx->surface_ids[i] == surface)
+            break;
+    }
+    if(i >= ctx->surface_count) {
+        mp_msg(MSGT_VO, MSGL_WARN, "[vaapi] draw called with unknown surface %#x.\n",
+               surface);
+        return VO_ERROR;
+    }
+
+    if(ctx->next_draw_index >= 0) {
+        mp_msg(MSGT_VO, MSGL_WARN, "[vaapi] discarding never-displayed surface"
+               "%#x (%d) - framerate is incorrect?\n",
+               ctx->surface_ids[ctx->next_draw_index], ctx->next_draw_index);
+        --ctx->surface_refs[ctx->next_draw_index];
+    }
+
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] draw surface %#x (%d).\n", surface, i);
+    ++ctx->surface_refs[i];
+    ctx->next_draw_index = i;
+
+    return VO_TRUE;
+}
+
+static int get_image(mp_image_t *mpi)
+{
+    vo_vaapi_context *ctx = &static_context;
+    VASurfaceID surface;
+    int i;
+
+    for(i = 0; i < ctx->surface_count; i++) {
+        if(ctx->surface_refs[i] == 0)
+            break;
+    }
+    if(i >= ctx->surface_count) {
+        mp_msg(MSGT_VO, MSGL_WARN, "[vaapi] argh, ran out of surfaces (%d in use); "
+               "maybe this stream needs more reference frames?\n", ctx->surface_count);
+        return VO_ERROR;
+    }
+
+    surface = ctx->surface_ids[i];
+    ++ctx->surface_refs[i];
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] using surface %#x (%d).\n", surface, i);
+
+    mpi->flags |= MP_IMGFLAG_DIRECT;
+    mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = 0;
+    mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = 0;
+    mpi->planes[0] = (void*)(uintptr_t)surface;
+    mpi->num_planes = 1;
+    mpi->priv = (void*)(uintptr_t)surface;
+    return VO_TRUE;
+}
+
+static int release_image(mp_image_t *mpi)
+{
+    vo_vaapi_context *ctx = &static_context;
+    VASurfaceID surface;
+    int i;
+
+    surface = (VASurfaceID)(uintptr_t)mpi->priv;
+
+    for(i = 0; i < ctx->surface_count; i++)
+        if(surface == ctx->surface_ids[i])
+            break;
+    if(i >= ctx->surface_count) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] tried to release unknown image: "
+               "surface %#x.\n", surface);
+        return -1;
+    }
+    if(ctx->surface_refs[i] == 0) {
+        mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] tried to release free image "
+               "(decoder error?): surface %#x.\n", surface);
+        return -1;
+    }
+    mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] release surface %#x (%d).\n", surface, i);
+    --ctx->surface_refs[i];
+  
+    return 0;
+}
+
+// Hack - this needsd to feed back from vd_ffmpeg.c.
+int vo_vaapi_release_image(mp_image_t *mpi)
+{
+    return release_image(mpi);
+}
+
+static int query_format(uint32_t format)
+{
+    int flags = VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VOCAP_NOSLICES;
+
+    switch(format) {
+    case IMGFMT_VAAPI_MPEG2:
+    case IMGFMT_VAAPI_H264:
+    case IMGFMT_VAAPI_WMV3:
+    case IMGFMT_VAAPI_VC1:
+    case IMGFMT_VAAPI_MPEG4:
+    case IMGFMT_VAAPI_HEVC:
+        return flags | VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
+    }
+    return 0;
+}
+
+static int preinit(const char *args)
+{
+    vo_vaapi_context *ctx = &static_context;
+
+    int surface_count = DEFAULT_SURFACES;
+    const opt_t subopts[] = {
+        { "surface_count", OPT_ARG_INT, &surface_count, int_pos },
+        { 0 }
+    };
+
+    if(subopt_parse(args, subopts))
+        return -1;
+
+    if(!vo_init())
+        return -1;
+
+    memset(ctx, 0, sizeof(vo_vaapi_context));
+
+    {
+        VAStatus vas;
+        int major, minor;
+
+        ctx->display = vaGetDisplay(mDisplay);
+        if(!ctx->display) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to get VA handle for display\n");
+            return -1;
+        }
+
+        vas = vaInitialize(ctx->display, &major, &minor);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to initialise VAAPI: %d (%s).\n",
+                   vas, vaErrorStr(vas));
+            return -1;
+        }
+
+        mp_msg(MSGT_VO, MSGL_INFO, "[vaapi] initialised (API version %d.%d).\n",
+               major, minor);
+
+        ctx->surface_count = surface_count;
+    }
+
+    return 0;
+}
+
+static int config(uint32_t width, uint32_t height, uint32_t d_width,
+                  uint32_t d_height, uint32_t flags, char *title,
+                  uint32_t format)
+{
+    vo_vaapi_context *ctx = &static_context;
+    VAStatus vas;
+    VAProfile codec;
+
+    mp_msg(MSGT_VO, MSGL_V, "[vaapi] configure for size %ux%u and "
+           "format %#x (%s).\n", width, height, format, vo_format_name(format));
+
+    if(ctx->video_format == format &&
+       ctx->video_width  == width && ctx->video_height == height) {
+        XVisualInfo vinfo;
+        vo_x11_create_vo_window(&vinfo, vo_dx, vo_dy, d_width, d_height, flags,
+                                CopyFromParent, "vaapi", title);
+        resize();
+        return 0;
+    }
+
+    ctx->video_format = format;
+    ctx->video_width   = width;
+    ctx->video_height  = height;
+    ctx->window_width  = d_width;
+    ctx->window_height = d_height;
+
+    {
+        int i, profiles;
+        VAProfile *profile_list;
+        int found_profile;
+
+        switch(format) {
+        case IMGFMT_VAAPI_MPEG2: codec = VAProfileMPEG2Main;   break;
+        case IMGFMT_VAAPI_MPEG4: codec = VAProfileMPEG4AdvancedSimple; break;
+        case IMGFMT_VAAPI_H264:  codec = VAProfileH264High;    break;
+        case IMGFMT_VAAPI_WMV3:  codec = VAProfileVC1Main;     break;
+        case IMGFMT_VAAPI_VC1:   codec = VAProfileVC1Advanced; break;
+        case IMGFMT_VAAPI_HEVC:  codec = VAProfileHEVCMain;    break;
+        default:
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] invalid format %u (%s)?\n",
+                   format, vo_format_name(format));
+            return -1;
+        }
+
+        profiles = vaMaxNumProfiles(ctx->display);
+        profile_list = calloc(profiles, sizeof(VAProfile));
+
+        vas = vaQueryConfigProfiles(ctx->display, profile_list, &profiles);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to fetch profiles: %d (%s).\n",
+                   vas, vaErrorStr(vas));
+            return -1;
+        }
+
+        for(i = 0; i < profiles; i++) {
+            if(profile_list[i] == codec)
+                found_profile = 1;
+        }
+        if(found_profile) {
+            mp_msg(MSGT_VO, MSGL_INFO, "[vaapi] hardware supports codec: "
+                   "format %s -> VAProfile %d.\n", vo_format_name(format), codec);
+        } else {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] hardware does not support codec: "
+                   "format %s.\n", vo_format_name(format));
+            return -1;
+        }
+
+        free(profile_list);
+    }
+
+    {
+        VAConfigAttrib attr;
+        int i;
+
+        memset(&attr, 0, sizeof(attr));
+        attr.type = VAConfigAttribRTFormat;
+        vas = vaGetConfigAttributes(ctx->display, codec, VAEntrypointVLD, &attr, 1);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to fetch config attributes: "
+                   "%d (%s).\n", vas, vaErrorStr(vas));
+            return -1;
+        }
+        attr.value &= VA_RT_FORMAT_YUV420;
+        if(!attr.value) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] hardware does not support YUV420; "
+                   " this probably isn't going to work... sorry.");
+            return -1;
+        }
+
+        vas = vaCreateConfig(ctx->display, codec, VAEntrypointVLD,
+                             &attr, 1, &ctx->config_id);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to create config: %d (%s).\n",
+                   vas, vaErrorStr(vas));
+            return -1;
+        }
+
+        vas = vaCreateSurfaces(ctx->display, VA_RT_FORMAT_YUV420, width, height,
+                               ctx->surface_ids, ctx->surface_count, 0, 0);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to create surfaces: %d (%s).\n",
+                   vas, vaErrorStr(vas));
+            return -1;
+        }
+        for(i = 0; i < ctx->surface_count; i++)
+            ctx->surface_refs[i] = 0;
+
+        ctx->current_draw_index = ctx->next_draw_index = -1;
+
+        vas = vaCreateContext(ctx->display, ctx->config_id, width, height,
+                              VA_PROGRESSIVE, ctx->surface_ids, ctx->surface_count,
+                              &ctx->context_id);
+        if(vas != VA_STATUS_SUCCESS) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] failed to create context: %d (%s).\n",
+                   vas, vaErrorStr(vas));
+            return -1;
+        }
+
+        mp_msg(MSGT_VO, MSGL_STATUS, "[vaapi] context initialised: "
+               "config %#x, context %#x.\n", ctx->config_id, ctx->context_id);
+
+        if(ctx->hwaccel) {
+            ctx->hwaccel->display    = ctx->display;
+            ctx->hwaccel->config_id  = ctx->config_id;
+            ctx->hwaccel->context_id = ctx->context_id;
+        }
+    }
+
+    {
+        XVisualInfo vinfo;
+        XSetWindowAttributes swa;
+        int ret;
+
+        ret = XMatchVisualInfo(mDisplay, mScreen, 24, TrueColor, &vinfo);
+        if(ret == 0) {
+            mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] X does not support a 24-bit visual "
+                   "(required by this driver, sorry).\n");
+            return -1;
+        }
+
+        vo_x11_create_vo_window(&vinfo, vo_dx, vo_dy,
+                                ctx->video_width, ctx->video_height,
+                                flags, CopyFromParent, "vaapi", title);
+
+        swa.background_pixel = 0;
+        swa.border_pixel = 0;
+        XChangeWindowAttributes(mDisplay, vo_window, 0, &swa);
+    }
+
+    return 0;
+}
+
+static void uninit(void)
+{
+    vo_vaapi_context *ctx = &static_context;
+
+    if(vo_config_count == 0)
+        return;
+
+    mp_msg(MSGT_VO, MSGL_V, "[vaapi] uninit.\n");
+
+    vaDestroyContext(ctx->display, ctx->context_id);
+    vaDestroySurfaces(ctx->display, ctx->surface_ids, ctx->surface_count);
+    vaDestroyConfig(ctx->display, ctx->config_id);
+    vaTerminate(ctx->display);
+
+    vo_x11_uninit();
+}
+
+static int control(uint32_t request, void *data)
+{
+    mp_msg(MSGT_VO, MSGL_DBG3, "[vaapi] control called (request %u).\n", request);
+
+    switch(request) {
+    case VOCTRL_QUERY_FORMAT:
+        return query_format(*(uint32_t*)data);
+
+    case VOCTRL_GET_IMAGE:
+        return get_image((mp_image_t*)data);
+    case VOCTRL_DRAW_IMAGE:
+        return draw_image((mp_image_t*)data);
+
+    case VOCTRL_FULLSCREEN:
+        vo_x11_fullscreen();
+        resize();
+        return VO_TRUE;
+    case VOCTRL_ONTOP:
+        vo_x11_ontop();
+        return VO_TRUE;
+    case VOCTRL_UPDATE_SCREENINFO:
+        update_xinerama_info();
+        return VO_TRUE;
+
+    default:
+        return VO_NOTIMPL;
+    }
+}
+

_______________________________________________
MPlayer-dev-eng mailing list
MPlayer-dev-eng@xxxxxxxxxxxx
https://lists.mplayerhq.hu/mailman/listinfo/mplayer-dev-eng