summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/en/options.rst3
-rw-r--r--DOCS/man/en/vo.rst33
-rw-r--r--Makefile2
-rwxr-xr-xconfigure24
-rw-r--r--mpvcore/options.c3
-rw-r--r--video/decode/dec_video.h1
-rw-r--r--video/decode/lavc.h1
-rw-r--r--video/decode/vaapi.c414
-rw-r--r--video/decode/vd_lavc.c4
-rw-r--r--video/fmt-conversion.c5
-rw-r--r--video/img_format.c3
-rw-r--r--video/img_format.h14
-rw-r--r--video/out/vo.c4
-rw-r--r--video/out/vo.h2
-rw-r--r--video/out/vo_vaapi.c1054
-rw-r--r--video/vaapi.h73
16 files changed, 1635 insertions, 5 deletions
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index 14cb2ace67..1cfc4679fc 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -1052,7 +1052,8 @@
``<api>`` can be one of the following:
:no: always use software decoding (default)
- :vdpau: works with nvidia drivers only, requires ``--vo=vdpau``
+ :vdpau: requires ``--vo=vdpau``
+ :vaapi: requires ``--vo=vaapi``
:vda: OSX
:crystalhd: Broadcom Crystal HD
diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst
index 42078dd6f8..2e24b670d8 100644
--- a/DOCS/man/en/vo.rst
+++ b/DOCS/man/en/vo.rst
@@ -665,6 +665,39 @@ Available video output drivers are:
``switch-mode``
Instruct SDL to switch the monitor video mode when going fullscreen.
+``vaapi``
+ Intel VA API video output driver with support for hardware decoding. Note
+ that there is absolutely no reason to use this, other than wanting to use
+ hardware decoding to save power on laptops, or possibly preventing video
+ tearing with some setups.
+
+ ``scaling=<algorithm>``
+ default
+ Driver default (mpv default as well).
+ fast
+ Fast, but low quality.
+ hq
+ Unspecified driver dependent high-quality scaling, slow.
+ nla
+ ``non-linear anamorphic scaling``
+
+ ``deint-mode=<mode>``
+ Select deinterlacing algorithm. Note that by default deinterlacing is
+ initially always off, and needs to be enabled with the ``D`` key
+ (default key binding for ``cycle deinterlace``).
+
+ no
+ Don't allow deinterlacing.
+ first-field
+ Show only first field (going by ``--field-dominance``).
+ bob
+ bob deinterlacing (default).
+
+ ``scaled-osd=<yes|no>``
+ If enabled, then the OSD is rendered at video resolution and scaled to
+ display resolution. By default, this is disabled, and the OSD is
+ rendered at display resolution if the driver supports it.
+
``null``
Produces no video output. Useful for benchmarking.
diff --git a/Makefile b/Makefile
index be182e394e..4d3a179d13 100644
--- a/Makefile
+++ b/Makefile
@@ -109,6 +109,8 @@ SOURCES-$(RSOUND) += audio/out/ao_rsound.c
SOURCES-$(VDPAU) += video/vdpau.c video/out/vo_vdpau.c
SOURCES-$(VDPAU_DEC) += video/decode/vdpau.c
SOURCES-$(VDPAU_DEC_OLD) += video/decode/vdpau_old.c
+SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
+ video/decode/vaapi.c
SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
SOURCES-$(XV) += video/out/vo_xv.c
diff --git a/configure b/configure
index 25d755918f..8e41362e26 100755
--- a/configure
+++ b/configure
@@ -347,6 +347,7 @@ Video output:
--enable-sdl2 enable SDL 2.0+ audio and video output [disable]
--enable-xv enable Xv video output [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]
@@ -421,6 +422,7 @@ _wayland=auto
_xss=auto
_xv=auto
_vdpau=auto
+_vaapi=auto
_direct3d=auto
_sdl=no
_sdl2=no
@@ -582,6 +584,8 @@ for ac_option do
--disable-xv) _xv=no ;;
--enable-vdpau) _vdpau=yes ;;
--disable-vdpau) _vdpau=no ;;
+ --enable-vaapi) _vaapi=yes ;;
+ --disable-vaapi) _vaapi=no ;;
--enable-direct3d) _direct3d=yes ;;
--disable-direct3d) _direct3d=no ;;
--enable-sdl) _sdl=yes ;;
@@ -1845,6 +1849,23 @@ fi
echores "$_vdpau"
+echocheck "VAAPI"
+if test "$_vaapi" = auto && test "$_x11" = yes ; then
+ _vaapi=no
+ if test "$_dl" = yes ; then
+ pkg_config_add 'libva >= 0.32.0 libva-x11 >= 0.32.0' && _vaapi=yes
+ fi
+fi
+if test "$_vaapi" = yes ; then
+ def_vaapi='#define CONFIG_VAAPI 1'
+ vomodules="vaapi $vomodules"
+else
+ def_vaapi='#define CONFIG_VAAPI 0'
+ novomodules="vaapi $novomodules"
+fi
+echores "$_vdpau"
+
+
echocheck "Xinerama"
if test "$_xinerama" = auto && test "$_x11" = yes ; then
_xinerama=no
@@ -3099,6 +3120,7 @@ VCD = $_vcd
VDPAU = $_vdpau
VDPAU_DEC = $_vdpau_dec
VDPAU_DEC_OLD = $_vdpau_dec_old
+VAAPI = $_vaapi
WIN32 = $_win32
X11 = $_x11
WAYLAND = $_wayland
@@ -3108,6 +3130,7 @@ XV = $_xv
ENCODING = $_encoding
CONFIG_VDPAU = $_vdpau
+CONFIG_VAAPI = $_vaapi
CONFIG_ZLIB = $_zlib
HAVE_PTHREADS = $_pthreads
@@ -3274,6 +3297,7 @@ $def_jpeg
$def_mng
$def_v4l2
$def_vdpau
+$def_vaapi
$def_vm
$def_x11
$def_wayland
diff --git a/mpvcore/options.c b/mpvcore/options.c
index ff278cc7a2..a94b7628c9 100644
--- a/mpvcore/options.c
+++ b/mpvcore/options.c
@@ -469,7 +469,8 @@ const m_option_t mp_opts[] = {
{"auto", -1},
{"vdpau", 1},
{"vda", 2},
- {"crystalhd", 3})),
+ {"crystalhd", 3},
+ {"vaapi", 4})),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
// postprocessing:
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
index 4ba052afd1..021abaaf22 100644
--- a/video/decode/dec_video.h
+++ b/video/decode/dec_video.h
@@ -51,6 +51,7 @@ extern int divx_quality;
// The VO can set the context pointer for supported APIs.
struct mp_hwdec_info {
struct mp_vdpau_ctx *vdpau_ctx;
+ struct mp_vaapi_ctx *vaapi_ctx;
};
#endif /* MPLAYER_DEC_VIDEO_H */
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 94973cb2d8..4252034b2c 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -17,6 +17,7 @@ enum hwdec_type {
HWDEC_VDPAU = 1,
HWDEC_VDA = 2,
HWDEC_CRYSTALHD = 3,
+ HWDEC_VAAPI = 4,
};
typedef struct lavc_ctx {
diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c
new file mode 100644
index 0000000000..baa13588c9
--- /dev/null
+++ b/video/decode/vaapi.c
@@ -0,0 +1,414 @@
+/*
+ * This file is part of mpv.
+ *
+ * With some chunks from original MPlayer VAAPI patch:
+ * Copyright (C) 2008-2009 Splitted-Desktop Systems
+ *
+ * mpv 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.
+ *
+ * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+
+#include <libavcodec/avcodec.h>
+#include <libavcodec/vaapi.h>
+#include <libavutil/common.h>
+
+#include "lavc.h"
+#include "mpvcore/mp_common.h"
+#include "mpvcore/av_common.h"
+#include "video/fmt-conversion.h"
+#include "video/vaapi.h"
+#include "video/decode/dec_video.h"
+
+/*
+ * The VAAPI decoder can work only with surfaces passed to the decoder at
+ * creation time. This means all surfaces have to be created in advance.
+ * So, additionally to the maximum number of reference frames, we need
+ * surfaces for:
+ * - 1 decode frame
+ * - decoding 1 frame ahead (done by generic playback code)
+ * - keeping the reference to the previous frame (done by vo_vaapi.c)
+ * Note that redundant additional surfaces also might allow for some
+ * buffering (i.e. not trying to reuse a surface while it's busy).
+ */
+#define ADDTIONAL_SURFACES 3
+
+// Magic number taken from original MPlayer vaapi patch.
+#define MAX_DECODER_SURFACES 21
+
+#define MAX_SURFACES (MAX_DECODER_SURFACES + ADDTIONAL_SURFACES)
+
+struct priv {
+ struct mp_vaapi_ctx *ctx;
+ VADisplay display;
+
+ // libavcodec shared struct
+ struct vaapi_context *va_context;
+ struct vaapi_context va_context_storage;
+
+ int format, w, h;
+ VASurfaceID surfaces[MAX_SURFACES];
+};
+
+struct profile_entry {
+ enum AVCodecID av_codec;
+ int ff_profile;
+ VAProfile va_profile;
+ int maxrefs;
+};
+
+#define PE(av_codec_id, ff_profile, va_dcoder_profile, maxrefs) \
+ {AV_CODEC_ID_ ## av_codec_id, \
+ FF_PROFILE_ ## ff_profile, \
+ VAProfile ## va_dcoder_profile, \
+ maxrefs}
+
+static const struct profile_entry profiles[] = {
+ PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple, 2),
+ PE(MPEG2VIDEO, UNKNOWN, MPEG2Main, 2),
+ PE(H264, H264_BASELINE, H264Baseline, 16),
+ PE(H264, H264_CONSTRAINED_BASELINE, H264ConstrainedBaseline, 16),
+ PE(H264, H264_MAIN, H264Main, 16),
+ PE(H264, UNKNOWN, H264High, 16),
+ PE(WMV3, VC1_SIMPLE, VC1Simple, 2),
+ PE(WMV3, VC1_MAIN, VC1Main, 2),
+ PE(WMV3, UNKNOWN, VC1Advanced, 2),
+ PE(VC1, VC1_SIMPLE, VC1Simple, 2),
+ PE(VC1, VC1_MAIN, VC1Main, 2),
+ PE(VC1, UNKNOWN, VC1Advanced, 2),
+ // No idea whether these are correct
+ PE(MPEG4, MPEG4_SIMPLE, MPEG4Simple, 2),
+ PE(MPEG4, MPEG4_MAIN, MPEG4Main, 2),
+ PE(MPEG4, UNKNOWN, MPEG4AdvancedSimple, 2),
+};
+
+static const struct profile_entry *find_codec(enum AVCodecID id, int ff_profile)
+{
+ for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
+ if (profiles[n].av_codec == id &&
+ (profiles[n].ff_profile == ff_profile ||
+ profiles[n].ff_profile == FF_PROFILE_UNKNOWN))
+ {
+ return &profiles[n];
+ }
+ }
+ return NULL;
+}
+
+
+static const char *str_va_profile(VAProfile profile)
+{
+ switch (profile) {
+#define PROFILE(profile) \
+ case VAProfile##profile: return "VAProfile" #profile
+ PROFILE(MPEG2Simple);
+ PROFILE(MPEG2Main);
+ PROFILE(MPEG4Simple);
+ PROFILE(MPEG4AdvancedSimple);
+ PROFILE(MPEG4Main);
+ PROFILE(H264Baseline);
+ PROFILE(H264Main);
+ PROFILE(H264High);
+ PROFILE(VC1Simple);
+ PROFILE(VC1Main);
+ PROFILE(VC1Advanced);
+#undef PROFILE
+ }
+ return "<unknown>";
+}
+
+static int find_entrypoint(int format, VAEntrypoint *ep, int num_ep)
+{
+ int entrypoint = -1;
+ switch (format) {
+ case IMGFMT_VAAPI: entrypoint = VAEntrypointVLD; break;
+ case IMGFMT_VAAPI_MPEG2_IDCT: entrypoint = VAEntrypointIDCT; break;
+ case IMGFMT_VAAPI_MPEG2_MOCO: entrypoint = VAEntrypointMoComp; break;
+ }
+ for (int n = 0; n < num_ep; n++) {
+ if (ep[n] == entrypoint)
+ return entrypoint;
+ }
+ return -1;
+}
+
+static int is_direct_mapping(VADisplay display)
+{
+ VADisplayAttribute attr;
+ VAStatus status;
+
+#if VA_CHECK_VERSION(0,34,0)
+ attr.type = VADisplayAttribRenderMode;
+ attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
+
+ status = vaGetDisplayAttributes(display, &attr, 1);
+ if (status == VA_STATUS_SUCCESS)
+ return !(attr.value & (VA_RENDER_MODE_LOCAL_OVERLAY|
+ VA_RENDER_MODE_EXTERNAL_OVERLAY));
+#else
+ /* 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(display, &attr, 1);
+ if (status == VA_STATUS_SUCCESS)
+ return !attr.value;
+#endif
+ return 0;
+}
+
+// Make vo_vaapi.c pool the required number of surfaces.
+// This is very touchy: vo_vaapi.c must not free surfaces while we decode,
+// and we must allocate only surfaces that were passed to the decoder on
+// creation.
+// We achieve this by deleting all previous surfaces, then allocate every
+// surface needed. Then we free these surfaces, and rely on the fact that
+// vo_vaapi.c keeps the released surfaces in the pool, and only allocates
+// new surfaces out of that pool.
+static int preallocate_surfaces(struct lavc_ctx *ctx, int va_rt_format, int num)
+{
+ struct priv *p = ctx->hwdec_priv;
+ int res = -1;
+
+ struct mp_image *tmp_surfaces[MAX_SURFACES] = {0};
+
+ p->ctx->flush(p->ctx); // free previously allocated surfaces
+
+ for (int n = 0; n < num; n++) {
+ tmp_surfaces[n] = p->ctx->get_surface(p->ctx, va_rt_format, p->format,
+ p->w, p->h);
+ if (!tmp_surfaces[n])
+ goto done;
+ p->surfaces[n] = (uintptr_t)tmp_surfaces[n]->planes[3];
+ }
+ res = 0;
+
+done:
+ for (int n = 0; n < num; n++)
+ talloc_free(tmp_surfaces[n]);
+ return res;
+}
+
+static void destroy_decoder(struct lavc_ctx *ctx)
+{
+ struct priv *p = ctx->hwdec_priv;
+
+ if (p->va_context->context_id != VA_INVALID_ID) {
+ vaDestroyContext(p->display, p->va_context->context_id);
+ p->va_context->context_id = VA_INVALID_ID;
+ }
+
+ if (p->va_context->config_id != VA_INVALID_ID) {
+ vaDestroyConfig(p->display, p->va_context->config_id);
+ p->va_context->config_id = VA_INVALID_ID;
+ }
+
+ for (int n = 0; n < MAX_SURFACES; n++)
+ p->surfaces[n] = VA_INVALID_ID;
+}
+
+static int create_decoder(struct lavc_ctx *ctx)
+{
+ void *tmp = talloc_new(NULL);
+
+ struct priv *p = ctx->hwdec_priv;
+ VAStatus status;
+ int res = -1;
+
+ assert(IMGFMT_IS_VAAPI(p->format));
+
+ destroy_decoder(ctx);
+
+ const struct profile_entry *pe = find_codec(ctx->avctx->codec_id,
+ ctx->avctx->profile);
+ if (!pe) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Unknown codec!\n");
+ goto error;
+ }
+
+ int num_profiles = vaMaxNumProfiles(p->display);
+ VAProfile *va_profiles = talloc_zero_array(tmp, VAProfile, num_profiles);
+ status = vaQueryConfigProfiles(p->display, va_profiles, &num_profiles);
+ if (!check_va_status(status, "vaQueryConfigProfiles()"))
+ goto error;
+ mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] %d profiles available:\n", num_profiles);
+ for (int i = 0; i < num_profiles; i++)
+ mp_msg(MSGT_VO, MSGL_DBG2, " %s\n", str_va_profile(va_profiles[i]));
+
+ bool profile_found = false;
+ for (int i = 0; i < num_profiles; i++) {
+ if (pe->va_profile == va_profiles[i]) {
+ profile_found = true;
+ break;
+ }
+ }
+ if (!profile_found) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Profile '%s' not available.\n",
+ str_va_profile(pe->va_profile));
+ goto error;
+ }
+
+ int num_surfaces = pe->maxrefs;
+ if (!is_direct_mapping(p->display)) {
+ mp_msg(MSGT_VO, MSGL_V, "[vaapi] No direct mapping.\n");
+ // Note: not sure why it has to be *=2 rather than +=1.
+ num_surfaces *= 2;
+ }
+ num_surfaces = MPMIN(num_surfaces, MAX_DECODER_SURFACES) + ADDTIONAL_SURFACES;
+
+ if (num_surfaces > MAX_SURFACES) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Internal error: too many surfaces.\n");
+ goto error;
+ }
+
+ if (preallocate_surfaces(ctx, VA_RT_FORMAT_YUV420, num_surfaces) < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not allocate surfaces.\n");
+ goto error;
+ }
+
+ int num_ep = vaMaxNumEntrypoints(p->display);
+ VAEntrypoint *ep = talloc_zero_array(tmp, VAEntrypoint, num_ep);
+ status = vaQueryConfigEntrypoints(p->display, pe->va_profile, ep, &num_ep);
+ if (!check_va_status(status, "vaQueryConfigEntrypoints()"))
+ goto error;
+
+ VAEntrypoint entrypoint = find_entrypoint(p->format, ep, num_ep);
+ if (entrypoint < 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not find VA entrypoint.\n");
+ goto error;
+ }
+
+ VAConfigAttrib attrib = {
+ .type = VAConfigAttribRTFormat,
+ };
+ status = vaGetConfigAttributes(p->display, pe->va_profile, entrypoint,
+ &attrib, 1);
+ if (!check_va_status(status, "vaGetConfigAttributes()"))
+ goto error;
+ if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Chroma format not supported.\n");
+ goto error;
+ }
+
+ status = vaCreateConfig(p->display, pe->va_profile, entrypoint, &attrib, 1,
+ &p->va_context->config_id);
+ if (!check_va_status(status, "vaCreateConfig()"))
+ goto error;
+
+ status = vaCreateContext(p->display, p->va_context->config_id,
+ p->w, p->h, VA_PROGRESSIVE,
+ p->surfaces, num_surfaces,
+ &p->va_context->context_id);
+ if (!check_va_status(status, "vaCreateContext()"))
+ goto error;
+
+ res = 0;
+error:
+ talloc_free(tmp);
+ return res;
+}
+
+static struct mp_image *allocate_image(struct lavc_ctx *ctx, AVFrame *frame)
+{
+ struct priv *p = ctx->hwdec_priv;
+ int format = pixfmt2imgfmt(frame->format);
+
+ if (!IMGFMT_IS_VAAPI(format))
+ return NULL;
+
+ // frame->width/height lie. Using them breaks with non-mod 16 video.
+ int w = ctx->avctx->width;
+ int h = ctx->avctx->height;
+
+ if (format != p->format || w != p->w || h != p->h ||
+ p->va_context->context_id == VA_INVALID_ID)
+ {
+ p->format = format;
+ p->w = w;
+ p->h = h;
+ if (create_decoder(ctx) < 0)
+ return NULL;
+ }
+
+ struct mp_image *img = p->ctx->get_surface(p->ctx, VA_RT_FORMAT_YUV420,
+ format, p->w, p->h);
+ if (img) {
+ for (int n = 0; n < MAX_SURFACES; n++) {
+ if (p->surfaces[n] == (uintptr_t)img->planes[3])
+ return img;
+ }
+ talloc_free(img);
+ }
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Insufficient number of surfaces.\n");
+ return NULL;
+}
+
+static void uninit(struct lavc_ctx *ctx)
+{
+ struct priv *p = ctx->hwdec_priv;
+
+ if (!p)
+ return;
+
+ destroy_decoder(ctx);
+
+ talloc_free(p);
+ ctx->hwdec_priv = NULL;
+}
+
+static int init(struct lavc_ctx *ctx)
+{
+ struct priv *p = talloc_ptrtype(NULL, p);
+ *p = (struct priv) {
+ .ctx = ctx->hwdec_info->vaapi_ctx,
+ .va_context = &p->va_context_storage,
+ };
+ ctx->hwdec_priv = p;
+
+ p->display = p->ctx->display;
+
+ p->va_context->display = p->display;
+ p->va_context->config_id = VA_INVALID_ID;
+ p->va_context->context_id = VA_INVALID_ID;
+
+ ctx->avctx->hwaccel_context = p->va_context;
+
+ return 0;
+}
+
+
+static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder)
+{
+ if (!info || !info->vaapi_ctx)
+ return HWDEC_ERR_NO_CTX;
+ if (!find_codec(mp_codec_to_av_codec_id(decoder), FF_PROFILE_UNKNOWN))
+ return HWDEC_ERR_NO_CODEC;
+ return 0;
+}
+
+const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
+ .type = HWDEC_VAAPI,
+ .image_formats = (const int[]) {IMGFMT_VAAPI, IMGFMT_VAAPI_MPEG2_IDCT,
+ IMGFMT_VAAPI_MPEG2_MOCO, 0},
+ .probe = probe,
+ .init = init,
+ .uninit = uninit,
+ .allocate_image = allocate_image,
+};
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 2fc7a1ea4c..639e46ebcc 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -84,6 +84,7 @@ const m_option_t lavc_decode_opts_conf[] = {
const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
const struct vd_lavc_hwdec mp_vd_lavc_vdpau_old;
+const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
.type = HWDEC_CRYSTALHD,
@@ -113,6 +114,9 @@ static const struct vd_lavc_hwdec *hwdec_list[] = {
#endif // CONFIG_VDPAU
&mp_vd_lavc_vda,
&mp_vd_lavc_crystalhd,
+#if CONFIG_VAAPI
+ &mp_vd_lavc_vaapi,
+#endif
NULL
};
diff --git a/video/fmt-conversion.c b/video/fmt-conversion.c
index 509ff31535..842d95ce89 100644
--- a/video/fmt-conversion.c
+++ b/video/fmt-conversion.c
@@ -183,6 +183,11 @@ static const struct {
// map to an arbitrary but existing vdpau format
{IMGFMT_VDPAU, PIX_FMT_VDPAU_H264},
#endif
+
+ {IMGFMT_VAAPI, PIX_FMT_VAAPI_VLD},
+ {IMGFMT_VAAPI_MPEG2_IDCT, PIX_FMT_VAAPI_IDCT},
+ {IMGFMT_VAAPI_MPEG2_MOCO, PIX_FMT_VAAPI_MOCO},
+
{0, PIX_FMT_NONE}
};
diff --git a/video/img_format.c b/video/img_format.c
index 9422fbdd27..56d5ddc412 100644
--- a/video/img_format.c
+++ b/video/img_format.c
@@ -119,6 +119,9 @@ struct mp_imgfmt_entry mp_imgfmt_list[] = {
FMT("vdpau_vc1", IMGFMT_VDPAU_VC1)
FMT("vdpau_mpeg4", IMGFMT_VDPAU_MPEG4)
FMT("vdpau", IMGFMT_VDPAU)
+ FMT("vaapi", IMGFMT_VAAPI)
+ FMT("vaapi_mpeg2_idct", IMGFMT_VAAPI_MPEG2_IDCT)
+ FMT("vaapi_mpeg2_moco", IMGFMT_VAAPI_MPEG2_MOCO)
{0}
};
diff --git a/video/img_format.h b/video/img_format.h
index 7261971e43..e83f5ec25b 100644
--- a/video/img_format.h
+++ b/video/img_format.h
@@ -87,7 +87,6 @@ enum mp_imgfmt {
IMGFMT_START = 1000,
// Planar YUV formats
-
IMGFMT_444P, // 1x1
IMGFMT_422P, // 2x1
IMGFMT_440P, // 1x2
@@ -253,6 +252,14 @@ enum mp_imgfmt {
IMGFMT_VDPAU_FIRST = IMGFMT_VDPAU,
IMGFMT_VDPAU_LAST = IMGFMT_VDPAU_MPEG4,
+ IMGFMT_VAAPI,
+ IMGFMT_VAAPI_MPEG2_IDCT,
+ IMGFMT_VAAPI_MPEG2_MOCO,
+
+ IMGFMT_VAAPI_FIRST = IMGFMT_VAAPI,
+ IMGFMT_VAAPI_LAST = IMGFMT_VAAPI_MPEG2_MOCO,
+
+
IMGFMT_END,
// Redundant format aliases for native endian access
@@ -328,7 +335,10 @@ static inline bool IMGFMT_IS_RGB(unsigned int fmt)
#define IMGFMT_IS_VDPAU(fmt) \
(((fmt) >= IMGFMT_VDPAU_FIRST) && ((fmt) <= IMGFMT_VDPAU_LAST))
-#define IMGFMT_IS_HWACCEL(fmt) IMGFMT_IS_VDPAU(fmt)
+#define IMGFMT_IS_VAAPI(fmt) \
+ (((fmt) >= IMGFMT_VAAPI_FIRST) && ((fmt) <= IMGFMT_VAAPI_LAST))
+
+#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_VAAPI(fmt))
struct mp_imgfmt_entry {
diff --git a/video/out/vo.c b/video/out/vo.c
index 5a1b6f6fcd..592dfe6243 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -61,6 +61,7 @@ extern struct vo_driver video_out_direct3d;
extern struct vo_driver video_out_direct3d_shaders;
extern struct vo_driver video_out_sdl;
extern struct vo_driver video_out_corevideo;
+extern struct vo_driver video_out_vaapi;
const struct vo_driver *video_out_drivers[] =
{
@@ -86,6 +87,9 @@ const struct vo_driver *video_out_drivers[] =
#ifdef CONFIG_GL
&video_out_opengl_old,
#endif
+#ifdef CONFIG_VAAPI
+ &video_out_vaapi,
+#endif
#ifdef CONFIG_X11
&video_out_x11,
#endif
diff --git a/video/out/vo.h b/video/out/vo.h
index 4052d11773..8bb1bd5617 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -50,7 +50,7 @@ enum mp_voctrl {
VOCTRL_SET_EQUALIZER, // struct voctrl_set_equalizer_args*
VOCTRL_GET_EQUALIZER, // struct voctrl_get_equalizer_args*
- /* for vdpau hardware decoding */
+ /* for hardware decoding */
VOCTRL_GET_HWDEC_INFO, // struct mp_hwdec_info*
VOCTRL_NEWFRAME,
diff --git a/video/out/vo_vaapi.c b/video/out/vo_vaapi.c
new file mode 100644
index 0000000000..48eac61b9b
--- /dev/null
+++ b/video/out/vo_vaapi.c
@@ -0,0 +1,1054 @@
+/*
+ * 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 <assert.h>
+#include <stdarg.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <va/va_x11.h>
+
+#include <libavutil/common.h>
+#include <libavcodec/vaapi.h>
+
+#include "config.h"
+#include "mpvcore/mp_msg.h"
+#include "video/out/vo.h"
+#include "video/memcpy_pic.h"
+#include "sub/sub.h"
+#include "sub/img_convert.h"
+#include "x11_common.h"
+
+#include "video/vfcap.h"
+#include "video/mp_image.h"
+#include "video/vaapi.h"
+#include "video/decode/dec_video.h"
+
+#define STR_FOURCC(fcc) \
+ (const char[]){(fcc), (fcc) >> 8u, (fcc) >> 16u, (fcc) >> 24u, 0}
+
+struct vaapi_surface {
+ VASurfaceID id; // VA_INVALID_ID if unallocated
+ int w, h, va_format; // parameters of allocated image (0/0/-1 unallocated)
+ VAImage image; // used for sofwtare decoding case
+ bool is_bound; // image bound to the surface?
+ bool is_used; // referenced by a mp_image
+ bool is_dead; // used, but deallocate VA objects as soon as possible
+ int order; // for LRU allocation
+
+ // convenience shortcut for mp_image deallocation callback
+ struct priv *p;
+};
+
+struct vaapi_osd_image {
+ int w, h;
+ VAImage image;
+ VASubpictureID subpic_id;
+ bool is_used;
+};
+
+struct vaapi_subpic {
+ VASubpictureID id;
+ int src_x, src_y, src_w, src_h;
+ int dst_x, dst_y, dst_w, dst_h;
+};
+
+struct vaapi_osd_part {
+ bool active;
+ int bitmap_pos_id;
+ struct vaapi_osd_image image;
+ struct vaapi_subpic subpic;
+ struct osd_conv_cache *conv_cache;
+};
+
+#define MAX_OUTPUT_SURFACES 2
+
+struct priv {
+ struct mp_log *log;
+ struct vo *vo;
+ VADisplay display;
+ struct mp_vaapi_ctx mpvaapi;
+
+ struct mp_image_params image_params;
+ struct mp_rect src_rect;
+ struct mp_rect dst_rect;
+ struct mp_osd_res screen_osd_res;
+
+ struct mp_image *output_surfaces[MAX_OUTPUT_SURFACES];
+ struct mp_image *swdec_surfaces[MAX_OUTPUT_SURFACES];
+
+ int output_surface;
+ int visible_surface;
+ int deint;
+ int deint_type;
+ int scaling;
+ int force_scaled_osd;
+
+ VAImageFormat osd_format; // corresponds to OSD_VA_FORMAT
+ struct vaapi_osd_part osd_parts[MAX_OSD_PARTS];
+ bool osd_screen;
+
+ int num_video_surfaces;
+ struct vaapi_surface **video_surfaces;
+ int video_surface_lru_counter;
+
+ VAImageFormat *va_image_formats;
+ int va_num_image_formats;
+ VAImageFormat *va_subpic_formats;
+ unsigned int *va_subpic_flags;
+ int va_num_subpic_formats;
+ VADisplayAttribute *va_display_attrs;
+ int va_num_display_attrs;
+};
+
+#define OSD_VA_FORMAT VA_FOURCC_BGRA
+
+static const bool osd_formats[SUBBITMAP_COUNT] = {
+ // Actually BGRA, but only on little endian.
+ // This will break on big endian, I think.
+ [SUBBITMAP_RGBA_STR] = true,
+};
+
+struct fmtentry {
+ uint32_t va;
+ int mp;
+};
+static struct fmtentry va_to_imgfmt[] = {
+ {VA_FOURCC('Y','V','1','2'), IMGFMT_420P},
+ {VA_FOURCC('I','4','2','0'), IMGFMT_420P},
+ {VA_FOURCC('I','Y','U','V'), IMGFMT_420P},
+ {VA_FOURCC('N','V','1','2'), IMGFMT_NV12},
+ // Note: not sure about endian issues (the mp formats are byte-addressed)
+ {VA_FOURCC_RGBA, IMGFMT_RGBA},
+ {VA_FOURCC_BGRA, IMGFMT_BGRA},
+ // Untested.
+ //{VA_FOURCC_UYVY, IMGFMT_UYVY},
+ //{VA_FOURCC_YUY2, IMGFMT_YUYV},
+ {0}
+};
+
+
+static int va_fourcc_to_imgfmt(uint32_t fourcc)
+{
+ for (int n = 0; va_to_imgfmt[n].mp; n++) {
+ if (va_to_imgfmt[n].va == fourcc)
+ return va_to_imgfmt[n].mp;
+ }
+ return 0;
+}
+
+static VAImageFormat *VAImageFormat_from_imgfmt(struct priv *p, int format)
+{
+ for (int i = 0; i < p->va_num_image_formats; i++) {
+ if (va_fourcc_to_imgfmt(p->va_image_formats[i].fourcc) == format)
+ return &p->va_image_formats[i];
+ }
+ return NULL;
+}
+
+static struct vaapi_surface *to_vaapi_surface(struct priv *p,
+ struct mp_image *img)
+{
+ if (!img || !IMGFMT_IS_VAAPI(img->imgfmt))
+ return NULL;
+ // Note: we _could_ use planes[1] or planes[2] to store a vaapi_surface
+ // pointer, but I just don't trust libavcodec enough.
+ VASurfaceID id = (uintptr_t)img->planes[3];
+ for (int n = 0; n < p->num_video_surfaces; n++) {
+ struct vaapi_surface *s = p->video_surfaces[n];
+ if (s->id == id)
+ return s;
+ }
+ return NULL;
+}
+
+stat