summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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))
- might_be_wrapper = true;
- }
- for (int n = 0; hwdec_list[n]; n++) {
- hwdec = probe_hwdec(vd, true, hwdec_list[n]->type, codec);
- if (hwdec) {
- if (might_be_wrapper && !hwdec_is_wrapper(hwdec, decoder)) {
- MP_VERBOSE(vd, "This hwaccel is not compatible.\n");
- continue;
- }
- if (api == HWDEC_AUTO_COPY && !hwdec->copying) {
- MP_VERBOSE(vd, "Not using this for auto-copy mode.\n");
- continue;
- }
- break;
- }
- }
- } else if (api != HWDEC_NONE) {
- hwdec = probe_hwdec(vd, false, api, codec);
- }
- } 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 (hwdec) {
- const char *orig_decoder = decoder;
- if (hwdec->lavc_suffix)
- decoder = hwdec_find_decoder(codec, hwdec->lavc_suffix);
- MP_VERBOSE(vd, "Trying hardware decoding.\n");
- if (strcmp(orig_decoder, dec