diff options
Diffstat (limited to 'video/vaapi.c')
-rw-r--r-- | video/vaapi.c | 315 |
1 files changed, 249 insertions, 66 deletions
diff --git a/video/vaapi.c b/video/vaapi.c index 604fffa738..5c8ce4c693 100644 --- a/video/vaapi.c +++ b/video/vaapi.c @@ -17,6 +17,8 @@ #include <assert.h> +#include "config.h" + #include "vaapi.h" #include "common/common.h" #include "common/msg.h" @@ -25,6 +27,9 @@ #include "img_format.h" #include "mp_image_pool.h" +#include <libavutil/hwcontext.h> +#include <libavutil/hwcontext_vaapi.h> + bool check_va_status(struct mp_log *log, VAStatus status, const char *msg) { if (status != VA_STATUS_SUCCESS) { @@ -108,23 +113,67 @@ static void va_get_formats(struct mp_vaapi_ctx *ctx) ctx->image_formats = formats; } -struct mp_vaapi_ctx *va_initialize(VADisplay *display, struct mp_log *plog, - bool probing) +// 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) { - struct mp_vaapi_ctx *res = NULL; - struct mp_log *log = mp_log_new(NULL, plog, "/vaapi"); - int major_version, minor_version; - int status = vaInitialize(display, &major_version, &minor_version); - if (status != VA_STATUS_SUCCESS && probing) - goto error; - if (!check_va_status(log, status, "vaInitialize()")) - goto error; + 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); +} + +static void open_lavu_vaapi_device(struct mp_vaapi_ctx *ctx) +{ + ctx->av_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI); + if (!ctx->av_device_ref) + return; + + AVHWDeviceContext *hwctx = (void *)ctx->av_device_ref->data; + AVVAAPIDeviceContext *vactx = hwctx->hwctx; - mp_verbose(log, "VA API version %d.%d\n", major_version, minor_version); + vactx->display = ctx->display; - res = talloc_ptrtype(NULL, res); + if (av_hwdevice_ctx_init(ctx->av_device_ref) < 0) + av_buffer_unref(&ctx->av_device_ref); + + ctx->hwctx.av_device_ref = ctx->av_device_ref; +} + +struct mp_vaapi_ctx *va_initialize(VADisplay *display, struct mp_log *plog, + bool probing) +{ + struct mp_vaapi_ctx *res = talloc_ptrtype(NULL, res); *res = (struct mp_vaapi_ctx) { - .log = talloc_steal(res, log), + .log = mp_log_new(res, plog, "/vaapi"), .display = display, .hwctx = { .type = HWDEC_VAAPI, @@ -132,18 +181,40 @@ struct mp_vaapi_ctx *va_initialize(VADisplay *display, struct mp_log *plog, .download_image = ctx_download_image, }, }; - mpthread_mutex_init_recursive(&res->lock); + + 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 + + int major_version, minor_version; + int status = vaInitialize(display, &major_version, &minor_version); + if (status != VA_STATUS_SUCCESS && probing) + goto error; + if (!check_va_status(res->log, status, "vaInitialize()")) + goto error; + + MP_VERBOSE(res, "VA API version %d.%d\n", major_version, minor_version); va_get_formats(res); if (!res->image_formats) goto error; + + // For now, some code will still work even if libavutil fails on old crap + // libva drivers (such as the vdpau wraper). So don't error out on failure. + open_lavu_vaapi_device(res); + return res; error: - if (res && res->display) - vaTerminate(res->display); - talloc_free(log); - talloc_free(res); + res->display = NULL; // do not vaTerminate this + va_destroy(res); return NULL; } @@ -153,7 +224,21 @@ void va_destroy(struct mp_vaapi_ctx *ctx) if (ctx) { if (ctx->display) vaTerminate(ctx->display); - pthread_mutex_destroy(&ctx->lock); + + if (ctx->destroy_native_ctx) + ctx->destroy_native_ctx(ctx->native_ctx); + + 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); + talloc_free(ctx); } } @@ -209,22 +294,27 @@ int va_surface_rt_format(struct mp_image *mpi) // padded surfaces for example.) void va_surface_get_uncropped_size(struct mp_image *mpi, int *out_w, int *out_h) { - struct va_surface *s = va_surface_in_mp_image(mpi); - *out_w = s ? s->w : 0; - *out_h = s ? s->h : 0; + if (mpi->hwctx) { + AVHWFramesContext *fctx = (void *)mpi->hwctx->data; + *out_w = fctx->width; + *out_h = fctx->height; + } else { + struct va_surface *s = va_surface_in_mp_image(mpi); + *out_w = s ? s->w : 0; + *out_h = s ? s->h : 0; + } } static void release_va_surface(void *arg) { struct va_surface *surface = arg; - va_lock(surface->ctx); if (surface->id != VA_INVALID_ID) { if (surface->image.image_id != VA_INVALID_ID) vaDestroyImage(surface->display, surface->image.image_id); vaDestroySurfaces(surface->display, &surface->id, 1); } - va_unlock(surface->ctx); + talloc_free(surface); } @@ -233,9 +323,7 @@ static struct mp_image *alloc_surface(struct mp_vaapi_ctx *ctx, int rt_format, { VASurfaceID id = VA_INVALID_ID; VAStatus status; - va_lock(ctx); status = vaCreateSurfaces(ctx->display, rt_format, w, h, &id, 1, NULL, 0); - va_unlock(ctx); if (!CHECK_VA_STATUS(ctx, "vaCreateSurfaces()")) return NULL; @@ -270,11 +358,8 @@ static void va_surface_image_destroy(struct va_surface *surface) surface->is_derived = false; } -static int va_surface_image_alloc(struct mp_image *img, VAImageFormat *format) +static int va_surface_image_alloc(struct va_surface *p, VAImageFormat *format) { - struct va_surface *p = va_surface_in_mp_image(img); - if (!format || !p) - return -1; VADisplay *display = p->display; if (p->image.image_id != VA_INVALID_ID && @@ -282,7 +367,6 @@ static int va_surface_image_alloc(struct mp_image *img, VAImageFormat *format) return 0; int r = 0; - va_lock(p->ctx); va_surface_image_destroy(p); @@ -308,7 +392,6 @@ static int va_surface_image_alloc(struct mp_image *img, VAImageFormat *format) } } - va_unlock(p->ctx); return r; } @@ -327,7 +410,7 @@ int va_surface_alloc_imgfmt(struct mp_image *img, int imgfmt) VAImageFormat *format = va_image_format_from_imgfmt(p->ctx, imgfmt); if (!format) return -1; - if (va_surface_image_alloc(img, format) < 0) + if (va_surface_image_alloc(p, format) < 0) return -1; return 0; } @@ -338,9 +421,7 @@ bool va_image_map(struct mp_vaapi_ctx *ctx, VAImage *image, struct mp_image *mpi if (imgfmt == IMGFMT_NONE) return false; void *data = NULL; - va_lock(ctx); const VAStatus status = vaMapBuffer(ctx->display, image->buf, &data); - va_unlock(ctx); if (!CHECK_VA_STATUS(ctx, "vaMapBuffer()")) return false; @@ -363,9 +444,7 @@ bool va_image_map(struct mp_vaapi_ctx *ctx, VAImage *image, struct mp_image *mpi bool va_image_unmap(struct mp_vaapi_ctx *ctx, VAImage *image) { - va_lock(ctx); const VAStatus status = vaUnmapBuffer(ctx->display, image->buf); - va_unlock(ctx); return CHECK_VA_STATUS(ctx, "vaUnmapBuffer()"); } @@ -389,12 +468,10 @@ int va_surface_upload(struct mp_image *va_dst, struct mp_image *sw_src) va_image_unmap(p->ctx, &p->image); if (!p->is_derived) { - va_lock(p->ctx); VAStatus status = vaPutImage(p->display, p->id, p->image.image_id, 0, 0, sw_src->w, sw_src->h, 0, 0, sw_src->w, sw_src->h); - va_unlock(p->ctx); if (!CHECK_VA_STATUS(p->ctx, "vaPutImage()")) return -1; } @@ -404,14 +481,10 @@ int va_surface_upload(struct mp_image *va_dst, struct mp_image *sw_src) return 0; } -static struct mp_image *try_download(struct mp_image *src, +static struct mp_image *try_download(struct va_surface *p, struct mp_image *src, struct mp_image_pool *pool) { VAStatus status; - struct va_surface *p = va_surface_in_mp_image(src); - if (!p) - return NULL; - VAImage *image = &p->image; if (image->image_id == VA_INVALID_ID || @@ -419,10 +492,8 @@ static struct mp_image *try_download(struct mp_image *src, return NULL; if (!p->is_derived) { - va_lock(p->ctx); status = vaGetImage(p->display, p->id, 0, 0, p->w, p->h, image->image_id); - va_unlock(p->ctx); if (status != VA_STATUS_SUCCESS) return NULL; } @@ -434,9 +505,7 @@ static struct mp_image *try_download(struct mp_image *src, mp_image_set_size(&tmp, src->w, src->h); // copy only visible part dst = mp_image_pool_get(pool, tmp.imgfmt, tmp.w, tmp.h); if (dst) { - va_lock(p->ctx); mp_check_gpu_memcpy(p->ctx->log, &p->ctx->gpu_memcpy_message); - va_unlock(p->ctx); mp_image_copy_gpu(dst, &tmp); mp_image_copy_attributes(dst, src); @@ -453,19 +522,23 @@ static struct mp_image *try_download(struct mp_image *src, struct mp_image *va_surface_download(struct mp_image *src, struct mp_image_pool *pool) { - struct va_surface *p = va_surface_in_mp_image(src); - if (!p) + if (!src || src->imgfmt != IMGFMT_VAAPI) return NULL; + struct va_surface *p = va_surface_in_mp_image(src); + if (!p) { + // We might still be able to get to the cheese if this is a surface + // produced by libavutil's vaapi glue code. + return mp_image_hw_download(src, pool); + } + struct mp_image *mpi = NULL; struct mp_vaapi_ctx *ctx = p->ctx; - va_lock(ctx); VAStatus status = vaSyncSurface(p->display, p->id); - va_unlock(ctx); if (!CHECK_VA_STATUS(ctx, "vaSyncSurface()")) - return NULL; + goto done; - struct mp_image *mpi = try_download(src, pool); + mpi = try_download(p, src, pool); if (mpi) - return mpi; + goto done; // We have no clue which format will work, so try them all. // Make sure to start with the most preferred format (nv12), to avoid @@ -474,16 +547,19 @@ struct mp_image *va_surface_download(struct mp_image *src, VAImageFormat *format = va_image_format_from_imgfmt(ctx, va_to_imgfmt[n].mp); if (format) { - if (va_surface_image_alloc(src, format) < 0) + if (va_surface_image_alloc(p, format) < 0) continue; - mpi = try_download(src, pool); + mpi = try_download(p, src, pool); if (mpi) - return mpi; + goto done; } } - MP_ERR(ctx, "failed to get surface data.\n"); - return NULL; +done: + + if (!mpi) + MP_ERR(ctx, "failed to get surface data.\n"); + return mpi; } // Set the hw_subfmt from the surface's real format. Because of this bug: @@ -503,8 +579,6 @@ void va_surface_init_subformat(struct mp_image *mpi) VAImage va_image = { .image_id = VA_INVALID_ID }; - va_lock(p->ctx); - status = vaDeriveImage(p->display, va_surface_id(mpi), &va_image); if (status != VA_STATUS_SUCCESS) goto err; @@ -514,8 +588,7 @@ void va_surface_init_subformat(struct mp_image *mpi) status = vaDestroyImage(p->display, va_image.image_id); CHECK_VA_STATUS(p->ctx, "vaDestroyImage()"); -err: - va_unlock(p->ctx); +err: ; } struct pool_alloc_ctx { @@ -548,8 +621,118 @@ void va_pool_set_allocator(struct mp_image_pool *pool, struct mp_vaapi_ctx *ctx, bool va_guess_if_emulated(struct mp_vaapi_ctx *ctx) { - va_lock(ctx); const char *s = vaQueryVendorString(ctx->display); - va_unlock(ctx); return s && strstr(s, "VDPAU backend"); } + +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 +}; + +struct mp_vaapi_ctx *va_create_standalone(struct mp_log *plog, bool probing) +{ + 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, plog, 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; + } + } + return NULL; +} |