Skip to content
This repository
Browse code

video: add vaapi decode and output support

This is based on the MPlayer VA API patches. To be exact it's based on
a very stripped down version of commit f1ad459a263f8537f6c from
git://gitorious.org/vaapi/mplayer.git.

This doesn't contain useless things like benchmarking hacks and the
demo code for GLX interop. Also, unlike in the original patch, decoding
and video output are split into separate source files (the separation
between decoding and display also makes pixel format hacks unnecessary).

On the other hand, some features not present in the original patch were
added, like screenshot support.

VA API is rather bad for actual video output. Dealing with older libva
versions or the completely broken vdpau backend doesn't help. OSD is
low quality and should be rather slow. In some cases, only either OSD
or subtitles can be shown at the same time (because OSD is drawn first,
OSD is prefered).

Also, libva can't decide whether it accepts straight or premultiplied
alpha for OSD sub-pictures: the vdpau backend seems to assume
premultiplied, while a native vaapi driver uses straight. So I picked
straight alpha. It doesn't matter much, because the blending code for
straight alpha I added to img_convert.c is probably buggy, and ASS
subtitles might be blended incorrectly.

Really good video output with VA API would probably use OpenGL and the
GL interop features, but at this point you might just use vo_opengl.
(Patches for making HW decoding with vo_opengl have a chance of being
accepted.)

Despite these issues, decoding seems to work ok. I still got tearing
on the Intel system I tested (Intel(R) Core(TM) i3-2350M). It was also
tested with the vdpau vaapi wrapper on a nvidia system; however this
was rather broken. (Fortunately, there is no reason to use mpv's VAAPI
support over native VDPAU.)
  • Loading branch information...
commit 2827295703c74e3c119df9a435aa856e268c2ea9 1 parent c7da4ba
wm4 authored
3  DOCS/man/en/options.rst
Source Rendered
@@ -1052,7 +1052,8 @@
1052 1052 ``<api>`` can be one of the following:
1053 1053
1054 1054 :no: always use software decoding (default)
1055   - :vdpau: works with nvidia drivers only, requires ``--vo=vdpau``
  1055 + :vdpau: requires ``--vo=vdpau``
  1056 + :vaapi: requires ``--vo=vaapi``
1056 1057 :vda: OSX
1057 1058 :crystalhd: Broadcom Crystal HD
1058 1059
33 DOCS/man/en/vo.rst
Source Rendered
@@ -665,6 +665,39 @@ Available video output drivers are:
665 665 ``switch-mode``
666 666 Instruct SDL to switch the monitor video mode when going fullscreen.
667 667
  668 +``vaapi``
  669 + Intel VA API video output driver with support for hardware decoding. Note
  670 + that there is absolutely no reason to use this, other than wanting to use
  671 + hardware decoding to save power on laptops, or possibly preventing video
  672 + tearing with some setups.
  673 +
  674 + ``scaling=<algorithm>``
  675 + default
  676 + Driver default (mpv default as well).
  677 + fast
  678 + Fast, but low quality.
  679 + hq
  680 + Unspecified driver dependent high-quality scaling, slow.
  681 + nla
  682 + ``non-linear anamorphic scaling``
  683 +
  684 + ``deint-mode=<mode>``
  685 + Select deinterlacing algorithm. Note that by default deinterlacing is
  686 + initially always off, and needs to be enabled with the ``D`` key
  687 + (default key binding for ``cycle deinterlace``).
  688 +
  689 + no
  690 + Don't allow deinterlacing.
  691 + first-field
  692 + Show only first field (going by ``--field-dominance``).
  693 + bob
  694 + bob deinterlacing (default).
  695 +
  696 + ``scaled-osd=<yes|no>``
  697 + If enabled, then the OSD is rendered at video resolution and scaled to
  698 + display resolution. By default, this is disabled, and the OSD is
  699 + rendered at display resolution if the driver supports it.
  700 +
668 701 ``null``
669 702 Produces no video output. Useful for benchmarking.
670 703
2  Makefile
@@ -109,6 +109,8 @@ SOURCES-$(RSOUND) += audio/out/ao_rsound.c
109 109 SOURCES-$(VDPAU) += video/vdpau.c video/out/vo_vdpau.c
110 110 SOURCES-$(VDPAU_DEC) += video/decode/vdpau.c
111 111 SOURCES-$(VDPAU_DEC_OLD) += video/decode/vdpau_old.c
  112 +SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
  113 + video/decode/vaapi.c
112 114
113 115 SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
114 116 SOURCES-$(XV) += video/out/vo_xv.c
24 configure
@@ -347,6 +347,7 @@ Video output:
347 347 --enable-sdl2 enable SDL 2.0+ audio and video output [disable]
348 348 --enable-xv enable Xv video output [autodetect]
349 349 --enable-vdpau enable VDPAU acceleration [autodetect]
  350 + --enable-vaapi enable VAAPI acceleration [autodetect]
350 351 --enable-vm enable XF86VidMode support [autodetect]
351 352 --enable-xinerama enable Xinerama support [autodetect]
352 353 --enable-x11 enable X11 video output [autodetect]
@@ -421,6 +422,7 @@ _wayland=auto
421 422 _xss=auto
422 423 _xv=auto
423 424 _vdpau=auto
  425 +_vaapi=auto
424 426 _direct3d=auto
425 427 _sdl=no
426 428 _sdl2=no
@@ -582,6 +584,8 @@ for ac_option do
582 584 --disable-xv) _xv=no ;;
583 585 --enable-vdpau) _vdpau=yes ;;
584 586 --disable-vdpau) _vdpau=no ;;
  587 + --enable-vaapi) _vaapi=yes ;;
  588 + --disable-vaapi) _vaapi=no ;;
585 589 --enable-direct3d) _direct3d=yes ;;
586 590 --disable-direct3d) _direct3d=no ;;
587 591 --enable-sdl) _sdl=yes ;;
@@ -1845,6 +1849,23 @@ fi
1845 1849 echores "$_vdpau"
1846 1850
1847 1851
  1852 +echocheck "VAAPI"
  1853 +if test "$_vaapi" = auto && test "$_x11" = yes ; then
  1854 + _vaapi=no
  1855 + if test "$_dl" = yes ; then
  1856 + pkg_config_add 'libva >= 0.32.0 libva-x11 >= 0.32.0' && _vaapi=yes
  1857 + fi
  1858 +fi
  1859 +if test "$_vaapi" = yes ; then
  1860 + def_vaapi='#define CONFIG_VAAPI 1'
  1861 + vomodules="vaapi $vomodules"
  1862 +else
  1863 + def_vaapi='#define CONFIG_VAAPI 0'
  1864 + novomodules="vaapi $novomodules"
  1865 +fi
  1866 +echores "$_vdpau"
  1867 +
  1868 +
