summaryrefslogtreecommitdiffstats
path: root/video/decode/vd_lavc.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-01-10 16:19:57 +0100
committerwm4 <wm4@nowhere>2017-01-10 16:20:02 +0100
commited937b6eca269c653fcaa39db14c2623dd3d2862 (patch)
tree461d31459f6ee3e14e95ce1b71e78e86d275444b /video/decode/vd_lavc.c
parent4e25feda0d5e084761a9935de7c1e592e86de94f (diff)
downloadmpv-ed937b6eca269c653fcaa39db14c2623dd3d2862.tar.bz2
mpv-ed937b6eca269c653fcaa39db14c2623dd3d2862.tar.xz
video: restructure decode loop
Basically change everything. Why does the code get larger? No idea.
Diffstat (limited to 'video/decode/vd_lavc.c')
-rw-r--r--video/decode/vd_lavc.c184
1 files changed, 106 insertions, 78 deletions
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 1c6a1fef11..8aa1e7a921 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -578,6 +578,9 @@ static void flush_all(struct dec_video *vd)
talloc_free(ctx->delay_queue[n]);
ctx->num_delay_queue = 0;
+ talloc_free(ctx->prev_packet);
+ ctx->prev_packet = NULL;
+
reset_avctx(vd);
}
@@ -758,87 +761,114 @@ static struct mp_image *read_output(struct dec_video *vd)
if (ctx->hwdec && ctx->hwdec->process_image)
res = ctx->hwdec->process_image(ctx, res);
- return res ? mp_img_swap_to_native(res) : NULL;
+ res = res ? mp_img_swap_to_native(res) : NULL;
+ if (!res)
+ return NULL;
+
+ if (!ctx->hwdec_notified && vd->opts->hwdec_api != HWDEC_NONE) {
+ if (ctx->hwdec) {
+ MP_INFO(vd, "Using hardware decoding (%s).\n",
+ m_opt_choice_str(mp_hwdec_names, ctx->hwdec->type));
+ } else {
+ MP_INFO(vd, "Using software decoding.\n");
+ }
+ ctx->hwdec_notified = true;
+ }
+
+ return res;
}
-static void decode(struct dec_video *vd, struct demux_packet *packet,
- int flags, struct mp_image **out_image)
+static bool prepare_decoding(struct dec_video *vd)
{
- int got_picture = 0;
- int ret;
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
- bool consumed = false;
- AVPacket pkt;
- if (!avctx)
- return;
+ if (!avctx || ctx->hwdec_failed)
+ return false;
- if (flags) {
+ int drop = ctx->framedrop_flags;
+ if (drop) {
// hr-seek framedrop vs. normal framedrop
- avctx->skip_frame = flags == 2 ? AVDISCARD_NONREF : opts->framedrop;
+ avctx->skip_frame = drop == 2 ? AVDISCARD_NONREF : opts->framedrop;
} else {
// normal playback
avctx->skip_frame = ctx->skip_frame;
}
- mp_set_av_packet(&pkt, packet, &ctx->codec_timebase);
- ctx->flushing |= !pkt.data;
-
- // Reset decoder if hw state got reset, or new data comes during flushing.
- if (ctx->hwdec_request_reinit || (pkt.data && ctx->flushing))
+ if (ctx->hwdec_request_reinit)
reset_avctx(vd);
- hwdec_lock(ctx);
- ret = avcodec_send_packet(avctx, packet ? &pkt : NULL);
- if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
- if (ret >= 0)
- consumed = true;
- ret = avcodec_receive_frame(avctx, ctx->pic);
- if (ret >= 0)
- got_picture = 1;
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- ret = 0;
- } else {
- consumed = true;
+ 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->hwdec) {
+ ctx->hwdec_fail_count += 1;
+ // The FFmpeg VT hwaccel is buggy and can crash after 1 broken frame.
+ bool vt = ctx->hwdec && ctx->hwdec->type == HWDEC_VIDEOTOOLBOX;
+ if (ctx->hwdec_fail_count >= opts->software_fallback || vt)
+ ctx->hwdec_failed = true;
}
+}
+
+static bool 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;
+
+ AVPacket avpkt;
+ mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase);
+
+ hwdec_lock(ctx);
+ int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL);
hwdec_unlock(ctx);
- // Reset decoder if it was fully flushed. Caller might send more flush
- // packets, or even new actual packets.
- if (ctx->flushing && (ret < 0 || !got_picture))
- reset_avctx(vd);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ return false;
- if (ret < 0) {
- MP_WARN(vd, "Error while decoding frame!\n");
- if (ctx->hwdec) {
- ctx->hwdec_fail_count += 1;
- // The FFmpeg VT hwaccel is buggy and can crash after 1 broken frame.
- bool vt = ctx->hwdec && ctx->hwdec->type == HWDEC_VIDEOTOOLBOX;
- if (ctx->hwdec_fail_count >= opts->software_fallback || vt)
- ctx->hwdec_failed = true;
- }
- if (!ctx->hwdec_failed && packet)
- packet->len = 0; // skip failed packet
- return;
- }
+ talloc_free(ctx->prev_packet);
+ ctx->prev_packet = pkt ? demux_copy_packet(pkt) : NULL;
- if (ctx->hwdec && ctx->hwdec_failed) {
- av_frame_unref(ctx->pic);
- return;
- }
+ if (ret < 0)
+ handle_err(vd);
+ return true;
+}
- if (packet && consumed)
- packet->len = 0;
+// Returns EOF state.
+static bool decode_frame(struct dec_video *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+ AVCodecContext *avctx = ctx->avctx;
- // Skipped frame, or delayed output due to multithreaded decoding.
- if (!got_picture) {
- if (!packet)
- *out_image = read_output(vd);
- return;
+ if (!prepare_decoding(vd))
+ return false;
+
+ hwdec_lock(ctx);
+ int ret = avcodec_receive_frame(avctx, ctx->pic);
+ hwdec_unlock(ctx);
+
+ 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 true;
+ } else if (ret < 0 && ret != AVERROR(EAGAIN)) {
+ handle_err(vd);
}
+ if (!ctx->pic->buf[0])
+ return false;
+
ctx->hwdec_fail_count = 0;
AVFrameSideData *sd = NULL;
@@ -853,7 +883,7 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
if (!mpi) {
av_frame_unref(ctx->pic);
- return;
+ return false;
}
assert(mpi->planes[0] || mpi->planes[3]);
mpi->pts = mp_pts_from_av(ctx->pic->pts, &ctx->codec_timebase);
@@ -871,37 +901,31 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
av_frame_unref(ctx->pic);
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);
+ return false;
}
-static struct mp_image *decode_with_fallback(struct dec_video *vd,
- struct demux_packet *packet, int flags)
+static struct mp_image *receive_frame(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- if (!ctx->avctx)
- return NULL;
- struct mp_image *mpi = NULL;
- decode(vd, packet, flags, &mpi);
+ bool eof = decode_frame(vd);
+
if (ctx->hwdec_failed) {
// Failed hardware decoding? Try again in software.
+ struct demux_packet *pkt = ctx->prev_packet;
+ ctx->prev_packet = NULL;
+
force_fallback(vd);
- if (ctx->avctx)
- decode(vd, packet, flags, &mpi);
- }
+ if (pkt)
+ send_packet(vd, pkt);
+ talloc_free(pkt);
- if (mpi && !ctx->hwdec_notified && vd->opts->hwdec_api != HWDEC_NONE) {
- if (ctx->hwdec) {
- MP_INFO(vd, "Using hardware decoding (%s).\n",
- m_opt_choice_str(mp_hwdec_names, ctx->hwdec->type));
- } else {
- MP_INFO(vd, "Using software decoding.\n");
- }
- ctx->hwdec_notified = true;
+ eof = decode_frame(vd);
}
- return mpi;
+ if (eof || ctx->num_delay_queue > ctx->max_delay_queue)
+ return read_output(vd);
+ return NULL;
}
static int control(struct dec_video *vd, int cmd, void *arg)
@@ -911,6 +935,9 @@ static int control(struct dec_video *vd, int cmd, void *arg)
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)
@@ -950,5 +977,6 @@ const struct vd_functions mpcodecs_vd_ffmpeg = {
.init = init,
.uninit = uninit,
.control = control,
- .decode = decode_with_fallback,
+ .send_packet = send_packet,
+ .receive_frame = receive_frame,
};