summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-12-01 21:05:54 +0100
committerwm4 <wm4@nowhere>2017-12-01 21:11:43 +0100
commiteb8957cea110a9aa652894d8bb897a9b1ff91e0b (patch)
treea418036f0b8da42fac96921e3075c4dc92462ef6
parent43af055a70a7b604e1e936575213aa561ac915d1 (diff)
downloadmpv-eb8957cea110a9aa652894d8bb897a9b1ff91e0b.tar.bz2
mpv-eb8957cea110a9aa652894d8bb897a9b1ff91e0b.tar.xz
vd_lavc: rewrite how --hwdec is handled
Change it from explicit metadata about every hwaccel method to trying to get it from libavcodec. As shown by add_all_hwdec_methods(), this is a quite bumpy road, and a bit worse than expected. This will probably cause a bunch of regressions. In particular I didn't check all the strange decoder wrappers, which all cause some sort of special cases each. You're volunteering for beta testing by using this commit. One interesting thing is that we completely get rid of mp_hwdec_ctx in vd_lavc.c, and that HWDEC_* mostly goes away (some filters still use it, and the VO hwdec interops still have a lot of code to set it up, so it's not going away completely for now).
-rw-r--r--DOCS/interface-changes.rst2
-rw-r--r--options/options.c33
-rw-r--r--options/options.h5
-rw-r--r--player/command.c39
-rw-r--r--video/decode/lavc.h65
-rw-r--r--video/decode/vd_lavc.c737
-rw-r--r--video/hwdec.c22
-rw-r--r--video/hwdec.h13
8 files changed, 377 insertions, 539 deletions
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 312fe9552a..7dcb9cda55 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -57,6 +57,8 @@ Interface changes
This option is hereby declared as unstable and may change any time - its
old use is deprecated, and it has very little use outside of debugging
now.
+ - change the --hwdec option from a choice to a plain string (affects
+ introspection of the option/property), also affects some properties
--- mpv 0.27.0 ---
- drop previously deprecated --field-dominance option
- drop previously deprecated "osd" command
diff --git a/options/options.c b/options/options.c
index c6837a03e8..63cd93c53f 100644
--- a/options/options.c
+++ b/options/options.c
@@ -93,34 +93,6 @@ extern const struct m_sub_options d3d11va_conf;
extern const struct m_sub_options angle_conf;
extern const struct m_sub_options cocoa_conf;
-const struct m_opt_choice_alternatives mp_hwdec_names[] = {
- {"no", HWDEC_NONE},
- {"auto", HWDEC_AUTO},
- {"yes" , HWDEC_AUTO},
- {"auto-copy", HWDEC_AUTO_COPY},
- {"vdpau", HWDEC_VDPAU},
- {"vdpau-copy", HWDEC_VDPAU_COPY},
- {"videotoolbox",HWDEC_VIDEOTOOLBOX},
- {"videotoolbox-copy",HWDEC_VIDEOTOOLBOX_COPY},
- {"vaapi", HWDEC_VAAPI},
- {"vaapi-copy", HWDEC_VAAPI_COPY},
- {"dxva2", HWDEC_DXVA2},
- {"dxva2-copy", HWDEC_DXVA2_COPY},
- {"d3d11va", HWDEC_D3D11VA},
- {"d3d11va-copy",HWDEC_D3D11VA_COPY},
- {"rpi", HWDEC_RPI},
- {"rpi-copy", HWDEC_RPI_COPY},
- {"rkmpp", HWDEC_RKMPP},
- {"mediacodec", HWDEC_MEDIACODEC},
- {"mediacodec-copy",HWDEC_MEDIACODEC_COPY},
- {"cuda", HWDEC_CUDA},
- {"cuda-copy", HWDEC_CUDA_COPY},
- {"nvdec", HWDEC_NVDEC},
- {"nvdec-copy", HWDEC_NVDEC_COPY},
- {"crystalhd", HWDEC_CRYSTALHD},
- {0}
-};
-
static const struct m_sub_options screenshot_conf = {
.opts = image_writer_opts,
.size = sizeof(struct image_writer_opts),
@@ -434,7 +406,8 @@ const m_option_t mp_opts[] = {
OPT_FLAG("ad-spdif-dtshd", dtshd, 0,
.deprecation_message = "use --audio-spdif instead"),
- OPT_CHOICE_C("hwdec", hwdec_api, 0, mp_hwdec_names),
+ OPT_STRING_VALIDATE("hwdec", hwdec_api, M_OPT_OPTIONAL_PARAM,
+ hwdec_validate_opt),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
#if HAVE_VIDEOTOOLBOX_HWACCEL
OPT_IMAGEFORMAT("videotoolbox-format", videotoolbox_format, 0, .min = -1,
@@ -943,7 +916,7 @@ const struct MPOpts mp_default_opts = {
.use_embedded_fonts = 1,
.screenshot_template = "mpv-shot%n",
- .hwdec_api = HAVE_RPI ? HWDEC_RPI : 0,
+ .hwdec_api = HAVE_RPI ? "mmal" : "no",
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9",
.videotoolbox_format = IMGFMT_NV12,
diff --git a/options/options.h b/options/options.h
index 8bf8f1ec6a..9c9dd64233 100644
--- a/options/options.h
+++ b/options/options.h
@@ -285,7 +285,7 @@ typedef struct MPOpts {
int sub_clear_on_seek;
int teletext_page;
- int hwdec_api;
+ char *hwdec_api;
char *hwdec_codecs;
int videotoolbox_format;
int hwdec_image_format;
@@ -349,4 +349,7 @@ extern const struct m_sub_options vo_sub_opts;
extern const struct m_sub_options stream_cache_conf;
extern const struct m_sub_options dvd_conf;
+int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param);
+
#endif
diff --git a/player/command.c b/player/command.c
index 9017278286..9c0dd31f28 100644
--- a/player/command.c
+++ b/player/command.c
@@ -2419,24 +2419,22 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
struct MPOpts *opts = mpctx->opts;
if (action == M_PROPERTY_SET) {
- int new = *(int *)arg;
+ char *new = *(char **)arg;
- if (opts->hwdec_api == new)
+ if (strcmp(opts->hwdec_api, new) == 0)
return M_PROPERTY_OK;
- opts->hwdec_api = new;
+ talloc_free(opts->hwdec_api);
+ opts->hwdec_api = talloc_strdup(NULL, new);
if (!vd)
return M_PROPERTY_OK;
- int current = -2;
- video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
- if (current != opts->hwdec_api) {
- video_vd_control(vd, VDCTRL_REINIT, NULL);
- double last_pts = mpctx->last_vo_pts;
- if (last_pts != MP_NOPTS_VALUE)
- queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, 0);
- }
+ video_vd_control(vd, VDCTRL_REINIT, NULL);
+ double last_pts = mpctx->last_vo_pts;
+ if (last_pts != MP_NOPTS_VALUE)
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, 0);
+
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@@ -2452,20 +2450,11 @@ static int mp_property_hwdec_current(void *ctx, struct m_property *prop,
if (!vd)
return M_PROPERTY_UNAVAILABLE;
- switch (action) {
- case M_PROPERTY_GET_TYPE: {
- // Abuse another hwdec option to resolve the value names
- struct m_property dummy = {.name = "hwdec"};
- return mp_property_generic_option(mpctx, &dummy, action, arg);
- }
- case M_PROPERTY_GET: {
- int current = HWDEC_NONE;
- video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
- *(int *)arg = current;
- return M_PROPERTY_OK;
- }
- }
- return M_PROPERTY_NOT_IMPLEMENTED;
+ char *current = NULL;
+ video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
+ if (!current)
+ current = "no";
+ return m_property_strdup_ro(action, arg, current);
}
static int mp_property_hwdec_interop(void *ctx, struct m_property *prop,
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 114e454c54..7f631fa9f4 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -24,12 +24,28 @@
struct mpv_global;
+struct hwdec_info {
+ char name[64];
+ char method_name[16]; // non-unique name describing the hwdec method
+ const AVCodec *codec; // implemented by this codec
+ enum AVHWDeviceType lavc_device; // if not NONE, get a hwdevice
+ bool copying; // if true, outputs sw frames, or copy to sw ourselves
+ enum AVPixelFormat pix_fmt; // if not NONE, select in get_format
+ bool use_hw_frames; // set AVCodecContext.hw_frames_ctx
+ bool use_hw_device; // set AVCodecContext.hw_device_ctx
+
+ // for internal sorting
+ int auto_pos;
+ int rank;
+};
+
typedef struct lavc_ctx {
struct mp_log *log;
struct MPOpts *opts;
AVCodecContext *avctx;
AVFrame *pic;
- struct vd_lavc_hwdec *hwdec;
+ bool use_hwdec;
+ struct hwdec_info hwdec; // valid only if use_hwdec==true
AVRational codec_timebase;
enum AVDiscard skip_frame;
bool flushing;
@@ -54,12 +70,8 @@ typedef struct lavc_ctx {
// From VO
struct mp_hwdec_devices *hwdec_devs;
- // For free use by hwdec implementation
- void *hwdec_priv;
-
- // Set by generic hwaccels.
- struct mp_hwdec_ctx *hwdec_dev;
- bool owns_hwdec_dev;
+ // Wrapped AVHWDeviceContext* used for decoding.
+ AVBufferRef *hwdec_dev;
bool hwdec_request_reinit;
int hwdec_fail_count;
@@ -75,43 +87,4 @@ typedef struct lavc_ctx {
int dr_imgfmt, dr_w, dr_h, dr_stride_align;
} vd_ffmpeg_ctx;
-struct vd_lavc_hwdec {
- enum hwdec_type type;
- // If non-0, get this hwdec type from the VO (for the AVHWDeviceContext).
- enum hwdec_type interop_type;
- // If true, create a AVHWDeviceContext with default parameters. In this
- // case, create_standalone_dev_type is set to a valid value.
- bool create_standalone_dev;
- enum AVHWDeviceType create_standalone_dev_type;
- // If not-0: the IMGFMT_ format that should be accepted in the libavcodec
- // get_format callback.
- int image_format;
- // Always returns a non-hwaccel image format.
- bool copying;
- // Setting this will queue the given number of frames before returning them
- // to the renderer. This can increase efficiency by not blocking on the
- // hardware pipeline by reading back immediately after decoding.
- int delay_queue;
- int (*probe)(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec,
- const char *codec);
- int (*init)(struct lavc_ctx *ctx);
- int (*init_decoder)(struct lavc_ctx *ctx);
- void (*uninit)(struct lavc_ctx *ctx);
- // Suffix for libavcodec decoder. If non-NULL, the codec is overridden
- // with hwdec_find_decoder.
- // Intuitively, this will force the corresponding wrapper decoder.
- const char *lavc_suffix;
- // Generic hwaccels set AVCodecContext.hw_frames_ctx in get_format().
- bool generic_hwaccel;
- // If set, AVCodecContext.hw_frames_ctx will be initialized in get_format,
- // and pixfmt_map must be non-NULL.
- bool set_hwframes;
-};
-
-enum {
- HWDEC_ERR_NO_CTX = -2,
- HWDEC_ERR_NO_CODEC = -3,
- HWDEC_ERR_EMULATED = -4, // probing successful, but emulated API detected
-};
-
#endif
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index a422a53819..af7e175db4 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -56,8 +56,7 @@
#include "options/m_option.h"
-static void init_avctx(struct dec_video *vd, const char *decoder,
- struct vd_lavc_hwdec *hwdec);
+static void init_avctx(struct dec_video *vd);
static void uninit_avctx(struct dec_video *vd);
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags);
@@ -123,216 +122,185 @@ const struct m_sub_options vd_lavc_conf = {
},
};
-#if HAVE_RPI
-static const struct vd_lavc_hwdec mp_vd_lavc_rpi = {
- .type = HWDEC_RPI,
- .lavc_suffix = "_mmal",
- .image_format = IMGFMT_MMAL,
+// Things not included in this list will be tried last, in random order.
+static const char *const hwdec_autoprobe_order[] = {
+ "d3d11va",
+ "dxva2",
+ "dxva2-copy",
+ "d3d11va-copy",
+ "nvdec",
+ "nvdec-copy",
+ "vdpau",
+ "vdpau-copy",
+ "vaapi",
+ "vaapi-copy",
+ 0
};
-static const struct vd_lavc_hwdec mp_vd_lavc_rpi_copy = {
- .type = HWDEC_RPI_COPY,
- .lavc_suffix = "_mmal",
- .copying = true,
-};
-#endif
-static const struct vd_lavc_hwdec mp_vd_lavc_rkmpp = {
- .type = HWDEC_RKMPP,
- .lavc_suffix = "_rkmpp",
- .image_format = IMGFMT_DRMPRIME,
+static const char *const hwdec_wrappers[] = {
+ "mmal",
+ "mediacodec",
+ "crystalhd",
+ "v4l2m2m",
+ "cuvid",
+ NULL
};
-#if HAVE_CUDA_HWACCEL
-static const struct vd_lavc_hwdec mp_vd_lavc_nvdec = {
- .type = HWDEC_NVDEC,
- .interop_type = HWDEC_CUDA,
- .image_format = IMGFMT_CUDA,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_nvdec_copy = {
- .type = HWDEC_NVDEC_COPY,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_CUDA,
- .generic_hwaccel = true,
- .set_hwframes = true,
- .copying = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_cuda = {
- .type = HWDEC_CUDA,
- .image_format = IMGFMT_CUDA,
- .lavc_suffix = "_cuvid",
- .generic_hwaccel = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_cuda_copy = {
- .type = HWDEC_CUDA_COPY,
- .lavc_suffix = "_cuvid",
- .copying = true,
-};
-#endif
+static int hwdec_compare(const void *p1, const void *p2)
+{
+ struct hwdec_info *h1 = (void *)p1;
+ struct hwdec_info *h2 = (void *)p2;
-static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
- .type = HWDEC_CRYSTALHD,
- .lavc_suffix = "_crystalhd",
- .copying = true,
-};
+ if (h1 == h2)
+ return 0;
-#if HAVE_VAAPI
-static const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
- .type = HWDEC_VAAPI,
- .image_format = IMGFMT_VAAPI,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
+ if (h1->auto_pos > h2->auto_pos)
+ return 1;
+ else if (h1->auto_pos < h2->auto_pos)
+ return -1;
+ return h1->rank < h2->rank ? 1 :-1;
+}
-#include "video/vaapi.h"
+static bool test_decoder_suffix(const char *name, const char *suffix)
+{
+ bstr bname = bstr0(name);
+ return bstr_eatend0(&bname, suffix) && bstr_eatend0(&bname, "_");
+}
-static const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = {
- .type = HWDEC_VAAPI_COPY,
- .copying = true,
- .image_format = IMGFMT_VAAPI,
- .generic_hwaccel = true,
- .set_hwframes = true,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_VAAPI,
-};
-#endif
+static void add_all_hwdec_methods(struct hwdec_info **infos, int *num_infos)
+{
+ AVCodec *codec = NULL;
+ while (1) {
+ codec = av_codec_next(codec);
+ if (!codec)
+ break;
-#if HAVE_VDPAU
-static const struct vd_lavc_hwdec mp_vd_lavc_vdpau = {
- .type = HWDEC_VDPAU,
- .image_format = IMGFMT_VDPAU,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
+ struct hwdec_info info_template = {
+ .pix_fmt = AV_PIX_FMT_NONE,
+ .codec = codec,
+ };
-#include "video/vdpau.h"
+ const char *wrapper = NULL;
+ for (int n = 0; hwdec_wrappers[n]; n++) {
+ if (test_decoder_suffix(codec->name, hwdec_wrappers[n])) {
+ wrapper = hwdec_wrappers[n];
+ // Different lavc/mpv names.
+ if (strcmp(wrapper, "mmal") == 0)
+ wrapper = "rpi";
+ break;
+ }
+ }
-static const struct vd_lavc_hwdec mp_vd_lavc_vdpau_copy = {
- .type = HWDEC_VDPAU_COPY,
- .copying = true,
- .image_format = IMGFMT_VDPAU,
- .generic_hwaccel = true,
- .set_hwframes = true,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_VDPAU,
-};
-#endif
+ // A decoder can provide multiple methods. In particular, hwaccels
+ // provide various methods (e.g. native h264 with vaapi & d3d11), but
+ // even wrapper decoders could provide multiple methods.
+ bool found_any = false;
+ for (int n = 0; ; n++) {
+ const AVCodecHWConfig *cfg = avcodec_get_hw_config(codec, n);
+ if (!cfg)
+ break;
-#if HAVE_VIDEOTOOLBOX_HWACCEL
-static const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox = {
- .type = HWDEC_VIDEOTOOLBOX,
- .image_format = IMGFMT_VIDEOTOOLBOX,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox_copy = {
- .type = HWDEC_VIDEOTOOLBOX_COPY,
- .copying = true,
- .image_format = IMGFMT_VIDEOTOOLBOX,
- .generic_hwaccel = true,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
- .set_hwframes = true,
- .delay_queue = HWDEC_DELAY_QUEUE_COUNT,
-};
-#endif
+ if ((cfg->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) ||
+ (cfg->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
+ {
+ struct hwdec_info info = info_template;
+ info.lavc_device = cfg->device_type;
+ info.pix_fmt = cfg->pix_fmt;
+
+ const char *name = av_hwdevice_get_type_name(cfg->device_type);
+ assert(name); // API violation by libavcodec
+
+ // nvdec hwaccels and the cuvid full decoder clash with their
+ // naming, so fix it here; we also prefer nvdec for the hwaccel.
+ if (strcmp(name, "cuda") == 0 && !wrapper)
+ name = "nvdec";
+
+ snprintf(info.method_name, sizeof(info.method_name), "%s", name);
+ snprintf(info.name, sizeof(info.name), "%s-%s",
+ codec->name, info.method_name);
+
+ // Usually we want to prefer using hw_frames_ctx for true
+ // hwaccels only, but we actually don't have any way to detect
+ // those, so always use hw_frames_ctx if offered.
+ if (cfg->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
+ info.use_hw_frames = true;
+ } else {
+ info.use_hw_device = true;
+ }
-#if HAVE_D3D_HWACCEL
-static const struct vd_lavc_hwdec mp_vd_lavc_d3d11va = {
- .type = HWDEC_D3D11VA,
- .image_format = IMGFMT_D3D11VA,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_d3d11va_copy = {
- .type = HWDEC_D3D11VA_COPY,
- .copying = true,
- .image_format = IMGFMT_D3D11VA,
- .generic_hwaccel = true,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_D3D11VA,
- .set_hwframes = true,
- .delay_queue = HWDEC_DELAY_QUEUE_COUNT,
-};
-#endif
+ // Direct variant.
+ MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
-#if HAVE_D3D9_HWACCEL
-static const struct vd_lavc_hwdec mp_vd_lavc_dxva2 = {
- .type = HWDEC_DXVA2,
- .image_format = IMGFMT_DXVA2,
- .generic_hwaccel = true,
- .set_hwframes = true,
-};
-static const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy = {
- .type = HWDEC_DXVA2_COPY,
- .copying = true,
- .image_format = IMGFMT_DXVA2,
- .generic_hwaccel = true,
- .create_standalone_dev = true,
- .create_standalone_dev_type = AV_HWDEVICE_TYPE_DXVA2,
- .set_hwframes = true,
- .delay_queue = HWDEC_DELAY_QUEUE_COUNT,
-};
-#endif
+ // Copy variant.
+ info.copying = true;
+ if (cfg->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
+ info.use_hw_frames = false;
+ info.use_hw_device = true;
+ }
+ mp_snprintf_cat(info.method_name, sizeof(info.method_name),
+ "-copy");
+ snprintf(info.name, sizeof(info.name), "%s-%s",
+ codec->name, info.method_name);
+ MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
+
+ found_any = true;
+ } else if (cfg->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL) {
+ struct hwdec_info info = info_template;
+ info.pix_fmt = cfg->pix_fmt;
+
+ const char *name = wrapper;
+ if (!name)
+ name = av_get_pix_fmt_name(info.pix_fmt);
+ assert(name); // API violation by libavcodec
+
+ snprintf(info.method_name, sizeof(info.method_name), "%s", name);
+ snprintf(info.name, sizeof(info.name), "%s-%s",
+ codec->name, info.method_name);
+
+ // Direct variant.
+ MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
+
+ // Copy variant.
+ mp_snprintf_cat(info.method_name, sizeof(info.method_name),
+ "-copy");
+ snprintf(info.name, sizeof(info.name), "%s-%s",
+ codec->name, info.method_name);
+ MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
+
+ found_any = true;
+ }
+ }
-#if HAVE_ANDROID
-static const struct vd_lavc_hwdec mp_vd_lavc_mediacodec_copy = {
- .type = HWDEC_MEDIACODEC_COPY,
- .lavc_suffix = "_mediacodec",
- .copying = true,
-};
-#endif
+ if (!found_any && wrapper) {
+ // We _know_ there's something supported here, usually outputting
+ // sw surfaces. E.g. mediacodec (before hw_device_ctx support).
-static const struct vd_lavc_hwdec *const hwdec_list[] = {
-#if HAVE_D3D_HWACCEL
- &mp_vd_lavc_d3d11va,
+ struct hwdec_info info = info_template;
+ info.copying = true; // probably
- #if HAVE_D3D9_HWACCEL
- &mp_vd_lavc_dxva2,
- &mp_vd_lavc_dxva2_copy,
- #endif
- &mp_vd_lavc_d3d11va_copy,
-#endif
-#if HAVE_RPI
- &mp_vd_lavc_rpi,
- &mp_vd_lavc_rpi_copy,
-#endif
-#if HAVE_CUDA_HWACCEL
- &mp_vd_lavc_nvdec,
- &mp_vd_lavc_nvdec_copy,
-#endif
-#if HAVE_VDPAU
- &mp_vd_lavc_vdpau,
- &mp_vd_lavc_vdpau_copy,
-#endif
-#if HAVE_VIDEOTOOLBOX_HWACCEL
- &mp_vd_lavc_videotoolbox,
- &mp_vd_lavc_videotoolbox_copy,
-#endif
-#if HAVE_VAAPI
- &mp_vd_lavc_vaapi,
- &mp_vd_lavc_vaapi_copy,
-#endif
-#if HAVE_ANDROID
- &mp_vd_lavc_mediacodec_copy,
-#endif
-#if HAVE_CUDA_HWACCEL
- &mp_vd_lavc_cuda,
- &mp_vd_lavc_cuda_copy,
-#endif
- &mp_vd_lavc_crystalhd,
- &mp_vd_lavc_rkmpp,
- NULL
-};
+ snprintf(info.method_name, sizeof(info.method_name), "%s", wrapper);
+ // (Including the codec name for wrappers looks pretty dumb, but
+ // better not have them clash with hwaccels and others.)
+ snprintf(info.name, sizeof(info.name), "%s-%s",
+ codec->name, info.method_name);
-static struct vd_lavc_hwdec *find_hwcodec(enum hwdec_type api)
-{
- for (int n = 0; hwdec_list[n]; n++) {
- if (hwdec_list[n]->type == api)
- return (struct vd_lavc_hwdec *)hwdec_list[n];
+ MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
+ }
}
- return NULL;
+
+ for (int n = 0; n < *num_infos; n++) {
+ struct hwdec_info *hwdec = &(*infos)[n];
+
+ hwdec->rank = n;
+ hwdec->auto_pos = MP_ARRAY_SIZE(hwdec_autoprobe_order);
+
+ for (int x = 0; hwdec_autoprobe_order[x]; x++) {
+ if (strcmp(hwdec_autoprobe_order[x], hwdec->method_name) == 0)
+ hwdec->auto_pos = x;
+ }
+ }
+
+ qsort(*infos, *num_infos, sizeof(struct hwdec_info), hwdec_compare);
}
static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
@@ -347,141 +315,121 @@ static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
return false;
}
-// This is intended to return the name of a decoder for a given wrapper API.
-// Decoder wrappers are usually added to libavcodec with a specific suffix.
-// For example the mmal h264 decoder is named h264_mmal.
-// This API would e.g. return h264_mmal for
-// hwdec_find_decoder("h264", "_mmal").
-// Just concatenating the two names will not always work due to inconsistencies
-// (e.g. "mpeg2video" vs. "mpeg2").
-static const char *hwdec_find_decoder(const char *codec, const char *suffix)
-{
- enum AVCodecID codec_id = mp_codec_to_av_codec_id(codec);
- if (codec_id == AV_CODEC_ID_NONE)
- return NULL;
- AVCodec *cur = NULL;
- for (;;) {
- cur = av_codec_next(cur);
- if (!cur)
- break;
- if (cur->id == codec_id && av_codec_is_decoder(cur) &&
- bstr_endswith0(bstr0(cur->name), suffix))
- return cur->name;
- }
- return NULL;
-}
-
-// Parallel to hwdec_find_decoder(): return whether a hwdec can use the given
-// decoder. This can't be answered accurately; it works for wrapper decoders
-// only (like mmal), and for real hwaccels this will always return false.
-static bool hwdec_is_wrapper(struct vd_lavc_hwdec *hwdec, const char *decoder)
+static AVBufferRef *hwdec_create_dev(struct dec_video *vd,
+ struct hwdec_info *hwdec,
+ bool autoprobe)
{
- if (!hwdec->lavc_suffix)
- return false;
- return bstr_endswith0(bstr0(decoder), hwdec->lavc_suffix);
-}
+ assert(hwdec->lavc_device);
-static void standalone_dev_destroy(struct mp_hwdec_ctx *ctx)
-{
- av_buffer_unref(&ctx->av_device_ref);
- talloc_free(ctx);
-}
-
-static struct mp_hwdec_ctx *hwdec_create_dev(struct dec_video *vd,
- struct vd_lavc_hwdec *hwdec,
- bool autoprobe)
-{
- if (hwdec->create_standalone_dev) {
- struct mp_hwdec_ctx *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (struct mp_hwdec_ctx) {
- .type = hwdec->type,
- .ctx = NULL,
- .destroy = standalone_dev_destroy,
- };
+ if (hwdec->copying) {
const struct hwcontext_fns *fns =
- hwdec_get_hwcontext_fns(hwdec->create_standalone_dev_type);
+ hwdec_get_hwcontext_fns(hwdec->lavc_device);
if (fns && fns->create_dev) {
struct hwcontext_create_dev_params params = {
.probing = autoprobe,
};
- ctx->av_device_ref = fns->create_dev(vd->global, vd->log, &params);
- if (!ctx->av_device_ref) {
- standalone_dev_destroy(ctx);
- ctx = NULL;
- }
+ return fns->create_dev(vd->global, vd->log, &params);
} else {
- if (av_hwdevice_ctx_create(&ctx->av_device_ref,
- hwdec->create_standalone_dev_type, NULL, NULL, 0) < 0)
- {
- standalone_dev_destroy(ctx);
- ctx = NULL;
- }
+ AVBufferRef* ref = NULL;
+ av_hwdevice_ctx_create(&ref, hwdec->lavc_device, NULL, NULL, 0);
+ return ref;
}
- return ctx;
- }
- if (vd->hwdec_devs) {
- int type = hwdec->interop_type ? hwdec->interop_type : hwdec->type;
+ } else if (vd->hwdec_devs) {
hwdec_devices_request_all(vd->hwdec_devs);
- return hwdec_devices_get(vd->hwdec_devs, type);
+ return hwdec_devices_get_lavc(vd->hwdec_devs, hwdec->lavc_device);
}
+
return NULL;
}
-static int hwdec_probe(struct dec_video *vd, struct vd_lavc_hwdec *hwdec,
- const char *codec, bool autoprobe)
+// Select if and which hwdec to use. Also makes sure to get the decode device.
+static void select_and_set_hwdec(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- int r = 0;
- if (hwdec->probe)
- r = hwdec->probe(ctx, hwdec, codec);
- if (hwdec->generic_hwaccel) {
- assert(!hwdec->probe && !hwdec->init && !hwdec->init_decoder &&
- !hwdec->uninit);
- struct mp_hwdec_ctx *dev = hwdec_create_dev(vd, hwdec, autoprobe);
- if (!dev)
- return hwdec->copying ? -1 : HWDEC_ERR_NO_CTX;
- if (dev->emulated)
- r = HWDEC_ERR_EMULATED;
- bool owns_hwdec_dev = !!hwdec->create_standalone_dev;
- if (owns_hwdec_dev && dev->destroy)
- dev->destroy(dev);
+ const char *codec = vd->codec->codec;
+
+ bstr opt = bstr0(vd->opts->hwdec_api);
+
+ bool hwdec_requested = !bstr_equals0(opt, "no");
+ bool hwdec_auto_all = bstr_equals0(opt, "auto") ||
+ bstr_equals0(opt, "yes") ||
+ bstr_equals0(opt, "");
+ bool hwdec_auto_copy = bstr_equals0(opt, "auto-copy");
+ bool hwdec_auto = hwdec_auto_all || hwdec_auto_copy;
+
+ if (hwdec_codec_allowed(vd, codec) && hwdec_requested) {
+ struct hwdec_info *hwdecs = NULL;
+ int num_hwdecs = 0;
+ add_all_hwdec_methods(&hwdecs, &num_hwdecs);
+
+ for (int n = 0; n < num_hwdecs; n++) {
+ struct hwdec_info *hwdec = &hwdecs[n];
+
+ const char *hw_codec = mp_codec_from_av_codec_id(hwdec->codec->id);
+ if (!hw_codec || strcmp(hw_codec, codec) != 0)
+ continue;
+
+ if (!hwdec_auto && !bstr_equals0(opt, hwdec->method_name))
+ continue;
+
+ MP_VERBOSE(vd, "Looking at hwdec %s...\n", hwdec->name);
+
+ if (hwdec_auto_copy && !hwdec->copying) {
+ MP_VERBOSE(vd, "Not using this for auto-copy.\n");
+ continue;
+ }
+
+ if (hwdec->lavc_device) {
+ ctx->hwdec_dev = hwdec_create_dev(vd, hwdec, hwdec_auto);
+ if (!ctx->hwdec_dev) {
+ MP_VERBOSE(vd, "Could not create device.\n");
+ continue;
+ }
+ }
+
+ ctx->use_hwdec = true;
+ ctx->hwdec = *hwdec;
+ break;
+ }
+
+ talloc_free(hwdecs);
+ } else {
+ MP_VERBOSE(vd, "Not trying to use hardware decoding: codec %s is not "
+ "on whitelist, or does not support hardware acceleration.\n",
+ codec);
}
- if (r >= 0) {
- if (hwdec->lavc_suffix && !hwdec_find_decoder(codec, hwdec->lavc_suffix))
- return HWDEC_ERR_NO_CODEC;
+
+ if (ctx->use_hwdec) {
+ MP_VERBOSE(vd, "Trying hardware decoding via %s.\n", ctx->hwdec.name);
+ if (strcmp(ctx->decoder, ctx->hwdec.codec->name) != 0)
+ MP_VERBOSE(vd, "Using underlying hw-decoder '%s'\n",
+ ctx->hwdec.codec->name);
+ } else {
+ MP_VERBOSE(vd, "Using software decoding.\n");
}
- return r;
}
-static struct vd_lavc_hwdec *probe_hwdec(struct dec_video *vd, bool autoprobe,
- enum hwdec_type api,
- const char *codec)
+int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param)
{
- MP_VERBOSE(vd, "Probing '%s'...\n", m_opt_choice_str(mp_hwdec_names, api));
- struct vd_lavc_hwdec *hwdec = find_hwcodec(api);
- if (!hwdec) {
- int level = autoprobe ? MSGL_V : MSGL_WARN;
- MP_MSG(vd, level, "Requested hardware decoder not compiled.\n");
- return NULL;
- }
- int r = hwdec_probe(vd, hwdec, codec, autoprobe);
- if (r == HWDEC_ERR_EMULATED) {
- if (autoprobe)
- return NULL;
- // User requested this explicitly.
- MP_WARN(vd, "Using emulated hardware decoding API.\n");
- r = 0;
- }
- if (r >= 0) {
- return hwdec;
- } else if (r == HWDEC_ERR_NO_CODEC) {
- MP_VERBOSE(vd, "Hardware decoder for '%s' with the given API not found "
- "in libavcodec.\n", codec);
- } else if (r == HWDEC_ERR_NO_CTX && !autoprobe) {
- MP_WARN(vd, "VO does not support requested hardware decoder, or "
- "loading it failed.\n");
+ if (bstr_equals0(param, "help")) {
+ struct hwdec_info *hwdecs = NULL;
+ int num_hwdecs = 0;
+ add_all_hwdec_methods(&hwdecs, &num_hwdecs);
+
+ mp_info(log, "Valid values:\n");
+
+ for (int n = 0; n < num_hwdecs; n++) {
+ struct hwdec_info *hwdec = &hwdecs[n];
+
+ mp_info(log, " %s (%s)\n", hwdec->method_name, hwdec->name);
+ }
+
+ talloc_free(hwdecs);
+
+ return M_OPT_EXIT;
}
- return NULL;
+ return 0;
}
static void uninit(struct dec_video *vd)
@@ -501,72 +449,20 @@ static void force_fallback(struct dec_video *vd)
uninit_avctx(vd);
int lev = ctx->hwdec_notified ? MSGL_WARN : MSGL_V;
mp_msg(vd->log, lev, "Falling back to software decoding.\n");
- init_avctx(vd, ctx->decoder, NULL);
+ init_avctx(vd);
}
static void reinit(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- const char *decoder = ctx->decoder;
- const char *codec = vd->codec->codec;
uninit_avctx(vd);
- struct vd_lavc_hwdec *hwdec = NULL;
-
- if (hwdec_codec_allowed(vd, codec)) {
- int api = vd->opts->hwdec_api;
- if (HWDEC_IS_AUTO(api)) {
- // If a specific decoder is forced, we should try a hwdec method
- // that works with it, instead of simply failing later at runtime.
- // This is good for avoiding trying "normal" hwaccels on wrapper
- // decoders (like vaapi on a mmal decoder). Since libavcodec doesn't
- // tell us which decoder supports which hwaccel methods without
- // actually running it, do it by detecting such wrapper decoders.
- // On the other hand, e.g. "--hwdec=rpi" should always force the
- // wrapper decoder, so be careful not to break this case.
- bool might_be_wrapper = false;
- for (int n = 0; hwdec_list[n]; n++) {
- struct vd_lavc_hwdec *other = (void *)hwdec_list[n];
- if (hwdec_is_wrapper(other, decoder))
-