1848 1869 echocheck "Xinerama"
1849 1870 if test "$_xinerama" = auto && test "$_x11" = yes ; then
1850 1871 _xinerama=no
@@ -3099,6 +3120,7 @@ VCD = $_vcd
3099 3120 VDPAU = $_vdpau
3100 3121 VDPAU_DEC = $_vdpau_dec
3101 3122 VDPAU_DEC_OLD = $_vdpau_dec_old
  3123 +VAAPI = $_vaapi
3102 3124 WIN32 = $_win32
3103 3125 X11 = $_x11
3104 3126 WAYLAND = $_wayland
@@ -3108,6 +3130,7 @@ XV = $_xv
3108 3130 ENCODING = $_encoding
3109 3131
3110 3132 CONFIG_VDPAU = $_vdpau
  3133 +CONFIG_VAAPI = $_vaapi
3111 3134 CONFIG_ZLIB = $_zlib
3112 3135
3113 3136 HAVE_PTHREADS = $_pthreads
@@ -3274,6 +3297,7 @@ $def_jpeg
3274 3297 $def_mng
3275 3298 $def_v4l2
3276 3299 $def_vdpau
  3300 +$def_vaapi
3277 3301 $def_vm
3278 3302 $def_x11
3279 3303 $def_wayland
3  mpvcore/options.c
@@ -469,7 +469,8 @@ const m_option_t mp_opts[] = {
469 469 {"auto", -1},
470 470 {"vdpau", 1},
471 471 {"vda", 2},
472   - {"crystalhd", 3})),
  472 + {"crystalhd", 3},
  473 + {"vaapi", 4})),
