From 5cca9143abdbf413641dc8a657ca869a9425b5a9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 5 Nov 2013 22:06:32 +0100 Subject: vdpau: move device and video surface management from vo_vdpau.c to vdpau.c The goal is being able to use vdpau decoding independently from vo_vdpau.c. --- video/vdpau.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) (limited to 'video/vdpau.c') diff --git a/video/vdpau.c b/video/vdpau.c index 697bda8447..a9991e2b12 100644 --- a/video/vdpau.c +++ b/video/vdpau.c @@ -15,21 +15,233 @@ * with mpv. If not, see . */ +#include + #include "vdpau.h" +#include "osdep/timer.h" + +#include "video/out/x11_common.h" #include "video/img_format.h" +#include "video/mp_image.h" + +static void mark_vdpau_objects_uninitialized(struct mp_vdpau_ctx *ctx) +{ + for (int i = 0; i < MAX_VIDEO_SURFACES; i++) + ctx->video_surfaces[i].surface = VDP_INVALID_HANDLE; + ctx->vdp_device = VDP_INVALID_HANDLE; +} + +static void preemption_callback(VdpDevice device, void *context) +{ + struct mp_vdpau_ctx *ctx = context; + ctx->is_preempted = true; +} + +static int win_x11_init_vdpau_procs(struct mp_vdpau_ctx *ctx) +{ + struct vo_x11_state *x11 = ctx->x11; + VdpStatus vdp_st; + + // Don't operate on ctx->vdp directly, so that even if init fails, ctx->vdp + // will have the function pointers from the previous successful init, and + // won't randomly make other code crash on calling NULL pointers. + struct vdp_functions vdp = {0}; + + if (!x11) + return -1; + + struct vdp_function { + const int id; + int offset; + }; + + static const struct vdp_function vdp_func[] = { +#define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)}, +#include "video/vdpau_functions.inc" +#undef VDP_FUNCTION + {0, -1} + }; + + VdpGetProcAddress *get_proc_address; + vdp_st = vdp_device_create_x11(x11->display, x11->screen, &ctx->vdp_device, + &get_proc_address); + if (vdp_st != VDP_STATUS_OK) { + if (ctx->is_preempted) + MP_DBG(ctx, "Error calling vdp_device_create_x11 while preempted: %d\n", + vdp_st); + else + MP_ERR(ctx, "Error when calling vdp_device_create_x11: %d\n", vdp_st); + return -1; + } + + for (const struct vdp_function *dsc = vdp_func; dsc->offset >= 0; dsc++) { + vdp_st = get_proc_address(ctx->vdp_device, dsc->id, + (void **)((char *)&vdp + dsc->offset)); + if (vdp_st != VDP_STATUS_OK) { + MP_ERR(ctx, "Error when calling vdp_get_proc_address(function " + "id %d): %s\n", dsc->id, + vdp.get_error_string ? vdp.get_error_string(vdp_st) : "?"); + return -1; + } + } + + *ctx->vdp = vdp; + ctx->get_proc_address = get_proc_address; + + vdp_st = vdp.preemption_callback_register(ctx->vdp_device, + preemption_callback, ctx); + return 0; +} + +static int handle_preemption(struct mp_vdpau_ctx *ctx) +{ + if (!ctx->is_preempted) + return 0; + mark_vdpau_objects_uninitialized(ctx); + if (!ctx->preemption_user_notified) { + MP_ERR(ctx, "Got display preemption notice! Will attempt to recover.\n"); + ctx->preemption_user_notified = true; + } + /* Trying to initialize seems to be quite slow, so only try once a + * second to avoid using 100% CPU. */ + if (ctx->last_preemption_retry_fail && + mp_time_sec() - ctx->last_preemption_retry_fail < 1.0) + return -1; + if (win_x11_init_vdpau_procs(ctx) < 0) { + ctx->last_preemption_retry_fail = mp_time_sec(); + return -1; + } + ctx->preemption_user_notified = false; + ctx->last_preemption_retry_fail = 0; + ctx->is_preempted = false; + ctx->preemption_counter++; + MP_INFO(ctx, "Recovered from display preemption.\n"); + return 1; +} // Check whether vdpau initialization and preemption status is ok and we can // proceed normally. bool mp_vdpau_status_ok(struct mp_vdpau_ctx *ctx) { - return ctx->status_ok(ctx); + return handle_preemption(ctx) >= 0; +} + +static void release_decoder_surface(void *ptr) +{ + bool *in_use_ptr = ptr; + *in_use_ptr = false; +} + +static struct mp_image *create_ref(struct surface_entry *e) +{ + assert(!e->in_use); + e->in_use = true; + struct mp_image *res = + mp_image_new_custom_ref(&(struct mp_image){0}, &e->in_use, + release_decoder_surface); + mp_image_setfmt(res, e->fmt); + mp_image_set_size(res, e->w, e->h); + res->planes[0] = (void *)"dummy"; // must be non-NULL, otherwise arbitrary + res->planes[3] = (void *)(intptr_t)e->surface; + return res; } struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx, int fmt, VdpChromaType chroma, int w, int h) { - return ctx->get_video_surface(ctx, fmt, chroma, w, h); + struct vdp_functions *vdp = ctx->vdp; + VdpStatus vdp_st; + + assert(IMGFMT_IS_VDPAU(fmt)); + + // Destroy all unused surfaces that don't have matching parameters + for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { + struct surface_entry *e = &ctx->video_surfaces[n]; + if (!e->in_use && e->surface != VDP_INVALID_HANDLE) { + if (e->fmt != fmt || e->chroma != chroma || e->w != w || e->h != h) { + vdp_st = vdp->video_surface_destroy(e->surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); + e->surface = VDP_INVALID_HANDLE; + } + } + } + + // Try to find an existing unused surface + for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { + struct surface_entry *e = &ctx->video_surfaces[n]; + if (!e->in_use && e->surface != VDP_INVALID_HANDLE) { + assert(e->w == w && e->h == h); + assert(e->fmt == fmt && e->chroma == chroma); + return create_ref(e); + } + } + + // Allocate new surface + for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { + struct surface_entry *e = &ctx->video_surfaces[n]; + if (!e->in_use) { + assert(e->surface == VDP_INVALID_HANDLE); + e->fmt = fmt; + e->chroma = chroma; + e->w = w; + e->h = h; + if (ctx->is_preempted) { + MP_WARN(ctx, "Preempted, no surface.\n"); + } else { + vdp_st = vdp->video_surface_create(ctx->vdp_device, chroma, + w, h, &e->surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_create"); + } + return create_ref(e); + } + } + + MP_ERR(ctx, "no surfaces available in mp_vdpau_get_video_surface\n"); + return NULL; +} + +struct mp_vdpau_ctx *mp_vdpau_create_device_x11(struct mp_log *log, + struct vo_x11_state *x11) +{ + struct mp_vdpau_ctx *ctx = talloc_ptrtype(NULL, ctx); + *ctx = (struct mp_vdpau_ctx) { + .log = log, + .x11 = x11, + .vdp = talloc_zero(ctx, struct vdp_functions), + }; + + mark_vdpau_objects_uninitialized(ctx); + + if (win_x11_init_vdpau_procs(ctx) < 0) { + if (ctx->vdp->device_destroy) + ctx->vdp->device_destroy(ctx->vdp_device); + talloc_free(ctx); + return NULL; + } + return ctx; +} + +void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx) +{ + struct vdp_functions *vdp = ctx->vdp; + VdpStatus vdp_st; + + for (int i = 0; i < MAX_VIDEO_SURFACES; i++) { + // can't hold references past context lifetime + assert(!ctx->video_surfaces[i].in_use); + if (ctx->video_surfaces[i].surface != VDP_INVALID_HANDLE) { + vdp_st = vdp->video_surface_destroy(ctx->video_surfaces[i].surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); + } + } + + if (ctx->vdp_device != VDP_INVALID_HANDLE) { + vdp_st = vdp->device_destroy(ctx->vdp_device); + CHECK_ST_WARNING("Error when calling vdp_device_destroy"); + } + + talloc_free(ctx); } bool mp_vdpau_get_format(int imgfmt, VdpChromaType *out_chroma_type, -- cgit v1.2.3