summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/example.conf9
-rw-r--r--mpvcore/options.c1
-rw-r--r--video/decode/lavc.h32
-rw-r--r--video/decode/vd_lavc.c194
-rw-r--r--video/decode/vdpau.c44
-rw-r--r--video/decode/vdpau_old.c24
6 files changed, 204 insertions, 100 deletions
diff --git a/etc/example.conf b/etc/example.conf
index e610b81b4f..96c7afaca0 100644
--- a/etc/example.conf
+++ b/etc/example.conf
@@ -81,6 +81,9 @@
# Use this for a widescreen monitor, non-square pixels.
#monitoraspect=16:9
+# Enable hardware decoding if available. Often, this requires using an certain
+# video output, otherwise no hardware decoding will be used.
+#hwdec = auto
############
# Profiles #
@@ -89,7 +92,11 @@
# The options declared as part of profiles override global default settings,
# but only take effect when the profile is active.
-#[vo.vdpau]
+# The following profile can be enabled on the command line with: --profile=vdpau
+
+#[vdpau]
+# The profile forces the vdpau VO.
+#vo=vdpau
# Use hardware decoding (this might break playback of some h264 files)
#hwdec=vdpau
# Most video filters do not work with vdpau.
diff --git a/mpvcore/options.c b/mpvcore/options.c
index b66556d8bd..ff278cc7a2 100644
--- a/mpvcore/options.c
+++ b/mpvcore/options.c
@@ -466,6 +466,7 @@ const m_option_t mp_opts[] = {
OPT_CHOICE("hwdec", hwdec_api, 0,
({"no", 0},
+ {"auto", -1},
{"vdpau", 1},
{"vda", 2},
{"crystalhd", 3})),
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 3611530400..94973cb2d8 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -10,10 +10,19 @@
#include "demux/stheader.h"
#include "video/mp_image.h"
+// keep in sync with --hwdec option
+enum hwdec_type {
+ HWDEC_AUTO = -1,
+ HWDEC_NONE = 0,
+ HWDEC_VDPAU = 1,
+ HWDEC_VDA = 2,
+ HWDEC_CRYSTALHD = 3,
+};
+
typedef struct lavc_ctx {
AVCodecContext *avctx;
AVFrame *pic;
- struct hwdec *hwdec;
+ struct vd_lavc_hwdec *hwdec;
enum PixelFormat pix_fmt;
int do_hw_dr1;
int vo_initialized;
@@ -35,16 +44,31 @@ typedef struct lavc_ctx {
struct mp_image_pool *non_dr1_pool;
} vd_ffmpeg_ctx;
-struct vd_lavc_hwdec_functions {
- // If not-NULL, a 0 terminated list of IMGFMT_ formats. Only one of these
- // formats is accepted when handling the libavcodec get_format callback.
+struct vd_lavc_hwdec {
+ enum hwdec_type type;
+ // If non-NULL: lists pairs software and hardware decoders. If the current
+ // codec is not one of the listed software decoders, probing fails.
+ // Otherwise, the AVCodecContext is initialized with the associated
+ // hardware decoder.
+ // Useful only if hw decoding requires a special codec, instead of using
+ // the libavcodec hwaccel infrastructure.
+ const char **codec_pairs;
+ // If not-NULL: a 0 terminated list of IMGFMT_ formats, and only one of
+ // these formats is accepted in the libavcodec get_format callback.
const int *image_formats;
+ int (*probe)(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder);
int (*init)(struct lavc_ctx *ctx);
void (*uninit)(struct lavc_ctx *ctx);
struct mp_image *(*allocate_image)(struct lavc_ctx *ctx, AVFrame *frame);
void (*fix_image)(struct lavc_ctx *ctx, struct mp_image *img);
};
+enum {
+ HWDEC_ERR_NO_CTX = -2,
+ HWDEC_ERR_NO_CODEC = -3,
+};
+
// lavc_dr1.c
int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame);
void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame);
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 0367c2afed..2fc7a1ea4c 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -59,7 +59,8 @@
#include "mpvcore/m_option.h"
-static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec);
+static void init_avctx(sh_video_t *sh, const char *decoder,
+ struct vd_lavc_hwdec *hwdec);
static void uninit_avctx(sh_video_t *sh);
static void setup_refcounting_hw(struct AVCodecContext *s);
@@ -81,72 +82,56 @@ const m_option_t lavc_decode_opts_conf[] = {
{NULL, NULL, 0, 0, 0, 0, NULL}
};
-// keep in sync with --hwdec option
-enum hwdec_type {
- HWDEC_NONE = 0,
- HWDEC_VDPAU = 1,
- HWDEC_VDA = 2,
- HWDEC_CRYSTALHD = 3,
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau_old;
+
+static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
+ .type = HWDEC_CRYSTALHD,
+ .codec_pairs = (const char *[]) {
+ "mpeg2", "mpeg2_crystalhd",
+ "msmpeg4", "msmpeg4_crystalhd",
+ "wmv3", "wmv3_crystalhd",
+ "vc1", "vc1_crystalhd",
+ "h264", "h264_crystalhd",
+ "mpeg4", "mpeg4_crystalhd",
+ NULL
+ },
};
-struct hwdec {
- enum hwdec_type api;
- const char *codec, *hw_codec;
- const struct vd_lavc_hwdec_functions *fns;
+static const struct vd_lavc_hwdec mp_vd_lavc_vda = {
+ .type = HWDEC_VDA,
+ .codec_pairs = (const char *[]) {"h264", "h264_vda", NULL},
};
-const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau;
-const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau_old;
-
-static const struct hwdec hwdec_list[] = {
+static const struct vd_lavc_hwdec *hwdec_list[] = {
#if CONFIG_VDPAU
#if HAVE_AV_CODEC_NEW_VDPAU_API
- {HWDEC_VDPAU, "h264", NULL, &mp_vd_lavc_vdpau},
- {HWDEC_VDPAU, "wmv3", NULL, &mp_vd_lavc_vdpau},
- {HWDEC_VDPAU, "vc1", NULL, &mp_vd_lavc_vdpau},
- {HWDEC_VDPAU, "mpeg1video", NULL, &mp_vd_lavc_vdpau},
- {HWDEC_VDPAU, "mpeg2video", NULL, &mp_vd_lavc_vdpau},
- {HWDEC_VDPAU, "mpeg4", NULL, &mp_vd_lavc_vdpau},
+ &mp_vd_lavc_vdpau,
#else
- {HWDEC_VDPAU, "h264", "h264_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "wmv3", "wmv3_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "vc1", "vc1_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "mpegvideo", "mpegvideo_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "mpeg1video", "mpeg1video_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "mpeg2video", "mpegvideo_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "mpeg2", "mpeg2_vdpau", &mp_vd_lavc_vdpau_old},
- {HWDEC_VDPAU, "mpeg4", "mpeg4_vdpau", &mp_vd_lavc_vdpau_old},
+ &mp_vd_lavc_vdpau_old,
#endif
#endif // CONFIG_VDPAU
-
- {HWDEC_VDA, "h264", "h264_vda"},
-
- {HWDEC_CRYSTALHD, "mpeg2", "mpeg2_crystalhd"},
- {HWDEC_CRYSTALHD, "msmpeg4", "msmpeg4_crystalhd"},
- {HWDEC_CRYSTALHD, "wmv3", "wmv3_crystalhd"},
- {HWDEC_CRYSTALHD, "vc1", "vc1_crystalhd"},
- {HWDEC_CRYSTALHD, "h264", "h264_crystalhd"},
- {HWDEC_CRYSTALHD, "mpeg4", "mpeg4_crystalhd"},
-
- {0}
+ &mp_vd_lavc_vda,
+ &mp_vd_lavc_crystalhd,
+ NULL
};
-static struct hwdec *find_hwcodec(enum hwdec_type api, const char *codec)
+static struct vd_lavc_hwdec *find_hwcodec(enum hwdec_type api)
{
- for (int n = 0; hwdec_list[n].api; n++) {
- if (hwdec_list[n].api == api && strcmp(hwdec_list[n].codec, codec) == 0)
- return (struct hwdec *)&hwdec_list[n];
+ for (int n = 0; hwdec_list[n]; n++) {
+ if (hwdec_list[n]->type == api)
+ return (struct vd_lavc_hwdec *)hwdec_list[n];
}
return NULL;
}
-static bool hwdec_codec_allowed(sh_video_t *sh, struct hwdec *hwdec)
+static bool hwdec_codec_allowed(sh_video_t *sh, const char *codec)
{
bstr s = bstr0(sh->opts->hwdec_codecs);
while (s.len) {
bstr item;
bstr_split_tok(s, ",", &item, &s);
- if (bstr_equals0(item, "all") || bstr_equals0(item, hwdec->codec))
+ if (bstr_equals0(item, "all") || bstr_equals0(item, codec))
return true;
}
return false;
@@ -165,6 +150,56 @@ static enum AVDiscard str2AVDiscard(char *str)
return AVDISCARD_DEFAULT;
}
+static int hwdec_probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder, const char **hw_decoder)
+{
+ if (hwdec->codec_pairs) {
+ for (int n = 0; hwdec->codec_pairs[n + 0]; n += 2) {
+ const char *sw = hwdec->codec_pairs[n + 0];
+ const char *hw = hwdec->codec_pairs[n + 1];
+ if (decoder && strcmp(decoder, sw) == 0) {
+ AVCodec *codec = avcodec_find_decoder_by_name(hw);
+ *hw_decoder = hw;
+ if (codec)
+ goto found;
+ }
+ }
+ return HWDEC_ERR_NO_CODEC;
+ found: ;
+ }
+ int r = 0;
+ if (hwdec->probe)
+ r = hwdec->probe(hwdec, info, decoder);
+ return r;
+}
+
+static bool probe_hwdec(sh_video_t *sh, bool autoprobe, enum hwdec_type api,
+ const char *decoder, struct vd_lavc_hwdec **use_hwdec,
+ const char **use_decoder)
+{
+ struct vd_lavc_hwdec *hwdec = find_hwcodec(api);
+ if (!hwdec) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Requested hardware decoder not "
+ "compiled.\n");
+ return false;
+ }
+ const char *hw_decoder = NULL;
+ int r = hwdec_probe(hwdec, sh->hwdec_info, decoder, &hw_decoder);
+ if (r >= 0) {
+ *use_hwdec = hwdec;
+ *use_decoder = hw_decoder;
+ return true;
+ } else if (r == HWDEC_ERR_NO_CODEC) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Hardware decoder '%s' not found in "
+ "libavcodec.\n", hw_decoder ? hw_decoder : decoder);
+ } else if (r == HWDEC_ERR_NO_CTX && !autoprobe) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "VO does not support requested "
+ "hardware decoder.\n");
+ }
+ return false;
+}
+
+
static int init(sh_video_t *sh, const char *decoder)
{
vd_ffmpeg_ctx *ctx;
@@ -182,29 +217,35 @@ static int init(sh_video_t *sh, const char *decoder)
return 0;
}
- struct hwdec *hwdec = find_hwcodec(sh->opts->hwdec_api, decoder);
- struct hwdec *use_hwdec = NULL;
- if (hwdec && hwdec_codec_allowed(sh, hwdec)) {
- if (hwdec->hw_codec) {
- AVCodec *lavc_hwcodec = avcodec_find_decoder_by_name(hwdec->hw_codec);
- if (lavc_hwcodec) {
- ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
- decoder = lavc_hwcodec->name;
- use_hwdec = hwdec;
- } else {
- mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "Decoder '%s' not found in "
- "libavcodec, using software decoding.\n", hwdec->hw_codec);
+ struct vd_lavc_hwdec *hwdec = NULL;
+ const char *hw_decoder = NULL;
+
+ if (hwdec_codec_allowed(sh, decoder)) {
+ if (sh->opts->hwdec_api == HWDEC_AUTO) {
+ for (int n = 0; hwdec_list[n]; n++) {
+ if (probe_hwdec(sh, true, hwdec_list[n]->type, decoder,
+ &hwdec, &hw_decoder))
+ break;
}
- } else {
- ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
- use_hwdec = hwdec;
+ } else if (sh->opts->hwdec_api != HWDEC_NONE) {
+ probe_hwdec(sh, false, sh->opts->hwdec_api, decoder,
+ &hwdec, &hw_decoder);
}
- } else if (!hwdec && sh->opts->hwdec_api) {
- mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, "Selected hardware decoding API not "
- "available, using software decoding.\n");
+ } else {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_V, "Not trying to use hardware decoding: "
+ "codec %s is blacklisted by user.\n", decoder);
+ }
+
+ if (hwdec) {
+ ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
+ if (hw_decoder)
+ decoder = hw_decoder;
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Trying to use hardware decoding.\n");
+ } else if (sh->opts->hwdec_api != HWDEC_NONE) {
+ mp_tmsg(MSGT_DECVIDEO, MSGL_INFO, "Using software decoding.\n");
}
- init_avctx(sh, decoder, use_hwdec);
+ init_avctx(sh, decoder, hwdec);
if (!ctx->avctx) {
if (ctx->software_fallback_decoder) {
mp_tmsg(MSGT_DECVIDEO, MSGL_ERR, "Error initializing hardware "
@@ -279,7 +320,8 @@ static void set_from_bih(AVCodecContext *avctx, uint32_t format,
avctx->coded_height = bih->biHeight;
}
-static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
+static void init_avctx(sh_video_t *sh, const char *decoder,
+ struct vd_lavc_hwdec *hwdec)
{
vd_ffmpeg_ctx *ctx = sh->context;
struct lavc_param *lavc_param = &sh->opts->lavc_param;
@@ -311,13 +353,13 @@ static void init_avctx(sh_video_t *sh, const char *decoder, struct hwdec *hwdec)
avctx->thread_count = lavc_param->threads;
- if (ctx->hwdec && ctx->hwdec->fns) {
+ if (ctx->hwdec && ctx->hwdec->allocate_image) {
ctx->do_hw_dr1 = true;
avctx->thread_count = 1;
- if (ctx->hwdec->fns->image_formats)
+ if (ctx->hwdec->image_formats)
avctx->get_format = get_format_hwdec;
setup_refcounting_hw(avctx);
- if (ctx->hwdec->fns->init(ctx) < 0) {
+ if (ctx->hwdec->init && ctx->hwdec->init(ctx) < 0) {
uninit_avctx(sh);
return;
}
@@ -404,8 +446,8 @@ static void uninit_avctx(sh_video_t *sh)
av_freep(&ctx->avctx);
avcodec_free_frame(&ctx->pic);
- if (ctx->hwdec && ctx->hwdec->fns)
- ctx->hwdec->fns->uninit(ctx);
+ if (ctx->hwdec && ctx->hwdec->uninit)
+ ctx->hwdec->uninit(ctx);
#if !HAVE_AVUTIL_REFCOUNTING
mp_buffer_pool_free(&ctx->dr1_buffer_pool);
@@ -487,10 +529,10 @@ static enum PixelFormat get_format_hwdec(struct AVCodecContext *avctx,
mp_msg(MSGT_DECVIDEO, MSGL_V, " %s", av_get_pix_fmt_name(fmt[i]));
mp_msg(MSGT_DECVIDEO, MSGL_V, "\n");
- assert(ctx->hwdec && ctx->hwdec->fns);
+ assert(ctx->hwdec);
for (int i = 0; fmt[i] != PIX_FMT_NONE; i++) {
- const int *okfmt = ctx->hwdec->fns->image_formats;
+ const int *okfmt = ctx->hwdec->image_formats;
for (int n = 0; okfmt && okfmt[n]; n++) {
if (imgfmt2pixfmt(okfmt[n]) == fmt[i])
return fmt[i];
@@ -519,7 +561,7 @@ static struct mp_image *get_surface_hwdec(struct sh_video *sh, AVFrame *pic)
if (!IMGFMT_IS_HWACCEL(imgfmt))
return NULL;
- struct mp_image *mpi = ctx->hwdec->fns->allocate_image(ctx, pic);
+ struct mp_image *mpi = ctx->hwdec->allocate_image(ctx, pic);
if (mpi) {
for (int i = 0; i < 4; i++)
@@ -694,8 +736,8 @@ static int decode(struct sh_video *sh, struct demux_packet *packet,
struct mp_image *mpi = image_from_decoder(sh);
assert(mpi->planes[0]);
- if (ctx->hwdec && ctx->hwdec->fns && ctx->hwdec->fns->fix_image)
- ctx->hwdec->fns->fix_image(ctx, mpi);
+ if (ctx->hwdec && ctx->hwdec->fix_image)
+ ctx->hwdec->fix_image(ctx, mpi);
mpi->colorspace = ctx->image_params.colorspace;
mpi->levels = ctx->image_params.colorlevels;
diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c
index b259ae303a..e20abdafca 100644
--- a/video/decode/vdpau.c
+++ b/video/decode/vdpau.c
@@ -23,6 +23,7 @@
#include "lavc.h"
#include "mpvcore/mp_common.h"
+#include "mpvcore/av_common.h"
#include "video/fmt-conversion.h"
#include "video/vdpau.h"
#include "video/decode/dec_video.h"
@@ -109,6 +110,19 @@ static int handle_preemption(struct lavc_ctx *ctx)
return 0;
}
+static const struct profile_entry *find_codec(enum AVCodecID id, int ff_profile)
+{
+ for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
+ if (profiles[n].av_codec == id &&
+ (profiles[n].ff_profile == ff_profile ||
+ profiles[n].ff_profile == FF_PROFILE_UNKNOWN))
+ {
+ return &profiles[n];
+ }
+ }
+ return NULL;
+}
+
static bool create_vdp_decoder(struct lavc_ctx *ctx)
{
struct priv *p = ctx->hwdec_priv;
@@ -121,17 +135,8 @@ static bool create_vdp_decoder(struct lavc_ctx *ctx)
if (p->context.decoder != VDP_INVALID_HANDLE)
vdp->decoder_destroy(p->context.decoder);
- const struct profile_entry *pe = NULL;
- for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
- if (profiles[n].av_codec == ctx->avctx->codec_id &&
- (profiles[n].ff_profile == ctx->avctx->profile ||
- profiles[n].ff_profile == FF_PROFILE_UNKNOWN))
- {
- pe = &profiles[n];
- break;
- }
- }
-
+ const struct profile_entry *pe = find_codec(ctx->avctx->codec_id,
+ ctx->avctx->profile);
if (!pe) {
mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown codec!\n");
goto fail;
@@ -200,9 +205,6 @@ static void uninit(struct lavc_ctx *ctx)
static int init(struct lavc_ctx *ctx)
{
- if (!ctx->hwdec_info || !ctx->hwdec_info->vdpau_ctx)
- return -1;
-
struct priv *p = talloc_ptrtype(NULL, p);
*p = (struct priv) {
.mpvdp = ctx->hwdec_info->vdpau_ctx,
@@ -220,8 +222,20 @@ static int init(struct lavc_ctx *ctx)
return 0;
}
-const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau = {
+static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder)
+{
+ if (!info || !info->vdpau_ctx)
+ return HWDEC_ERR_NO_CTX;
+ if (!find_codec(mp_codec_to_av_codec_id(decoder), FF_PROFILE_UNKNOWN))
+ return HWDEC_ERR_NO_CODEC;
+ return 0;
+}
+
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau = {
+ .type = HWDEC_VDPAU,
.image_formats = (const int[]) {IMGFMT_VDPAU, 0},
+ .probe = probe,
.init = init,
.uninit = uninit,
.allocate_image = allocate_image,
diff --git a/video/decode/vdpau_old.c b/video/decode/vdpau_old.c
index e9c88b69ea..4abdafa9c0 100644
--- a/video/decode/vdpau_old.c
+++ b/video/decode/vdpau_old.c
@@ -223,9 +223,6 @@ static void uninit(struct lavc_ctx *ctx)
static int init(struct lavc_ctx *ctx)
{
- if (!ctx->hwdec_info || !ctx->hwdec_info->vdpau_ctx)
- return -1;
-
struct priv *p = talloc_ptrtype(NULL, p);
*p = (struct priv) {
.mpvdp = ctx->hwdec_info->vdpau_ctx,
@@ -246,6 +243,14 @@ static int init(struct lavc_ctx *ctx)
return 0;
}
+static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder)
+{
+ if (!info || !info->vdpau_ctx)
+ return HWDEC_ERR_NO_CTX;
+ return 0;
+}
+
static void fix_image(struct lavc_ctx *ctx, struct mp_image *img)
{
// Make it follow the convention of the "new" vdpau decoder
@@ -254,12 +259,23 @@ static void fix_image(struct lavc_ctx *ctx, struct mp_image *img)
img->planes[3] = (void *)(intptr_t)rndr->surface;
}
-const struct vd_lavc_hwdec_functions mp_vd_lavc_vdpau_old = {
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau_old = {
.image_formats = (const int[]) {
IMGFMT_VDPAU_MPEG1, IMGFMT_VDPAU_MPEG2, IMGFMT_VDPAU_H264,
IMGFMT_VDPAU_WMV3, IMGFMT_VDPAU_VC1, IMGFMT_VDPAU_MPEG4,
0
},
+ .codec_pairs = (const char *[]) {
+ "h264", "h264_vdpau",
+ "wmv3", "wmv3_vdpau",
+ "vc1", "vc1_vdpau",
+ "mpegvideo", "mpegvideo_vdpau",
+ "mpeg1video", "mpeg1video_vdpau",
+ "mpeg2video", "mpegvideo_vdpau",
+ "mpeg2", "mpeg2_vdpau",
+ "mpeg4", "mpeg4_vdpau",
+ NULL
+ },
.init = init,
.uninit = uninit,
.allocate_image = allocate_image,