473 474 OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
474 475
475 476 // postprocessing:
1  video/decode/dec_video.h
@@ -51,6 +51,7 @@ extern int divx_quality;
51 51 // The VO can set the context pointer for supported APIs.
52 52 struct mp_hwdec_info {
53 53 struct mp_vdpau_ctx *vdpau_ctx;
  54 + struct mp_vaapi_ctx *vaapi_ctx;
54 55 };
55 56
56 57 #endif /* MPLAYER_DEC_VIDEO_H */
1  video/decode/lavc.h
@@ -17,6 +17,7 @@ enum hwdec_type {
17 17 HWDEC_VDPAU = 1,
18 18 HWDEC_VDA = 2,
19 19 HWDEC_CRYSTALHD = 3,
  20 + HWDEC_VAAPI = 4,
20 21 };
21 22
22 23 typedef struct lavc_ctx {
414 video/decode/vaapi.c
... ... @@ -0,0 +1,414 @@
  1 +/*
  2 + * This file is part of mpv.
  3 + *
  4 + * With some chunks from original MPlayer VAAPI patch:
  5 + * Copyright (C) 2008-2009 Splitted-Desktop Systems
  6 + *
  7 + * mpv is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; either version 2 of the License, or
  10 + * (at your option) any later version.
  11 + *
  12 + * mpv is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License along
  18 + * with mpv. If not, see <http://www.gnu.org/licenses/>.
  19 + */
  20 +
  21 +#include <stddef.h>
  22 +#include <assert.h>
  23 +
  24 +#include <libavcodec/avcodec.h>
  25 +#include <libavcodec/vaapi.h>
  26 +#include <libavutil/common.h>
  27 +
  28 +#include "lavc.h"
  29 +#include "mpvcore/mp_common.h"
  30 +#include "mpvcore/av_common.h"
  31 +#include "video/fmt-conversion.h"
  32 +#include "video/vaapi.h"
  33 +#include "video/decode/dec_video.h"
  34 +
  35 +/*
  36 + * The VAAPI decoder can work only with surfaces passed to the decoder at
  37 + * creation time. This means all surfaces have to be created in advance.
  38 + * So, additionally to the maximum number of reference frames, we need
  39 + * surfaces for:
  40 + * - 1 decode frame
  41 + * - decoding 1 frame ahead (done by generic playback code)
  42 + * - keeping the reference to the previous frame (done by vo_vaapi.c)
  43 + * Note that redundant additional surfaces also might allow for some
  44 + * buffering (i.e. not trying to reuse a surface while it's busy).
  45 + */
  46 +#define ADDTIONAL_SURFACES 3
  47 +
  48 +// Magic number taken from original MPlayer vaapi patch.
  49 +#define MAX_DECODER_SURFACES 21
  50 +
  51 +#define MAX_SURFACES (MAX_DECODER_SURFACES + ADDTIONAL_SURFACES)
  52 +
  53 +struct priv {
  54 + struct mp_vaapi_ctx *ctx;
  55 + VADisplay display;
  56 +
  57 + // libavcodec shared struct
  58 + struct vaapi_context *va_context;
  59 + struct vaapi_context va_context_storage;
  60 +
  61 + int format, w, h;
  62 + VASurfaceID surfaces[MAX_SURFACES];
  63 +};
  64 +
  65 +struct profile_entry {
  66 + enum AVCodecID av_codec;
  67 + int ff_profile;
  68 + VAProfile va_profile;
  69 + int maxrefs;
  70 +};
  71 +
  72 +#define PE(av_codec_id, ff_profile, va_dcoder_profile, maxrefs) \
  73 + {AV_CODEC_ID_ ## av_codec_id, \
  74 + FF_PROFILE_ ## ff_profile, \
  75 + VAProfile ## va_dcoder_profile, \
  76 + maxrefs}
  77 +
  78 +static const struct profile_entry profiles[] = {
  79 + PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple, 2),
  80 + PE(MPEG2VIDEO, UNKNOWN, MPEG2Main, 2),
  81 + PE(H264, H264_BASELINE, H264Baseline, 16),
  82 + PE(H264, H264_CONSTRAINED_BASELINE, H264ConstrainedBaseline, 16),
  83 + PE(H264, H264_MAIN, H264Main, 16),
  84 + PE(H264, UNKNOWN, H264High, 16),
  85 + PE(WMV3, VC1_SIMPLE, VC1Simple, 2),
  86 + PE(WMV3, VC1_MAIN, VC1Main, 2),
  87 + PE(WMV3, UNKNOWN, VC1Advanced, 2),
  88 + PE(VC1, VC1_SIMPLE, VC1Simple, 2),
  89 + PE(VC1, VC1_MAIN, VC1Main, 2),
  90 + PE(VC1, UNKNOWN, VC1Advanced, 2),
  91 + // No idea whether these are correct
  92 + PE(MPEG4, MPEG4_SIMPLE, MPEG4Simple, 2),
  93 + PE(MPEG4, MPEG4_MAIN, MPEG4Main, 2),
  94 + PE(MPEG4, UNKNOWN, MPEG4AdvancedSimple, 2),
  95 +};
  96 +
  97 +static const struct profile_entry *find_codec(enum AVCodecID id, int ff_profile)
  98 +{
  99 + for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
  100 + if (profiles[n].av_codec == id &&
  101 + (profiles[n].ff_profile == ff_profile ||
  102 + profiles[n].ff_profile == FF_PROFILE_UNKNOWN))
  103 + {
  104 + return &profiles[n];
  105 + }
  106 + }
  107 + return NULL;
  108 +}
  109 +
  110 +
  111 +static const char *str_va_profile(VAProfile profile)
  112 +{
  113 + switch (profile) {
  114 +#define PROFILE(profile) \
  115 + case VAProfile##profile: return "VAProfile" #profile
  116 + PROFILE(MPEG2Simple);
  117 + PROFILE(MPEG2Main);
  118 + PROFILE(MPEG4Simple);
  119 + PROFILE(MPEG4AdvancedSimple);
  120 + PROFILE(MPEG4Main);
  121 + PROFILE(H264Baseline);
  122 + PROFILE(H264Main);
  123 + PROFILE(H264High);
  124 + PROFILE(VC1Simple);
  125 + PROFILE(VC1Main);
  126 + PROFILE(VC1Advanced);
  127 +#undef PROFILE
  128 + }
  129 + return "<unknown>";
  130 +}
  131 +
  132 +static int find_entrypoint(int format, VAEntrypoint *ep, int num_ep)
  133 +{
  134 + int entrypoint = -1;
  135 + switch (format) {
  136 + case IMGFMT_VAAPI: entrypoint = VAEntrypointVLD; break;
  137 + case IMGFMT_VAAPI_MPEG2_IDCT: entrypoint = VAEntrypointIDCT; break;
  138 + case IMGFMT_VAAPI_MPEG2_MOCO: entrypoint = VAEntrypointMoComp; break;
  139 + }
  140 + for (int n = 0; n < num_ep; n++) {
  141 + if (ep[n] == entrypoint)
  142 + return entrypoint;
  143 + }
  144 + return -1;
  145 +}
  146 +
  147 +static int is_direct_mapping(VADisplay display)
  148 +{
  149 + VADisplayAttribute attr;
  150 + VAStatus status;
  151 +
  152 +#if VA_CHECK_VERSION(0,34,0)
  153 + attr.type = VADisplayAttribRenderMode;
  154 + attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
  155 +
  156 + status = vaGetDisplayAttributes(display, &attr, 1);
  157 + if (status == VA_STATUS_SUCCESS)
  158 + return !(attr.value & (VA_RENDER_MODE_LOCAL_OVERLAY|
  159 + VA_RENDER_MODE_EXTERNAL_OVERLAY));
  160 +#else
  161 + /* If the driver doesn't make a copy of the VA surface for
  162 + display, then we have to retain it until it's no longer the
  163 + visible surface. In other words, if the driver is using
  164 + DirectSurface mode, we don't want to decode the new surface
  165 + into the previous one that was used for display. */
  166 + attr.type = VADisplayAttribDirectSurface;
  167 + attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
  168 +
  169 + status = vaGetDisplayAttributes(display, &attr, 1);
  170 + if (status == VA_STATUS_SUCCESS)
  171 + return !attr.value;
  172 +#endif
  173 + return 0;
  174 +}
  175 +
  176 +// Make vo_vaapi.c pool the required number of surfaces.
  177 +// This is very touchy: vo_vaapi.c must not free surfaces while we decode,
  178 +// and we must allocate only surfaces that were passed to the decoder on
  179 +// creation.
  180 +// We achieve this by deleting all previous surfaces, then allocate every
  181 +// surface needed. Then we free these surfaces, and rely on the fact that
  182 +// vo_vaapi.c keeps the released surfaces in the pool, and only allocates
  183 +// new surfaces out of that pool.
  184 +static int preallocate_surfaces(struct lavc_ctx *ctx, int va_rt_format, int num)
  185 +{
  186 + struct priv *p = ctx->hwdec_priv;
  187 + int res = -1;
  188 +
  189 + struct mp_image *tmp_surfaces[MAX_SURFACES] = {0};
  190 +
  191 + p->ctx->flush(p->ctx); // free previously allocated surfaces
  192 +
  193 + for (int n = 0; n < num; n++) {
  194 + tmp_surfaces[n] = p->ctx->get_surface(p->ctx, va_rt_format, p->format,
  195 + p->w, p->h);
  196 + if (!tmp_surfaces[n])
  197 + goto done;
  198 + p->surfaces[n] = (uintptr_t)tmp_surfaces[n]->planes[3];
  199 + }
  200 + res = 0;
  201 +
  202 +done:
  203 + for (int n = 0; n < num; n++)
  204 + talloc_free(tmp_surfaces[n]);
  205 + return res;
  206 +}
  207 +
  208 +static void destroy_decoder(struct lavc_ctx *ctx)
  209 +{
  210 + struct priv *p = ctx->hwdec_priv;
  211 +
  212 + if (p->va_context->context_id != VA_INVALID_ID) {
  213 + vaDestroyContext(p->display, p->va_context->context_id);
  214 + p->va_context->context_id = VA_INVALID_ID;
  215 + }
  216 +
  217 + if (p->va_context->config_id != VA_INVALID_ID) {
  218 + vaDestroyConfig(p->display, p->va_context->config_id);
  219 + p->va_context->config_id = VA_INVALID_ID;
  220 + }
  221 +
  222 + for (int n = 0; n < MAX_SURFACES; n++)
  223 + p->surfaces[n] = VA_INVALID_ID;
  224 +}
  225 +
  226 +static int create_decoder(struct lavc_ctx *ctx)
  227 +{
  228 + void *tmp = talloc_new(NULL);
  229 +
  230 + struct priv *p = ctx->hwdec_priv;
  231 + VAStatus status;
  232 + int res = -1;
  233 +
  234 + assert(IMGFMT_IS_VAAPI(p->format));
  235 +
  236 + destroy_decoder(ctx);
  237 +
  238 + const struct profile_entry *pe = find_codec(ctx->avctx->codec_id,
  239 + ctx->avctx->profile);
  240 + if (!pe) {
  241 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Unknown codec!\n");
  242 + goto error;
  243 + }
  244 +
  245 + int num_profiles = vaMaxNumProfiles(p->display);
  246 + VAProfile *va_profiles = talloc_zero_array(tmp, VAProfile, num_profiles);
  247 + status = vaQueryConfigProfiles(p->display, va_profiles, &num_profiles);
  248 + if (!check_va_status(status, "vaQueryConfigProfiles()"))
  249 + goto error;
  250 + mp_msg(MSGT_VO, MSGL_DBG2, "[vaapi] %d profiles available:\n", num_profiles);
  251 + for (int i = 0; i < num_profiles; i++)
  252 + mp_msg(MSGT_VO, MSGL_DBG2, " %s\n", str_va_profile(va_profiles[i]));
  253 +
  254 + bool profile_found = false;
  255 + for (int i = 0; i < num_profiles; i++) {
  256 + if (pe->va_profile == va_profiles[i]) {
  257 + profile_found = true;
  258 + break;
  259 + }
  260 + }
  261 + if (!profile_found) {
  262 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Profile '%s' not available.\n",
  263 + str_va_profile(pe->va_profile));
  264 + goto error;
  265 + }
  266 +
  267 + int num_surfaces = pe->maxrefs;
  268 + if (!is_direct_mapping(p->display)) {
  269 + mp_msg(MSGT_VO, MSGL_V, "[vaapi] No direct mapping.\n");
  270 + // Note: not sure why it has to be *=2 rather than +=1.
  271 + num_surfaces *= 2;
  272 + }
  273 + num_surfaces = MPMIN(num_surfaces, MAX_DECODER_SURFACES) + ADDTIONAL_SURFACES;
  274 +
  275 + if (num_surfaces > MAX_SURFACES) {
  276 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Internal error: too many surfaces.\n");
  277 + goto error;
  278 + }
  279 +
  280 + if (preallocate_surfaces(ctx, VA_RT_FORMAT_YUV420, num_surfaces) < 0) {
  281 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not allocate surfaces.\n");
  282 + goto error;
  283 + }
  284 +
  285 + int num_ep = vaMaxNumEntrypoints(p->display);
  286 + VAEntrypoint *ep = talloc_zero_array(tmp, VAEntrypoint, num_ep);
  287 + status = vaQueryConfigEntrypoints(p->display, pe->va_profile, ep, &num_ep);
  288 + if (!check_va_status(status, "vaQueryConfigEntrypoints()"))
  289 + goto error;
  290 +
  291 + VAEntrypoint entrypoint = find_entrypoint(p->format, ep, num_ep);
  292 + if (entrypoint < 0) {
  293 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Could not find VA entrypoint.\n");
  294 + goto error;
  295 + }
  296 +
  297 + VAConfigAttrib attrib = {
  298 + .type = VAConfigAttribRTFormat,
  299 + };
  300 + status = vaGetConfigAttributes(p->display, pe->va_profile, entrypoint,
  301 + &attrib, 1);
  302 + if (!check_va_status(status, "vaGetConfigAttributes()"))
  303 + goto error;
  304 + if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) {
  305 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Chroma format not supported.\n");
  306 + goto error;
  307 + }
  308 +
  309 + status = vaCreateConfig(p->display, pe->va_profile, entrypoint, &attrib, 1,
  310 + &p->va_context->config_id);
  311 + if (!check_va_status(status, "vaCreateConfig()"))
  312 + goto error;
  313 +
  314 + status = vaCreateContext(p->display, p->va_context->config_id,
  315 + p->w, p->h, VA_PROGRESSIVE,
  316 + p->surfaces, num_surfaces,
  317 + &p->va_context->context_id);
  318 + if (!check_va_status(status, "vaCreateContext()"))
  319 + goto error;
  320 +
  321 + res = 0;
  322 +error:
  323 + talloc_free(tmp);
  324 + return res;
  325 +}
  326 +
  327 +static struct mp_image *allocate_image(struct lavc_ctx *ctx, AVFrame *frame)
  328 +{
  329 + struct priv *p = ctx->hwdec_priv;
  330 + int format = pixfmt2imgfmt(frame->format);
  331 +
  332 + if (!IMGFMT_IS_VAAPI(format))
  333 + return NULL;
  334 +
  335 + // frame->width/height lie. Using them breaks with non-mod 16 video.
  336 + int w = ctx->avctx->width;
  337 + int h = ctx->avctx->height;
  338 +
  339 + if (format != p->format || w != p->w || h != p->h ||
  340 + p->va_context->context_id == VA_INVALID_ID)
  341 + {
  342 + p->format = format;
  343 + p->w = w;
  344 + p->h = h;
  345 + if (create_decoder(ctx) < 0)
  346 + return NULL;
  347 + }
  348 +
  349 + struct mp_image *img = p->ctx->get_surface(p->ctx, VA_RT_FORMAT_YUV420,
  350 + format, p->w, p->h);
  351 + if (img) {
  352 + for (int n = 0; n < MAX_SURFACES; n++) {
  353 + if (p->surfaces[n] == (uintptr_t)img->planes[3])
  354 + return img;
  355 + }
  356 + talloc_free(img);
  357 + }
  358 + mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Insufficient number of surfaces.\n");
  359 + return NULL;
  360 +}
  361 +
  362 +static void uninit(struct lavc_ctx *ctx)
  363 +{
  364 + struct priv *p = ctx->hwdec_priv;
  365 +
  366 + if (!p)
  367 + return;
  368 +
  369 + destroy_decoder(ctx);
  370 +
  371 + talloc_free(p);
  372 + ctx->hwdec_priv = NULL;
  373 +}
  374 +
  375 +static int init(struct lavc_ctx *ctx)
  376 +{
  377 + struct priv *p = talloc_ptrtype(NULL, p);
  378 + *p = (struct priv) {
  379 + .ctx = ctx->hwdec_info->vaapi_ctx,
  380 + .va_context = &p->va_context_storage,
  381 + };
  382 + ctx->hwdec_priv = p;
  383 +
  384 + p->display = p->ctx->display;
  385 +
  386 + p->va_context->display = p->display;
  387 + p->va_context->config_id = VA_INVALID_ID;
  388 + p->va_context->context_id = VA_INVALID_ID;
  389 +
  390 + ctx->avctx->hwaccel_context = p->va_context;
  391 +
  392 + return 0;
  393 +}
  394 +
  395 +
  396 +static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
  397 + const char *decoder)
  398 +{
  399 + if (!info || !info->vaapi_ctx)
  400 + return HWDEC_ERR_NO_CTX;
  401 + if (!find_codec(mp_codec_to_av_codec_id(decoder), FF_PROFILE_UNKNOWN))
  402 + return HWDEC_ERR_NO_CODEC;
  403 + return 0;
  404 +}
  405 +
  406 +const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
  407 + .type = HWDEC_VAAPI,
  408 + .image_formats = (const int[]) {IMGFMT_VAAPI, IMGFMT_VAAPI_MPEG2_IDCT,
  409 + IMGFMT_VAAPI_MPEG2_MOCO, 0},
  410 + .probe = probe,
  411 + .init = init,
  412 + .uninit = uninit,
  413 + .allocate_image = allocate_image,
  414 +};
