vaapi: allow GPU read-back with --hwdec=vaapi-copy

This code is actually quite inefficient: it reuses the (slow, simple)
screenshot code. It uses an inefficient method to read the image
(vaGetImage() instead of vaDeriveImage()), allocates new memory for
each frame that is read, and it tries all image formats again each
time.

Also, in my tests it always picked NV12 as image format, which is not
ideal if you actually want to filter the video, and vo_xv can't handle
this format without conversion either.

However, a user confirmed that it worked for him, so everything is fine.
This commit is contained in:
wm4 2013-09-22 22:47:50 +02:00
parent 7c3f1ffc44
commit 641e94cd27
5 changed files with 114 additions and 4 deletions

View File

@ -1083,6 +1083,7 @@
:auto: see below
:vdpau: requires ``--vo=vdpau`` (Linux only)
:vaapi: requires ``--vo=vaapi`` (Linux with Intel GPUs only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:vda: requires ``--vo=corevideo`` (OSX only)
:crystalhd: Broadcom Crystal HD
@ -1092,6 +1093,9 @@
Also note that if the first found method doesn't actually work, it will
always fall back to software decoding, instead of trying the next method.
The ``vaapi-copy`` function allows you to use vaapi with any VO. Because
this copies the decoded video back to system RAM, it's quite inefficient.
``--hwdec-codecs=<codec1,codec2,...|all>``
Allow hardware decoding for a given list of codecs only. The default is the
special value ``all``, which always allows all codecs.

View File

@ -479,7 +479,8 @@ const m_option_t mp_opts[] = {
{"vdpau", 1},
{"vda", 2},
{"crystalhd", 3},
{"vaapi", 4})),
{"vaapi", 4},
{"vaapi-copy", 5})),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
// postprocessing:

View File

@ -18,6 +18,7 @@ enum hwdec_type {
HWDEC_VDA = 2,
HWDEC_CRYSTALHD = 3,
HWDEC_VAAPI = 4,
HWDEC_VAAPI_COPY = 5,
};
typedef struct lavc_ctx {

View File

@ -25,6 +25,8 @@
#include <libavcodec/vaapi.h>
#include <libavutil/common.h>
#include <X11/Xlib.h>
#include "lavc.h"
#include "mpvcore/mp_common.h"
#include "mpvcore/av_common.h"
@ -54,6 +56,7 @@
struct priv {
struct mp_vaapi_ctx *ctx;
VADisplay display;
Display *x11_display;
// libavcodec shared struct
struct vaapi_context *va_context;
@ -64,6 +67,8 @@ struct priv {
struct va_surface_pool *pool;
int rt_format;
bool printed_readback_warning;
};
struct profile_entry {
@ -342,6 +347,38 @@ static struct mp_image *allocate_image(struct lavc_ctx *ctx, int format,
return NULL;
}
static void destroy_va_dummy_ctx(struct priv *p)
{
if (p->x11_display)
XCloseDisplay(p->x11_display);
p->x11_display = NULL;
va_destroy(p->ctx);
p->ctx = NULL;
}
// 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_info, if we wanted.)
static bool create_va_dummy_ctx(struct priv *p)
{
p->x11_display = XOpenDisplay(NULL);
if (!p->x11_display)
goto destroy_ctx;
VADisplay *display = vaGetDisplay(p->x11_display);
if (!display)
goto destroy_ctx;
p->ctx = va_initialize(display);
if (!p->ctx) {
vaTerminate(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;
@ -350,21 +387,31 @@ static void uninit(struct lavc_ctx *ctx)
return;
destroy_decoder(ctx);
va_surface_pool_release(p->pool);
if (p->x11_display)
destroy_va_dummy_ctx(p);
talloc_free(p);
ctx->hwdec_priv = NULL;
}
static int init(struct lavc_ctx *ctx)
static int init_with_vactx(struct lavc_ctx *ctx, struct mp_vaapi_ctx *vactx)
{
struct priv *p = talloc_ptrtype(NULL, p);
*p = (struct priv) {
.ctx = ctx->hwdec_info->vaapi_ctx,
.ctx = vactx,
.va_context = &p->va_context_storage,
.rt_format = VA_RT_FORMAT_YUV420
};
if (!p->ctx)
create_va_dummy_ctx(p);
if (!p->ctx) {
talloc_free(p);
return -1;
}
p->display = p->ctx->display;
p->pool = va_surface_pool_alloc(p->display, p->rt_format);
@ -378,6 +425,12 @@ static int init(struct lavc_ctx *ctx)
return 0;
}
static int init(struct lavc_ctx *ctx)
{
if (!ctx->hwdec_info->vaapi_ctx)
return -1;
return init_with_vactx(ctx, ctx->hwdec_info->vaapi_ctx);
}
static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
const char *decoder)
@ -389,6 +442,44 @@ static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
return 0;
}
static int probe_copy(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
const char *decoder)
{
struct priv dummy = {0};
if (!create_va_dummy_ctx(&dummy))
return HWDEC_ERR_NO_CTX;
destroy_va_dummy_ctx(&dummy);
if (!find_codec(mp_codec_to_av_codec_id(decoder)))
return HWDEC_ERR_NO_CODEC;
return 0;
}
static int init_copy(struct lavc_ctx *ctx)
{
return init_with_vactx(ctx, NULL);
}
static struct mp_image *copy_image(struct lavc_ctx *ctx, struct mp_image *img)
{
struct priv *p = ctx->hwdec_priv;
struct va_surface *surface = va_surface_in_mp_image(img);
if (surface) {
struct mp_image *simg =
va_surface_download(surface, p->ctx->image_formats);
if (simg) {
if (!p->printed_readback_warning) {
mp_msg(MSGT_VO, MSGL_WARN, "[vaapi] Using GPU readback. This "
"is usually inefficient.\n");
p->printed_readback_warning = true;
}
talloc_free(img);
return simg;
}
}
return img;
}
const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
.type = HWDEC_VAAPI,
.image_formats = (const int[]) {IMGFMT_VAAPI, IMGFMT_VAAPI_MPEG2_IDCT,
@ -398,3 +489,14 @@ const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
.uninit = uninit,
.allocate_image = allocate_image,
};
const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = {
.type = HWDEC_VAAPI_COPY,
.image_formats = (const int[]) {IMGFMT_VAAPI, IMGFMT_VAAPI_MPEG2_IDCT,
IMGFMT_VAAPI_MPEG2_MOCO, 0},
.probe = probe_copy,
.init = init_copy,
.uninit = uninit,
.allocate_image = allocate_image,
.process_image = copy_image,
};

View File

@ -86,6 +86,7 @@ const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
const struct vd_lavc_hwdec mp_vd_lavc_vdpau_old;
const struct vd_lavc_hwdec mp_vd_lavc_vda;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
.type = HWDEC_CRYSTALHD,
@ -114,6 +115,7 @@ static const struct vd_lavc_hwdec *hwdec_list[] = {
&mp_vd_lavc_crystalhd,
#if CONFIG_VAAPI
&mp_vd_lavc_vaapi,
&mp_vd_lavc_vaapi_copy,
#endif
NULL
};