diff options
-rw-r--r-- | DOCS/interface-changes.rst | 2 | ||||
-rw-r--r-- | options/options.c | 33 | ||||
-rw-r--r-- | options/options.h | 5 | ||||
-rw-r--r-- | player/command.c | 39 | ||||
-rw-r--r-- | video/decode/lavc.h | 65 | ||||
-rw-r--r-- | video/decode/vd_lavc.c | 737 | ||||
-rw-r--r-- | video/hwdec.c | 22 | ||||
-rw-r--r-- | video/hwdec.h | 13 |
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, ¤t); - 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, ¤t); - *(int *)arg = current; - return M_PROPERTY_OK; - } - } - return M_PROPERTY_NOT_IMPLEMENTED; + char *current = NULL; + video_vd_control(vd, VDCTRL_GET_HWDEC, ¤t); + 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, ¶ms); - if (!ctx->av_device_ref) { - standalone_dev_destroy(ctx); - ctx = NULL; - } + return fns->create_dev(vd->global, vd->log, ¶ms); } 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); - } |