1
0
mirror of https://github.com/mpv-player/mpv synced 2025-02-03 13:41:49 +00:00

filters: lavfi: allow hwdec_interop selection for filters

Today, lavfi filters are provided a hw_device from the first
hwdec_interop that was loaded, regardless of whether it's the right one
or not. In most situations where a hardware based filter is used, we
need more control over the device.

In this change, a `hwdec_interop` option is added to the lavfi wrapper
filter configuration and this is used to pick the correct hw_device to
inject into the filter or graph (in the case of a graph, all filters
get the same device).

Note that this requires the use of the explicit lavfi syntax to allow
for the extra configuration.

eg:

```
mpv --vf=hwupload
```

becomes

```
mpv --vf=lavfi=[hwupload]:hwdec_interop=cuda-nvdec
```

or

```
mpv --vf=lavfi-bridge=[hwupload]:hwdec_interop=cuda-nvdec
```
This commit is contained in:
Philip Langdale 2022-03-12 11:24:51 -08:00 committed by Philip Langdale
parent 5629ed81ee
commit 989d873d6e
10 changed files with 100 additions and 28 deletions

View File

@ -47,6 +47,7 @@
#include "audio/fmt-conversion.h"
#include "video/fmt-conversion.h"
#include "video/hwdec.h"
#include "video/out/gpu/hwdec.h"
#include "f_lavfi.h"
#include "filter.h"
@ -98,6 +99,9 @@ struct lavfi {
double delay; // seconds of audio apparently buffered by filter
struct mp_lavfi public;
// Identify a specific hwdec_interop to use
char *hwdec_interop;
};
struct lavfi_pad {
@ -548,11 +552,22 @@ static void init_graph(struct lavfi *c)
if (init_pads(c)) {
struct mp_stream_info *info = mp_filter_find_stream_info(c->f);
if (info && info->hwdec_devs) {
struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(info->hwdec_devs);
for (int n = 0; n < c->graph->nb_filters; n++) {
AVFilterContext *filter = c->graph->filters[n];
if (hwdec && hwdec->av_device_ref)
filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref);
struct mp_hwdec_ctx *hwdec_ctx = NULL;
if (c->hwdec_interop) {
int imgfmt =
ra_hwdec_driver_get_imgfmt_for_name(c->hwdec_interop);
hwdec_ctx = mp_filter_load_hwdec_device(c->f, imgfmt);
} else {
hwdec_ctx = hwdec_devices_get_first(info->hwdec_devs);
}
if (hwdec_ctx && hwdec_ctx->av_device_ref) {
MP_VERBOSE(c, "Configuring hwdec_interop=%s for filters\n",
hwdec_ctx->driver_name);
for (int n = 0; n < c->graph->nb_filters; n++) {
AVFilterContext *filter = c->graph->filters[n];
filter->hw_device_ctx =
av_buffer_ref(hwdec_ctx->av_device_ref);
}
}
}
@ -886,6 +901,7 @@ error:
struct mp_lavfi *mp_lavfi_create_graph(struct mp_filter *parent,
enum mp_frame_type type, bool bidir,
char *hwdec_interop,
char **graph_opts, const char *graph)
{
struct lavfi *c = lavfi_alloc(parent);
@ -896,12 +912,14 @@ struct mp_lavfi *mp_lavfi_create_graph(struct mp_filter *parent,
c->force_bidir = bidir;
c->graph_opts = mp_dup_str_array(c, graph_opts);
c->graph_string = talloc_strdup(c, graph);
c->hwdec_interop = talloc_strdup(c, hwdec_interop);
return do_init(c);
}
struct mp_lavfi *mp_lavfi_create_filter(struct mp_filter *parent,
enum mp_frame_type type, bool bidir,
char *hwdec_interop,
char **graph_opts,
const char *filter, char **filter_opts)
{
@ -911,6 +929,7 @@ struct mp_lavfi *mp_lavfi_create_filter(struct mp_filter *parent,
c->force_type = type;
c->force_bidir = bidir;
c->hwdec_interop = talloc_strdup(c, hwdec_interop);
c->graph_opts = mp_dup_str_array(c, graph_opts);
c->graph_string = talloc_strdup(c, filter);
c->direct_filter_opts = mp_dup_str_array(c, filter_opts);
@ -930,6 +949,8 @@ struct lavfi_user_opts {
char **filter_opts;
int fix_pts;
char *hwdec_interop;
};
static struct mp_filter *lavfi_create(struct mp_filter *parent, void *options)
@ -937,10 +958,11 @@ static struct mp_filter *lavfi_create(struct mp_filter *parent, void *options)
struct lavfi_user_opts *opts = options;
struct mp_lavfi *l;
if (opts->is_bridge) {
l = mp_lavfi_create_filter(parent, opts->type, true, opts->avopts,
l = mp_lavfi_create_filter(parent, opts->type, true,
opts->hwdec_interop, opts->avopts,
opts->filter_name, opts->filter_opts);
} else {
l = mp_lavfi_create_graph(parent, opts->type, true,
l = mp_lavfi_create_graph(parent, opts->type, true, opts->hwdec_interop,
opts->avopts, opts->graph);
}
if (l) {
@ -1101,6 +1123,9 @@ const struct mp_user_filter_entry af_lavfi = {
{"graph", OPT_STRING(graph)},
{"fix-pts", OPT_FLAG(fix_pts)},
{"o", OPT_KEYVALUELIST(avopts)},
{"hwdec_interop",
OPT_STRING_VALIDATE(hwdec_interop,
ra_hwdec_validate_drivers_only_opt)},
{0}
},
.priv_defaults = &(const OPT_BASE_STRUCT){
@ -1120,6 +1145,9 @@ const struct mp_user_filter_entry af_lavfi_bridge = {
{"name", OPT_STRING(filter_name)},
{"opts", OPT_KEYVALUELIST(filter_opts)},
{"o", OPT_KEYVALUELIST(avopts)},
{"hwdec_interop",
OPT_STRING_VALIDATE(hwdec_interop,
ra_hwdec_validate_drivers_only_opt)},
{0}
},
.priv_defaults = &(const OPT_BASE_STRUCT){
@ -1139,6 +1167,9 @@ const struct mp_user_filter_entry vf_lavfi = {
.options = (const m_option_t[]){
{"graph", OPT_STRING(graph)},
{"o", OPT_KEYVALUELIST(avopts)},
{"hwdec_interop",
OPT_STRING_VALIDATE(hwdec_interop,
ra_hwdec_validate_drivers_only_opt)},
{0}
},
.priv_defaults = &(const OPT_BASE_STRUCT){
@ -1158,6 +1189,10 @@ const struct mp_user_filter_entry vf_lavfi_bridge = {
{"name", OPT_STRING(filter_name)},
{"opts", OPT_KEYVALUELIST(filter_opts)},
{"o", OPT_KEYVALUELIST(avopts)},
{"hwdec_interop",
OPT_STRING_VALIDATE(hwdec_interop,
ra_hwdec_validate_drivers_only_opt)},
{0}
},
.priv_defaults = &(const OPT_BASE_STRUCT){

View File

@ -18,6 +18,7 @@ struct mp_lavfi {
// graph: a libavfilter graph specification
struct mp_lavfi *mp_lavfi_create_graph(struct mp_filter *parent,
enum mp_frame_type type, bool bidir,
char *hwdec_interop,
char **graph_opts, const char *graph);
// Unlike mp_lavfi_create_graph(), this creates a single filter, using explicit
@ -26,6 +27,7 @@ struct mp_lavfi *mp_lavfi_create_graph(struct mp_filter *parent,
// (which are applied with mp_set_avopts()).
struct mp_lavfi *mp_lavfi_create_filter(struct mp_filter *parent,
enum mp_frame_type type, bool bidir,
char *hwdec_interop,
char **graph_opts,
const char *filter, char **filter_opts);

View File

@ -685,7 +685,7 @@ struct mp_stream_info *mp_filter_find_stream_info(struct mp_filter *f)
return NULL;
}
struct AVBufferRef *mp_filter_load_hwdec_device(struct mp_filter *f, int imgfmt)
struct mp_hwdec_ctx *mp_filter_load_hwdec_device(struct mp_filter *f, int imgfmt)
{
struct mp_stream_info *info = mp_filter_find_stream_info(f);
if (!info || !info->hwdec_devs)
@ -697,7 +697,7 @@ struct AVBufferRef *mp_filter_load_hwdec_device(struct mp_filter *f, int imgfmt)
};
hwdec_devices_request_for_img_fmt(info->hwdec_devs, &params);
return hwdec_devices_get_imgfmt(info->hwdec_devs, imgfmt);
return hwdec_devices_get_by_imgfmt(info->hwdec_devs, imgfmt);
}
static void filter_wakeup(struct mp_filter *f, bool mark_only)

View File

@ -408,8 +408,7 @@ struct mp_stream_info {
// Search for a parent filter (including f) that has this set, and return it.
struct mp_stream_info *mp_filter_find_stream_info(struct mp_filter *f);
struct AVBufferRef;
struct AVBufferRef *mp_filter_load_hwdec_device(struct mp_filter *f, int imgfmt);
struct mp_hwdec_ctx *mp_filter_load_hwdec_device(struct mp_filter *f, int imgfmt);
// Perform filtering. This runs until the filter graph is blocked (due to
// missing external input or unread output). It returns whether any outside

View File

@ -155,7 +155,7 @@ struct mp_filter *mp_create_user_filter(struct mp_filter *parent,
if (strncmp(name, "lavfi-", 6) == 0)
name += 6;
struct mp_lavfi *l =
mp_lavfi_create_filter(parent, frame_type, true, NULL, name, args);
mp_lavfi_create_filter(parent, frame_type, true, NULL, NULL, name, args);
if (l)
f = l->f;
goto done;

View File

@ -1257,7 +1257,7 @@ static int reinit_complex_filters(struct MPContext *mpctx, bool force_uninit)
}
struct mp_lavfi *l =
mp_lavfi_create_graph(mpctx->filter_root, 0, false, NULL, graph);
mp_lavfi_create_graph(mpctx->filter_root, 0, false, NULL, NULL, graph);
if (!l)
goto done;
mpctx->lavfi = l->f;

View File

@ -448,7 +448,11 @@ static struct mp_filter *vf_vavpp_create(struct mp_filter *parent, void *options
p->queue = mp_refqueue_alloc(f);
p->av_device_ref = mp_filter_load_hwdec_device(f, IMGFMT_VAAPI);
struct mp_hwdec_ctx *hwdec_ctx =
mp_filter_load_hwdec_device(f, IMGFMT_VAAPI);
if (!hwdec_ctx || !hwdec_ctx->av_device_ref)
goto error;
p->av_device_ref = av_buffer_ref(hwdec_ctx->av_device_ref);
if (!p->av_device_ref)
goto error;

View File

@ -136,11 +136,11 @@ static struct mp_filter *vf_vdpaupp_create(struct mp_filter *parent, void *optio
p->queue = mp_refqueue_alloc(f);
AVBufferRef *ref = mp_filter_load_hwdec_device(f, IMGFMT_VDPAU);
if (!ref)
struct mp_hwdec_ctx *hwdec_ctx =
mp_filter_load_hwdec_device(f, IMGFMT_VDPAU);
if (!hwdec_ctx || !hwdec_ctx->av_device_ref)
goto error;
p->ctx = mp_vdpau_get_ctx_from_av(ref);
av_buffer_unref(&ref);
p->ctx = mp_vdpau_get_ctx_from_av(hwdec_ctx->av_device_ref);
if (!p->ctx)
goto error;

View File

@ -26,17 +26,14 @@
#include "hwdec.h"
extern const struct ra_hwdec_driver ra_hwdec_vaegl;
extern const struct ra_hwdec_driver ra_hwdec_vaglx;
extern const struct ra_hwdec_driver ra_hwdec_videotoolbox;
extern const struct ra_hwdec_driver ra_hwdec_vdpau;
extern const struct ra_hwdec_driver ra_hwdec_dxva2egl;
extern const struct ra_hwdec_driver ra_hwdec_d3d11egl;
extern const struct ra_hwdec_driver ra_hwdec_dxva2gldx;
extern const struct ra_hwdec_driver ra_hwdec_dxva2;
extern const struct ra_hwdec_driver ra_hwdec_d3d11va;
extern const struct ra_hwdec_driver ra_hwdec_dxva2dxgi;
extern const struct ra_hwdec_driver ra_hwdec_cuda;
extern const struct ra_hwdec_driver ra_hwdec_cuda_nvdec;
extern const struct ra_hwdec_driver ra_hwdec_rpi_overlay;
extern const struct ra_hwdec_driver ra_hwdec_drmprime;
extern const struct ra_hwdec_driver ra_hwdec_drmprime_drm;
@ -174,8 +171,9 @@ int ra_hwdec_mapper_map(struct ra_hwdec_mapper *mapper, struct mp_image *img)
return 0;
}
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
static int ra_hwdec_validate_opt_full(struct mp_log *log, bool include_modes,
const m_option_t *opt,
struct bstr name, const char **value)
{
struct bstr param = bstr0(*value);
bool help = bstr_equals0(param, "help");
@ -190,20 +188,36 @@ int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
}
}
if (help) {
mp_info(log, " auto (behavior depends on context)\n"
" all (load all hwdecs)\n"
" no (do not load any and block loading on demand)\n");
if (include_modes) {
mp_info(log, " auto (behavior depends on context)\n"
" all (load all hwdecs)\n"
" no (do not load any and block loading on demand)\n");
}
return M_OPT_EXIT;
}
if (!param.len)
return 1; // "" is treated specially
if (bstr_equals0(param, "all") || bstr_equals0(param, "auto") ||
bstr_equals0(param, "no"))
if (include_modes &&
(bstr_equals0(param, "all") || bstr_equals0(param, "auto") ||
bstr_equals0(param, "no")))
return 1;
mp_fatal(log, "No hwdec backend named '%.*s' found!\n", BSTR_P(param));
return M_OPT_INVALID;
}
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
{
return ra_hwdec_validate_opt_full(log, true, opt, name, value);
}
int ra_hwdec_validate_drivers_only_opt(struct mp_log *log,
const m_option_t *opt,
struct bstr name, const char **value)
{
return ra_hwdec_validate_opt_full(log, false, opt, name, value);
}
static void load_add_hwdec(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
const struct ra_hwdec_driver *drv, bool is_auto)
{
@ -323,3 +337,13 @@ struct ra_hwdec *ra_hwdec_get(struct ra_hwdec_ctx *ctx, int imgfmt)
return NULL;
}
int ra_hwdec_driver_get_imgfmt_for_name(const char *name)
{
for (int i = 0; ra_hwdec_drivers[i]; i++) {
if (!strcmp(ra_hwdec_drivers[i]->name, name)) {
return ra_hwdec_drivers[i]->imgfmts[0];
}
}
return IMGFMT_NONE;
}

View File

@ -20,6 +20,10 @@ struct ra_hwdec_ctx {
int ra_hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value);
int ra_hwdec_validate_drivers_only_opt(struct mp_log *log,
const m_option_t *opt,
struct bstr name, const char **value);
void ra_hwdec_ctx_init(struct ra_hwdec_ctx *ctx, struct mp_hwdec_devices *devs,
const char *opt, bool load_all_by_default);
void ra_hwdec_ctx_uninit(struct ra_hwdec_ctx *ctx);
@ -143,4 +147,8 @@ void ra_hwdec_mapper_free(struct ra_hwdec_mapper **mapper);
void ra_hwdec_mapper_unmap(struct ra_hwdec_mapper *mapper);
int ra_hwdec_mapper_map(struct ra_hwdec_mapper *mapper, struct mp_image *img);
// Get the primary image format for the given driver name.
// Returns IMGFMT_NONE if the name doesn't get matched.
int ra_hwdec_driver_get_imgfmt_for_name(const char *name);
#endif