Something went wrong. Try again.
🎥 Command line media player
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337/* * 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/msg.h"#include "osdep/threads.h"#include "mp_image.h"#include "img_format.h"#include "mp_image_pool.h"
#include <libavutil/hwcontext.h>#include <libavutil/hwcontext_vaapi.h>
int va_get_colorspace_flag(enum mp_csp csp){ switch (csp) { case MP_CSP_BT_601: return VA_SRC_BT601; case MP_CSP_BT_709: return VA_SRC_BT709; case MP_CSP_SMPTE_240M: return VA_SRC_SMPTE_240; } return 0;}
#if VA_CHECK_VERSION(1, 0, 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);}#else// Pre-libva2 VA message callbacks are global and do not have a context// parameter, so it's impossible to know from which VADisplay they// originate. Try to route them to existing mpv/libmpv instances within// this process.static pthread_mutex_t va_log_mutex = PTHREAD_MUTEX_INITIALIZER;static struct mp_vaapi_ctx **va_mpv_clients;static int num_va_mpv_clients;
static void va_message_callback(const char *msg, int mp_level){ pthread_mutex_lock(&va_log_mutex);
if (num_va_mpv_clients) { struct mp_log *dst = va_mpv_clients[num_va_mpv_clients - 1]->log; mp_msg(dst, mp_level, "libva: %s", msg); } else { // We can't get or call the original libva handler (vaSet... return // them, but it might be from some other lib etc.). So just do what // libva happened to do at the time of this writing. if (mp_level <= MSGL_ERR) { fprintf(stderr, "libva error: %s", msg); } else { fprintf(stderr, "libva info: %s", msg); } }
pthread_mutex_unlock(&va_log_mutex);}
static void va_error_callback(const char *msg){ va_message_callback(msg, MSGL_ERR);}
static void va_info_callback(const char *msg){ va_message_callback(msg, MSGL_V);}#endif
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);
#if !VA_CHECK_VERSION(1, 0, 0) pthread_mutex_lock(&va_log_mutex); for (int n = 0; n < num_va_mpv_clients; n++) { if (va_mpv_clients[n] == ctx) { MP_TARRAY_REMOVE_AT(va_mpv_clients, num_va_mpv_clients, n); break; } } if (num_va_mpv_clients == 0) TA_FREEP(&va_mpv_clients); // avoid triggering leak detectors pthread_mutex_unlock(&va_log_mutex);#endif
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;
#if VA_CHECK_VERSION(1, 0, 0) vaSetErrorCallback(display, va_error_callback, res); vaSetInfoCallback(display, va_info_callback, res);#else pthread_mutex_lock(&va_log_mutex); MP_TARRAY_APPEND(NULL, va_mpv_clients, num_va_mpv_clients, res); pthread_mutex_unlock(&va_log_mutex);
// Check some random symbol added after message callbacks. // VA_MICRO_VERSION wasn't bumped at the time.#ifdef VA_FOURCC_I010 vaSetErrorCallback(va_error_callback); vaSetInfoCallback(va_info_callback);#endif#endif
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)(VADisplay **out_display, void **out_native_ctx); 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(VADisplay **out_display, void **out_native_ctx){ 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_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(VADisplay **out_display, void **out_native_ctx){ 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 *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_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){ for (int n = 0; native_displays[n]; n++) { VADisplay *display = NULL; void *native_ctx = NULL; native_displays[n]->create(&display, &native_ctx); if (display) { struct mp_vaapi_ctx *ctx = va_initialize(display, log, params->probing); if (!ctx) { vaTerminate(display); native_displays[n]->destroy(native_ctx); return NULL; } ctx->native_ctx = native_ctx; ctx->destroy_native_ctx = native_displays[n]->destroy; return ctx->hwctx.av_device_ref; } } return NULL;}
const struct hwcontext_fns hwcontext_fns_vaapi = { .av_hwdevice_type = AV_HWDEVICE_TYPE_VAAPI, .create_dev = va_create_standalone, .is_emulated = is_emulated,};