From 9f42760538f02699945803f4a98b4dd8c4202774 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 27 May 2016 17:01:36 +0200 Subject: vf_vdpaupp: use refqueue helper This makes vf_vdpaupp use the deinterlacer helper code already used by vf_vavpp. I nice side-effect is that this also removes some traces of code originating from vo_vdpau.c, so we can switch it to LGPL. Extend the refqueue helper with a deint setting. If not set, mp_refqueue_should_deint() always returns false, which slightly simplifies vf_vdpaupp. It's of no consequence to vf_vavpp (other than it has to set it to get expected behavior). --- video/filter/refqueue.c | 17 ++++- video/filter/refqueue.h | 6 +- video/filter/vf_vavpp.c | 1 + video/filter/vf_vdpaupp.c | 170 +++++++++++++++++----------------------------- 4 files changed, 83 insertions(+), 111 deletions(-) diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c index 8ca0e28f91..aea535cbb6 100644 --- a/video/filter/refqueue.c +++ b/video/filter/refqueue.c @@ -67,7 +67,7 @@ void mp_refqueue_set_mode(struct mp_refqueue *q, int flags) // Whether the current frame should be deinterlaced. bool mp_refqueue_should_deint(struct mp_refqueue *q) { - if (!mp_refqueue_has_output(q)) + if (!mp_refqueue_has_output(q) || !(q->flags & MP_MODE_DEINT)) return false; return (q->queue[q->pos]->fields & MP_IMGFIELD_INTERLACED) || @@ -146,8 +146,7 @@ static bool output_next_field(struct mp_refqueue *q) 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)) + if (!mp_refqueue_should_deint(q)) return false; assert(q->pos >= 0); @@ -211,3 +210,15 @@ 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; } + +// Same as mp_refqueue_get(), but return the frame which contains a field +// relative to the current field's position. +struct mp_image *mp_refqueue_get_field(struct mp_refqueue *q, int pos) +{ + // If the current field is the second field (conceptually), then pos=1 + // needs to get the next frame. Similarly, pos=-1 needs to get the current + // frame, so round towards negative infinity. + int round = mp_refqueue_top_field_first(q) != mp_refqueue_is_top_field(q); + int frame = (pos < 0 ? pos - (1 - round) : pos + round) / 2; + return mp_refqueue_get(q, frame); +} diff --git a/video/filter/refqueue.h b/video/filter/refqueue.h index 52ecc10953..62c0d4fecb 100644 --- a/video/filter/refqueue.h +++ b/video/filter/refqueue.h @@ -20,8 +20,9 @@ void mp_refqueue_next_field(struct mp_refqueue *q); struct mp_image *mp_refqueue_get(struct mp_refqueue *q, int pos); enum { - MP_MODE_OUTPUT_FIELDS = (1 << 0), // output fields separately - MP_MODE_INTERLACED_ONLY = (1 << 1), // only deinterlace marked frames + MP_MODE_DEINT = (1 << 0), // deinterlacing enabled + MP_MODE_OUTPUT_FIELDS = (1 << 1), // output fields separately + MP_MODE_INTERLACED_ONLY = (1 << 2), // only deinterlace marked frames }; void mp_refqueue_set_mode(struct mp_refqueue *q, int flags); @@ -29,5 +30,6 @@ 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); +struct mp_image *mp_refqueue_get_field(struct mp_refqueue *q, int pos); #endif diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c index b3adec0402..0365b55fb3 100644 --- a/video/filter/vf_vavpp.c +++ b/video/filter/vf_vavpp.c @@ -137,6 +137,7 @@ static void update_pipeline(struct vf_instance *vf) mp_refqueue_set_refs(p->queue, caps.num_backward_references, caps.num_forward_references); mp_refqueue_set_mode(p->queue, + (p->do_deint ? MP_MODE_DEINT : 0) | (p->deint_type >= 2 ? MP_MODE_OUTPUT_FIELDS : 0) | (p->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); return; diff --git a/video/filter/vf_vdpaupp.c b/video/filter/vf_vdpaupp.c index 23afeafe6a..077faba0b3 100644 --- a/video/filter/vf_vdpaupp.c +++ b/video/filter/vf_vdpaupp.c @@ -1,20 +1,18 @@ /* * This file is part of mpv. * - * Parts based on fragments of vo_vdpau.c: Copyright (C) 2009 Uoti Urpala - * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see . + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see . */ #include @@ -33,6 +31,7 @@ #include "video/vdpau.h" #include "video/vdpau_mixer.h" #include "vf.h" +#include "refqueue.h" // Note: this filter does no actual filtering; it merely sets appropriate // flags on vdpau images (mp_vdpau_mixer_frame) to do the appropriate @@ -40,13 +39,7 @@ struct vf_priv_s { struct mp_vdpau_ctx *ctx; - - // This is needed to supply past/future fields and to calculate the - // interpolated timestamp. - struct mp_image *buffered[3]; - int num_buffered; - - int prev_pos; // last field that was output + struct mp_refqueue *queue; int def_deintmode; int deint_enabled; @@ -54,128 +47,89 @@ struct vf_priv_s { struct mp_vdpau_mixer_opts opts; }; -static void forget_frames(struct vf_instance *vf) +static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) { struct vf_priv_s *p = vf->priv; - for (int n = 0; n < p->num_buffered; n++) - talloc_free(p->buffered[n]); - p->num_buffered = 0; - p->prev_pos = 0; -} -#define FIELD_VALID(p, f) ((f) >= 0 && (f) < (p)->num_buffered * 2) + if (p->opts.deint >= 2) { + mp_refqueue_set_refs(p->queue, 1, 1); // 2 past fields, 1 future field + } else { + mp_refqueue_set_refs(p->queue, 0, 0); + } + mp_refqueue_set_mode(p->queue, + (p->deint_enabled ? MP_MODE_DEINT : 0) | + (p->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0) | + (p->opts.deint >= 2 ? MP_MODE_OUTPUT_FIELDS : 0)); + + if (mpi) { + struct mp_image *new = mp_vdpau_upload_video_surface(p->ctx, mpi); + talloc_free(mpi); + if (!new) + return -1; + mpi = new; + + if (mp_vdpau_mixed_frame_get(mpi)) { + MP_ERR(vf, "Can't apply vdpaupp filter multiple times.\n"); + vf_add_output_frame(vf, mpi); + return -1; + } + } + + mp_refqueue_add_input(p->queue, mpi); + return 0; +} static VdpVideoSurface ref_field(struct vf_priv_s *p, struct mp_vdpau_mixer_frame *frame, int pos) { - if (!FIELD_VALID(p, pos)) - return VDP_INVALID_HANDLE; - struct mp_image *mpi = mp_image_new_ref(p->buffered[pos / 2]); + struct mp_image *mpi = mp_image_new_ref(mp_refqueue_get_field(p->queue, pos)); if (!mpi) return VDP_INVALID_HANDLE; talloc_steal(frame, mpi); return (uintptr_t)mpi->planes[3]; } -// pos==0 means last field of latest frame, 1 earlier field of latest frame, -// 2 last field of previous frame and so on -static bool output_field(struct vf_instance *vf, int pos, bool deint) +static int filter_out(struct vf_instance *vf) { struct vf_priv_s *p = vf->priv; - if (!FIELD_VALID(p, pos)) - return false; + if (!mp_refqueue_has_output(p->queue)) + return 0; - struct mp_image *mpi = mp_vdpau_mixed_frame_create(p->buffered[pos / 2]); + struct mp_image *mpi = + mp_vdpau_mixed_frame_create(mp_refqueue_get_field(p->queue, 0)); if (!mpi) - return false; // skip output on OOM + return -1; // OOM struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(mpi); - frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; - if (p->opts.deint && deint) { - int top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST); - frame->field = top_field_first ^ (pos & 1) ? - VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD: - VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; + if (!mp_refqueue_should_deint(p->queue)) { + frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; + } else if (mp_refqueue_is_top_field(p->queue)) { + frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; + } else { + frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD; } - frame->future[0] = ref_field(p, frame, pos - 1); - frame->current = ref_field(p, frame, pos); - frame->past[0] = ref_field(p, frame, pos + 1); - frame->past[1] = ref_field(p, frame, pos + 2); + frame->future[0] = ref_field(p, frame, 1); + frame->current = ref_field(p, frame, 0); + frame->past[0] = ref_field(p, frame, -1); + frame->past[1] = ref_field(p, frame, -2); frame->opts = p->opts; mpi->planes[3] = (void *)(uintptr_t)frame->current; - // Interpolate timestamps of extra fields (these always have even indexes) - int idx = pos / 2; - if (idx > 0 && !(pos & 1) && p->opts.deint >= 2 && deint) { - double pts1 = p->buffered[idx - 1]->pts; - double pts2 = p->buffered[idx]->pts; - double diff = pts1 - pts2; - mpi->pts = diff > 0 && diff < 0.5 ? (pts1 + pts2) / 2 : pts2; - } + mp_refqueue_next_field(p->queue); vf_add_output_frame(vf, mpi); - return true; -} - -static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) -{ - struct vf_priv_s *p = vf->priv; - int maxbuffer = p->opts.deint >= 2 ? 3 : 2; - bool eof = !mpi; - - if (mpi) { - struct mp_image *new = mp_vdpau_upload_video_surface(p->ctx, mpi); - talloc_free(mpi); - if (!new) - return -1; - mpi = new; - - if (mp_vdpau_mixed_frame_get(mpi)) { - MP_ERR(vf, "Can't apply vdpaupp filter multiple times.\n"); - vf_add_output_frame(vf, mpi); - return -1; - } - - while (p->num_buffered >= maxbuffer) { - talloc_free(p->buffered[p->num_buffered - 1]); - p->num_buffered--; - } - for (int n = p->num_buffered; n > 0; n--) - p->buffered[n] = p->buffered[n - 1]; - p->buffered[0] = mpi; - p->num_buffered++; - p->prev_pos += 2; - } - - bool deint = (mpi && (mpi->fields & MP_IMGFIELD_INTERLACED)) || !p->interlaced_only; - - while (1) { - int current = p->prev_pos - 1; - if (!FIELD_VALID(p, current)) - break; - // No field-splitting deinterlace -> only output first field (odd index) - if ((current & 1) || (deint && p->opts.deint >= 2)) { - // Wait for enough future frames being buffered. - // (Past frames are always around if available at all.) - if (!eof && !FIELD_VALID(p, current - 1)) - break; - if (!output_field(vf, current, deint)) - break; - } - p->prev_pos = current; - } - return 0; } static int reconfig(struct vf_instance *vf, struct mp_image_params *in, struct mp_image_params *out) { - forget_frames(vf); + struct vf_priv_s *p = vf->priv; + mp_refqueue_flush(p->queue); *out = *in; out->imgfmt = IMGFMT_VDPAU; return 0; @@ -194,7 +148,7 @@ static int control(vf_instance_t *vf, int request, void *data) switch (request) { case VFCTRL_SEEK_RESET: - forget_frames(vf); + mp_refqueue_flush(p->queue); return CONTROL_OK; case VFCTRL_GET_DEINTERLACE: *(int *)data = !!p->deint_enabled; @@ -209,7 +163,9 @@ static int control(vf_instance_t *vf, int request, void *data) static void uninit(struct vf_instance *vf) { - forget_frames(vf); + struct vf_priv_s *p = vf->priv; + + mp_refqueue_free(p->queue); } static int vf_open(vf_instance_t *vf) @@ -218,11 +174,13 @@ static int vf_open(vf_instance_t *vf) vf->reconfig = reconfig; vf->filter_ext = filter_ext; - vf->filter = NULL; + vf->filter_out = filter_out; vf->query_format = query_format; vf->control = control; vf->uninit = uninit; + p->queue = mp_refqueue_alloc(); + p->ctx = hwdec_devices_load(vf->hwdec_devs, HWDEC_VDPAU); if (!p->ctx) return 0; -- cgit v1.2.3