mirror of https://github.com/mpv-player/mpv
video: don't drop last frame when deinterlacing with yadif
Or in other words, add support for properly draining remaining frames from video filters. vf_yadif is buffering at least one frame, and the buffered frame was not retrieved on EOF. For most filters, ignore this for now, and just adjust them to the changed semantics of filter_ext. But for vf_lavfi (used by vf_yadif), real support is implemented. libavfilter handles this simply by passing a NULL frame to av_buffersrc_add_frame(), so we just have to make mp_to_av() handle NULL arguments. In load_next_vo_frame(), we first try to output a frame buffered in the VO, then the filter, and then (if EOF is reached and there's still no new frame) the VO again, with draining enabled. I guess this was implemented slightly incorrectly before, because the filter chain still could have had remaining output frames.
This commit is contained in:
parent
a6dafb061f
commit
42f65ce108
|
@ -301,12 +301,12 @@ void mp_force_video_refresh(struct MPContext *mpctx)
|
|||
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts, 1, true);
|
||||
}
|
||||
|
||||
static bool filter_output_queued_frame(struct MPContext *mpctx)
|
||||
static bool filter_output_queued_frame(struct MPContext *mpctx, bool eof)
|
||||
{
|
||||
struct dec_video *d_video = mpctx->d_video;
|
||||
struct vo *video_out = mpctx->video_out;
|
||||
|
||||
struct mp_image *img = vf_output_queued_frame(d_video->vfilter);
|
||||
struct mp_image *img = vf_output_queued_frame(d_video->vfilter, eof);
|
||||
if (img)
|
||||
vo_queue_image(video_out, img);
|
||||
talloc_free(img);
|
||||
|
@ -316,9 +316,11 @@ static bool filter_output_queued_frame(struct MPContext *mpctx)
|
|||
|
||||
static bool load_next_vo_frame(struct MPContext *mpctx, bool eof)
|
||||
{
|
||||
if (vo_get_buffered_frame(mpctx->video_out, eof) >= 0)
|
||||
if (vo_get_buffered_frame(mpctx->video_out, false) >= 0)
|
||||
return true;
|
||||
if (filter_output_queued_frame(mpctx))
|
||||
if (filter_output_queued_frame(mpctx, eof))
|
||||
return true;
|
||||
if (eof && vo_get_buffered_frame(mpctx->video_out, true) >= 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -366,7 +368,7 @@ static void filter_video(struct MPContext *mpctx, struct mp_image *frame,
|
|||
|
||||
mp_image_set_params(frame, &d_video->vf_input); // force csp/aspect overrides
|
||||
vf_filter_frame(d_video->vfilter, frame);
|
||||
filter_output_queued_frame(mpctx);
|
||||
filter_output_queued_frame(mpctx, false);
|
||||
}
|
||||
|
||||
// Reconfigure the video chain and the VO on a format change. This is separate,
|
||||
|
|
|
@ -363,14 +363,17 @@ static struct mp_image *vf_dequeue_output_frame(struct vf_instance *vf)
|
|||
static int vf_do_filter(struct vf_instance *vf, struct mp_image *img)
|
||||
{
|
||||
assert(vf->fmt_in.imgfmt);
|
||||
if (img)
|
||||
vf_fix_img_params(img, &vf->fmt_in);
|
||||
|
||||
if (vf->filter_ext) {
|
||||
return vf->filter_ext(vf, img);
|
||||
} else {
|
||||
if (img) {
|
||||
if (vf->filter)
|
||||
img = vf->filter(vf, img);
|
||||
vf_add_output_frame(vf, img);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -379,6 +382,7 @@ static int vf_do_filter(struct vf_instance *vf, struct mp_image *img)
|
|||
// Return >= 0 on success, < 0 on failure (even if output frames were produced)
|
||||
int vf_filter_frame(struct vf_chain *c, struct mp_image *img)
|
||||
{
|
||||
assert(img);
|
||||
if (c->initialized < 1) {
|
||||
talloc_free(img);
|
||||
return -1;
|
||||
|
@ -387,13 +391,19 @@ int vf_filter_frame(struct vf_chain *c, struct mp_image *img)
|
|||
}
|
||||
|
||||
// Output the next queued image (if any) from the full filter chain.
|
||||
struct mp_image *vf_output_queued_frame(struct vf_chain *c)
|
||||
// eof: if set, assume there's no more input i.e. vf_filter_frame() will
|
||||
// not be called (until reset) - flush all internally delayed frames
|
||||
struct mp_image *vf_output_queued_frame(struct vf_chain *c, bool eof)
|
||||
{
|
||||
if (c->initialized < 1)
|
||||
return NULL;
|
||||
while (1) {
|
||||
struct vf_instance *last = NULL;
|
||||
for (struct vf_instance * cur = c->first; cur; cur = cur->next) {
|
||||
// Flush remaining frames on EOF, but do that only if the previous
|
||||
// filters have been flushed (i.e. they have no more output).
|
||||
if (eof && !last)
|
||||
vf_do_filter(cur, NULL);
|
||||
if (cur->num_out_queued)
|
||||
last = cur;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,9 @@ typedef struct vf_instance {
|
|||
// Like filter(), but can return an error code ( >= 0 means success). This
|
||||
// callback is also more practical when the filter can return multiple
|
||||
// output images. Use vf_add_output_frame() to queue output frames.
|
||||
// Warning: this is called with mpi==NULL if there is no more input at
|
||||
// all (i.e. the video has reached end of file condition). This
|
||||
// can be used to output delayed or otherwise remaining images.
|
||||
int (*filter_ext)(struct vf_instance *vf, struct mp_image *mpi);
|
||||
|
||||
void (*uninit)(struct vf_instance *vf);
|
||||
|
@ -133,7 +136,7 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params);
|
|||
int vf_control_any(struct vf_chain *c, int cmd, void *arg);
|
||||
int vf_control_by_label(struct vf_chain *c, int cmd, void *arg, bstr label);
|
||||
int vf_filter_frame(struct vf_chain *c, struct mp_image *img);
|
||||
struct mp_image *vf_output_queued_frame(struct vf_chain *c);
|
||||
struct mp_image *vf_output_queued_frame(struct vf_chain *c, bool eof);
|
||||
void vf_seek_reset(struct vf_chain *c);
|
||||
struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
|
||||
char **args);
|
||||
|
|
|
@ -225,6 +225,9 @@ static int filter(struct vf_instance *vf, struct mp_image *mpi)
|
|||
{
|
||||
int i, k;
|
||||
|
||||
if (!mpi)
|
||||
return 0;
|
||||
|
||||
set_imgprop(&vf->priv->filter.inpic, mpi);
|
||||
if (mpi->qscale) {
|
||||
if (mpi->qscale_type != 0) {
|
||||
|
|
|
@ -251,6 +251,8 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
|
|||
static AVFrame *mp_to_av(struct vf_instance *vf, struct mp_image *img)
|
||||
{
|
||||
struct vf_priv_s *p = vf->priv;
|
||||
if (!img)
|
||||
return NULL;
|
||||
uint64_t pts = img->pts == MP_NOPTS_VALUE ?
|
||||
AV_NOPTS_VALUE : img->pts * av_q2d(av_inv_q(p->timebase_in));
|
||||
AVFrame *frame = mp_image_to_av_frame_and_unref(img);
|
||||
|
|
|
@ -54,6 +54,9 @@ static int filter(struct vf_instance *vf, struct mp_image *mpi)
|
|||
int state = vf->priv->state;
|
||||
struct vf_priv_s *p = vf->priv;
|
||||
|
||||
if (!mpi)
|
||||
return 0;
|
||||
|
||||
if (!p->buffer || p->buffer->w != mpi->w || p->buffer->h != mpi->h ||
|
||||
p->buffer->imgfmt != mpi->imgfmt)
|
||||
{
|
||||
|
|
|
@ -180,6 +180,9 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
|
|||
if (!p->out_node)
|
||||
return -1;
|
||||
|
||||
if (!mpi)
|
||||
return 0;
|
||||
|
||||
// Try to get new frames until we get rid of the input mpi.
|
||||
pthread_mutex_lock(&p->lock);
|
||||
while (1) {
|
||||
|
|
|
@ -222,6 +222,8 @@ static struct mp_image *upload(struct vf_instance *vf, struct mp_image *in)
|
|||
static int filter_ext(struct vf_instance *vf, struct mp_image *in)
|
||||
{
|
||||
struct vf_priv_s *p = vf->priv;
|
||||
if (!in)
|
||||
return 0;
|
||||
int rt_format = in->imgfmt == IMGFMT_VAAPI ? va_surface_rt_format(in)
|
||||
: VA_RT_FORMAT_YUV420;
|
||||
if (!p->pool || p->current_rt_format != rt_format) {
|
||||
|
|
Loading…
Reference in New Issue