4 video/decode/vd_lavc.c
@@ -84,6 +84,7 @@ const m_option_t lavc_decode_opts_conf[] = {
84 84
85 85 const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
86 86 const struct vd_lavc_hwdec mp_vd_lavc_vdpau_old;
  87 +const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
87 88
88 89 static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
89 90 .type = HWDEC_CRYSTALHD,
@@ -113,6 +114,9 @@ static const struct vd_lavc_hwdec *hwdec_list[] = {
113 114 #endif // CONFIG_VDPAU
114 115 &mp_vd_lavc_vda,
115 116 &mp_vd_lavc_crystalhd,
  117 +#if CONFIG_VAAPI
  118 + &mp_vd_lavc_vaapi,
  119 +#endif
116 120 NULL
117 121 };
118 122
5 video/fmt-conversion.c
@@ -183,6 +183,11 @@ static const struct {
183 183 // map to an arbitrary but existing vdpau format
184 184 {IMGFMT_VDPAU, PIX_FMT_VDPAU_H264},
185 185 #endif
  186 +
  187 + {IMGFMT_VAAPI, PIX_FMT_VAAPI_VLD},
  188 + {IMGFMT_VAAPI_MPEG2_IDCT, PIX_FMT_VAAPI_IDCT},
  189 + {IMGFMT_VAAPI_MPEG2_MOCO, PIX_FMT_VAAPI_MOCO},
  190 +
186 191 {0, PIX_FMT_NONE}
187 192 };
188 193
3  video/img_format.c
@@ -119,6 +119,9 @@ struct mp_imgfmt_entry mp_imgfmt_list[] = {
119 119 FMT("vdpau_vc1", IMGFMT_VDPAU_VC1)
120 120 FMT("vdpau_mpeg4", IMGFMT_VDPAU_MPEG4)
121 121 FMT("vdpau", IMGFMT_VDPAU)
  122 + FMT("vaapi", IMGFMT_VAAPI)
  123 + FMT("vaapi_mpeg2_idct", IMGFMT_VAAPI_MPEG2_IDCT)
  124 + FMT("vaapi_mpeg2_moco", IMGFMT_VAAPI_MPEG2_MOCO)
122 125 {0}
123 126 };
124 127
14 video/img_format.h
@@ -87,7 +87,6 @@ enum mp_imgfmt {
87 87 IMGFMT_START = 1000,
88 88
89 89 // Planar YUV formats
90   -
91 90 IMGFMT_444P, // 1x1
92 91 IMGFMT_422P, // 2x1
93 92 IMGFMT_440P, // 1x2
@@ -253,6 +252,14 @@ enum mp_imgfmt {
253 252 IMGFMT_VDPAU_FIRST = IMGFMT_VDPAU,
254 253 IMGFMT_VDPAU_LAST = IMGFMT_VDPAU_MPEG4,
255 254
  255 + IMGFMT_VAAPI,
  256 + IMGFMT_VAAPI_MPEG2_IDCT,
  257 + IMGFMT_VAAPI_MPEG2_MOCO,
  258 +
  259 + IMGFMT_VAAPI_FIRST = IMGFMT_VAAPI,
  260 + IMGFMT_VAAPI_LAST = IMGFMT_VAAPI_MPEG2_MOCO,
  261 +
  262 +
256 263 IMGFMT_END,
257 264
258 265 // Redundant format aliases for native endian access
@@ -328,7 +335,10 @@ static inline bool IMGFMT_IS_RGB(unsigned int fmt)
328 335 #define IMGFMT_IS_VDPAU(fmt) \
329 336 (((fmt) >= IMGFMT_VDPAU_FIRST) && ((fmt) <= IMGFMT_VDPAU_LAST))
330 337
331   -#define IMGFMT_IS_HWACCEL(fmt) IMGFMT_IS_VDPAU(fmt)
  338 +#define IMGFMT_IS_VAAPI(fmt) \
  339 + (((fmt) >= IMGFMT_VAAPI_FIRST) && ((fmt) <= IMGFMT_VAAPI_LAST))
  340 +
  341 +#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_VAAPI(fmt))
332 342
333 343
334 344 struct mp_imgfmt_entry {
4 video/out/vo.c
@@ -61,6 +61,7 @@ extern struct vo_driver video_out_direct3d;
61 61 extern struct vo_driver video_out_direct3d_shaders;
62 62 extern struct vo_driver video_out_sdl;
63 63 extern struct vo_driver video_out_corevideo;
  64 +extern struct vo_driver video_out_vaapi;
64 65
65 66 const struct vo_driver *video_out_drivers[] =
66 67 {
@@ -86,6 +87,9 @@ const struct vo_driver *video_out_drivers[] =
86 87 #ifdef CONFIG_GL
87 88 &video_out_opengl_old,
88 89 #endif
  90 +#ifdef CONFIG_VAAPI
  91 + &video_out_vaapi,
  92 +#endif
89 93 #ifdef CONFIG_X11
90 94 &video_out_x11,
91 95 #endif
2  video/out/vo.h
@@ -50,7 +50,7 @@ enum mp_voctrl {
50 50 VOCTRL_SET_EQUALIZER, // struct voctrl_set_equalizer_args*
51 51 VOCTRL_GET_EQUALIZER, // struct voctrl_get_equalizer_args*
52 52
53   - /* for vdpau hardware decoding */
  53 + /* for hardware decoding */
54 54 VOCTRL_GET_HWDEC_INFO, // struct mp_hwdec_info*
55 55
56 56 VOCTRL_NEWFRAME,
1,054 video/out/vo_vaapi.c
... ... @@ -0,0 +1,1054 @@
  1 +/*
  2 + * VA API output module
  3 + *
  4 + * Copyright (C) 2008-2009 Splitted-Desktop Systems
  5 + *
  6 + * This file is part of MPlayer.
  7 + *
  8 + * MPlayer is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License as published by
  10 + * the Free Software Foundation; either version 2 of the License, or
  11 + * (at your option) any later version.
  12 + *
  13 + * MPlayer is distributed in the hope that it will be useful,
  14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 + * GNU General Public License for more details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License along
  19 + * with MPlayer; if not, write to the Free Software Foundation, Inc.,
  20 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21 + */
  22 +
  23 +#include <assert.h>
  24 +#include <stdarg.h>
  25 +
  26 +#include <X11/Xlib.h>
  27 +#include <X11/Xutil.h>
  28 +#include <va/va_x11.h>
  29 +
  30 +#include <libavutil/common.h>
  31 +#include <libavcodec/vaapi.h>
  32 +
  33 +#include "config.h"
  34 +#include "mpvcore/mp_msg.h"
  35 +#include "video/out/vo.h"
  36 +#include "video/memcpy_pic.h"
  37 +#include "sub/sub.h"
  38 +#include "sub/img_convert.h"
  39 +#include "x11_common.h"
  40 +
  41 +#include "video/vfcap.h"
  42 +#include "video/mp_image.h"
  43 +#include "video/vaapi.h"
  44 +#include "video/decode/dec_video.h"
  45 +
  46 +#define STR_FOURCC(fcc) \
  47 + (const char[]){(fcc), (fcc) >> 8u, (fcc) >> 16u, (fcc) >> 24u, 0}
  48 +
  49 +struct vaapi_surface {
  50 + VASurfaceID id; // VA_INVALID_ID if unallocated
  51 + int w, h, va_format; // parameters of allocated image (0/0/-1 unallocated)
  52 + VAImage image; // used for sofwtare decoding case
  53 + bool is_bound; // image bound to the surface?
  54 + bool is_used; // referenced by a mp_image
  55 + bool is_dead; // used, but deallocate VA objects as soon as possible
  56 + int order; // for LRU allocation
  57 +
  58 + // convenience shortcut for mp_image deallocation callback
  59 + struct priv *p;
  60 +};
  61 +
  62 +struct vaapi_osd_image {
  63 + int w, h;
  64 + VAImage image;
  65 + VASubpictureID subpic_id;
  66 + bool is_used;
  67 +};
  68 +
  69 +struct vaapi_subpic {
  70 + VASubpictureID id;
  71 + int src_x, src_y, src_w, src_h;
  72 + int dst_x, dst_y, dst_w, dst_h;
  73 +};
  74 +
  75 +struct vaapi_osd_part {
  76 + bool active;
  77 + int bitmap_pos_id;
  78 + struct vaapi_osd_image image;
  79 + struct vaapi_subpic subpic;
  80 + struct osd_conv_cache *conv_cache;
  81 +};
  82 +
  83 +#define MAX_OUTPUT_SURFACES 2
  84 +
  85 +struct priv {
  86 + struct mp_log *log;
  87 + struct vo *vo;
  88 + VADisplay display;
  89 + struct mp_vaapi_ctx mpvaapi;
  90 +
  91 + struct mp_image_params image_params;
  92 + struct mp_rect src_rect;
  93 + struct mp_rect dst_rect;
  94 + struct mp_osd_res screen_osd_res;
  95 +
  96 + struct mp_image *output_surfaces[MAX_OUTPUT_SURFACES];
  97 + struct mp_image *swdec_surfaces[MAX_OUTPUT_SURFACES];
  98 +
  99 + int output_surface;
  100 + int visible_surface;
  101 + int deint;
  102 + int deint_type;
  103 + int scaling;
  104 + int force_scaled_osd;
  105 +
  106 + VAImageFormat osd_format; // corresponds to OSD_VA_FORMAT
  107 + struct vaapi_osd_part osd_parts[MAX_OSD_PARTS];
  108 + bool osd_screen;
  109 +
  110 + int num_video_surfaces;
  111 + struct vaapi_surface **video_surfaces;
  112 + int video_surface_lru_counter;
  113 +
  114 + VAImageFormat *va_image_formats;
  115 + int va_num_image_formats;
  116 + VAImageFormat *va_subpic_formats;
  117 + unsigned int *va_subpic_flags;
  118 + int va_num_subpic_formats;
  119 + VADisplayAttribute *va_display_attrs;
  120 + int va_num_display_attrs;
  121 +};
  122 +
  123 +#define OSD_VA_FORMAT VA_FOURCC_BGRA
  124 +
  125 +static const bool osd_formats[SUBBITMAP_COUNT] = {
  126 + // Actually BGRA, but only on little endian.
  127 + // This will break on big endian, I think.
  128 + [SUBBITMAP_RGBA_STR] = true,
  129 +};
  130 +
  131 +struct fmtentry {
  132 + uint32_t va;
  133 + int mp;
  134 +};
  135 +static struct fmtentry va_to_imgfmt[] = {
  136 + {VA_FOURCC('Y','V','1','2'), IMGFMT_420P},
  137 + {VA_FOURCC('I','4','2','0'), IMGFMT_420P},
  138 + {VA_FOURCC('I','Y','U','V'), IMGFMT_420P},
  139 + {VA_FOURCC('N','V','1','2'), IMGFMT_NV12},
  140 + // Note: not sure about endian issues (the mp formats are byte-addressed)
  141 + {VA_FOURCC_RGBA, IMGFMT_RGBA},
  142 + {VA_FOURCC_BGRA, IMGFMT_BGRA},
  143 + // Untested.
  144 + //{VA_FOURCC_UYVY, IMGFMT_UYVY},
  145 + //{VA_FOURCC_YUY2, IMGFMT_YUYV},
  146 + {0}
  147 +};
  148 +
  149 +
  150 +static int va_fourcc_to_imgfmt(uint32_t fourcc)
  151 +{
  152 + for (int n = 0; va_to_imgfmt[n].mp; n++) {
  153 + if (va_to_imgfmt[n].va == fourcc)
  154 + return va_to_imgfmt[n].mp;
  155 + }
  156 + return 0;
  157 +}
  158 +
  159 +static VAImageFormat *VAImageFormat_from_imgfmt(struct priv *p, int format)
  160 +{
  161 + for (int i = 0; i < p->va_num_image_formats; i++) {
  162 + if (va_fourcc_to_imgfmt(p->va_image_formats[i].fourcc) == format)
  163 + return &p->va_image_formats[i];
  164 + }
  165 + return NULL;
  166 +}
  167 +
  168 +static struct vaapi_surface *to_vaapi_surface(struct priv *p,
  169 + struct mp_image *img)
  170 +{
  171 + if (!img || !IMGFMT_IS_VAAPI(img->imgfmt))
  172 + return NULL;
  173 + // Note: we _could_ use planes[1] or planes[2] to store a vaapi_surface
  174 + // pointer, but I just don't trust libavcodec enough.
  175 + VASurfaceID id = (uintptr_t)img->planes[3];
  176 + for (int n = 0; n < p->num_video_surfaces; n++) {
  177 + struct vaapi_surface *s = p->video_surfaces[n];
  178 + if (s->id == id)
  179 + return s;
  180 + }
  181 + return NULL;
  182 +}
  183 +
  184 +static struct vaapi_surface *alloc_vaapi_surface(struct priv *p, int w, int h,
  185 + int va_format)
  186 +{
  187 + VAStatus status;
  188 +
  189 + VASurfaceID id = VA_INVALID_ID;
  190 + status = vaCreateSurfaces(p->display, w, h, va_format, 1, &id);
  191 + if (!check_va_status(status, "vaCreateSurfaces()"))
  192 + return NULL;
  193 +
  194 + struct vaapi_surface *surface = NULL;
  195 + for (int n = 0; n < p->num_video_surfaces; n++) {
  196 + struct vaapi_surface *s = p->video_surfaces[n];
  197 + if (s->id == VA_INVALID_ID) {
  198 + surface = s;
  199 + break;
  200 + }
  201 + }
  202 + if (!surface) {
  203 + surface = talloc_ptrtype(NULL, surface);
  204 + MP_TARRAY_APPEND(p, p->video_surfaces, p->num_video_surfaces, surface);
  205 + }
  206 +
  207 + *surface = (struct vaapi_surface) {
  208 + .id = id,
  209 + .image = { .image_id = VA_INVALID_ID, .buf = VA_INVALID_ID },
  210 + .w = w,
  211 + .h = h,
  212 + .va_format = va_format,
  213 + .p = p,
  214 + };
  215 + return surface;
  216 +}
  217 +
  218 +static void destroy_vaapi_surface(struct priv *p, struct vaapi_surface *s)
  219 +{
  220 + if (!s || s->id == VA_INVALID_ID)
  221 + return;
  222 + assert(!s->is_used);
  223 +
  224 + if (s->image.image_id != VA_INVALID_ID)
  225 + vaDestroyImage(p->display, s->image.image_id);
  226 + vaDestroySurfaces(p->display, &s->id, 1);
  227 + s->id = VA_INVALID_ID;
  228 + s->w = 0;
  229 + s->h = 0;
  230 + s->va_format = -1;
  231 +}
  232 +
  233 +static struct vaapi_surface *get_vaapi_surface(struct priv *p, int w, int h,
  234 + int va_format)
  235 +{
  236 + struct vaapi_surface *best = NULL;
  237 +
  238 + for (int n = 0; n < p->num_video_surfaces; n++) {
  239 + struct vaapi_surface *s = p->video_surfaces[n];
  240 + if (!s->is_used && s->w == w && s->h == h && s->va_format == va_format) {
  241 + if (!best || best->order > s->order)
  242 + best = s;
  243 + }
  244 + }
  245 +
  246 + if (!best)
  247 + best = alloc_vaapi_surface(p, w, h, va_format);
  248 +
  249 + if (best) {
  250 + best->is_used = true;
  251 + best->order = ++p->video_surface_lru_counter;
  252 + }
  253 + return best;
  254 +}
  255 +
  256 +static void release_video_surface(void *ptr)
  257 +{
  258 + struct vaapi_surface *surface = ptr;
  259 + surface->is_used = false;
  260 + if (surface->is_dead)
  261 + destroy_vaapi_surface(surface->p, surface);
  262 +}
  263 +
  264 +static struct mp_image *get_surface(struct mp_vaapi_ctx *ctx, int va_rt_format,
  265 + int mp_format, int w, int h)
  266 +{
  267 + assert(IMGFMT_IS_VAAPI(mp_format));
  268 +
  269 + struct vo *vo = ctx->priv;
  270 + struct priv *p = vo->priv;
  271 +
  272 + struct mp_image img = {0};
  273 + mp_image_setfmt(&img, mp_format);
  274 + mp_image_set_size(&img, w, h);
  275 +
  276 + struct vaapi_surface *surface = get_vaapi_surface(p, w, h, va_rt_format);
  277 + if (!surface)
  278 + return NULL;
  279 +
  280 + // libavcodec probably wants it at [0] and [3]
  281 + // [1] and [2] are possibly free for own use.
  282 + for (int n = 0; n < 4; n++)
  283 + img.planes[n] = (void *)(uintptr_t)surface->id;
  284 +
  285 + return mp_image_new_custom_ref(&img, surface, release_video_surface);
  286 +}
  287 +
  288 +// This should be called only by code that is going to preallocate surfaces
  289 +// (and by uninit). Otherwise, hw decoder init might get confused by
  290 +// accidentally releasing hw decoder preallocated surfaces.
  291 +static void flush_surfaces(struct mp_vaapi_ctx *ctx)
  292 +{
  293 + struct vo *vo = ctx->priv;
  294 + struct priv *p = vo->priv;
  295 +
  296 + for (int n = 0; n < p->num_video_surfaces; n++) {
  297 + struct vaapi_surface *s = p->video_surfaces[n];
  298 + if (s->is_used) {
  299 + s->is_dead = true;
  300 + } else {
  301 + destroy_vaapi_surface(p, s);
  302 + }
  303 + }
  304 +}
  305 +
  306 +static void flush_output_surfaces(struct priv *p)
  307 +{
  308 + for (int n = 0; n < MAX_OUTPUT_SURFACES; n++) {
  309 + talloc_free(p->output_surfaces[n]);
  310 + p->output_surfaces[n] = NULL;
  311 + }
  312 + p->output_surface = 0;
  313 + p->visible_surface = 0;
  314 +}
  315 +
  316 +// See flush_surfaces() remarks - the same applies.
  317 +static void free_video_specific(struct priv *p)
  318 +{
  319 + flush_output_surfaces(p);
  320 +
  321 + for (int n = 0; n < MAX_OUTPUT_SURFACES; n++) {
  322 + talloc_free(p->swdec_surfaces[n]);
  323 + p->swdec_surfaces[n] = NULL;
  324 + }
  325 +
  326 + flush_surfaces(&p->mpvaapi);
  327 +}
  328 +
  329 +static int alloc_swdec_surfaces(struct priv *p, int w, int h, int format)
  330 +{
  331 + VAStatus status;
  332 +
  333 + free_video_specific(p);
  334 +
  335 + VAImageFormat *image_format = VAImageFormat_from_imgfmt(p, format);
  336 + if (!image_format)
  337 + return -1;
  338 + for (int i = 0; i < MAX_OUTPUT_SURFACES; i++) {
  339 + // WTF: no mapping from VAImageFormat -> VA_RT_FORMAT_
  340 + struct mp_image *img =
  341 + get_surface(&p->mpvaapi, VA_RT_FORMAT_YUV420, IMGFMT_VAAPI, w, h);
  342 + struct vaapi_surface *s = to_vaapi_surface(p, img);
  343 + if (!s)
  344 + return -1;
  345 +
  346 + if (s->image.image_id != VA_INVALID_ID) {
  347 + vaDestroyImage(p->display, s->image.image_id);
  348 + s->image.image_id = VA_INVALID_ID;
  349 + }
  350 +
  351 + status = vaDeriveImage(p->display, s->id, &s->image);
  352 + if (status == VA_STATUS_SUCCESS) {
  353 + /* vaDeriveImage() is supported, check format */
  354 + if (s->image.format.fourcc == image_format->fourcc &&
  355 + s->image.width == w && s->image.height == h)
  356 + {
  357 + s->is_bound = true;
  358 + MP_VERBOSE(p, "Using vaDeriveImage()\n");
  359 + } else {
  360 + vaDestroyImage(p->display, s->image.image_id);
  361 + s->image.image_id = VA_INVALID_ID;
  362 + status = VA_STATUS_ERROR_OPERATION_FAILED;
  363 + }
  364 + }
  365 + if (status != VA_STATUS_SUCCESS) {
  366 + status = vaCreateImage(p->display, image_format, w, h, &s->image);
  367 + if (!check_va_status(status, "vaCreateImage()")) {
  368 + talloc_free(img);
  369 + return -1;
  370 + }
  371 + }
  372 + p->swdec_surfaces[i] = img;
  373 + }
  374 + return 0;
  375 +}
  376 +
  377 +static void resize(struct priv *p)
  378 +{
  379 + vo_get_src_dst_rects(p->vo, &p->src_rect, &p->dst_rect, &p->screen_osd_res);
  380 +
  381 + // It's not clear whether this is needed; maybe not.
  382 + //vo_x11_clearwindow(p->vo, p->vo->x11->window);
  383 +
  384 + p->vo->want_redraw = true;
  385 +}
  386 +
  387 +static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
  388 +{
  389 + struct priv *p = vo->priv;
  390 +
  391 + vo_x11_config_vo_window(vo, NULL, vo->dx, vo->dy, vo->dwidth, vo->dheight,
  392 + flags, "vaapi");
  393 +
  394 + if (!IMGFMT_IS_VAAPI(params->imgfmt)) {
  395 + if (alloc_swdec_surfaces(p, params->w, params->h, params->imgfmt) < 0)
  396 + return -1;
  397 + }
  398 +
  399 + p->image_params = *params;
  400 + resize(p);
  401 + return 0;
  402 +}
  403 +
  404 +static int query_format(struct vo *vo, uint32_t format)
  405 +{
  406 + struct priv *p = vo->priv;
  407 +
  408 + if (IMGFMT_IS_VAAPI(format) || VAImageFormat_from_imgfmt(p, format))
  409 + return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
  410 +
  411 + return 0;
  412 +}
  413 +
  414 +static inline int get_field_flags(struct priv *p, int i, int flags)
  415 +{
  416 + return (p->deint && (flags & MP_IMGFIELD_INTERLACED) ?
  417 + (((!!(flags & MP_IMGFIELD_TOP_FIRST)) ^ i) == 0 ?
  418 + VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE);
  419 +}
  420 +
  421 +static inline int get_colorspace_flags(struct priv *p)
  422 +{
  423 +#if USE_VAAPI_COLORSPACE
  424 + switch (p->image_params.colorspace) {
  425 + case MP_CSP_BT_601: return VA_SRC_BT601;
  426 + case MP_CSP_BT_709: return VA_SRC_BT709;
  427 + case MP_CSP_SMPTE_240M: return VA_SRC_SMPTE_240;
  428 + }
  429 +#endif
  430 + return 0;
  431 +}
  432 +
  433 +static bool render_to_screen(struct priv *p, struct mp_image *mpi)
  434 +{
  435 + bool res = true;
  436 + VAStatus status;
  437 +
  438 + struct vaapi_surface *surface = to_vaapi_surface(p, mpi);
  439 + if (!surface)
  440 + return false;
  441 +
  442 + for (int n = 0; n < MAX_OSD_PARTS; n++) {
  443 + struct vaapi_osd_part *part = &p->osd_parts[n];
  444 + if (part->active) {
  445 + struct vaapi_subpic *sp = &part->subpic;
  446 + int flags = 0;
  447 + if (p->osd_screen)
  448 + flags |= VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD;
  449 + status = vaAssociateSubpicture2(p->display,
  450 + sp->id, &surface->id, 1,
  451 + sp->src_x, sp->src_y,
  452 + sp->src_w, sp->src_h,
  453 + sp->dst_x, sp->dst_y,
  454 + sp->dst_w, sp->dst_h,
  455 + flags);
  456 + check_va_status(status, "vaAssociateSubpicture()");
  457 + }
  458 + }
  459 +
  460 + for (int i = 0; i <= !!(p->deint > 1); i++) {
  461 + unsigned int flags = (get_field_flags(p, i, mpi->fields) |
  462 + get_colorspace_flags(p) |
  463 + p->scaling);
  464 + status = vaPutSurface(p->display,
  465 + surface->id,
  466 + p->vo->x11->window,
  467 + p->src_rect.x0,
  468 + p->src_rect.y0,
  469 + p->src_rect.x1 - p->src_rect.x0,
  470 + p->src_rect.y1 - p->src_rect.y0,
  471 + p->dst_rect.x0,
  472 + p->dst_rect.y0,
  473 + p->dst_rect.x1 - p->dst_rect.x0,
  474 + p->dst_rect.y1 - p->dst_rect.y0,
  475 + NULL, 0,
  476 + flags);
  477 + if (!check_va_status(status, "vaPutSurface()"))
  478 + res = false;
  479 + }
  480 +
  481 + for (int n = 0; n < MAX_OSD_PARTS; n++) {
  482 + struct vaapi_osd_part *part = &p->osd_parts[n];
  483 + if (part->active) {
  484 + struct vaapi_subpic *sp = &part->subpic;
  485 + status = vaDeassociateSubpicture(p->display, sp->id,
  486 + &surface->id, 1);
  487 + check_va_status(status, "vaDeassociateSubpicture()");
  488 + }
  489 + }
  490 +
  491 + return res;
  492 +}
  493 +
  494 +static void flip_page(struct vo *vo)
  495 +{
  496 + struct priv *p = vo->priv;
  497 +
  498 + p->visible_surface = p->output_surface;
  499 + render_to_screen(p, p->output_surfaces[p->output_surface]);
  500 + p->output_surface = (p->output_surface + 1) % MAX_OUTPUT_SURFACES;
  501 +}
  502 +
  503 +static int map_image(struct priv *p, VAImage *va_image, int mpfmt,
  504 + struct mp_image *dst)
  505 +{
  506 + VAStatus status;
  507 +
  508 + if (mpfmt != va_fourcc_to_imgfmt(va_image->format.fourcc))
  509 + return -1;
  510 +
  511 + void *image_data = NULL;
  512 + status = vaMapBuffer(p->display, va_image->buf, &image_data);
  513 + if (!check_va_status(status, "vaMapBuffer()"))