From 5c9f164caf88d8049477727450845cbab2ffead2 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 25 May 2016 23:40:47 +0200 Subject: vf_vavpp: make refqueue logic field-based Abstracts the annoying framerate-doubling behavior. Same deal as with refqueue introduction: the code size blows up, but at least it can be reused for other filters. --- video/filter/refqueue.c | 118 ++++++++++++++++++++++++++++++++++++------------ video/filter/refqueue.h | 13 +++++- video/filter/vf_vavpp.c | 98 +++++++++++++++++----------------------- 3 files changed, 143 insertions(+), 86 deletions(-) diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c index 64c5b9b03b..8ca0e28f91 100644 --- a/video/filter/refqueue.c +++ b/video/filter/refqueue.c @@ -25,7 +25,9 @@ struct mp_refqueue { int needed_past_frames; int needed_future_frames; + int flags; + bool second_field; // current frame has to output a second field yet bool eof; // Queue of input frames, used to determine past/current/future frames. @@ -56,6 +58,50 @@ void mp_refqueue_set_refs(struct mp_refqueue *q, int past, int future) q->needed_future_frames = MPMAX(future, 1); // at least 1 for determining PTS } +// MP_MODE_* flags +void mp_refqueue_set_mode(struct mp_refqueue *q, int flags) +{ + q->flags = flags; +} + +// Whether the current frame should be deinterlaced. +bool mp_refqueue_should_deint(struct mp_refqueue *q) +{ + if (!mp_refqueue_has_output(q)) + return false; + + return (q->queue[q->pos]->fields & MP_IMGFIELD_INTERLACED) || + !(q->flags & MP_MODE_INTERLACED_ONLY); +} + +// Whether the current output frame is marked as interlaced. +bool mp_refqueue_is_interlaced(struct mp_refqueue *q) +{ + if (!mp_refqueue_has_output(q)) + return false; + + return q->queue[q->pos]->fields & MP_IMGFIELD_INTERLACED; +} + +// Whether the current output frame (field) is the top field, bottom field +// otherwise. (Assumes the caller forces deinterlacing.) +bool mp_refqueue_is_top_field(struct mp_refqueue *q) +{ + if (!mp_refqueue_has_output(q)) + return false; + + return !!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field; +} + +// Whether top-field-first mode is enabled. +bool mp_refqueue_top_field_first(struct mp_refqueue *q) +{ + if (!mp_refqueue_has_output(q)) + return false; + + return q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST; +} + // Discard all state. void mp_refqueue_flush(struct mp_refqueue *q) { @@ -63,6 +109,7 @@ void mp_refqueue_flush(struct mp_refqueue *q) talloc_free(q->queue[n]); q->num_queue = 0; q->pos = -1; + q->second_field = false; q->eof = false; } @@ -93,13 +140,54 @@ bool mp_refqueue_has_output(struct mp_refqueue *q) return q->pos >= 0 && !mp_refqueue_need_input(q); } -// Advance current frame by 1 (or 2 fields if interlaced). +static bool output_next_field(struct mp_refqueue *q) +{ + if (q->second_field) + return false; + if (!(q->flags & MP_MODE_OUTPUT_FIELDS)) + return false; + if (!(q->queue[q->pos]->fields & MP_IMGFIELD_INTERLACED) && + (q->flags & MP_MODE_INTERLACED_ONLY)) + return false; + + assert(q->pos >= 0); + + // If there's no (reasonable) timestamp, also skip the field. + if (q->pos == 0) + return false; + + double pts = q->queue[q->pos]->pts; + double next_pts = q->queue[q->pos - 1]->pts; + if (pts == MP_NOPTS_VALUE || next_pts == MP_NOPTS_VALUE) + return false; + + double frametime = next_pts - pts; + if (frametime <= 0.0 || frametime >= 1.0) + return false; + + q->queue[q->pos]->pts = pts + frametime / 2; + q->second_field = true; + return true; +} + +// Advance current field, depending on interlace flags. +void mp_refqueue_next_field(struct mp_refqueue *q) +{ + if (!mp_refqueue_has_output(q)) + return; + + if (!output_next_field(q)) + mp_refqueue_next(q); +} + +// Advance to next input frame (skips fields even in field output mode). void mp_refqueue_next(struct mp_refqueue *q) { if (!mp_refqueue_has_output(q)) return; q->pos--; + q->second_field = false; assert(q->pos >= -1 && q->pos < q->num_queue); @@ -123,31 +211,3 @@ struct mp_image *mp_refqueue_get(struct mp_refqueue *q, int pos) int i = q->pos - pos; return i >= 0 && i < q->num_queue ? q->queue[i] : NULL; } - -// Get the pts of field 0/1 (0 being the first to output). -double mp_refqueue_get_field_pts(struct mp_refqueue *q, int field) -{ - assert(field == 0 || field == 1); - - if (q->pos < 0) - return MP_NOPTS_VALUE; - - double pts = q->queue[q->pos]->pts; - - if (field == 0 || pts == MP_NOPTS_VALUE) - return pts; - - if (q->pos == 0) - return MP_NOPTS_VALUE; - - double next_pts = q->queue[q->pos - 1]->pts; - if (next_pts == MP_NOPTS_VALUE) - return MP_NOPTS_VALUE; - - double frametime = next_pts - pts; - if (frametime <= 0.0 || frametime >= 1.0) - return MP_NOPTS_VALUE; - - return pts + frametime / 2; -} - diff --git a/video/filter/refqueue.h b/video/filter/refqueue.h index 69b3987a8f..52ecc10953 100644 --- a/video/filter/refqueue.h +++ b/video/filter/refqueue.h @@ -16,7 +16,18 @@ void mp_refqueue_add_input(struct mp_refqueue *q, struct mp_image *img); bool mp_refqueue_need_input(struct mp_refqueue *q); bool mp_refqueue_has_output(struct mp_refqueue *q); void mp_refqueue_next(struct mp_refqueue *q); +void mp_refqueue_next_field(struct mp_refqueue *q); struct mp_image *mp_refqueue_get(struct mp_refqueue *q, int pos); -double mp_refqueue_get_field_pts(struct mp_refqueue *q, int field); + +enum { + MP_MODE_OUTPUT_FIELDS = (1 << 0), // output fields separately + MP_MODE_INTERLACED_ONLY = (1 << 1), // only deinterlace marked frames +}; + +void mp_refqueue_set_mode(struct mp_refqueue *q, int flags); +bool mp_refqueue_should_deint(struct mp_refqueue *q); +bool mp_refqueue_is_interlaced(struct mp_refqueue *q); +bool mp_refqueue_is_top_field(struct mp_refqueue *q); +bool mp_refqueue_top_field_first(struct mp_refqueue *q); #endif diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c index 1d131d0cde..b3adec0402 100644 --- a/video/filter/vf_vavpp.c +++ b/video/filter/vf_vavpp.c @@ -136,39 +136,45 @@ static void update_pipeline(struct vf_instance *vf) p->pipe.num_output_colors = caps.num_output_color_standards; mp_refqueue_set_refs(p->queue, caps.num_backward_references, caps.num_forward_references); + mp_refqueue_set_mode(p->queue, + (p->deint_type >= 2 ? MP_MODE_OUTPUT_FIELDS : 0) | + (p->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); return; nodeint: mp_refqueue_set_refs(p->queue, 0, 0); + mp_refqueue_set_mode(p->queue, 0); } -static inline int get_deint_field(struct vf_priv_s *p, int i, - struct mp_image *mpi) -{ - if (!p->do_deint || !(mpi->fields & MP_IMGFIELD_INTERLACED)) - return VA_FRAME_PICTURE; - return !!(mpi->fields & MP_IMGFIELD_TOP_FIRST) ^ i ? VA_TOP_FIELD : VA_BOTTOM_FIELD; -} - -static struct mp_image *render(struct vf_instance *vf, unsigned int flags) +static struct mp_image *render(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; struct mp_image *in = mp_refqueue_get(p->queue, 0); + struct mp_image *img = NULL; + bool need_end_picture = false; + bool success = false; VASurfaceID in_id = va_surface_id(in); if (!p->pipe.filters || in_id == VA_INVALID_ID) - return NULL; + goto cleanup; int r_w, r_h; va_surface_get_uncropped_size(in, &r_w, &r_h); - struct mp_image *img = mp_image_pool_get(p->pool, IMGFMT_VAAPI, r_w, r_h); + img = mp_image_pool_get(p->pool, IMGFMT_VAAPI, r_w, r_h); if (!img) - return NULL; + goto cleanup; mp_image_set_size(img, in->w, in->h); - - bool need_end_picture = false; - bool success = false; + mp_image_copy_attributes(img, in); + + unsigned int flags = va_get_colorspace_flag(p->params.colorspace); + if (!mp_refqueue_is_interlaced(p->queue)) { + flags |= VA_FRAME_PICTURE; + } else if (mp_refqueue_is_top_field(p->queue)) { + flags |= VA_TOP_FIELD; + } else { + flags |= VA_BOTTOM_FIELD; + } VASurfaceID id = va_surface_id(img); if (id == VA_INVALID_ID) @@ -194,7 +200,7 @@ static struct mp_image *render(struct vf_instance *vf, unsigned int flags) goto cleanup; filter_params->flags = flags & VA_TOP_FIELD ? 0 : VA_DEINTERLACING_BOTTOM_FIELD; - if (!(in->fields & MP_IMGFIELD_TOP_FIRST)) + if (!mp_refqueue_top_field_first(p->queue)) filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST; vaUnmapBuffer(p->display, *(p->pipe.filters)); @@ -236,44 +242,6 @@ cleanup: return NULL; } -static void output_frames(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - - struct mp_image *in = mp_refqueue_get(p->queue, 0); - - if (!p->pipe.num_filters) { // no filtering - vf_add_output_frame(vf, mp_image_new_ref(in)); - return; - } - unsigned int csp = va_get_colorspace_flag(p->params.colorspace); - unsigned int field = get_deint_field(p, 0, in); - if (field == VA_FRAME_PICTURE && p->interlaced_only) { - vf_add_output_frame(vf, mp_image_new_ref(in)); - return; - } - struct mp_image *out1 = render(vf, field | csp); - if (!out1) { // cannot render - vf_add_output_frame(vf, mp_image_new_ref(in)); - return; - } - mp_image_copy_attributes(out1, in); - vf_add_output_frame(vf, out1); - // first-field only - if (field == VA_FRAME_PICTURE || (p->do_deint && p->deint_type < 2)) - return; - double next_pts = mp_refqueue_get_field_pts(p->queue, 1); - if (next_pts == MP_NOPTS_VALUE) // no pts, skip it - return; - struct mp_image *out2 = render(vf, get_deint_field(p, 1, in) | csp); - if (!out2) // cannot render - return; - mp_image_copy_attributes(out2, in); - out2->pts = next_pts; - vf_add_output_frame(vf, out2); - return; -} - static struct mp_image *upload(struct vf_instance *vf, struct mp_image *in) { struct vf_priv_s *p = vf->priv; @@ -303,12 +271,29 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *in) } mp_refqueue_add_input(p->queue, in); + return 0; +} - if (mp_refqueue_has_output(p->queue)) { - output_frames(vf); +static int filter_out(struct vf_instance *vf) +{ + struct vf_priv_s *p = vf->priv; + + if (!mp_refqueue_has_output(p->queue)) + return 0; + + // no filtering + if (!p->pipe.num_filters || !mp_refqueue_should_deint(p->queue)) { + struct mp_image *in = mp_refqueue_get(p->queue, 0); + vf_add_output_frame(vf, mp_image_new_ref(in)); mp_refqueue_next(p->queue); + return 0; } + struct mp_image *out = render(vf); + mp_refqueue_next_field(p->queue); + if (!out) + return -1; // cannot render + vf_add_output_frame(vf, out); return 0; } @@ -463,6 +448,7 @@ static int vf_open(vf_instance_t *vf) vf->reconfig = reconfig; vf->filter_ext = filter_ext; + vf->filter_out = filter_out; vf->query_format = query_format; vf->uninit = uninit; vf->control = control; -- cgit v1.2.3