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.c199
1 files changed, 124 insertions, 75 deletions
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index f47e72a1cd..0520c3a4fd 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -41,6 +41,7 @@
#include "video/img_format.h"
#include "video/filter/vf.h"
#include "video/decode/dec_video.h"
+#include "demux/demux.h"
#include "demux/stheader.h"
#include "demux/packet.h"
#include "video/csputils.h"
@@ -123,6 +124,7 @@ extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
extern const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox;
extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
+extern const struct vd_lavc_hwdec mp_vd_lavc_dxva2;
extern const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy;
extern const struct vd_lavc_hwdec mp_vd_lavc_rpi;
@@ -141,6 +143,7 @@ static const struct vd_lavc_hwdec *const hwdec_list[] = {
&mp_vd_lavc_vaapi_copy,
#endif
#if HAVE_DXVA2_HWACCEL
+ &mp_vd_lavc_dxva2,
&mp_vd_lavc_dxva2_copy,
#endif
NULL
@@ -276,35 +279,22 @@ static void uninit(struct dec_video *vd)
static bool force_fallback(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- if (!ctx->software_fallback_decoder)
+ if (!ctx->hwdec)
return false;
uninit_avctx(vd);
int lev = ctx->hwdec_notified ? MSGL_WARN : MSGL_V;
mp_msg(vd->log, lev, "Falling back to software decoding.\n");
- const char *decoder = ctx->software_fallback_decoder;
- ctx->software_fallback_decoder = NULL;
- init_avctx(vd, decoder, NULL);
+ init_avctx(vd, ctx->decoder, NULL);
return true;
}
-static int init(struct dec_video *vd, const char *decoder)
+static void reinit(struct dec_video *vd)
{
- vd_ffmpeg_ctx *ctx;
- ctx = vd->priv = talloc_zero(NULL, vd_ffmpeg_ctx);
- ctx->log = vd->log;
- ctx->opts = vd->opts;
+ vd_ffmpeg_ctx *ctx = vd->priv;
+ const char *decoder = ctx->decoder;
- if (bstr_endswith0(bstr0(decoder), "_vdpau")) {
- MP_WARN(vd, "VDPAU decoder '%s' was requested. "
- "This way of enabling hardware\ndecoding is not supported "
- "anymore. Use --hwdec=vdpau instead.\nThe --hwdec-codec=... "
- "option can be used to restrict which codecs are\nenabled, "
- "otherwise all hardware decoding is tried for all codecs.\n",
- decoder);
- uninit(vd);
- return 0;
- }
+ uninit_avctx(vd);
struct vd_lavc_hwdec *hwdec = NULL;
@@ -325,7 +315,6 @@ static int init(struct dec_video *vd, const char *decoder)
}
if (hwdec) {
- ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
if (hwdec->get_codec)
decoder = hwdec->get_codec(ctx, decoder);
MP_VERBOSE(vd, "Trying hardware decoding.\n");
@@ -334,14 +323,35 @@ static int init(struct dec_video *vd, const char *decoder)
}
init_avctx(vd, decoder, hwdec);
- if (!ctx->avctx) {
+ if (!ctx->avctx)
force_fallback(vd);
- if (!ctx->avctx) {
- uninit(vd);
- return 0;
- }
+}
+
+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);
+
+ if (bstr_endswith0(bstr0(decoder), "_vdpau")) {
+ MP_WARN(vd, "VDPAU decoder '%s' was requested. "
+ "This way of enabling hardware\ndecoding is not supported "
+ "anymore. Use --hwdec=vdpau instead.\nThe --hwdec-codec=... "
+ "option can be used to restrict which codecs are\nenabled, "
+ "otherwise all hardware decoding is tried for all codecs.\n",
+ decoder);
+ uninit(vd);
+ return 0;
}
+ reinit(vd);
+
+ if (!ctx->avctx) {
+ uninit(vd);
+ return 0;
+ }
return 1;
}
@@ -351,8 +361,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
vd_ffmpeg_ctx *ctx = vd->priv;
struct vd_lavc_params *lavc_param = vd->opts->vd_lavc_params;
bool mp_rawvideo = false;
- struct sh_stream *sh = vd->header;
- struct mp_codec_params *c = sh->codec;
+ struct mp_codec_params *c = vd->codec;
assert(!ctx->avctx);
@@ -367,6 +376,10 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
ctx->hwdec_info = vd->hwdec_info;
+ ctx->codec_timebase = (AVRational){0};
+ if (strstr(decoder, "_mmal"))
+ ctx->codec_timebase = (AVRational){1, 1000000};
+
ctx->pix_fmt = AV_PIX_FMT_NONE;
ctx->hwdec = hwdec;
ctx->hwdec_fmt = 0;
@@ -390,6 +403,10 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
avctx->get_buffer2 = get_buffer2_hwdec;
if (ctx->hwdec->init(ctx) < 0)
goto error;
+ // This can increase efficiency by not blocking on the hardware
+ // pipeline by reading back immediately after decoding.
+ if (ctx->hwdec->process_image)
+ ctx->max_delay_queue = HWDEC_DELAY_QUEUE_COUNT;
} else {
mp_set_avcodec_threads(vd->log, avctx, lavc_param->threads);
}
@@ -448,21 +465,33 @@ static void reset_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- if (ctx->avctx)
+ if (ctx->avctx && avcodec_is_open(ctx->avctx))
avcodec_flush_buffers(ctx->avctx);
ctx->flushing = 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;
+
+ reset_avctx(vd);
+}
+
static void uninit_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- AVCodecContext *avctx = ctx->avctx;
- if (avctx) {
- if (avctx->codec && avcodec_close(avctx) < 0)
- MP_ERR(vd, "Could not close codec.\n");
+ flush_all(vd);
+ av_frame_free(&ctx->pic);
- av_freep(&avctx->extradata);
+ if (ctx->avctx) {
+ if (avcodec_close(ctx->avctx) < 0)
+ MP_ERR(vd, "Could not close codec.\n");
+ av_freep(&ctx->avctx->extradata);
}
if (ctx->hwdec && ctx->hwdec->uninit)
@@ -471,11 +500,9 @@ static void uninit_avctx(struct dec_video *vd)
av_freep(&ctx->avctx);
- av_frame_free(&ctx->pic);
-
- ctx->flushing = false;
ctx->hwdec_failed = false;
ctx->hwdec_fail_count = 0;
+ ctx->max_delay_queue = 0;
}
static void update_image_params(struct dec_video *vd, AVFrame *frame,
@@ -483,22 +510,11 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
{
vd_ffmpeg_ctx *ctx = vd->priv;
struct MPOpts *opts = ctx->opts;
- int width = frame->width;
- int height = frame->height;
- int pix_fmt = frame->format;
-
- if (pix_fmt != ctx->pix_fmt) {
- ctx->pix_fmt = pix_fmt;
- ctx->best_csp = pixfmt2imgfmt(pix_fmt);
- if (!ctx->best_csp)
- MP_ERR(vd, "lavc pixel format %s not supported.\n",
- av_get_pix_fmt_name(pix_fmt));
- }
*out_params = (struct mp_image_params) {
- .imgfmt = ctx->best_csp,
- .w = width,
- .h = height,
+ .imgfmt = pixfmt2imgfmt(frame->format),
+ .w = frame->width,
+ .h = frame->height,
.p_w = frame->sample_aspect_ratio.num,
.p_h = frame->sample_aspect_ratio.den,
.colorspace = avcol_spc_to_mp_csp(ctx->avctx->colorspace),
@@ -507,8 +523,8 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
.gamma = avcol_trc_to_mp_csp_trc(ctx->avctx->color_trc),
.chroma_location =
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),
- .rotate = vd->header->codec->rotate,
- .stereo_in = vd->header->codec->stereo_mode,
+ .rotate = vd->codec->rotate,
+ .stereo_in = vd->codec->stereo_mode,
};
if (opts->video_rotate < 0) {
@@ -584,7 +600,7 @@ static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
vd_ffmpeg_ctx *ctx = vd->priv;
int imgfmt = pixfmt2imgfmt(pic->format);
- if (!IMGFMT_IS_HWACCEL(imgfmt) || !ctx->hwdec)
+ if (!ctx->hwdec || ctx->hwdec_fmt != imgfmt)
ctx->hwdec_failed = true;
/* Hardware decoding failed, and we will trigger a proper fallback later
@@ -617,6 +633,22 @@ static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
return 0;
}
+static struct mp_image *read_output(struct dec_video *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ if (!ctx->num_delay_queue)
+ return NULL;
+
+ struct mp_image *res = ctx->delay_queue[0];
+ MP_TARRAY_REMOVE_AT(ctx->delay_queue, ctx->num_delay_queue, 0);
+
+ if (ctx->hwdec && ctx->hwdec->process_image)
+ res = ctx->hwdec->process_image(ctx, res);
+
+ return mp_img_swap_to_native(res);
+}
+
static void decode(struct dec_video *vd, struct demux_packet *packet,
int flags, struct mp_image **out_image)
{
@@ -625,6 +657,7 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
+ AVRational *tb = ctx->codec_timebase.num ? &ctx->codec_timebase : NULL;
AVPacket pkt;
if (!avctx)
@@ -638,7 +671,7 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
avctx->skip_frame = ctx->skip_frame;
}
- mp_set_av_packet(&pkt, packet, NULL);
+ mp_set_av_packet(&pkt, packet, tb);
ctx->flushing |= !pkt.data;
// Reset decoder if hw state got reset, or new data comes during flushing.
@@ -669,28 +702,48 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
return;
}
+ if (packet) {
+ // always fully consumed
+ packet->len = 0;
+ }
+
// Skipped frame, or delayed output due to multithreaded decoding.
- if (!got_picture)
+ if (!got_picture) {
+ if (!packet)
+ *out_image = read_output(vd);
return;
+ }
ctx->hwdec_fail_count = 0;
- struct mp_image_params params;
- update_image_params(vd, ctx->pic, &params);
- vd->codec_pts = mp_pts_from_av(ctx->pic->pkt_pts, NULL);
- vd->codec_dts = mp_pts_from_av(ctx->pic->pkt_dts, NULL);
+ 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;
+ cc->pos = -1;
+ demuxer_feed_caption(vd->header, cc);
+ }
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
- av_frame_unref(ctx->pic);
- if (!mpi)
+ if (!mpi) {
+ av_frame_unref(ctx->pic);
return;
+ }
assert(mpi->planes[0] || mpi->planes[3]);
+ mpi->pts = mp_pts_from_av(ctx->pic->pkt_pts, tb);
+ mpi->dts = mp_pts_from_av(ctx->pic->pkt_dts, tb);
+
+ struct mp_image_params params;
+ update_image_params(vd, ctx->pic, &params);
mp_image_set_params(mpi, &params);
- if (ctx->hwdec && ctx->hwdec->process_image)
- mpi = ctx->hwdec->process_image(ctx, mpi);
+ av_frame_unref(ctx->pic);
- *out_image = mp_img_swap_to_native(mpi);
+ MP_TARRAY_APPEND(ctx, ctx->delay_queue, ctx->num_delay_queue, mpi);
+ if (ctx->num_delay_queue > ctx->max_delay_queue)
+ *out_image = read_output(vd);
}
static struct mp_image *decode_with_fallback(struct dec_video *vd,
@@ -726,32 +779,28 @@ static int control(struct dec_video *vd, int cmd, void *arg)
vd_ffmpeg_ctx *ctx = vd->priv;
switch (cmd) {
case VDCTRL_RESET:
- reset_avctx(vd);
+ flush_all(vd);
return CONTROL_TRUE;
- case VDCTRL_QUERY_UNSEEN_FRAMES: {
+ case VDCTRL_GET_BFRAMES: {
AVCodecContext *avctx = ctx->avctx;
if (!avctx)
break;
if (ctx->hwdec && ctx->hwdec->type == HWDEC_RPI)
break; // MMAL has arbitrary buffering, thus unknown
- int delay = avctx->has_b_frames;
- assert(delay >= 0);
- if (avctx->active_thread_type & FF_THREAD_FRAME)
- delay += avctx->thread_count - 1;
- *(int *)arg = delay;
+ *(int *)arg = avctx->has_b_frames;
return CONTROL_TRUE;
}
case VDCTRL_GET_HWDEC: {
- int hwdec = ctx->hwdec ? ctx->hwdec->type : 0;
- if (!ctx->software_fallback_decoder)
- hwdec = 0;
- *(int *)arg = hwdec;
+ *(int *)arg = ctx->hwdec ? ctx->hwdec->type : 0;
return CONTROL_TRUE;
}
case VDCTRL_FORCE_HWDEC_FALLBACK:
if (force_fallback(vd))
return ctx->avctx ? CONTROL_OK : CONTROL_ERROR;
return CONTROL_FALSE;
+ case VDCTRL_REINIT:
+ reinit(vd);
+ return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}