summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-05-25 23:40:47 +0200
committerwm4 <wm4@nowhere>2016-05-25 23:51:24 +0200
commit5c9f164caf88d8049477727450845cbab2ffead2 (patch)
treec925a21cdfb8503fa763f27ce9e0209ba287f2c7
parentfc18e1df6224229d5e95bb9d1855f88690e655b3 (diff)
downloadmpv-5c9f164caf88d8049477727450845cbab2ffead2.tar.bz2
mpv-5c9f164caf88d8049477727450845cbab2ffead2.tar.xz
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.
-rw-r--r--video/filter/refqueue.c118
-rw-r--r--video/filter/refqueue.h13
-rw-r--r--video/filter/vf_vavpp.c98
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;