1
0
mirror of https://github.com/mpv-player/mpv synced 2025-01-20 06:11:10 +00:00
mpv/video/decode/vd_lavc.c
wm4 83ab873497 video: change some mp_image_pool semantics
Remove the max_count creation parameter, because it's pointless and
rarely ever did anything. Add a talloc parent parameter instead (which
is something completely different, but convenient, and all callers needs
to be changed anyway).

Instead of clearing the pool when the now removed maximum is reached,
clear it on image parameter changes instead.
2018-01-13 03:26:45 -08:00

1190 lines
36 KiB
C

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <assert.h>
#include <stdbool.h>
#include <libavcodec/avcodec.h>
#include <libavutil/common.h>
#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/pixdesc.h>
#include "mpv_talloc.h"
#include "config.h"
#include "common/msg.h"
#include "options/options.h"
#include "misc/bstr.h"
#include "common/av_common.h"
#include "common/codecs.h"
#include "video/fmt-conversion.h"
#include "vd.h"
#include "video/hwdec.h"
#include "video/img_format.h"
#include "video/filter/vf.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
#include "video/decode/dec_video.h"
#include "demux/demux.h"
#include "demux/stheader.h"
#include "demux/packet.h"
#include "video/csputils.h"
#include "video/sws_utils.h"
#include "video/out/vo.h"
#include "options/m_option.h"
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);
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *pix_fmt);
#define HWDEC_DELAY_QUEUE_COUNT 2
// Maximum number of surfaces the player wants to buffer.
// This number might require adjustment depending on whatever the player does;
// for example, if vo_opengl increases the number of reference surfaces for
// interpolation, this value has to be increased too.
#define HWDEC_EXTRA_SURFACES 6
#define OPT_BASE_STRUCT struct vd_lavc_params
struct vd_lavc_params {
int fast;
int 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;
int software_fallback;
char **avopts;
int dr;
};
static const struct m_opt_choice_alternatives discard_names[] = {
{"none", AVDISCARD_NONE},
{"default", AVDISCARD_DEFAULT},
{"nonref", AVDISCARD_NONREF},
{"bidir", AVDISCARD_BIDIR},
{"nonkey", AVDISCARD_NONKEY},
{"all", AVDISCARD_ALL},
{0}
};
#define OPT_DISCARD(name, field, flags) \
OPT_GENERAL(int, name, field, flags, .type = CONF_TYPE_CHOICE, \
.priv = (void *)discard_names)
const struct m_sub_options vd_lavc_conf = {
.opts = (const m_option_t[]){
OPT_FLAG("fast", fast, 0),
OPT_FLAG("show-all", show_all, 0),
OPT_DISCARD("skiploopfilter", skip_loop_filter, 0),
OPT_DISCARD("skipidct", skip_idct, 0),
OPT_DISCARD("skipframe", skip_frame, 0),
OPT_DISCARD("framedrop", framedrop, 0),
OPT_INT("threads", threads, M_OPT_MIN, .min = 0),
OPT_FLAG("bitexact", bitexact, 0),
OPT_FLAG("assume-old-x264", old_x264, 0),
OPT_FLAG("check-hw-profile", check_hw_profile, 0),
OPT_CHOICE_OR_INT("software-fallback", software_fallback, 0, 1, INT_MAX,
({"no", INT_MAX}, {"yes", 1})),
OPT_KEYVALUELIST("o", avopts, 0),
OPT_FLAG("dr", dr, 0),
{0}
},
.size = sizeof(struct vd_lavc_params),
.defaults = &(const struct vd_lavc_params){
.show_all = 0,
.check_hw_profile = 1,
.software_fallback = 3,
.skip_loop_filter = AVDISCARD_DEFAULT,
.skip_idct = AVDISCARD_DEFAULT,
.skip_frame = AVDISCARD_DEFAULT,
.framedrop = AVDISCARD_NONREF,
},
};
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;
bool use_hwdec;
struct hwdec_info hwdec; // valid only if use_hwdec==true
AVRational codec_timebase;
enum AVDiscard skip_frame;
bool flushing;
const char *decoder;
bool hwdec_requested;
bool hwdec_failed;
bool hwdec_notified;
bool intra_only;
int framedrop_flags;
bool hw_probing;
struct demux_packet **sent_packets;
int num_sent_packets;
struct demux_packet **requeue_packets;
int num_requeue_packets;
struct mp_image **delay_queue;
int num_delay_queue;
int max_delay_queue;
// From VO
struct mp_hwdec_devices *hwdec_devs;
// Wrapped AVHWDeviceContext* used for decoding.
AVBufferRef *hwdec_dev;
bool hwdec_request_reinit;
int hwdec_fail_count;
struct mp_image_pool *hwdec_swpool;
AVBufferRef *cached_hw_frames_ctx;
// --- The following fields are protected by dr_lock.
pthread_mutex_t dr_lock;
bool dr_failed;
struct mp_image_pool *dr_pool;
int dr_imgfmt, dr_w, dr_h, dr_stride_align;
} vd_ffmpeg_ctx;
// 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 int hwdec_compare(const void *p1, const void *p2)
{
struct hwdec_info *h1 = (void *)p1;
struct hwdec_info *h2 = (void *)p2;
if (h1 == h2)
return 0;
// Strictly put non-preferred hwdecs to the end of the list.
if ((h1->auto_pos == INT_MAX) != (h2->auto_pos == INT_MAX))
return h1->auto_pos == INT_MAX ? 1 : -1;
// 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.
if (h1->auto_pos != h2->auto_pos)
return h1->auto_pos > h2->auto_pos ? 1 : -1;
// Fallback sort order to make sorting stable.
return h1->rank > h2->rank ? 1 :-1;
}
// (This takes care of some bookkeeping too, like setting info.name)
static void add_hwdec_item(struct hwdec_info **infos, int *num_infos,
struct hwdec_info info)
{
if (info.copying)
mp_snprintf_cat(info.method_name, sizeof(info.method_name), "-copy");
// (Including the codec name in case this is a wrapper looks pretty dumb,
// but better not have them clash with hwaccels and others.)
snprintf(info.name, sizeof(info.name), "%s-%s",
info.codec->name, info.method_name);
info.rank = *num_infos;
info.auto_pos = INT_MAX;
for (int x = 0; hwdec_autoprobe_order[x]; x++) {
if (strcmp(hwdec_autoprobe_order[x], info.method_name) == 0)
info.auto_pos = x;
}
MP_TARRAY_APPEND(NULL, *infos, *num_infos, info);
}
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 (codec->type != AVMEDIA_TYPE_VIDEO || !av_codec_is_decoder(codec))
continue;
struct hwdec_info info_template = {
.pix_fmt = AV_PIX_FMT_NONE,
.codec = codec,
};
const char *wrapper = NULL;
if (codec->capabilities & (AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_HYBRID))
wrapper = codec->wrapper_name;
// 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 ((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);
// 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;
}
// Direct variant.
add_hwdec_item(infos, num_infos, info);
// 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;
}
add_hwdec_item(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);
// Direct variant.
add_hwdec_item(infos, num_infos, info);
// Copy variant.
info.copying = true;
info.pix_fmt = AV_PIX_FMT_NONE; // trust it can do sw output
add_hwdec_item(infos, num_infos, info);
found_any = true;
}
}
if (!found_any && wrapper) {
// We _know_ there's something supported here, usually outputting
// sw surfaces. E.g. mediacodec (before hw_device_ctx support).
struct hwdec_info info = info_template;
info.copying = true; // probably
snprintf(info.method_name, sizeof(info.method_name), "%s", wrapper);
add_hwdec_item(infos, num_infos, info);
}
}
qsort(*infos, *num_infos, sizeof(struct hwdec_info), hwdec_compare);
}
static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
{
bstr s = bstr0(vd->opts->hwdec_codecs);
while (s.len) {
bstr item;
bstr_split_tok(s, ",", &item, &s);
if (bstr_equals0(item, "all") || bstr_equals0(item, codec))
return true;
}
return false;
}
static AVBufferRef *hwdec_create_dev(struct dec_video *vd,
struct hwdec_info *hwdec,
bool autoprobe)
{
assert(hwdec->lavc_device);
if (hwdec->copying) {
const struct hwcontext_fns *fns =
hwdec_get_hwcontext_fns(hwdec->lavc_device);
if (fns && fns->create_dev) {
struct hwcontext_create_dev_params params = {
.probing = autoprobe,
};
return fns->create_dev(vd->global, vd->log, &params);
} else {
AVBufferRef* ref = NULL;
av_hwdevice_ctx_create(&ref, hwdec->lavc_device, NULL, NULL, 0);
return ref;
}
} else if (vd->hwdec_devs) {
hwdec_devices_request_all(vd->hwdec_devs);
return hwdec_devices_get_lavc(vd->hwdec_devs, hwdec->lavc_device);
}
return NULL;
}
// 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;
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);
ctx->hwdec_requested = true;
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;
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;
}
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 (vd->hwdec_devs)
hwdec_devices_request_all(vd->hwdec_devs);
}
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 (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");
}
}
int hwdec_validate_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param)
{
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 (with alternative full names):\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 0;
}
static void uninit(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
uninit_avctx(vd);
pthread_mutex_destroy(&ctx->dr_lock);
talloc_free(vd->priv);
}
static void force_fallback(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
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);
}
static void reinit(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
uninit_avctx(vd);
select_and_set_hwdec(vd);
bool use_hwdec = ctx->use_hwdec;
init_avctx(vd);
if (!ctx->avctx && use_hwdec)
force_fallback(vd);
}
static int init(struct dec_video *vd, const char *decoder)
{
vd_ffmpeg_ctx *ctx;
ctx = vd->priv = talloc_zero(NULL, vd_ffmpeg_ctx);
ctx->log = vd->log;
ctx->opts = vd->opts;
ctx->decoder = talloc_strdup(ctx, decoder);
ctx->hwdec_devs = vd->hwdec_devs;
ctx->hwdec_swpool = mp_image_pool_new(ctx);
ctx->dr_pool = mp_image_pool_new(ctx);
pthread_mutex_init(&ctx->dr_lock, NULL);
reinit(vd);
if (!ctx->avctx) {
uninit(vd);
return 0;
}
return 1;
}
static void init_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
struct vd_lavc_params *lavc_param = vd->opts->vd_lavc_params;
struct mp_codec_params *c = vd->codec;
assert(!ctx->avctx);
const AVCodec *lavc_codec = NULL;
if (ctx->use_hwdec) {
lavc_codec = ctx->hwdec.codec;
} else {
lavc_codec = avcodec_find_decoder_by_name(ctx->decoder);
}
if (!lavc_codec)
return;
const AVCodecDescriptor *desc = avcodec_descriptor_get(lavc_codec->id);
ctx->intra_only = desc && (desc->props & AV_CODEC_PROP_INTRA_ONLY);
ctx->codec_timebase = mp_get_codec_timebase(vd->codec);
// This decoder does not read pkt_timebase correctly yet.
if (strstr(lavc_codec->name, "_mmal"))
ctx->codec_timebase = (AVRational){1, 1000000};
ctx->hwdec_failed = false;
ctx->hwdec_request_reinit = false;
ctx->avctx = avcodec_alloc_context3(lavc_codec);
AVCodecContext *avctx = ctx->avctx;
if (!ctx->avctx)
goto error;
avctx->codec_type = AVMEDIA_TYPE_VIDEO;
avctx->codec_id = lavc_codec->id;
#if LIBAVCODEC_VERSION_MICRO >= 100
avctx->pkt_timebase = ctx->codec_timebase;
#endif
ctx->pic = av_frame_alloc();
if (!ctx->pic)
goto error;
if (ctx->use_hwdec) {
avctx->opaque = vd;
avctx->thread_count = 1;
avctx->hwaccel_flags |= AV_HWACCEL_FLAG_IGNORE_LEVEL;
if (!lavc_param->check_hw_profile)
avctx->hwaccel_flags |= AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH;
if (ctx->hwdec.use_hw_device) {
if (ctx->hwdec_dev)
avctx->hw_device_ctx = av_buffer_ref(ctx->hwdec_dev);
if (!avctx->hw_device_ctx)
goto error;
}
if (ctx->hwdec.use_hw_frames) {
if (!ctx->hwdec_dev)
goto error;
}
if (ctx->hwdec.pix_fmt != AV_PIX_FMT_NONE)
avctx->get_format = get_format_hwdec;
// Some APIs benefit from this, for others it's additional bloat.
if (ctx->hwdec.copying)
ctx->max_delay_queue = HWDEC_DELAY_QUEUE_COUNT;
ctx->hw_probing = true;
} else {
mp_set_avcodec_threads(vd->log, avctx, lavc_param->threads);
}
if (!ctx->use_hwdec && vd->vo && lavc_param->dr) {
avctx->opaque = vd;
avctx->get_buffer2 = get_buffer2_direct;
avctx->thread_safe_callbacks = 1;
}
avctx->flags |= lavc_param->bitexact ? AV_CODEC_FLAG_BITEXACT : 0;
avctx->flags2 |= lavc_param->fast ? AV_CODEC_FLAG2_FAST : 0;
if (lavc_param->show_all)
avctx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
avctx->skip_loop_filter = lavc_param->skip_loop_filter;
avctx->skip_idct = lavc_param->skip_idct;
avctx->skip_frame = lavc_param->skip_frame;
if (lavc_codec->id == AV_CODEC_ID_H264 && lavc_param->old_x264)
av_opt_set(avctx, "x264_build", "150", AV_OPT_SEARCH_CHILDREN);
mp_set_avopts(vd->log, avctx, lavc_param->avopts);
// Do this after the above avopt handling in case it changes values
ctx->skip_frame = avctx->skip_frame;
if (mp_set_avctx_codec_headers(avctx, c) < 0) {
MP_ERR(vd, "Could not set codec parameters.\n");
goto error;
}
/* open it */
if (avcodec_open2(avctx, lavc_codec, NULL) < 0)
goto error;
// Sometimes, the first packet contains information required for correct
// decoding of the rest of the stream. The only currently known case is the
// 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);
avcodec_receive_frame(avctx, ctx->pic);
av_frame_unref(ctx->pic);
avcodec_flush_buffers(ctx->avctx);
}
return;
error:
MP_ERR(vd, "Could not open codec.\n");
uninit_avctx(vd);
}
static void reset_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
if (ctx->avctx && avcodec_is_open(ctx->avctx))
avcodec_flush_buffers(ctx->avctx);
ctx->flushing = false;
ctx->hwdec_request_reinit = false;
}
static void flush_all(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
for (int n = 0; n < ctx->num_delay_queue; n++)
talloc_free(ctx->delay_queue[n]);
ctx->num_delay_queue = 0;
for (int n = 0; n < ctx->num_sent_packets; n++)
talloc_free(ctx->sent_packets[n]);
ctx->num_sent_packets = 0;
for (int n = 0; n < ctx->num_requeue_packets; n++)
talloc_free(ctx->requeue_packets[n]);
ctx->num_requeue_packets = 0;
reset_avctx(vd);
}
static void uninit_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
flush_all(vd);
av_frame_free(&ctx->pic);
av_buffer_unref(&ctx->cached_hw_frames_ctx);
avcodec_free_context(&ctx->avctx);
av_buffer_unref(&ctx->hwdec_dev);
ctx->hwdec_failed = false;
ctx->hwdec_fail_count = 0;
ctx->max_delay_queue = 0;
ctx->hw_probing = false;
ctx->hwdec = (struct hwdec_info){0};
ctx->use_hwdec = false;
}
static int init_generic_hwaccel(struct dec_video *vd, enum AVPixelFormat hw_fmt)
{
struct lavc_ctx *ctx = vd->priv;
AVBufferRef *new_frames_ctx = NULL;
if (!ctx->hwdec.use_hw_frames)
return 0;
if (!ctx->hwdec_dev) {
MP_ERR(ctx, "Missing device context.\n");
goto error;
}
if (avcodec_get_hw_frames_parameters(ctx->avctx,
ctx->hwdec_dev, hw_fmt, &new_frames_ctx) < 0)
{
MP_VERBOSE(ctx, "Hardware decoding of this stream is unsupported?\n");
goto error;
}
AVHWFramesContext *new_fctx = (void *)new_frames_ctx->data;
if (vd->opts->hwdec_image_format)
new_fctx->sw_format = imgfmt2pixfmt(vd->opts->hwdec_image_format);
// 1 surface is already included by libavcodec. The field is 0 if the
// hwaccel supports dynamic surface allocation.
if (new_fctx->initial_pool_size)
new_fctx->initial_pool_size += HWDEC_EXTRA_SURFACES - 1;
const struct hwcontext_fns *fns =
hwdec_get_hwcontext_fns(new_fctx->device_ctx->type);
if (fns && fns->refine_hwframes)
fns->refine_hwframes(new_frames_ctx);
// We might be able to reuse a previously allocated frame pool.
if (ctx->cached_hw_frames_ctx) {
AVHWFramesContext *old_fctx = (void *)ctx->cached_hw_frames_ctx->data;
if (new_fctx->format != old_fctx->format ||
new_fctx->sw_format != old_fctx->sw_format ||
new_fctx->width != old_fctx->width ||
new_fctx->height != old_fctx->height ||
new_fctx->initial_pool_size != old_fctx->initial_pool_size)
av_buffer_unref(&ctx->cached_hw_frames_ctx);
}
if (!ctx->cached_hw_frames_ctx) {
if (av_hwframe_ctx_init(new_frames_ctx) < 0) {
MP_ERR(ctx, "Failed to allocate hw frames.\n");
goto error;
}
ctx->cached_hw_frames_ctx = new_frames_ctx;
new_frames_ctx = NULL;
}
ctx->avctx->hw_frames_ctx = av_buffer_ref(ctx->cached_hw_frames_ctx);
if (!ctx->avctx->hw_frames_ctx)
goto error;
av_buffer_unref(&new_frames_ctx);
return 0;
error:
av_buffer_unref(&new_frames_ctx);
av_buffer_unref(&ctx->cached_hw_frames_ctx);
return -1;
}
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *fmt)
{
struct dec_video *vd = avctx->opaque;
vd_ffmpeg_ctx *ctx = vd->priv;
MP_VERBOSE(vd, "Pixel formats supported by decoder:");
for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++)
MP_VERBOSE(vd, " %s", av_get_pix_fmt_name(fmt[i]));
MP_VERBOSE(vd, "\n");
const char *profile = avcodec_profile_name(avctx->codec_id, avctx->profile);
MP_VERBOSE(vd, "Codec profile: %s (0x%x)\n", profile ? profile : "unknown",
avctx->profile);
assert(ctx->use_hwdec);
ctx->hwdec_request_reinit |= ctx->hwdec_failed;
ctx->hwdec_failed = false;
enum AVPixelFormat select = AV_PIX_FMT_NONE;
for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) {
if (ctx->hwdec.pix_fmt == fmt[i]) {
if (init_generic_hwaccel(vd, fmt[i]) < 0)
break;
select = fmt[i];
break;
}
}
if (select == AV_PIX_FMT_NONE) {
ctx->hwdec_failed = true;
for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) {
const AVPixFmtDescriptor *d = av_pix_fmt_desc_get(fmt[i]);
if (d && !(d->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
select = fmt[i];
break;
}
}
}
const char *name = av_get_pix_fmt_name(select);
MP_VERBOSE(vd, "Requesting pixfmt '%s' from decoder.\n", name ? name : "-");
return select;
}
static int get_buffer2_direct(AVCodecContext *avctx, AVFrame *pic, int flags)
{
struct dec_video *vd = avctx->opaque;
vd_ffmpeg_ctx *p = vd->priv;
pthread_mutex_lock(&p->dr_lock);
int w = pic->width;
int h = pic->height;
int linesize_align[AV_NUM_DATA_POINTERS] = {0};
avcodec_align_dimensions2(avctx, &w, &h, linesize_align);
// We assume that different alignments are just different power-of-2s.
// Thus, a higher alignment always satisfies a lower alignment.
int stride_align = 0;
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++)
stride_align = MPMAX(stride_align, linesize_align[n]);
int imgfmt = pixfmt2imgfmt(pic->format);
if (!imgfmt)
goto fallback;
if (p->dr_failed)
goto fallback;
// (For simplicity, we realloc on any parameter change, instead of trying
// to be clever.)
if (stride_align != p->dr_stride_align || w != p->dr_w || h != p->dr_h ||
imgfmt != p->dr_imgfmt)
{
mp_image_pool_clear(p->dr_pool);
p->dr_imgfmt = imgfmt;
p->dr_w = w;
p->dr_h = h;
p->dr_stride_align = stride_align;
MP_DBG(p, "DR parameter change to %dx%d %s align=%d\n", w, h,
mp_imgfmt_to_name(imgfmt), stride_align);
}
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(vd->vo, imgfmt, w, h, stride_align);
if (!img) {
MP_DBG(p, "...failed..\n");
goto fallback;
}
// Now make the mp_image part of the pool. This requires doing magic to
// the image, so just add it to the pool and get it back to avoid
// dealing with magic ourselves. (Normally this never fails.)
mp_image_pool_add(p->dr_pool, img);
img = mp_image_pool_get_no_alloc(p->dr_pool, imgfmt, w, h);
if (!img)
goto fallback;
}
// get_buffer2 callers seem very unappreciative of overwriting pic with a
// new reference. The AVCodecContext.get_buffer2 comments tell us exactly
// what we should do, so follow that.
for (int n = 0; n < 4; n++) {
pic->data[n] = img->planes[n];
pic->linesize[n] = img->stride[n];
pic->buf[n] = img->bufs[n];
img->bufs[n] = NULL;
}
talloc_free(img);
pthread_mutex_unlock(&p->dr_lock);
return 0;
fallback:
if (!p->dr_failed)
MP_VERBOSE(p, "DR failed - disabling.\n");
p->dr_failed = true;
pthread_mutex_unlock(&p->dr_lock);
return avcodec_default_get_buffer2(avctx, pic, flags);
}
static bool prepare_decoding(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
if (!avctx || ctx->hwdec_failed)
return false;
int drop = ctx->framedrop_flags;
if (drop == 1) {
avctx->skip_frame = opts->framedrop; // normal framedrop
} else if (drop == 2) {
avctx->skip_frame = AVDISCARD_NONREF; // hr-seek framedrop
// Can be much more aggressive for true intra codecs.
if (ctx->intra_only)
avctx->skip_frame = AVDISCARD_ALL;
} else {
avctx->skip_frame = ctx->skip_frame; // normal playback
}
if (ctx->hwdec_request_reinit)
reset_avctx(vd);
return true;
}
static void handle_err(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
MP_WARN(vd, "Error while decoding frame!\n");
if (ctx->use_hwdec) {
ctx->hwdec_fail_count += 1;
if (ctx->hwdec_fail_count >= opts->software_fallback)
ctx->hwdec_failed = true;
}
}
static bool do_send_packet(struct dec_video *vd, struct demux_packet *pkt)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
if (!prepare_decoding(vd))
return false;
if (avctx->skip_frame == AVDISCARD_ALL)
return true;
AVPacket avpkt;
mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase);
int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return false;
if (ctx->hw_probing && ctx->num_sent_packets < 32) {
pkt = pkt ? demux_copy_packet(pkt) : NULL;
MP_TARRAY_APPEND(ctx, ctx->sent_packets, ctx->num_sent_packets, pkt);
}
if (ret < 0)
handle_err(vd);
return true;
}
static bool send_packet(struct dec_video *vd, struct demux_packet *pkt)
{
vd_ffmpeg_ctx *ctx = vd->priv;
if (ctx->num_requeue_packets) {
if (do_send_packet(vd, ctx->requeue_packets[0])) {
talloc_free(ctx->requeue_packets[0]);
MP_TARRAY_REMOVE_AT(ctx->requeue_packets, ctx->num_requeue_packets, 0);
}
return false;
}
return do_send_packet(vd, pkt);
}
// Returns whether decoder is still active (!EOF state).
static bool decode_frame(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
if (!prepare_decoding(vd))
return true;
int ret = avcodec_receive_frame(avctx, ctx->pic);
if (ret == AVERROR_EOF) {
// If flushing was initialized earlier and has ended now, make it start
// over in case we get new packets at some point in the future.
reset_avctx(vd);
return false;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
handle_err(vd);
}
if (!ctx->pic->buf[0])
return true;
ctx->hwdec_fail_count = 0;
AVFrameSideData *sd = NULL;
sd = av_frame_get_side_data(ctx->pic, AV_FRAME_DATA_A53_CC);
if (sd) {
struct demux_packet *cc = new_demux_packet_from(sd->data, sd->size);
cc->pts = vd->codec_pts;
cc->dts = vd->codec_dts;
demuxer_feed_caption(vd->header, cc);
}
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
if (!mpi) {
av_frame_unref(ctx->pic);
return true;
}
assert(mpi->planes[0] || mpi->planes[3]);
mpi->pts = mp_pts_from_av(ctx->pic->pts, &ctx->codec_timebase);
mpi->dts = mp_pts_from_av(ctx->pic->pkt_dts, &ctx->codec_timebase);
#if LIBAVCODEC_VERSION_MICRO >= 100
mpi->pkt_duration =
mp_pts_from_av(ctx->pic->pkt_duration, &ctx->codec_timebase);
#endif
av_frame_unref(ctx->pic);
MP_TARRAY_APPEND(ctx, ctx->delay_queue, ctx->num_delay_queue, mpi);
return true;
}
static bool receive_frame(struct dec_video *vd, struct mp_image **out_image)
{
vd_ffmpeg_ctx *ctx = vd->priv;
assert(!*out_image);
bool progress = decode_frame(vd);
if (ctx->hwdec_failed) {
// Failed hardware decoding? Try again in 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);
ctx->requeue_packets = pkts;
ctx->num_requeue_packets = num_pkts;
}
if (!ctx->num_delay_queue)
return progress;
if (ctx->num_delay_queue <= ctx->max_delay_queue && progress)
return true;
struct mp_image *res = ctx->delay_queue[0];
MP_TARRAY_REMOVE_AT(ctx->delay_queue, ctx->num_delay_queue, 0);
res = res ? mp_img_swap_to_native(res) : NULL;
if (!res)
return progress;
if (ctx->use_hwdec && ctx->hwdec.copying && res->hwctx) {
struct mp_image *sw = mp_image_hw_download(res, ctx->hwdec_swpool);
mp_image_unrefp(&res);
res = sw;
if (!res) {
MP_ERR(vd, "Could not copy back hardware decoded frame.\n");
ctx->hwdec_fail_count = INT_MAX - 1; // force fallback
handle_err(vd);
return false;
}
}
if (!ctx->hwdec_notified && ctx->hwdec_requested) {
if (ctx->use_hwdec) {
MP_INFO(vd, "Using hardware decoding (%s).\n",
ctx->hwdec.method_name);
} else {
MP_VERBOSE(vd, "Using software decoding.\n");
}
ctx->hwdec_notified = true;
}
if (ctx->hw_probing) {
for (int n = 0; n < ctx->num_sent_packets; n++)
talloc_free(ctx->sent_packets[n]);
ctx->num_sent_packets = 0;
ctx->hw_probing = false;
}
*out_image = res;
return true;
}
static int control(struct dec_video *vd, int cmd, void *arg)
{
vd_ffmpeg_ctx *ctx = vd->priv;
switch (cmd) {
case VDCTRL_RESET:
flush_all(vd);
return CONTROL_TRUE;
case VDCTRL_SET_FRAMEDROP:
ctx->framedrop_flags = *(int *)arg;
return CONTROL_TRUE;
case VDCTRL_GET_BFRAMES: {
AVCodecContext *avctx = ctx->avctx;
if (!avctx)
break;
if (ctx->use_hwdec && strcmp(ctx->hwdec.method_name, "mmal"))
break; // MMAL has arbitrary buffering, thus unknown
*(int *)arg = avctx->has_b_frames;
return CONTROL_TRUE;
}
case VDCTRL_GET_HWDEC: {
*(char **)arg = ctx->use_hwdec ? ctx->hwdec.method_name : NULL;
return CONTROL_TRUE;
}
case VDCTRL_FORCE_HWDEC_FALLBACK:
if (ctx->use_hwdec) {
force_fallback(vd);
return ctx->avctx ? CONTROL_OK : CONTROL_ERROR;
}
return CONTROL_FALSE;
case VDCTRL_REINIT:
reinit(vd);
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static void add_decoders(struct mp_decoder_list *list)
{
mp_add_lavc_decoders(list, AVMEDIA_TYPE_VIDEO);
}
const struct vd_functions mpcodecs_vd_ffmpeg = {
.name = "lavc",
.add_decoders = add_decoders,
.init = init,
.uninit = uninit,
.control = control,
.send_packet = send_packet,
.receive_frame = receive_frame,
};