summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--video/decode/dec_video.c38
-rw-r--r--video/decode/lavc.h4
-rw-r--r--video/decode/vd.h7
-rw-r--r--video/decode/vd_lavc.c184
4 files changed, 138 insertions, 95 deletions
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 95ca49250b..79b706d1fd 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -251,15 +251,8 @@ static void fix_image_params(struct dec_video *d_video,
d_video->fixed_format = p;
}
-static struct mp_image *decode_packet(struct dec_video *d_video,
- struct demux_packet *packet,
- int drop_frame)
+static bool send_packet(struct dec_video *d_video, struct demux_packet *packet)
{
- struct MPOpts *opts = d_video->opts;
-
- if (!d_video->vd_driver)
- return NULL;
-
double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE;
double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE;
@@ -272,15 +265,26 @@ static struct mp_image *decode_packet(struct dec_video *d_video,
MP_STATS(d_video, "start decode video");
- struct mp_image *mpi = d_video->vd_driver->decode(d_video, packet, drop_frame);
+ bool res = d_video->vd_driver->send_packet(d_video, packet);
+
+ MP_STATS(d_video, "end decode video");
+
+ return res;
+}
+
+static struct mp_image *receive_frame(struct dec_video *d_video)
+{
+ struct MPOpts *opts = d_video->opts;
+
+ MP_STATS(d_video, "start decode video");
+
+ struct mp_image *mpi = d_video->vd_driver->receive_frame(d_video);
MP_STATS(d_video, "end decode video");
// Error, discarded frame, dropped frame, or initial codec delay.
- if (!mpi || drop_frame) {
- talloc_free(mpi);
+ if (!mpi)
return NULL;
- }
if (opts->field_dominance == 0) {
mpi->fields |= MP_IMGFIELD_TOP_FIRST | MP_IMGFIELD_INTERLACED;
@@ -378,7 +382,7 @@ void video_set_start(struct dec_video *d_video, double start_pts)
void video_work(struct dec_video *d_video)
{
- if (d_video->current_mpi)
+ if (d_video->current_mpi || !d_video->vd_driver)
return;
if (!d_video->packet && !d_video->new_segment &&
@@ -414,12 +418,16 @@ void video_work(struct dec_video *d_video)
{
framedrop_type = 2;
}
- d_video->current_mpi = decode_packet(d_video, d_video->packet, framedrop_type);
- if (d_video->packet && d_video->packet->len == 0) {
+
+ d_video->vd_driver->control(d_video, VDCTRL_SET_FRAMEDROP, &framedrop_type);
+
+ if (send_packet(d_video, d_video->packet)) {
talloc_free(d_video->packet);
d_video->packet = NULL;
}
+ d_video->current_mpi = receive_frame(d_video);
+
d_video->current_state = DATA_OK;
if (!d_video->current_mpi) {
d_video->current_state = DATA_EOF;
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 993c3ec437..4f590fa52a 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -25,9 +25,13 @@ typedef struct lavc_ctx {
bool hwdec_failed;
bool hwdec_notified;
+ int framedrop_flags;
+
// For HDR side-data caching
double cached_hdr_peak;
+ struct demux_packet *prev_packet;
+
struct mp_image **delay_queue;
int num_delay_queue;
int max_delay_queue;
diff --git a/video/decode/vd.h b/video/decode/vd.h
index c4b77f31f1..4702b77860 100644
--- a/video/decode/vd.h
+++ b/video/decode/vd.h
@@ -33,8 +33,9 @@ typedef struct vd_functions
int (*init)(struct dec_video *vd, const char *decoder);
void (*uninit)(struct dec_video *vd);
int (*control)(struct dec_video *vd, int cmd, void *arg);
- struct mp_image *(*decode)(struct dec_video *vd, struct demux_packet *pkt,
- int flags);
+ // Return whether or not the packet has been consumed.
+ bool (*send_packet)(struct dec_video *vd, struct demux_packet *pkt);
+ struct mp_image *(*receive_frame)(struct dec_video *vd);
} vd_functions_t;
// NULL terminated array of all drivers
@@ -46,6 +47,8 @@ enum vd_ctrl {
VDCTRL_GET_HWDEC,
VDCTRL_REINIT,
VDCTRL_GET_BFRAMES,
+ // framedrop mode: 0=none, 1=standard, 2=hrseek
+ VDCTRL_SET_FRAMEDROP,
};
#endif /* MPLAYER_VD_H */
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,
};