From 7dc74ee03721e8ccfa09122c56e76a9c62c68db4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 11 Jan 2017 16:02:08 +0100 Subject: vaapi: rename vaapi.c to vaapi_old.c vaapi.c will be reintroduced with the new code using the new libavcodec vaapi API. --- video/decode/vaapi.c | 570 ----------------------------------------------- video/decode/vaapi_old.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 570 insertions(+), 570 deletions(-) delete mode 100644 video/decode/vaapi.c create mode 100644 video/decode/vaapi_old.c (limited to 'video') diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c deleted file mode 100644 index c095868816..0000000000 --- a/video/decode/vaapi.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * 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 . - */ - -#include -#include - -#include -#include -#include - -#include "config.h" - -#include "lavc.h" -#include "common/common.h" -#include "common/av_common.h" -#include "video/fmt-conversion.h" -#include "video/vaapi.h" -#include "video/mp_image_pool.h" -#include "video/hwdec.h" -#include "video/filter/vf.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 all kinds of buffering between decoder and VO. - * 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 MPMAX(6, HWDEC_DELAY_QUEUE_COUNT) - -// Some upper bound. -#define MAX_SURFACES 25 - -struct priv { - struct mp_log *log; - struct mp_vaapi_ctx *ctx; - VADisplay display; - - const struct va_native_display *native_display_fns; - void *native_display; - - // libavcodec shared struct - struct vaapi_context *va_context; - struct vaapi_context va_context_storage; - - struct mp_image_pool *pool; - int rt_format; - - struct mp_image_pool *sw_pool; -}; - -struct va_native_display { - void (*create)(struct priv *p); - void (*destroy)(struct priv *p); -}; - -#if HAVE_VAAPI_X11 -#include -#include - -static void x11_destroy(struct priv *p) -{ - if (p->native_display) - XCloseDisplay(p->native_display); - p->native_display = NULL; -} - -static void x11_create(struct priv *p) -{ - p->native_display = XOpenDisplay(NULL); - if (!p->native_display) - return; - p->display = vaGetDisplay(p->native_display); - if (!p->display) - x11_destroy(p); -} - -static const struct va_native_display disp_x11 = { - .create = x11_create, - .destroy = x11_destroy, -}; -#endif - -#if HAVE_VAAPI_DRM -#include -#include -#include - -struct va_native_display_drm { - int drm_fd; -}; - -static void drm_destroy(struct priv *p) -{ - struct va_native_display_drm *native_display = p->native_display; - if (native_display) { - if (native_display->drm_fd >= 0) - close(native_display->drm_fd); - talloc_free(native_display); - p->native_display = NULL; - } -} - -static void drm_create(struct priv *p) -{ - static const char *drm_device_paths[] = { - "/dev/dri/renderD128", - "/dev/dri/card0", - NULL - }; - - for (int i = 0; drm_device_paths[i]; i++) { - int drm_fd = open(drm_device_paths[i], O_RDWR); - if (drm_fd < 0) - continue; - - struct va_native_display_drm *native_display = talloc_ptrtype(NULL, native_display); - native_display->drm_fd = drm_fd; - p->native_display = native_display; - p->display = vaGetDisplayDRM(drm_fd); - if (p->display) - return; - - drm_destroy(p); - } -} - -static const struct va_native_display disp_drm = { - .create = drm_create, - .destroy = drm_destroy, -}; -#endif - -static const struct va_native_display *const native_displays[] = { -#if HAVE_VAAPI_DRM - &disp_drm, -#endif -#if HAVE_VAAPI_X11 - &disp_x11, -#endif - NULL -}; - -#define HAS_HEVC VA_CHECK_VERSION(0, 38, 0) -#define HAS_VP9 (VA_CHECK_VERSION(0, 38, 1) && defined(FF_PROFILE_VP9_0)) - -#define PE(av_codec_id, ff_profile, vdp_profile) \ - {AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \ - VAProfile ## vdp_profile} - -static const struct hwdec_profile_entry profiles[] = { - PE(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main), - PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple), - PE(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple), - PE(MPEG4, MPEG4_MAIN, MPEG4Main), - PE(MPEG4, MPEG4_SIMPLE, MPEG4Simple), - PE(H264, H264_HIGH, H264High), - PE(H264, H264_MAIN, H264Main), - PE(H264, H264_BASELINE, H264Baseline), - PE(VC1, VC1_ADVANCED, VC1Advanced), - PE(VC1, VC1_MAIN, VC1Main), - PE(VC1, VC1_SIMPLE, VC1Simple), - PE(WMV3, VC1_ADVANCED, VC1Advanced), - PE(WMV3, VC1_MAIN, VC1Main), - PE(WMV3, VC1_SIMPLE, VC1Simple), -#if HAS_HEVC - PE(HEVC, HEVC_MAIN, HEVCMain), - PE(HEVC, HEVC_MAIN_10, HEVCMain10), -#endif -#if HAS_VP9 - PE(VP9, VP9_0, VP9Profile0), -#endif - {0} -}; - -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); -#if HAS_HEVC - PROFILE(HEVCMain); - PROFILE(HEVCMain10); -#endif -#if HAS_VP9 - PROFILE(VP9Profile0); -#endif -#undef PROFILE - } - return ""; -} - -static int find_entrypoint(int format, VAEntrypoint *ep, int num_ep) -{ - int entrypoint = -1; - switch (format) { - case IMGFMT_VAAPI: entrypoint = VAEntrypointVLD; break; - } - for (int n = 0; n < num_ep; n++) { - if (ep[n] == entrypoint) - return entrypoint; - } - return -1; -} - -// We must allocate only surfaces that were passed to the decoder on creation. -// We achieve this by reserving surfaces in the pool as needed. -// Releasing surfaces is necessary after filling the surface id list so -// that reserved surfaces can be reused for decoding. -static bool preallocate_surfaces(struct lavc_ctx *ctx, int num, int w, int h, - VASurfaceID out_surfaces[MAX_SURFACES]) -{ - struct priv *p = ctx->hwdec_priv; - struct mp_image *reserve[MAX_SURFACES] = {0}; - bool res = true; - - if (num > MAX_SURFACES) - return false; - - for (int n = 0; n < num; n++) { - reserve[n] = mp_image_pool_get(p->pool, IMGFMT_VAAPI, w, h); - out_surfaces[n] = va_surface_id(reserve[n]); - if (out_surfaces[n] == VA_INVALID_ID) { - MP_ERR(p, "Could not allocate surfaces.\n"); - res = false; - break; - } - } - for (int i = 0; i < num; i++) - talloc_free(reserve[i]); - return res; -} - -static void destroy_decoder(struct lavc_ctx *ctx) -{ - struct priv *p = ctx->hwdec_priv; - - va_lock(p->ctx); - - 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; - } - - va_unlock(p->ctx); - - mp_image_pool_clear(p->pool); -} - -static bool has_profile(VAProfile *va_profiles, int num_profiles, VAProfile p) -{ - for (int i = 0; i < num_profiles; i++) { - if (va_profiles[i] == p) - return true; - } - return false; -} - -static int init_decoder(struct lavc_ctx *ctx, int w, int h) -{ - void *tmp = talloc_new(NULL); - - struct priv *p = ctx->hwdec_priv; - VAStatus status; - int res = -1; - - destroy_decoder(ctx); - - va_lock(p->ctx); - - const struct hwdec_profile_entry *pe = hwdec_find_profile(ctx, profiles); - if (!pe) { - MP_ERR(p, "Unsupported codec or profile.\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(p, "vaQueryConfigProfiles()")) - goto error; - MP_DBG(p, "%d profiles available:\n", num_profiles); - for (int i = 0; i < num_profiles; i++) - MP_DBG(p, " %s\n", str_va_profile(va_profiles[i])); - - VAProfile va_profile = pe->hw_profile; - if (!has_profile(va_profiles, num_profiles, va_profile)) { - MP_ERR(p, "Decoder profile '%s' not available.\n", - str_va_profile(va_profile)); - goto error; - } - - MP_VERBOSE(p, "Using profile '%s'.\n", str_va_profile(va_profile)); - - int num_surfaces = hwdec_get_max_refs(ctx) + ADDTIONAL_SURFACES; - if (num_surfaces > MAX_SURFACES) { - MP_ERR(p, "Internal error: too many surfaces.\n"); - goto error; - } - - VASurfaceID surfaces[MAX_SURFACES]; - if (!preallocate_surfaces(ctx, num_surfaces, w, h, surfaces)) { - MP_ERR(p, "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, va_profile, ep, &num_ep); - if (!CHECK_VA_STATUS(p, "vaQueryConfigEntrypoints()")) - goto error; - - int entrypoint = find_entrypoint(IMGFMT_VAAPI, ep, num_ep); - if (entrypoint < 0) { - MP_ERR(p, "Could not find VA entrypoint.\n"); - goto error; - } - - VAConfigAttrib attrib = { - .type = VAConfigAttribRTFormat, - }; - status = vaGetConfigAttributes(p->display, va_profile, entrypoint, - &attrib, 1); - if (!CHECK_VA_STATUS(p, "vaGetConfigAttributes()")) - goto error; - if ((attrib.value & p->rt_format) == 0) { - MP_ERR(p, "Chroma format not supported.\n"); - goto error; - } - - status = vaCreateConfig(p->display, va_profile, entrypoint, &attrib, 1, - &p->va_context->config_id); - if (!CHECK_VA_STATUS(p, "vaCreateConfig()")) - goto error; - - status = vaCreateContext(p->display, p->va_context->config_id, - w, h, VA_PROGRESSIVE, - surfaces, num_surfaces, - &p->va_context->context_id); - if (!CHECK_VA_STATUS(p, "vaCreateContext()")) - goto error; - - res = 0; -error: - va_unlock(p->ctx); - talloc_free(tmp); - return res; -} - -static struct mp_image *allocate_image(struct lavc_ctx *ctx, int w, int h) -{ - struct priv *p = ctx->hwdec_priv; - - struct mp_image *img = mp_image_pool_get(p->pool, IMGFMT_VAAPI, w, h); - if (!img) - MP_ERR(p, "Failed to allocate additional VAAPI surface.\n"); - return img; -} - -static struct mp_image *update_format(struct lavc_ctx *ctx, struct mp_image *img) -{ - va_surface_init_subformat(img); - return img; -} - -static void destroy_va_dummy_ctx(struct priv *p) -{ - va_destroy(p->ctx); - p->ctx = NULL; - p->display = NULL; - if (p->native_display_fns) - p->native_display_fns->destroy(p); -} - -// Creates a "private" VADisplay, disconnected from the VO. We just create a -// new X connection, because that's simpler. (We could also pass the X -// connection along with struct mp_hwdec_devices, if we wanted.) -static bool create_va_dummy_ctx(struct priv *p) -{ - for (int n = 0; native_displays[n]; n++) { - native_displays[n]->create(p); - if (p->display) { - p->native_display_fns = native_displays[n]; - break; - } - } - if (!p->display) - goto destroy_ctx; - p->ctx = va_initialize(p->display, p->log, true); - if (!p->ctx) { - vaTerminate(p->display); - goto destroy_ctx; - } - return true; -destroy_ctx: - destroy_va_dummy_ctx(p); - return false; -} - -static void uninit(struct lavc_ctx *ctx) -{ - struct priv *p = ctx->hwdec_priv; - - if (!p) - return; - - destroy_decoder(ctx); - - talloc_free(p->pool); - p->pool = NULL; - - if (p->native_display_fns) - destroy_va_dummy_ctx(p); - - talloc_free(p); - ctx->hwdec_priv = NULL; -} - -static int init(struct lavc_ctx *ctx, bool direct) -{ - struct priv *p = talloc_ptrtype(NULL, p); - *p = (struct priv) { - .log = mp_log_new(p, ctx->log, "vaapi"), - .va_context = &p->va_context_storage, - .rt_format = VA_RT_FORMAT_YUV420 - }; - - if (direct) { - p->ctx = hwdec_devices_get(ctx->hwdec_devs, HWDEC_VAAPI)->ctx; - } else { - create_va_dummy_ctx(p); - if (!p->ctx) { - talloc_free(p); - return -1; - } - } - - p->display = p->ctx->display; - p->pool = talloc_steal(p, mp_image_pool_new(MAX_SURFACES)); - va_pool_set_allocator(p->pool, p->ctx, p->rt_format); - p->sw_pool = talloc_steal(p, mp_image_pool_new(17)); - - 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; - ctx->hwdec_priv = p; - - return 0; -} - -static int init_direct(struct lavc_ctx *ctx) -{ - return init(ctx, true); -} - -static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, - const char *codec) -{ - if (!hwdec_devices_load(ctx->hwdec_devs, HWDEC_VAAPI)) - return HWDEC_ERR_NO_CTX; - if (!hwdec_check_codec_support(codec, profiles)) - return HWDEC_ERR_NO_CODEC; - return 0; -} - -static int probe_copy(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, - const char *codec) -{ - struct priv dummy = {mp_null_log}; - if (!create_va_dummy_ctx(&dummy)) - return HWDEC_ERR_NO_CTX; - bool emulated = va_guess_if_emulated(dummy.ctx); - destroy_va_dummy_ctx(&dummy); - if (!hwdec_check_codec_support(codec, profiles)) - return HWDEC_ERR_NO_CODEC; - if (emulated) - return HWDEC_ERR_EMULATED; - return 0; -} - -static int init_copy(struct lavc_ctx *ctx) -{ - return init(ctx, false); -} - -static struct mp_image *copy_image(struct lavc_ctx *ctx, struct mp_image *img) -{ - struct priv *p = ctx->hwdec_priv; - - struct mp_image *simg = va_surface_download(img, p->sw_pool); - if (simg) { - talloc_free(img); - return simg; - } - return img; -} - -static void intel_shit_lock(struct lavc_ctx *ctx) -{ - struct priv *p = ctx->hwdec_priv; - va_lock(p->ctx); -} - -static void intel_crap_unlock(struct lavc_ctx *ctx) -{ - struct priv *p = ctx->hwdec_priv; - va_unlock(p->ctx); -} - -const struct vd_lavc_hwdec mp_vd_lavc_vaapi = { - .type = HWDEC_VAAPI, - .image_format = IMGFMT_VAAPI, - .probe = probe, - .init = init_direct, - .uninit = uninit, - .init_decoder = init_decoder, - .allocate_image = allocate_image, - .lock = intel_shit_lock, - .unlock = intel_crap_unlock, - .process_image = update_format, -}; - -const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = { - .type = HWDEC_VAAPI_COPY, - .copying = true, - .image_format = IMGFMT_VAAPI, - .probe = probe_copy, - .init = init_copy, - .uninit = uninit, - .init_decoder = init_decoder, - .allocate_image = allocate_image, - .process_image = copy_image, - .delay_queue = HWDEC_DELAY_QUEUE_COUNT, -}; diff --git a/video/decode/vaapi_old.c b/video/decode/vaapi_old.c new file mode 100644 index 0000000000..c095868816 --- /dev/null +++ b/video/decode/vaapi_old.c @@ -0,0 +1,570 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include +#include + +#include "config.h" + +#include "lavc.h" +#include "common/common.h" +#include "common/av_common.h" +#include "video/fmt-conversion.h" +#include "video/vaapi.h" +#include "video/mp_image_pool.h" +#include "video/hwdec.h" +#include "video/filter/vf.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 all kinds of buffering between decoder and VO. + * 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 MPMAX(6, HWDEC_DELAY_QUEUE_COUNT) + +// Some upper bound. +#define MAX_SURFACES 25 + +struct priv { + struct mp_log *log; + struct mp_vaapi_ctx *ctx; + VADisplay display; + + const struct va_native_display *native_display_fns; + void *native_display; + + // libavcodec shared struct + struct vaapi_context *va_context; + struct vaapi_context va_context_storage; + + struct mp_image_pool *pool; + int rt_format; + + struct mp_image_pool *sw_pool; +}; + +struct va_native_display { + void (*create)(struct priv *p); + void (*destroy)(struct priv *p); +}; + +#if HAVE_VAAPI_X11 +#include +#include + +static void x11_destroy(struct priv *p) +{ + if (p->native_display) + XCloseDisplay(p->native_display); + p->native_display = NULL; +} + +static void x11_create(struct priv *p) +{ + p->native_display = XOpenDisplay(NULL); + if (!p->native_display) + return; + p->display = vaGetDisplay(p->native_display); + if (!p->display) + x11_destroy(p); +} + +static const struct va_native_display disp_x11 = { + .create = x11_create, + .destroy = x11_destroy, +}; +#endif + +#if HAVE_VAAPI_DRM +#include +#include +#include + +struct va_native_display_drm { + int drm_fd; +}; + +static void drm_destroy(struct priv *p) +{ + struct va_native_display_drm *native_display = p->native_display; + if (native_display) { + if (native_display->drm_fd >= 0) + close(native_display->drm_fd); + talloc_free(native_display); + p->native_display = NULL; + } +} + +static void drm_create(struct priv *p) +{ + static const char *drm_device_paths[] = { + "/dev/dri/renderD128", + "/dev/dri/card0", + NULL + }; + + for (int i = 0; drm_device_paths[i]; i++) { + int drm_fd = open(drm_device_paths[i], O_RDWR); + if (drm_fd < 0) + continue; + + struct va_native_display_drm *native_display = talloc_ptrtype(NULL, native_display); + native_display->drm_fd = drm_fd; + p->native_display = native_display; + p->display = vaGetDisplayDRM(drm_fd); + if (p->display) + return; + + drm_destroy(p); + } +} + +static const struct va_native_display disp_drm = { + .create = drm_create, + .destroy = drm_destroy, +}; +#endif + +static const struct va_native_display *const native_displays[] = { +#if HAVE_VAAPI_DRM + &disp_drm, +#endif +#if HAVE_VAAPI_X11 + &disp_x11, +#endif + NULL +}; + +#define HAS_HEVC VA_CHECK_VERSION(0, 38, 0) +#define HAS_VP9 (VA_CHECK_VERSION(0, 38, 1) && defined(FF_PROFILE_VP9_0)) + +#define PE(av_codec_id, ff_profile, vdp_profile) \ + {AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \ + VAProfile ## vdp_profile} + +static const struct hwdec_profile_entry profiles[] = { + PE(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main), + PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple), + PE(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple), + PE(MPEG4, MPEG4_MAIN, MPEG4Main), + PE(MPEG4, MPEG4_SIMPLE, MPEG4Simple), + PE(H264, H264_HIGH, H264High), + PE(H264, H264_MAIN, H264Main), + PE(H264, H264_BASELINE, H264Baseline), + PE(VC1, VC1_ADVANCED, VC1Advanced), + PE(VC1, VC1_MAIN, VC1Main), + PE(VC1, VC1_SIMPLE, VC1Simple), + PE(WMV3, VC1_ADVANCED, VC1Advanced), + PE(WMV3, VC1_MAIN, VC1Main), + PE(WMV3, VC1_SIMPLE, VC1Simple), +#if HAS_HEVC + PE(HEVC, HEVC_MAIN, HEVCMain), + PE(HEVC, HEVC_MAIN_10, HEVCMain10), +#endif +#if HAS_VP9 + PE(VP9, VP9_0, VP9Profile0), +#endif + {0} +}; + +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); +#if HAS_HEVC + PROFILE(HEVCMain); + PROFILE(HEVCMain10); +#endif +#if HAS_VP9 + PROFILE(VP9Profile0); +#endif +#undef PROFILE + } + return ""; +} + +static int find_entrypoint(int format, VAEntrypoint *ep, int num_ep) +{ + int entrypoint = -1; + switch (format) { + case IMGFMT_VAAPI: entrypoint = VAEntrypointVLD; break; + } + for (int n = 0; n < num_ep; n++) { + if (ep[n] == entrypoint) + return entrypoint; + } + return -1; +} + +// We must allocate only surfaces that were passed to the decoder on creation. +// We achieve this by reserving surfaces in the pool as needed. +// Releasing surfaces is necessary after filling the surface id list so +// that reserved surfaces can be reused for decoding. +static bool preallocate_surfaces(struct lavc_ctx *ctx, int num, int w, int h, + VASurfaceID out_surfaces[MAX_SURFACES]) +{ + struct priv *p = ctx->hwdec_priv; + struct mp_image *reserve[MAX_SURFACES] = {0}; + bool res = true; + + if (num > MAX_SURFACES) + return false; + + for (int n = 0; n < num; n++) { + reserve[n] = mp_image_pool_get(p->pool, IMGFMT_VAAPI, w, h); + out_surfaces[n] = va_surface_id(reserve[n]); + if (out_surfaces[n] == VA_INVALID_ID) { + MP_ERR(p, "Could not allocate surfaces.\n"); + res = false; + break; + } + } + for (int i = 0; i < num; i++) + talloc_free(reserve[i]); + return res; +} + +static void destroy_decoder(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + va_lock(p->ctx); + + 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; + } + + va_unlock(p->ctx); + + mp_image_pool_clear(p->pool); +} + +static bool has_profile(VAProfile *va_profiles, int num_profiles, VAProfile p) +{ + for (int i = 0; i < num_profiles; i++) { + if (va_profiles[i] == p) + return true; + } + return false; +} + +static int init_decoder(struct lavc_ctx *ctx, int w, int h) +{ + void *tmp = talloc_new(NULL); + + struct priv *p = ctx->hwdec_priv; + VAStatus status; + int res = -1; + + destroy_decoder(ctx); + + va_lock(p->ctx); + + const struct hwdec_profile_entry *pe = hwdec_find_profile(ctx, profiles); + if (!pe) { + MP_ERR(p, "Unsupported codec or profile.\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(p, "vaQueryConfigProfiles()")) + goto error; + MP_DBG(p, "%d profiles available:\n", num_profiles); + for (int i = 0; i < num_profiles; i++) + MP_DBG(p, " %s\n", str_va_profile(va_profiles[i])); + + VAProfile va_profile = pe->hw_profile; + if (!has_profile(va_profiles, num_profiles, va_profile)) { + MP_ERR(p, "Decoder profile '%s' not available.\n", + str_va_profile(va_profile)); + goto error; + } + + MP_VERBOSE(p, "Using profile '%s'.\n", str_va_profile(va_profile)); + + int num_surfaces = hwdec_get_max_refs(ctx) + ADDTIONAL_SURFACES; + if (num_surfaces > MAX_SURFACES) { + MP_ERR(p, "Internal error: too many surfaces.\n"); + goto error; + } + + VASurfaceID surfaces[MAX_SURFACES]; + if (!preallocate_surfaces(ctx, num_surfaces, w, h, surfaces)) { + MP_ERR(p, "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, va_profile, ep, &num_ep); + if (!CHECK_VA_STATUS(p, "vaQueryConfigEntrypoints()")) + goto error; + + int entrypoint = find_entrypoint(IMGFMT_VAAPI, ep, num_ep); + if (entrypoint < 0) { + MP_ERR(p, "Could not find VA entrypoint.\n"); + goto error; + } + + VAConfigAttrib attrib = { + .type = VAConfigAttribRTFormat, + }; + status = vaGetConfigAttributes(p->display, va_profile, entrypoint, + &attrib, 1); + if (!CHECK_VA_STATUS(p, "vaGetConfigAttributes()")) + goto error; + if ((attrib.value & p->rt_format) == 0) { + MP_ERR(p, "Chroma format not supported.\n"); + goto error; + } + + status = vaCreateConfig(p->display, va_profile, entrypoint, &attrib, 1, + &p->va_context->config_id); + if (!CHECK_VA_STATUS(p, "vaCreateConfig()")) + goto error; + + status = vaCreateContext(p->display, p->va_context->config_id, + w, h, VA_PROGRESSIVE, + surfaces, num_surfaces, + &p->va_context->context_id); + if (!CHECK_VA_STATUS(p, "vaCreateContext()")) + goto error; + + res = 0; +error: + va_unlock(p->ctx); + talloc_free(tmp); + return res; +} + +static struct mp_image *allocate_image(struct lavc_ctx *ctx, int w, int h) +{ + struct priv *p = ctx->hwdec_priv; + + struct mp_image *img = mp_image_pool_get(p->pool, IMGFMT_VAAPI, w, h); + if (!img) + MP_ERR(p, "Failed to allocate additional VAAPI surface.\n"); + return img; +} + +static struct mp_image *update_format(struct lavc_ctx *ctx, struct mp_image *img) +{ + va_surface_init_subformat(img); + return img; +} + +static void destroy_va_dummy_ctx(struct priv *p) +{ + va_destroy(p->ctx); + p->ctx = NULL; + p->display = NULL; + if (p->native_display_fns) + p->native_display_fns->destroy(p); +} + +// Creates a "private" VADisplay, disconnected from the VO. We just create a +// new X connection, because that's simpler. (We could also pass the X +// connection along with struct mp_hwdec_devices, if we wanted.) +static bool create_va_dummy_ctx(struct priv *p) +{ + for (int n = 0; native_displays[n]; n++) { + native_displays[n]->create(p); + if (p->display) { + p->native_display_fns = native_displays[n]; + break; + } + } + if (!p->display) + goto destroy_ctx; + p->ctx = va_initialize(p->display, p->log, true); + if (!p->ctx) { + vaTerminate(p->display); + goto destroy_ctx; + } + return true; +destroy_ctx: + destroy_va_dummy_ctx(p); + return false; +} + +static void uninit(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!p) + return; + + destroy_decoder(ctx); + + talloc_free(p->pool); + p->pool = NULL; + + if (p->native_display_fns) + destroy_va_dummy_ctx(p); + + talloc_free(p); + ctx->hwdec_priv = NULL; +} + +static int init(struct lavc_ctx *ctx, bool direct) +{ + struct priv *p = talloc_ptrtype(NULL, p); + *p = (struct priv) { + .log = mp_log_new(p, ctx->log, "vaapi"), + .va_context = &p->va_context_storage, + .rt_format = VA_RT_FORMAT_YUV420 + }; + + if (direct) { + p->ctx = hwdec_devices_get(ctx->hwdec_devs, HWDEC_VAAPI)->ctx; + } else { + create_va_dummy_ctx(p); + if (!p->ctx) { + talloc_free(p); + return -1; + } + } + + p->display = p->ctx->display; + p->pool = talloc_steal(p, mp_image_pool_new(MAX_SURFACES)); + va_pool_set_allocator(p->pool, p->ctx, p->rt_format); + p->sw_pool = talloc_steal(p, mp_image_pool_new(17)); + + 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; + ctx->hwdec_priv = p; + + return 0; +} + +static int init_direct(struct lavc_ctx *ctx) +{ + return init(ctx, true); +} + +static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, + const char *codec) +{ + if (!hwdec_devices_load(ctx->hwdec_devs, HWDEC_VAAPI)) + return HWDEC_ERR_NO_CTX; + if (!hwdec_check_codec_support(codec, profiles)) + return HWDEC_ERR_NO_CODEC; + return 0; +} + +static int probe_copy(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, + const char *codec) +{ + struct priv dummy = {mp_null_log}; + if (!create_va_dummy_ctx(&dummy)) + return HWDEC_ERR_NO_CTX; + bool emulated = va_guess_if_emulated(dummy.ctx); + destroy_va_dummy_ctx(&dummy); + if (!hwdec_check_codec_support(codec, profiles)) + return HWDEC_ERR_NO_CODEC; + if (emulated) + return HWDEC_ERR_EMULATED; + return 0; +} + +static int init_copy(struct lavc_ctx *ctx) +{ + return init(ctx, false); +} + +static struct mp_image *copy_image(struct lavc_ctx *ctx, struct mp_image *img) +{ + struct priv *p = ctx->hwdec_priv; + + struct mp_image *simg = va_surface_download(img, p->sw_pool); + if (simg) { + talloc_free(img); + return simg; + } + return img; +} + +static void intel_shit_lock(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + va_lock(p->ctx); +} + +static void intel_crap_unlock(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + va_unlock(p->ctx); +} + +const struct vd_lavc_hwdec mp_vd_lavc_vaapi = { + .type = HWDEC_VAAPI, + .image_format = IMGFMT_VAAPI, + .probe = probe, + .init = init_direct, + .uninit = uninit, + .init_decoder = init_decoder, + .allocate_image = allocate_image, + .lock = intel_shit_lock, + .unlock = intel_crap_unlock, + .process_image = update_format, +}; + +const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = { + .type = HWDEC_VAAPI_COPY, + .copying = true, + .image_format = IMGFMT_VAAPI, + .probe = probe_copy, + .init = init_copy, + .uninit = uninit, + .init_decoder = init_decoder, + .allocate_image = allocate_image, + .process_image = copy_image, + .delay_queue = HWDEC_DELAY_QUEUE_COUNT, +}; -- cgit v1.2.3