summaryrefslogtreecommitdiffstats
path: root/video/decode/vd_lavc.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/decode/vd_lavc.c')
-rw-r--r--video/decode/vd_lavc.c430
1 files changed, 295 insertions, 135 deletions
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index f965e00e5a..b971d26c10 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -18,24 +18,23 @@
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
-#include <pthread.h>
#include <assert.h>
#include <stdbool.h>
#include <libavcodec/avcodec.h>
+#include <libavformat/version.h>
#include <libavutil/common.h>
#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/pixdesc.h>
-#include "config.h"
-
#include "mpv_talloc.h"
#include "common/global.h"
#include "common/msg.h"
#include "options/m_config.h"
#include "options/options.h"
+#include "osdep/threads.h"
#include "misc/bstr.h"
#include "common/av_common.h"
#include "common/codecs.h"
@@ -63,28 +62,30 @@ static void uninit_avctx(struct mp_filter *vd);
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags);
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *pix_fmt);
-static int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
+static int hwdec_opt_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name);
#define HWDEC_DELAY_QUEUE_COUNT 2
#define OPT_BASE_STRUCT struct vd_lavc_params
struct vd_lavc_params {
- int fast;
- int show_all;
+ bool fast;
+ int film_grain;
+ bool show_all;
int skip_loop_filter;
int skip_idct;
int skip_frame;
int framedrop;
int threads;
- int bitexact;
- int old_x264;
- int check_hw_profile;
+ bool bitexact;
+ bool old_x264;
+ bool apply_cropping;
+ bool check_hw_profile;
int software_fallback;
char **avopts;
int dr;
- char *hwdec_api;
+ char **hwdec_api;
char *hwdec_codecs;
int hwdec_image_format;
int hwdec_extra_frames;
@@ -103,21 +104,26 @@ static const struct m_opt_choice_alternatives discard_names[] = {
const struct m_sub_options vd_lavc_conf = {
.opts = (const m_option_t[]){
- {"vd-lavc-fast", OPT_FLAG(fast)},
- {"vd-lavc-show-all", OPT_FLAG(show_all)},
+ {"vd-lavc-fast", OPT_BOOL(fast)},
+ {"vd-lavc-film-grain", OPT_CHOICE(film_grain,
+ {"auto", -1}, {"cpu", 0}, {"gpu", 1})},
+ {"vd-lavc-show-all", OPT_BOOL(show_all)},
{"vd-lavc-skiploopfilter", OPT_DISCARD(skip_loop_filter)},
{"vd-lavc-skipidct", OPT_DISCARD(skip_idct)},
{"vd-lavc-skipframe", OPT_DISCARD(skip_frame)},
{"vd-lavc-framedrop", OPT_DISCARD(framedrop)},
{"vd-lavc-threads", OPT_INT(threads), M_RANGE(0, DBL_MAX)},
- {"vd-lavc-bitexact", OPT_FLAG(bitexact)},
- {"vd-lavc-assume-old-x264", OPT_FLAG(old_x264)},
- {"vd-lavc-check-hw-profile", OPT_FLAG(check_hw_profile)},
+ {"vd-lavc-bitexact", OPT_BOOL(bitexact)},
+ {"vd-lavc-assume-old-x264", OPT_BOOL(old_x264)},
+ {"vd-lavc-check-hw-profile", OPT_BOOL(check_hw_profile)},
{"vd-lavc-software-fallback", OPT_CHOICE(software_fallback,
{"no", INT_MAX}, {"yes", 1}), M_RANGE(1, INT_MAX)},
{"vd-lavc-o", OPT_KEYVALUELIST(avopts)},
- {"vd-lavc-dr", OPT_FLAG(dr)},
- {"hwdec", OPT_STRING_VALIDATE(hwdec_api, hwdec_validate_opt),
+ {"vd-lavc-dr", OPT_CHOICE(dr,
+ {"auto", -1}, {"no", 0}, {"yes", 1})},
+ {"vd-apply-cropping", OPT_BOOL(apply_cropping)},
+ {"hwdec", OPT_STRINGLIST(hwdec_api),
+ .help = hwdec_opt_help,
.flags = M_OPT_OPTIONAL_PARAM | UPDATE_HWDEC},
{"hwdec-codecs", OPT_STRING(hwdec_codecs)},
{"hwdec-image-format", OPT_IMAGEFORMAT(hwdec_image_format)},
@@ -126,21 +132,22 @@ const struct m_sub_options vd_lavc_conf = {
},
.size = sizeof(struct vd_lavc_params),
.defaults = &(const struct vd_lavc_params){
- .show_all = 0,
- .check_hw_profile = 1,
+ .film_grain = -1 /*auto*/,
+ .check_hw_profile = true,
.software_fallback = 3,
.skip_loop_filter = AVDISCARD_DEFAULT,
.skip_idct = AVDISCARD_DEFAULT,
.skip_frame = AVDISCARD_DEFAULT,
.framedrop = AVDISCARD_NONREF,
- .dr = 1,
- .hwdec_api = "no",
- .hwdec_codecs = "h264,vc1,hevc,vp9,av1",
+ .dr = -1,
+ .hwdec_api = (char *[]){"no", NULL,},
+ .hwdec_codecs = "h264,vc1,hevc,vp8,vp9,av1,prores",
// Maximum number of surfaces the player wants to buffer. This number
// might require adjustment depending on whatever the player does;
// for example, if vo_gpu increases the number of reference surfaces for
// interpolation, this value has to be increased too.
.hwdec_extra_frames = 6,
+ .apply_cropping = true,
},
};
@@ -167,8 +174,11 @@ typedef struct lavc_ctx {
struct mp_codec_params *codec;
AVCodecContext *avctx;
AVFrame *pic;
+ AVPacket *avpkt;
bool use_hwdec;
struct hwdec_info hwdec; // valid only if use_hwdec==true
+ bstr *attempted_hwdecs;
+ int num_attempted_hwdecs;
AVRational codec_timebase;
enum AVDiscard skip_frame;
bool flushing;
@@ -176,6 +186,7 @@ typedef struct lavc_ctx {
const char *decoder;
bool hwdec_failed;
bool hwdec_notified;
+ bool force_eof;
bool intra_only;
int framedrop_flags;
@@ -206,7 +217,7 @@ typedef struct lavc_ctx {
AVBufferRef *cached_hw_frames_ctx;
// --- The following fields are protected by dr_lock.
- pthread_mutex_t dr_lock;
+ mp_mutex dr_lock;
bool dr_failed;
struct mp_image_pool *dr_pool;
int dr_imgfmt, dr_w, dr_h, dr_stride_align;
@@ -228,16 +239,20 @@ struct autoprobe_info {
const struct autoprobe_info hwdec_autoprobe_info[] = {
{"d3d11va", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"dxva2", HWDEC_FLAG_AUTO},
- {"dxva2-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"d3d11va-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
+ {"dxva2-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"nvdec", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"nvdec-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"vaapi", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"vaapi-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"vdpau", HWDEC_FLAG_AUTO},
{"vdpau-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
+ {"drm", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
+ {"drm-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"mmal", HWDEC_FLAG_AUTO},
{"mmal-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
+ {"mediacodec", HWDEC_FLAG_AUTO},
+ {"mediacodec-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"videotoolbox", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{"videotoolbox-copy", HWDEC_FLAG_AUTO | HWDEC_FLAG_WHITELIST},
{0}
@@ -257,9 +272,12 @@ static int hwdec_compare(const void *p1, const void *p2)
// List non-copying entries first, so --hwdec=auto takes them.
if (h1->copying != h2->copying)
return h1->copying ? 1 : -1;
- // Order by autoprobe preferrence order.
+ // Order by autoprobe preference order.
if (h1->auto_pos != h2->auto_pos)
return h1->auto_pos > h2->auto_pos ? 1 : -1;
+ // Put hwdecs without hw_device_ctx last
+ if ((!!h1->lavc_device) != (!!h2->lavc_device))
+ return h1->lavc_device ? -1 : 1;
// Fallback sort order to make sorting stable.
return h1->rank > h2->rank ? 1 :-1;
}
@@ -430,8 +448,18 @@ static AVBufferRef *hwdec_create_dev(struct mp_filter *vd,
return ref;
}
} else if (ctx->hwdec_devs) {
- hwdec_devices_request_all(ctx->hwdec_devs);
- return hwdec_devices_get_lavc(ctx->hwdec_devs, hwdec->lavc_device);
+ int imgfmt = pixfmt2imgfmt(hwdec->pix_fmt);
+ struct hwdec_imgfmt_request params = {
+ .imgfmt = imgfmt,
+ .probing = autoprobe,
+ };
+ hwdec_devices_request_for_img_fmt(ctx->hwdec_devs, &params);
+
+ const struct mp_hwdec_ctx *hw_ctx =
+ hwdec_devices_get_by_imgfmt(ctx->hwdec_devs, imgfmt);
+
+ if (hw_ctx && hw_ctx->av_device_ref)
+ return av_buffer_ref(hw_ctx->av_device_ref);
}
return NULL;
@@ -445,83 +473,119 @@ static void select_and_set_hwdec(struct mp_filter *vd)
m_config_cache_update(ctx->opts_cache);
- bstr opt = bstr0(ctx->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_safe = bstr_equals0(opt, "auto-safe") ||
- bstr_equals0(opt, "auto-copy-safe");
- bool hwdec_auto_copy = bstr_equals0(opt, "auto-copy") ||
- bstr_equals0(opt, "auto-copy-safe");
- bool hwdec_auto = hwdec_auto_all || hwdec_auto_copy || hwdec_auto_safe;
-
- if (!hwdec_requested) {
- MP_VERBOSE(vd, "No hardware decoding requested.\n");
- } else if (!hwdec_codec_allowed(vd, codec)) {
- MP_VERBOSE(vd, "Not trying to use hardware decoding: codec %s is not "
- "on whitelist.\n", codec);
- } else {
- 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];
+ struct hwdec_info *hwdecs = NULL;
+ int num_hwdecs = 0;
+ add_all_hwdec_methods(&hwdecs, &num_hwdecs);
+
+ char **hwdec_api = ctx->opts->hwdec_api;
+ for (int i = 0; hwdec_api[i]; i++) {
+ bstr opt = bstr0(hwdec_api[i]);
+
+ bool hwdec_requested = !bstr_equals0(opt, "no");
+ bool hwdec_auto_all = bstr_equals0(opt, "auto") ||
+ bstr_equals0(opt, "");
+ bool hwdec_auto_safe = bstr_equals0(opt, "auto-safe") ||
+ bstr_equals0(opt, "auto-copy-safe") ||
+ bstr_equals0(opt, "yes");
+ bool hwdec_auto_copy = bstr_equals0(opt, "auto-copy") ||
+ bstr_equals0(opt, "auto-copy-safe");
+ bool hwdec_auto = hwdec_auto_all || hwdec_auto_copy || hwdec_auto_safe;
+
+ if (!hwdec_requested) {
+ MP_VERBOSE(vd, "No hardware decoding requested.\n");
+ break;
+ } else if (!hwdec_codec_allowed(vd, codec)) {
+ MP_VERBOSE(vd, "Not trying to use hardware decoding: codec %s is not "
+ "on whitelist.\n", codec);
+ break;
+ } else {
+ bool hwdec_name_supported = false; // relevant only if !hwdec_auto
+ 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) ||
+ bstr_equals0(opt, hwdec->name)))
+ continue;
+ hwdec_name_supported = true;
+
+ bool already_attempted = false;
+ for (int j = 0; j < ctx->num_attempted_hwdecs; j++) {
+ if (bstr_equals0(ctx->attempted_hwdecs[j], hwdec->name)) {
+ MP_DBG(vd, "Skipping previously attempted hwdec: %s\n",
+ hwdec->name);
+ already_attempted = true;
+ break;
+ }
+ }
+ if (already_attempted)
+ continue;
- if (!hwdec_auto && !(bstr_equals0(opt, hwdec->method_name) ||
- bstr_equals0(opt, hwdec->name)))
- continue;
+ 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_safe && !(hwdec->flags & HWDEC_FLAG_WHITELIST))
- continue;
+ if (hwdec_auto_safe && !(hwdec->flags & HWDEC_FLAG_WHITELIST))
+ continue;
- MP_VERBOSE(vd, "Looking at hwdec %s...\n", hwdec->name);
+ 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;
- }
+ /*
+ * Past this point, any kind of failure that results in us
+ * looking for a new hwdec should not lead to use trying this
+ * hwdec again - so add it to the list, regardless of whether
+ * initialisation will succeed or not.
+ */
+ MP_TARRAY_APPEND(ctx, ctx->attempted_hwdecs,
+ ctx->num_attempted_hwdecs,
+ bstrdup(ctx, bstr0(hwdec->name)));
- 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");
+ if (hwdec_auto_copy && !hwdec->copying) {
+ MP_VERBOSE(vd, "Not using this for auto-copy.\n");
continue;
}
- const struct hwcontext_fns *fns =
- hwdec_get_hwcontext_fns(hwdec->lavc_device);
- if (fns && fns->is_emulated && fns->is_emulated(ctx->hwdec_dev)) {
- if (hwdec_auto) {
- MP_VERBOSE(vd, "Not using emulated API.\n");
- av_buffer_unref(&ctx->hwdec_dev);
+ 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;
}
- MP_WARN(vd, "Using emulated hardware decoding API.\n");
+
+ const struct hwcontext_fns *fns =
+ hwdec_get_hwcontext_fns(hwdec->lavc_device);
+ if (fns && fns->is_emulated && fns->is_emulated(ctx->hwdec_dev)) {
+ if (hwdec_auto) {
+ MP_VERBOSE(vd, "Not using emulated API.\n");
+ av_buffer_unref(&ctx->hwdec_dev);
+ continue;
+ }
+ MP_WARN(vd, "Using emulated hardware decoding API.\n");
+ }
+ } else if (!hwdec->copying) {
+ // Most likely METHOD_INTERNAL, which often use delay-loaded
+ // VO support as well.
+ if (ctx->hwdec_devs) {
+ struct hwdec_imgfmt_request params = {
+ .imgfmt = pixfmt2imgfmt(hwdec->pix_fmt),
+ .probing = hwdec_auto,
+ };
+ hwdec_devices_request_for_img_fmt(
+ ctx->hwdec_devs, &params);
+ }
}
- } else if (!hwdec->copying) {
- // Most likely METHOD_INTERNAL, which often use delay-loaded
- // VO support as well.
- if (ctx->hwdec_devs)
- hwdec_devices_request_all(ctx->hwdec_devs);
- }
- ctx->use_hwdec = true;
- ctx->hwdec = *hwdec;
- break;
+ ctx->use_hwdec = true;
+ ctx->hwdec = *hwdec;
+ break;
+ }
+ if (ctx->use_hwdec)
+ break;
+ else if (!hwdec_auto && !hwdec_name_supported)
+ MP_WARN(vd, "Unsupported hwdec: %.*s\n", BSTR_P(opt));
}
-
- talloc_free(hwdecs);
-
- if (!ctx->use_hwdec)
- MP_VERBOSE(vd, "No hardware decoding available for this codec.\n");
}
+ talloc_free(hwdecs);
+
if (ctx->use_hwdec) {
MP_VERBOSE(vd, "Trying hardware decoding via %s.\n", ctx->hwdec.name);
@@ -529,37 +593,41 @@ static void select_and_set_hwdec(struct mp_filter *vd)
MP_VERBOSE(vd, "Using underlying hw-decoder '%s'\n",
ctx->hwdec.codec->name);
} else {
- MP_VERBOSE(vd, "Using software decoding.\n");
+ // If software fallback is disabled and we get here, all hwdec must
+ // have failed. Tell the ctx to always force an eof.
+ if (ctx->opts->software_fallback == INT_MAX) {
+ MP_WARN(ctx, "Software decoding fallback is disabled.\n");
+ ctx->force_eof = true;
+ } else {
+ MP_VERBOSE(vd, "Using software decoding.\n");
+ }
}
}
-static int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
+static int hwdec_opt_help(struct mp_log *log, const m_option_t *opt,
+ struct bstr name)
{
- if (bstr_equals0(param, "help")) {
- struct hwdec_info *hwdecs = NULL;
- int num_hwdecs = 0;
- add_all_hwdec_methods(&hwdecs, &num_hwdecs);
+ struct hwdec_info *hwdecs = NULL;
+ int num_hwdecs = 0;
+ add_all_hwdec_methods(&hwdecs, &num_hwdecs);
- mp_info(log, "Valid values (with alternative full names):\n");
+ mp_info(log, "Valid values (with alternative full names):\n");
- for (int n = 0; n < num_hwdecs; n++) {
- struct hwdec_info *hwdec = &hwdecs[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);
- }
+ mp_info(log, " %s (%s)\n", hwdec->method_name, hwdec->name);
+ }
- talloc_free(hwdecs);
+ talloc_free(hwdecs);
- mp_info(log, " auto (yes '')\n");
- mp_info(log, " no\n");
- mp_info(log, " auto-safe\n");
- mp_info(log, " auto-copy\n");
- mp_info(log, " auto-copy-safe\n");
+ mp_info(log, " auto (yes '')\n");
+ mp_info(log, " no\n");
+ mp_info(log, " auto-safe\n");
+ mp_info(log, " auto-copy\n");
+ mp_info(log, " auto-copy-safe\n");
- return M_OPT_EXIT;
- }
- return 0;
+ return M_OPT_EXIT;
}
static void force_fallback(struct mp_filter *vd)
@@ -568,7 +636,9 @@ static void force_fallback(struct mp_filter *vd)
uninit_avctx(vd);
int lev = ctx->hwdec_notified ? MSGL_WARN : MSGL_V;
- mp_msg(vd->log, lev, "Falling back to software decoding.\n");
+ mp_msg(vd->log, lev, "Attempting next decoding method after failure of %.*s.\n",
+ BSTR_P(ctx->attempted_hwdecs[ctx->num_attempted_hwdecs - 1]));
+ select_and_set_hwdec(vd);
init_avctx(vd);
}
@@ -578,12 +648,25 @@ static void reinit(struct mp_filter *vd)
uninit_avctx(vd);
+ /*
+ * Reset attempted hwdecs so that if the hwdec list is reconfigured
+ * we attempt all of them from the beginning. The most practical
+ * reason for this is that ctrl+h toggles between `no` and
+ * `auto-safe`, and we want to reevaluate from a clean slate each time.
+ */
+ TA_FREEP(&ctx->attempted_hwdecs);
+ ctx->num_attempted_hwdecs = 0;
+ ctx->hwdec_notified = false;
+
select_and_set_hwdec(vd);
bool use_hwdec = ctx->use_hwdec;
init_avctx(vd);
- if (!ctx->avctx && use_hwdec)
- force_fallback(vd);
+ if (!ctx->avctx && use_hwdec) {
+ do {
+ force_fallback(vd);
+ } while (!ctx->avctx);
+ }
}
static void init_avctx(struct mp_filter *vd)
@@ -629,6 +712,10 @@ static void init_avctx(struct mp_filter *vd)
if (!ctx->pic)
goto error;
+ ctx->avpkt = av_packet_alloc();
+ if (!ctx->avpkt)
+ goto error;
+
if (ctx->use_hwdec) {
avctx->opaque = vd;
avctx->thread_count = 1;
@@ -636,6 +723,17 @@ static void init_avctx(struct mp_filter *vd)
if (!lavc_param->check_hw_profile)
avctx->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH;
+#ifdef AV_HWACCEL_FLAG_UNSAFE_OUTPUT
+ /*
+ * This flag primarily exists for nvdec which has a very limited
+ * output frame pool, which can get exhausted if consumers don't
+ * release frames quickly. However, as an implementation
+ * requirement, we have to copy the frames anyway, so we don't
+ * need this extra implicit copy.
+ */
+ avctx->hwaccel_flags |= AV_HWACCEL_FLAG_UNSAFE_OUTPUT;
+#endif
+
if (ctx->hwdec.use_hw_device) {
if (ctx->hwdec_dev)
avctx->hw_device_ctx = av_buffer_ref(ctx->hwdec_dev);
@@ -661,7 +759,11 @@ static void init_avctx(struct mp_filter *vd)
if (!ctx->use_hwdec && ctx->vo && lavc_param->dr) {
avctx->opaque = vd;
avctx->get_buffer2 = get_buffer2_direct;
- avctx->thread_safe_callbacks = 1;
+#if LIBAVCODEC_VERSION_MAJOR < 60
+ AV_NOWARN_DEPRECATED({
+ avctx->thread_safe_callbacks = 1;
+ });
+#endif
}
avctx->flags |= lavc_param->bitexact ? AV_CODEC_FLAG_BITEXACT : 0;
@@ -673,10 +775,41 @@ static void init_avctx(struct mp_filter *vd)
avctx->skip_loop_filter = lavc_param->skip_loop_filter;
avctx->skip_idct = lavc_param->skip_idct;
avctx->skip_frame = lavc_param->skip_frame;
+ avctx->apply_cropping = lavc_param->apply_cropping;
if (lavc_codec->id == AV_CODEC_ID_H264 && lavc_param->old_x264)
av_opt_set(avctx, "x264_build", "150", AV_OPT_SEARCH_CHILDREN);
+#ifndef AV_CODEC_EXPORT_DATA_FILM_GRAIN
+ if (ctx->opts->film_grain == 1)
+ MP_WARN(vd, "GPU film grain requested, but FFmpeg too old to expose "
+ "film grain parameters. Please update to latest master, "
+ "or at least to release 4.4.\n");
+#else
+ switch(ctx->opts->film_grain) {
+ case 0: /*CPU*/
+ // default lavc flags handle film grain within the decoder.
+ break;
+ case 1: /*GPU*/
+ if (!ctx->vo ||
+ (ctx->vo && !(ctx->vo->driver->caps & VO_CAP_FILM_GRAIN))) {
+ MP_MSG(vd, ctx->vo ? MSGL_WARN : MSGL_V,
+ "GPU film grain requested, but VO %s, expect wrong output.\n",
+ ctx->vo ?
+ "does not support applying film grain" :
+ "is not available at decoder initialization to verify support");
+ }
+
+ avctx->export_side_data |= AV_CODEC_EXPORT_DATA_FILM_GRAIN;
+ break;
+ default:
+ if (ctx->vo && (ctx->vo->driver->caps & VO_CAP_FILM_GRAIN))
+ avctx->export_side_data |= AV_CODEC_EXPORT_DATA_FILM_GRAIN;
+
+ break;
+ }
+#endif
+
mp_set_avopts(vd->log, avctx, lavc_param->avopts);
// Do this after the above avopt handling in case it changes values
@@ -696,9 +829,8 @@ static void init_avctx(struct mp_filter *vd)
// x264 build number (encoded in a SEI element), needed to enable a
// workaround for broken 4:4:4 streams produced by older x264 versions.
if (lavc_codec->id == AV_CODEC_ID_H264 && c->first_packet) {
- AVPacket avpkt;
- mp_set_av_packet(&avpkt, c->first_packet, &ctx->codec_timebase);
- avcodec_send_packet(avctx, &avpkt);
+ mp_set_av_packet(ctx->avpkt, c->first_packet, &ctx->codec_timebase);
+ avcodec_send_packet(avctx, ctx->avpkt);
avcodec_receive_frame(avctx, ctx->pic);
av_frame_unref(ctx->pic);
avcodec_flush_buffers(ctx->avctx);
@@ -746,6 +878,7 @@ static void uninit_avctx(struct mp_filter *vd)
flush_all(vd);
av_frame_free(&ctx->pic);
+ mp_free_av_packet(&ctx->avpkt);
av_buffer_unref(&ctx->cached_hw_frames_ctx);
avcodec_free_context(&ctx->avctx);
@@ -876,7 +1009,7 @@ static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
struct mp_filter *vd = avctx->opaque;
vd_ffmpeg_ctx *p = vd->priv;
- pthread_mutex_lock(&p->dr_lock);
+ mp_mutex_lock(&p->dr_lock);
int w = pic->width;
int h = pic->height;
@@ -889,6 +1022,13 @@ static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++)
stride_align = MPMAX(stride_align, linesize_align[n]);
+ // Note: texel sizes may be NPOT, so use full lcm instead of max
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pic->format);
+ if (!(desc->flags & AV_PIX_FMT_FLAG_BITSTREAM)) {
+ for (int n = 0; n < desc->nb_components; n++)
+ stride_align = mp_lcm(stride_align, desc->comp[n].step);
+ }
+
int imgfmt = pixfmt2imgfmt(pic->format);
if (!imgfmt)
goto fallback;
@@ -912,8 +1052,10 @@ static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
struct mp_image *img = mp_image_pool_get_no_alloc(p->dr_pool, imgfmt, w, h);
if (!img) {
- MP_DBG(p, "Allocating new DR image...\n");
- img = vo_get_image(p->vo, imgfmt, w, h, stride_align);
+ bool host_cached = p->opts->dr == -1; // auto
+ int dr_flags = host_cached ? VO_DR_FLAG_HOST_CACHED : 0;
+ MP_DBG(p, "Allocating new%s DR image...\n", host_cached ? " (host-cached)" : "");
+ img = vo_get_image(p->vo, imgfmt, w, h, stride_align, dr_flags);
if (!img) {
MP_DBG(p, "...failed..\n");
goto fallback;
@@ -939,7 +1081,7 @@ static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
}
talloc_free(img);
- pthread_mutex_unlock(&p->dr_lock);
+ mp_mutex_unlock(&p->dr_lock);
return 0;
@@ -947,7 +1089,7 @@ fallback:
if (!p->dr_failed)
MP_VERBOSE(p, "DR failed - disabling.\n");
p->dr_failed = true;
- pthread_mutex_unlock(&p->dr_lock);
+ mp_mutex_unlock(&p->dr_lock);
return avcodec_default_get_buffer2(avctx, pic, flags);
}
@@ -1011,10 +1153,9 @@ static int send_packet(struct mp_filter *vd, struct demux_packet *pkt)
if (avctx->skip_frame == AVDISCARD_ALL)
return 0;
- AVPacket avpkt;
- mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase);
+ mp_set_av_packet(ctx->avpkt, pkt, &ctx->codec_timebase);
- int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL);
+ int ret = avcodec_send_packet(avctx, pkt ? ctx->avpkt : NULL);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return ret;
@@ -1035,7 +1176,6 @@ static void send_queued_packet(struct mp_filter *vd)
vd_ffmpeg_ctx *ctx = vd->priv;
assert(ctx->num_requeue_packets);
- assert(!ctx->hw_probing);
if (send_packet(vd, ctx->requeue_packets[0]) != AVERROR(EAGAIN)) {
talloc_free(ctx->requeue_packets[0]);
@@ -1049,7 +1189,7 @@ static int decode_frame(struct mp_filter *vd)
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
- if (!avctx)
+ if (!avctx || ctx->force_eof)
return AVERROR_EOF;
prepare_decoding(vd);
@@ -1098,7 +1238,11 @@ static int decode_frame(struct mp_filter *vd)
mpi->dts = mp_pts_from_av(ctx->pic->pkt_dts, &ctx->codec_timebase);
mpi->pkt_duration =
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(59, 30, 100)
+ mp_pts_from_av(ctx->pic->duration, &ctx->codec_timebase);
+#else
mp_pts_from_av(ctx->pic->pkt_duration, &ctx->codec_timebase);
+#endif
av_frame_unref(ctx->pic);
@@ -1113,13 +1257,25 @@ static int receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
int ret = decode_frame(vd);
if (ctx->hwdec_failed) {
- // Failed hardware decoding? Try again in software.
+ // Failed hardware decoding? Try the next one, and eventually software.
struct demux_packet **pkts = ctx->sent_packets;
int num_pkts = ctx->num_sent_packets;
ctx->sent_packets = NULL;
ctx->num_sent_packets = 0;
- force_fallback(vd);
+ /*
+ * We repeatedly force_fallback until we get an avctx, because there are
+ * certain hwdecs that are really full decoders, and so if these fail,
+ * they also fail to give us a valid avctx, and the early return path
+ * here will simply give up on decoding completely if there is no
+ * decoder. We should never hit an infinite loop as the hwdec list is
+ * finite and we will eventually exhaust it and fall back to software
+ * decoding (and in practice, most hwdecs are hwaccels and so the
+ * decoder will successfully init even if the hwaccel fails later.)
+ */
+ do {
+ force_fallback(vd);
+ } while (!ctx->avctx);
ctx->requeue_packets = pkts;
ctx->num_requeue_packets = num_pkts;
@@ -1130,11 +1286,11 @@ static int receive_frame(struct mp_filter *vd, struct mp_frame *out_frame)
if (ret == AVERROR(EAGAIN) && ctx->num_requeue_packets)
return 0; // force retry, so send_queued_packet() gets called
- if (!ctx->num_delay_queue)
+ if (ctx->num_delay_queue <= ctx->max_delay_queue && ret != AVERROR_EOF)
return ret;
- if (ctx->num_delay_queue <= ctx->max_delay_queue && ret != AVERROR_EOF)
- return AVERROR(EAGAIN);
+ if (!ctx->num_delay_queue)
+ return ret;
struct mp_image *res = ctx->delay_queue[0];
MP_TARRAY_REMOVE_AT(ctx->delay_queue, ctx->num_delay_queue, 0);
@@ -1183,6 +1339,10 @@ static int control(struct mp_filter *vd, enum dec_ctrl cmd, void *arg)
case VDCTRL_SET_FRAMEDROP:
ctx->framedrop_flags = *(int *)arg;
return CONTROL_TRUE;
+ case VDCTRL_CHECK_FORCED_EOF: {
+ *(bool *)arg = ctx->force_eof;
+ return CONTROL_TRUE;
+ }
case VDCTRL_GET_BFRAMES: {
AVCodecContext *avctx = ctx->avctx;
if (!avctx)
@@ -1232,7 +1392,7 @@ static void destroy(struct mp_filter *vd)
uninit_avctx(vd);
- pthread_mutex_destroy(&ctx->dr_lock);
+ mp_mutex_destroy(&ctx->dr_lock);
}
static const struct mp_filter_info vd_lavc_filter = {
@@ -1268,7 +1428,7 @@ static struct mp_decoder *create(struct mp_filter *parent,
ctx->public.f = vd;
ctx->public.control = control;
- pthread_mutex_init(&ctx->dr_lock, NULL);
+ mp_mutex_init(&ctx->dr_lock);
// hwdec/DR
struct mp_stream_info *info = mp_filter_find_stream_info(vd);