Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338/* * This file is part of mpv. * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */
#include <assert.h>
#include "config.h"
#include "vaapi.h"#include "common/common.h"#include "common/global.h"#include "common/msg.h"#include "osdep/threads.h"#include "mp_image.h"#include "img_format.h"#include "mp_image_pool.h"#include "options/m_config.h"
#ifdef _WIN32#include "osdep/windows_utils.h"#include "out/d3d11/context.h"#include "out/gpu/d3d11_helpers.h"#endif
#include <libavutil/hwcontext.h>#include <libavutil/hwcontext_vaapi.h>
#ifdef _WIN32#define DEV_PATH_DEFAULT NULL#define DEV_PATH_VALIDATE mp_dxgi_validate_adapter#else#define DEV_PATH_DEFAULT "/dev/dri/renderD128"#define DEV_PATH_VALIDATE validate_path
static inline OPT_STRING_VALIDATE_FUNC(validate_path){ return (*value && **value) ? 0 : M_OPT_INVALID;}#endif
struct vaapi_opts { char *path;};
#define OPT_BASE_STRUCT struct vaapi_optsconst struct m_sub_options vaapi_conf = { .opts = (const struct m_option[]) { {"device", OPT_STRING_VALIDATE(path, DEV_PATH_VALIDATE)}, {0}, }, .defaults = &(const struct vaapi_opts) { .path = DEV_PATH_DEFAULT, }, .size = sizeof(struct vaapi_opts),};
int va_get_colorspace_flag(enum pl_color_system csp){ switch (csp) { case PL_COLOR_SYSTEM_BT_601: return VA_SRC_BT601; case PL_COLOR_SYSTEM_BT_709: return VA_SRC_BT709; case PL_COLOR_SYSTEM_SMPTE_240M: return VA_SRC_SMPTE_240; } return 0;}
static void va_message_callback(void *context, const char *msg, int mp_level){ struct mp_vaapi_ctx *res = context; mp_msg(res->log, mp_level, "libva: %s", msg);}
static void va_error_callback(void *context, const char *msg){ va_message_callback(context, msg, MSGL_ERR);}
static void va_info_callback(void *context, const char *msg){ va_message_callback(context, msg, MSGL_V);}
static void free_device_ref(struct AVHWDeviceContext *hwctx){ struct mp_vaapi_ctx *ctx = hwctx->user_opaque;
if (ctx->display) vaTerminate(ctx->display);
if (ctx->destroy_native_ctx) ctx->destroy_native_ctx(ctx->native_ctx);
talloc_free(ctx);}
struct mp_vaapi_ctx *va_initialize(VADisplay *display, struct mp_log *plog, bool probing){ AVBufferRef *avref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI); if (!avref) return NULL;
AVHWDeviceContext *hwctx = (void *)avref->data; AVVAAPIDeviceContext *vactx = hwctx->hwctx;
struct mp_vaapi_ctx *res = talloc_ptrtype(NULL, res); *res = (struct mp_vaapi_ctx) { .log = mp_log_new(res, plog, "/vaapi"), .display = display, .av_device_ref = avref, .hwctx = { .av_device_ref = avref, }, };
hwctx->free = free_device_ref; hwctx->user_opaque = res;
vaSetErrorCallback(display, va_error_callback, res); vaSetInfoCallback(display, va_info_callback, res);
int major, minor; int status = vaInitialize(display, &major, &minor); if (status != VA_STATUS_SUCCESS) { if (!probing) MP_ERR(res, "Failed to initialize VAAPI: %s\n", vaErrorStr(status)); goto error; } MP_VERBOSE(res, "Initialized VAAPI: version %d.%d\n", major, minor);
vactx->display = res->display;
if (av_hwdevice_ctx_init(res->av_device_ref) < 0) goto error;
return res;
error: res->display = NULL; // do not vaTerminate this va_destroy(res); return NULL;}
// Undo va_initialize, and close the VADisplay.void va_destroy(struct mp_vaapi_ctx *ctx){ if (!ctx) return;
AVBufferRef *ref = ctx->av_device_ref; av_buffer_unref(&ref); // frees ctx as well}
VASurfaceID va_surface_id(struct mp_image *mpi){ return mpi && mpi->imgfmt == IMGFMT_VAAPI ? (VASurfaceID)(uintptr_t)mpi->planes[3] : VA_INVALID_ID;}
static bool is_emulated(struct AVBufferRef *hw_device_ctx){ AVHWDeviceContext *hwctx = (void *)hw_device_ctx->data; AVVAAPIDeviceContext *vactx = hwctx->hwctx;
const char *s = vaQueryVendorString(vactx->display); return s && strstr(s, "VDPAU backend");}
bool va_guess_if_emulated(struct mp_vaapi_ctx *ctx){ return is_emulated(ctx->av_device_ref);}
struct va_native_display { void (*create)(struct mp_log *log, VADisplay **out_display, void **out_native_ctx, const char *path); void (*destroy)(void *native_ctx);};
#if HAVE_VAAPI_X11#include <X11/Xlib.h>#include <va/va_x11.h>
static void x11_destroy(void *native_ctx){ XCloseDisplay(native_ctx);}
static void x11_create(struct mp_log *log, VADisplay **out_display, void **out_native_ctx, const char *path){ void *native_display = XOpenDisplay(NULL); if (!native_display) return; *out_display = vaGetDisplay(native_display); if (*out_display) { *out_native_ctx = native_display; } else { XCloseDisplay(native_display); }}
static const struct va_native_display disp_x11 = { .create = x11_create, .destroy = x11_destroy,};#endif
#if HAVE_VAAPI_WIN32#include <va/va_win32.h>
static void win32_create(struct mp_log *log, VADisplay **out_display, void **out_native_ctx, const char *path){ LUID *luid = NULL; DXGI_ADAPTER_DESC1 desc = {0}; if (path && path[0]) { IDXGIAdapter1 *adapter = mp_get_dxgi_adapter(log, bstr0(path), NULL); if (!adapter || FAILED(IDXGIAdapter1_GetDesc1(adapter, &desc))) { mp_err(log, "Failed to get adapter LUID for name: %s\n", path); } else { luid = &desc.AdapterLuid; } SAFE_RELEASE(adapter); } *out_display = vaGetDisplayWin32(luid);}
static const struct va_native_display disp_win32 = { .create = win32_create,};#endif
#if HAVE_VAAPI_DRM#include <unistd.h>#include <fcntl.h>#include <va/va_drm.h>
struct va_native_display_drm { int drm_fd;};
static void drm_destroy(void *native_ctx){ struct va_native_display_drm *ctx = native_ctx; close(ctx->drm_fd); talloc_free(ctx);}
static void drm_create(struct mp_log *log, VADisplay **out_display, void **out_native_ctx, const char *path){ int drm_fd = open(path, O_RDWR); if (drm_fd < 0) return;
struct va_native_display_drm *ctx = talloc_ptrtype(NULL, ctx); ctx->drm_fd = drm_fd; *out_display = vaGetDisplayDRM(drm_fd); if (*out_display) { *out_native_ctx = ctx; return; }
close(drm_fd); talloc_free(ctx);}
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_WIN32 &disp_win32,#endif#if HAVE_VAAPI_X11 &disp_x11,#endif NULL};
static struct AVBufferRef *va_create_standalone(struct mpv_global *global, struct mp_log *log, struct hwcontext_create_dev_params *params){ struct AVBufferRef *ret = NULL; struct vaapi_opts *opts = mp_get_config_group(NULL, global, &vaapi_conf);
for (int n = 0; native_displays[n]; n++) { VADisplay *display = NULL; void *native_ctx = NULL; native_displays[n]->create(global->log, &display, &native_ctx, opts->path); if (display) { struct mp_vaapi_ctx *ctx = va_initialize(display, log, params->probing); if (!ctx) { vaTerminate(display); if (native_displays[n]->destroy) native_displays[n]->destroy(native_ctx); goto end; } ctx->native_ctx = native_ctx; ctx->destroy_native_ctx = native_displays[n]->destroy; ret = ctx->hwctx.av_device_ref; goto end; } }
end: talloc_free(opts); return ret;}
const struct hwcontext_fns hwcontext_fns_vaapi = { .av_hwdevice_type = AV_HWDEVICE_TYPE_VAAPI, .create_dev = va_create_standalone, .is_emulated = is_emulated,};