mirror of
https://github.com/mpv-player/mpv
synced 2024-12-25 00:02:13 +00:00
video: restructure decode loop
Basically change everything. Why does the code get larger? No idea.
This commit is contained in:
parent
4e25feda0d
commit
ed937b6eca
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user