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,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, };