video: restructure decode loop

Basically change everything. Why does the code get larger? No idea.
This commit is contained in:
wm4 2017-01-10 16:19:57 +01:00
parent 4e25feda0d
commit ed937b6eca
4 changed files with 141 additions and 98 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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,86 +761,113 @@ 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);
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);
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;
}
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))
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return false;
talloc_free(ctx->prev_packet);
ctx->prev_packet = pkt ? demux_copy_packet(pkt) : NULL;
if (ret < 0)
handle_err(vd);
return true;
}
// Returns 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 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);
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;
return true;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
handle_err(vd);
}
if (ctx->hwdec && ctx->hwdec_failed) {
av_frame_unref(ctx->pic);
return;
}
if (packet && consumed)
packet->len = 0;
// Skipped frame, or delayed output due to multithreaded decoding.
if (!got_picture) {
if (!packet)
*out_image = read_output(vd);
return;
}
if (!ctx->pic->buf[0])
return false;
ctx->hwdec_fail_count = 0;
@ -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);
eof = decode_frame(vd);
}
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;
}
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,
};