summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/en/options.rst7
-rw-r--r--mpvcore/options.c3
-rw-r--r--mpvcore/options.h1
-rw-r--r--video/decode/lavc.h13
-rw-r--r--video/decode/vaapi.c77
-rw-r--r--video/decode/vd_lavc.c43
-rw-r--r--video/decode/vdpau.c69
-rw-r--r--video/vdpau_functions.inc1
8 files changed, 142 insertions, 72 deletions
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index d07c65c934..18542b2e37 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -2526,6 +2526,13 @@ OPTIONS
See ``--vd=help`` for a full list of available decoders.
+``--vd-lavc-check-hw-profile=<yes|no>``
+ Check hardware decoder profile (default: yes). If ``no`` is set, the
+ highest profile of the hardware decoder is unconditionally selected, and
+ decoding is forced even if the profile of the video is higher than that.
+ The result is most likely broken decoding, but may also help if the
+ detected or reported profiles are somehow incorrect.
+
``--vd-lavc-bitexact``
Only use bit-exact algorithms in all decoding steps (for codec testing).
diff --git a/mpvcore/options.c b/mpvcore/options.c
index 32d108d119..44f821db75 100644
--- a/mpvcore/options.c
+++ b/mpvcore/options.c
@@ -864,6 +864,9 @@ const struct MPOpts mp_default_opts = {
.lavfdopts = {
.allow_mimetype = 1,
},
+ .lavc_param = {
+ .check_hw_profile = 1,
+ },
.input = {
.key_fifo_size = 7,
.doubleclick_time = 300,
diff --git a/mpvcore/options.h b/mpvcore/options.h
index 0f3fa51549..4a58a0e10d 100644
--- a/mpvcore/options.h
+++ b/mpvcore/options.h
@@ -217,6 +217,7 @@ typedef struct MPOpts {
char *skip_frame_str;
int threads;
int bitexact;
+ int check_hw_profile;
char *avopt;
} lavc_param;
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 32b827b964..9e2533cbd5 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -22,6 +22,7 @@ enum hwdec_type {
};
typedef struct lavc_ctx {
+ struct MPOpts *opts;
AVCodecContext *avctx;
AVFrame *pic;
struct vd_lavc_hwdec *hwdec;
@@ -73,6 +74,18 @@ enum {
HWDEC_ERR_NO_CODEC = -3,
};
+struct hwdec_profile_entry {
+ enum AVCodecID av_codec;
+ int ff_profile;
+ uint64_t hw_profile;
+};
+
+const struct hwdec_profile_entry *hwdec_find_profile(
+ struct lavc_ctx *ctx, const struct hwdec_profile_entry *table);
+bool hwdec_check_codec_support(const char *decoder,
+ const struct hwdec_profile_entry *table);
+int hwdec_get_max_refs(struct lavc_ctx *ctx);
+
// 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/vaapi.c b/video/decode/vaapi.c
index 65c75d1677..bb2a6c1049 100644
--- a/video/decode/vaapi.c
+++ b/video/decode/vaapi.c
@@ -73,38 +73,28 @@ struct priv {
bool printed_readback_warning;
};
-struct profile_entry {
- enum AVCodecID av_codec;
- int maxrefs;
- const VAProfile *va_profiles;
+#define PE(av_codec_id, ff_profile, vdp_profile) \
+ {AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \
+ VAProfile ## vdp_profile}
+
+static const struct hwdec_profile_entry profiles[] = {
+ PE(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main),
+ PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple),
+ PE(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple),
+ PE(MPEG4, MPEG4_MAIN, MPEG4Main),
+ PE(MPEG4, MPEG4_SIMPLE, MPEG4Simple),
+ PE(H264, H264_HIGH, H264High),
+ PE(H264, H264_MAIN, H264Main),
+ PE(H264, H264_BASELINE, H264Baseline),
+ PE(VC1, VC1_ADVANCED, VC1Advanced),
+ PE(VC1, VC1_MAIN, VC1Main),
+ PE(VC1, VC1_SIMPLE, VC1Simple),
+ PE(WMV3, VC1_ADVANCED, VC1Advanced),
+ PE(WMV3, VC1_MAIN, VC1Main),
+ PE(WMV3, VC1_SIMPLE, VC1Simple),
+ {0}
};
-#define RP(...) __VA_ARGS__
-
-#define PE(av_codec_id, maxrefs, ...) \
- {AV_CODEC_ID_ ## av_codec_id, \
- maxrefs, (const VAProfile[]) {RP __VA_ARGS__, -1}}
-
-static const struct profile_entry profiles[] = {
- PE(MPEG2VIDEO, 2, (VAProfileMPEG2Main, VAProfileMPEG2Simple)),
- PE(H264, 16, (VAProfileH264High, VAProfileH264Main,
- VAProfileH264Baseline)),
- PE(WMV3, 2, (VAProfileVC1Main, VAProfileVC1Simple)),
- PE(VC1, 2, (VAProfileVC1Advanced)),
- PE(MPEG4, 2, (VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple,
- VAProfileMPEG4Simple)),
-};
-
-static const struct profile_entry *find_codec(enum AVCodecID id)
-{
- for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
- if (profiles[n].av_codec == id)
- return &profiles[n];
- }
- return NULL;
-}
-
-
static const char *str_va_profile(VAProfile profile)
{
switch (profile) {
@@ -226,9 +216,9 @@ static int create_decoder(struct lavc_ctx *ctx)
destroy_decoder(ctx);
- const struct profile_entry *pe = find_codec(ctx->avctx->codec_id);
+ const struct hwdec_profile_entry *pe = hwdec_find_profile(ctx, profiles);
if (!pe) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Unknown codec!\n");
+ mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] Unsupported codec or profile.\n");
goto error;
}
@@ -241,23 +231,18 @@ static int create_decoder(struct lavc_ctx *ctx)
for (int i = 0; i < num_profiles; i++)
mp_msg(MSGT_VO, MSGL_DBG2, " %s\n", str_va_profile(va_profiles[i]));
- VAProfile va_profile = -1;
- for (int n = 0; ; n++) {
- if (pe->va_profiles[n] == -1)
- break;
- if (has_profile(va_profiles, num_profiles, pe->va_profiles[n])) {
- va_profile = pe->va_profiles[n];
- break;
- }
- }
- if (va_profile == -1) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vaapi] No decoder profile available.\n");
+ VAProfile va_profile = pe->hw_profile;
+ if (!has_profile(va_profiles, num_profiles, va_profile)) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[vaapi] Decoder profile '%s' not available.\n",
+ str_va_profile(va_profile));
goto error;
}
+
mp_msg(MSGT_VO, MSGL_V, "[vaapi] Using profile '%s'.\n",
str_va_profile(va_profile));
- int num_surfaces = pe->maxrefs;
+ int num_surfaces = hwdec_get_max_refs(ctx);
if (!is_direct_mapping(p->display)) {
mp_msg(MSGT_VO, MSGL_V, "[vaapi] No direct mapping.\n");
// Note: not sure why it has to be *=2 rather than +=1.
@@ -438,7 +423,7 @@ static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
{
if (!info || !info->vaapi_ctx)
return HWDEC_ERR_NO_CTX;
- if (!find_codec(mp_codec_to_av_codec_id(decoder)))
+ if (!hwdec_check_codec_support(decoder, profiles))
return HWDEC_ERR_NO_CODEC;
return 0;
}
@@ -450,7 +435,7 @@ static int probe_copy(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
if (!create_va_dummy_ctx(&dummy))
return HWDEC_ERR_NO_CTX;
destroy_va_dummy_ctx(&dummy);
- if (!find_codec(mp_codec_to_av_codec_id(decoder)))
+ if (!hwdec_check_codec_support(decoder, profiles))
return HWDEC_ERR_NO_CODEC;
return 0;
}
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 479e36df6a..309d5dbcfd 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -78,6 +78,7 @@ const m_option_t lavc_decode_opts_conf[] = {
OPT_STRING("skipframe", lavc_param.skip_frame_str, 0),
OPT_INTRANGE("threads", lavc_param.threads, 0, 0, 16),
OPT_FLAG_CONSTANTS("bitexact", lavc_param.bitexact, 0, 0, CODEC_FLAG_BITEXACT),
+ OPT_FLAG("check-hw-profile", lavc_param.check_hw_profile, 0),
OPT_STRING("o", lavc_param.avopt, 0),
{NULL, NULL, 0, 0, 0, 0, NULL}
};
@@ -154,6 +155,47 @@ static enum AVDiscard str2AVDiscard(char *str)
return AVDISCARD_DEFAULT;
}
+// Find the correct profile entry for the current codec and profile.
+// Assumes the table has higher profiles first (for each codec).
+const struct hwdec_profile_entry *hwdec_find_profile(
+ struct lavc_ctx *ctx, const struct hwdec_profile_entry *table)
+{
+ assert(AV_CODEC_ID_NONE == 0);
+ struct lavc_param *lavc_param = &ctx->opts->lavc_param;
+ enum AVCodecID codec = ctx->avctx->codec_id;
+ int profile = ctx->avctx->profile;
+ // Assume nobody cares about these aspects of the profile
+ if (codec == AV_CODEC_ID_H264)
+ profile &= ~(FF_PROFILE_H264_CONSTRAINED | FF_PROFILE_H264_INTRA);
+ for (int n = 0; table[n].av_codec; n++) {
+ if (table[n].av_codec == codec) {
+ if (table[n].ff_profile == FF_PROFILE_UNKNOWN ||
+ profile == FF_PROFILE_UNKNOWN ||
+ table[n].ff_profile == profile ||
+ !lavc_param->check_hw_profile)
+ return &table[n];
+ }
+ }
+ return NULL;
+}
+
+// Check codec support, without checking the profile.
+bool hwdec_check_codec_support(const char *decoder,
+ const struct hwdec_profile_entry *table)
+{
+ enum AVCodecID codec = mp_codec_to_av_codec_id(decoder);
+ for (int n = 0; table[n].av_codec; n++) {
+ if (table[n].av_codec == codec)
+ return true;
+ }
+ return false;
+}
+
+int hwdec_get_max_refs(struct lavc_ctx *ctx)
+{
+ return ctx->avctx->codec_id == AV_CODEC_ID_H264 ? 16 : 2;
+}
+
static int hwdec_probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
const char *decoder, const char **hw_decoder)
{
@@ -208,6 +250,7 @@ static int init(sh_video_t *sh, const char *decoder)
{
vd_ffmpeg_ctx *ctx;
ctx = sh->context = talloc_zero(NULL, vd_ffmpeg_ctx);
+ ctx->opts = sh->opts;
ctx->non_dr1_pool = talloc_steal(ctx, mp_image_pool_new(16));
if (bstr_endswith0(bstr0(decoder), "_vdpau")) {
diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c
index 57e29693c5..756c8e4d8e 100644
--- a/video/decode/vdpau.c
+++ b/video/decode/vdpau.c
@@ -47,18 +47,26 @@ struct profile_entry {
int maxrefs;
};
-#define PE(av_codec_id, vdp_dcoder_profile, maxrefs) \
- {AV_CODEC_ID_ ## av_codec_id, \
- VDP_DECODER_PROFILE_ ## vdp_dcoder_profile, \
- maxrefs}
-
-static const struct profile_entry profiles[] = {
- PE(MPEG1VIDEO, MPEG1, 2),
- PE(MPEG2VIDEO, MPEG2_MAIN, 2),
- PE(H264, H264_HIGH, 16),
- PE(WMV3, VC1_MAIN, 2),
- PE(VC1, VC1_ADVANCED, 2),
- PE(MPEG4, MPEG4_PART2_ASP,2),
+#define PE(av_codec_id, ff_profile, vdp_profile) \
+ {AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \
+ VDP_DECODER_PROFILE_ ## vdp_profile}
+
+static const struct hwdec_profile_entry profiles[] = {
+ PE(MPEG1VIDEO, UNKNOWN, MPEG1),
+ PE(MPEG2VIDEO, MPEG2_MAIN, MPEG2_MAIN),
+ PE(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2_SIMPLE),
+ PE(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4_PART2_ASP),
+ PE(MPEG4, MPEG4_SIMPLE, MPEG4_PART2_SP),
+ PE(H264, H264_HIGH, H264_HIGH),
+ PE(H264, H264_MAIN, H264_MAIN),
+ PE(H264, H264_BASELINE, H264_BASELINE),
+ PE(VC1, VC1_ADVANCED, VC1_ADVANCED),
+ PE(VC1, VC1_MAIN, VC1_MAIN),
+ PE(VC1, VC1_SIMPLE, VC1_SIMPLE),
+ PE(WMV3, VC1_ADVANCED, VC1_ADVANCED),
+ PE(WMV3, VC1_MAIN, VC1_MAIN),
+ PE(WMV3, VC1_SIMPLE, VC1_SIMPLE),
+ {0}
};
// libavcodec absolutely wants a non-NULL render callback
@@ -100,15 +108,6 @@ static int handle_preemption(struct lavc_ctx *ctx)
return 0;
}
-static const struct profile_entry *find_codec(enum AVCodecID id)
-{
- for (int n = 0; n < MP_ARRAY_SIZE(profiles); n++) {
- if (profiles[n].av_codec == id)
- return &profiles[n];
- }
- return NULL;
-}
-
static bool create_vdp_decoder(struct lavc_ctx *ctx)
{
struct priv *p = ctx->hwdec_priv;
@@ -121,14 +120,32 @@ 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 = find_codec(ctx->avctx->codec_id);
+ const struct hwdec_profile_entry *pe = hwdec_find_profile(ctx, profiles);
if (!pe) {
- mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unknown codec!\n");
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Unsupported codec or profile.\n");
+ goto fail;
+ }
+
+ VdpBool supported;
+ uint32_t maxl, maxm, maxw, maxh;
+ vdp_st = vdp->decoder_query_capabilities(p->vdp_device, pe->hw_profile,
+ &supported, &maxl, &maxm,
+ &maxw, &maxh);
+ CHECK_ST_WARNING("Querying VDPAU decoder capabilities");
+ if (!supported) {
+ mp_msg(MSGT_VO, MSGL_ERR,
+ "[vdpau] Codec or profile not supported by hardware.\n");
goto fail;
}
+ if (p->vid_width > maxw || p->vid_height > maxh) {
+ mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] Video too large.\n");
+ goto fail;
+ }
+
+ int maxrefs = hwdec_get_max_refs(ctx);
- vdp_st = vdp->decoder_create(p->vdp_device, pe->vdp_profile,
- p->vid_width, p->vid_height, pe->maxrefs,
+ vdp_st = vdp->decoder_create(p->vdp_device, pe->hw_profile,
+ p->vid_width, p->vid_height, maxrefs,
&p->context.decoder);
CHECK_ST_WARNING("Failed creating VDPAU decoder");
p->context.render = p->vdp->decoder_render;
@@ -209,7 +226,7 @@ static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
{
if (!info || !info->vdpau_ctx)
return HWDEC_ERR_NO_CTX;
- if (!find_codec(mp_codec_to_av_codec_id(decoder)))
+ if (!hwdec_check_codec_support(decoder, profiles))
return HWDEC_ERR_NO_CODEC;
return 0;
}
diff --git a/video/vdpau_functions.inc b/video/vdpau_functions.inc
index bbac30dedf..1789768c83 100644
--- a/video/vdpau_functions.inc
+++ b/video/vdpau_functions.inc
@@ -12,6 +12,7 @@ VDP_FUNCTION(VdpBitmapSurfaceQueryCapabilities, VDP_FUNC_ID_BITMAP_SURFACE_QUERY
VDP_FUNCTION(VdpDecoderCreate, VDP_FUNC_ID_DECODER_CREATE, decoder_create)
VDP_FUNCTION(VdpDecoderDestroy, VDP_FUNC_ID_DECODER_DESTROY, decoder_destroy)
VDP_FUNCTION(VdpDecoderRender, VDP_FUNC_ID_DECODER_RENDER, decoder_render)
+VDP_FUNCTION(VdpDecoderQueryCapabilities, VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES, decoder_query_capabilities)
VDP_FUNCTION(VdpDeviceDestroy, VDP_FUNC_ID_DEVICE_DESTROY, device_destroy)
VDP_FUNCTION(VdpGenerateCSCMatrix, VDP_FUNC_ID_GENERATE_CSC_MATRIX, generate_csc_matrix)
VDP_FUNCTION(VdpOutputSurfaceCreate, VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, output_surface_create)