diff options
Diffstat (limited to 'video/filter')
-rw-r--r-- | video/filter/refqueue.c | 192 | ||||
-rw-r--r-- | video/filter/refqueue.h | 18 | ||||
-rw-r--r-- | video/filter/vf.c | 797 | ||||
-rw-r--r-- | video/filter/vf.h | 179 | ||||
-rw-r--r-- | video/filter/vf_convert.c | 133 | ||||
-rw-r--r-- | video/filter/vf_d3d11vpp.c | 318 | ||||
-rw-r--r-- | video/filter/vf_format.c | 135 | ||||
-rw-r--r-- | video/filter/vf_lavfi.c | 517 | ||||
-rw-r--r-- | video/filter/vf_sub.c | 177 | ||||
-rw-r--r-- | video/filter/vf_vapoursynth.c | 682 | ||||
-rw-r--r-- | video/filter/vf_vavpp.c | 291 | ||||
-rw-r--r-- | video/filter/vf_vdpaupp.c | 175 |
12 files changed, 1031 insertions, 2583 deletions
diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c index 6b2e5a2110..964fa29c08 100644 --- a/video/filter/refqueue.c +++ b/video/filter/refqueue.c @@ -17,12 +17,25 @@ #include <assert.h> +#include <libavutil/buffer.h> + #include "common/common.h" +#include "filters/f_autoconvert.h" +#include "filters/filter_internal.h" #include "video/mp_image.h" #include "refqueue.h" struct mp_refqueue { + struct mp_filter *filter; + struct mp_autoconvert *conv; + struct mp_pin *in, *out; + + struct mp_image *in_format; + + // Buffered frame in case of format changes. + struct mp_image *next; + int needed_past_frames; int needed_future_frames; int flags; @@ -38,17 +51,37 @@ struct mp_refqueue { int pos; }; -struct mp_refqueue *mp_refqueue_alloc(void) +static bool mp_refqueue_has_output(struct mp_refqueue *q); + +static void refqueue_dtor(void *p) { - struct mp_refqueue *q = talloc_zero(NULL, struct mp_refqueue); + struct mp_refqueue *q = p; mp_refqueue_flush(q); - return q; + mp_image_unrefp(&q->in_format); + talloc_free(q->conv->f); } -void mp_refqueue_free(struct mp_refqueue *q) +struct mp_refqueue *mp_refqueue_alloc(struct mp_filter *f) { + struct mp_refqueue *q = talloc_zero(f, struct mp_refqueue); + talloc_set_destructor(q, refqueue_dtor); + q->filter = f; + + q->conv = mp_autoconvert_create(f); + if (!q->conv) + abort(); + + q->in = q->conv->f->pins[1]; + mp_pin_connect(q->conv->f->pins[0], f->ppins[0]); + q->out = f->ppins[1]; + mp_refqueue_flush(q); - talloc_free(q); + return q; +} + +void mp_refqueue_add_in_format(struct mp_refqueue *q, int fmt, int subfmt) +{ + mp_autoconvert_add_imgfmt(q->conv, fmt, subfmt); } // The minimum number of frames required before and after the current frame. @@ -103,18 +136,12 @@ void mp_refqueue_flush(struct mp_refqueue *q) q->pos = -1; q->second_field = false; q->eof = false; + mp_image_unrefp(&q->next); } -// Add a new frame to the queue. (Call mp_refqueue_next() to advance the -// current frame and to discard unneeded past frames.) -// Ownership goes to the mp_refqueue. -// Passing NULL means EOF, in which case mp_refqueue_need_input() will return -// false even if not enough future frames are available. -void mp_refqueue_add_input(struct mp_refqueue *q, struct mp_image *img) +static void mp_refqueue_add_input(struct mp_refqueue *q, struct mp_image *img) { - q->eof = !img; - if (!img) - return; + assert(img); MP_TARRAY_INSERT_AT(q, q->queue, q->num_queue, 0, img); q->pos++; @@ -122,12 +149,12 @@ void mp_refqueue_add_input(struct mp_refqueue *q, struct mp_image *img) assert(q->pos >= 0 && q->pos < q->num_queue); } -bool mp_refqueue_need_input(struct mp_refqueue *q) +static bool mp_refqueue_need_input(struct mp_refqueue *q) { return q->pos < q->needed_future_frames && !q->eof; } -bool mp_refqueue_has_output(struct mp_refqueue *q) +static bool mp_refqueue_has_output(struct mp_refqueue *q) { return q->pos >= 0 && !mp_refqueue_need_input(q); } @@ -161,18 +188,8 @@ static bool output_next_field(struct mp_refqueue *q) 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) +static void mp_refqueue_next(struct mp_refqueue *q) { if (!mp_refqueue_has_output(q)) return; @@ -192,6 +209,16 @@ void mp_refqueue_next(struct mp_refqueue *q) assert(q->pos >= -1 && q->pos < q->num_queue); } +// Advance current field, depending on interlace flags. +static 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); +} + // Return a frame by relative position: // -1: first past frame // 0: current frame @@ -219,3 +246,114 @@ bool mp_refqueue_is_second_field(struct mp_refqueue *q) { return mp_refqueue_has_output(q) && q->second_field; } + +// Return non-NULL if a format change happened. A format change is defined by +// a change in image parameters, using broad enough checks that happen to be +// sufficient for all users of refqueue. +// On format change, the refqueue transparently drains remaining frames, and +// once that is done, this function returns a mp_image reference of the new +// frame. Reinit the low level video processor based on it, and then leave the +// reference alone and continue normally. +// All frames returned in the future will have a compatible format. +struct mp_image *mp_refqueue_execute_reinit(struct mp_refqueue *q) +{ + if (mp_refqueue_has_output(q) || !q->next) + return NULL; + + struct mp_image *cur = q->next; + q->next = NULL; + + mp_image_unrefp(&q->in_format); + mp_refqueue_flush(q); + + q->in_format = mp_image_new_ref(cur); + if (!q->in_format) + abort(); + mp_image_unref_data(q->in_format); + + mp_refqueue_add_input(q, cur); + return cur; +} + +// Main processing function. Call this in the filter process function. +// Returns if enough input frames are available for filtering, and output pin +// needs data; in other words, if this returns true, you render a frame and +// output it. +// If this returns true, you must call mp_refqueue_write_out_pin() to make +// progress. +bool mp_refqueue_can_output(struct mp_refqueue *q) +{ + if (!mp_pin_in_needs_data(q->out)) + return false; + + // Strictly return any output first to reduce latency. + if (mp_refqueue_has_output(q)) + return true; + + if (q->next) { + // Make it call again for mp_refqueue_execute_reinit(). + mp_filter_internal_mark_progress(q->filter); + return false; + } + + struct mp_frame frame = mp_pin_out_read(q->in); + if (frame.type == MP_FRAME_NONE) + return false; + + if (frame.type == MP_FRAME_EOF) { + q->eof = true; + if (mp_refqueue_has_output(q)) { + mp_pin_out_unread(q->in, frame); + return true; + } + mp_pin_in_write(q->out, frame); + mp_refqueue_flush(q); + return false; + } + + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(q->filter, "unsupported frame type\n"); + mp_frame_unref(&frame); + mp_filter_internal_mark_failed(q->filter); + return false; + } + + struct mp_image *img = frame.data; + + if (!q->in_format || !!q->in_format->hwctx != !!img->hwctx || + (img->hwctx && img->hwctx->data != q->in_format->hwctx->data) || + !mp_image_params_equal(&q->in_format->params, &img->params)) + { + q->next = img; + q->eof = true; + mp_filter_internal_mark_progress(q->filter); + return false; + } + + mp_refqueue_add_input(q, img); + + if (mp_refqueue_has_output(q)) + return true; + + mp_pin_out_request_data(q->in); + return false; +} + +// (Accepts NULL for generic errors.) +void mp_refqueue_write_out_pin(struct mp_refqueue *q, struct mp_image *mpi) +{ + if (mpi) { + mp_pin_in_write(q->out, MAKE_FRAME(MP_FRAME_VIDEO, mpi)); + } else { + MP_WARN(q->filter, "failed to output frame\n"); + mp_filter_internal_mark_failed(q->filter); + } + mp_refqueue_next_field(q); +} + +// Return frame for current format (without data). Reference is owned by q, +// might go away on further queue accesses. NULL if none yet. +struct mp_image *mp_refqueue_get_format(struct mp_refqueue *q) +{ + return q->in_format; +} diff --git a/video/filter/refqueue.h b/video/filter/refqueue.h index bb23506ac2..0a8ace0031 100644 --- a/video/filter/refqueue.h +++ b/video/filter/refqueue.h @@ -3,22 +3,26 @@ #include <stdbool.h> +#include "filters/filter.h" + // A helper for deinterlacers which require past/future reference frames. struct mp_refqueue; -struct mp_refqueue *mp_refqueue_alloc(void); -void mp_refqueue_free(struct mp_refqueue *q); +struct mp_refqueue *mp_refqueue_alloc(struct mp_filter *f); + +void mp_refqueue_add_in_format(struct mp_refqueue *q, int fmt, int subfmt); void mp_refqueue_set_refs(struct mp_refqueue *q, int past, int future); void mp_refqueue_flush(struct mp_refqueue *q); -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); +struct mp_image *mp_refqueue_execute_reinit(struct mp_refqueue *q); +bool mp_refqueue_can_output(struct mp_refqueue *q); +void mp_refqueue_write_out_pin(struct mp_refqueue *q, struct mp_image *mpi); + +struct mp_image *mp_refqueue_get_format(struct mp_refqueue *q); + enum { MP_MODE_DEINT = (1 << 0), // deinterlacing enabled MP_MODE_OUTPUT_FIELDS = (1 << 1), // output fields separately diff --git a/video/filter/vf.c b/video/filter/vf.c deleted file mode 100644 index d5df466ba8..0000000000 --- a/video/filter/vf.c +++ /dev/null @@ -1,797 +0,0 @@ -/* - * This file is part of mpv. - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <sys/types.h> -#include <libavutil/buffer.h> -#include <libavutil/common.h> -#include <libavutil/mem.h> - -#include "config.h" - -#include "common/common.h" -#include "common/global.h" -#include "common/msg.h" -#include "options/m_option.h" -#include "options/m_config.h" - -#include "options/options.h" - -#include "video/img_format.h" -#include "video/mp_image.h" -#include "video/mp_image_pool.h" -#include "vf.h" - -extern const vf_info_t vf_info_format; -extern const vf_info_t vf_info_sub; -extern const vf_info_t vf_info_convert; -extern const vf_info_t vf_info_lavfi; -extern const vf_info_t vf_info_lavfi_bridge; -extern const vf_info_t vf_info_vaapi; -extern const vf_info_t vf_info_vapoursynth; -extern const vf_info_t vf_info_vapoursynth_lazy; -extern const vf_info_t vf_info_vdpaupp; -extern const vf_info_t vf_info_d3d11vpp; - -// list of available filters: -static const vf_info_t *const filter_list[] = { - &vf_info_format, - &vf_info_sub, - &vf_info_convert, - &vf_info_lavfi, - &vf_info_lavfi_bridge, -#if HAVE_VAPOURSYNTH_CORE && HAVE_VAPOURSYNTH - &vf_info_vapoursynth, -#endif -#if HAVE_VAPOURSYNTH_CORE && HAVE_VAPOURSYNTH_LAZY - &vf_info_vapoursynth_lazy, -#endif -#if HAVE_VAAPI - &vf_info_vaapi, -#endif -#if HAVE_VDPAU - &vf_info_vdpaupp, -#endif -#if HAVE_D3D_HWACCEL - &vf_info_d3d11vpp, -#endif - NULL -}; - -static void vf_uninit_filter(vf_instance_t *vf); - -static bool get_desc(struct m_obj_desc *dst, int index) -{ - if (index >= MP_ARRAY_SIZE(filter_list) - 1) - return false; - const vf_info_t *vf = filter_list[index]; - *dst = (struct m_obj_desc) { - .name = vf->name, - .description = vf->description, - .priv_size = vf->priv_size, - .priv_defaults = vf->priv_defaults, - .options = vf->options, - .p = vf, - .print_help = vf->print_help, - }; - return true; -} - -// For the vf option -const struct m_obj_list vf_obj_list = { - .get_desc = get_desc, - .description = "video filters", - .allow_disable_entries = true, - .allow_unknown_entries = true, -}; - -// Try the cmd on each filter (starting with the first), and stop at the first -// filter which does not return CONTROL_UNKNOWN for it. -int vf_control_any(struct vf_chain *c, int cmd, void *arg) -{ - for (struct vf_instance *cur = c->first; cur; cur = cur->next) { - if (cur->control) { - int r = cur->control(cur, cmd, arg); - if (r != CONTROL_UNKNOWN) - return r; - } - } - return CONTROL_UNKNOWN; -} - -int vf_control_by_label(struct vf_chain *c,int cmd, void *arg, bstr label) -{ - char *label_str = bstrdup0(NULL, label); - struct vf_instance *cur = vf_find_by_label(c, label_str); - talloc_free(label_str); - if (cur) { - return cur->control ? cur->control(cur, cmd, arg) : CONTROL_NA; - } else { - return CONTROL_UNKNOWN; - } -} - -static void vf_control_all(struct vf_chain *c, int cmd, void *arg) -{ - for (struct vf_instance *cur = c->first; cur; cur = cur->next) { - if (cur->control) - cur->control(cur, cmd, arg); - } -} - -int vf_send_command(struct vf_chain *c, char *label, char *cmd, char *arg) -{ - char *args[2] = {cmd, arg}; - if (strcmp(label, "all") == 0) { - vf_control_all(c, VFCTRL_COMMAND, args); - return 0; - } else { - return vf_control_by_label(c, VFCTRL_COMMAND, args, bstr0(label)); - } -} - -static void vf_fix_img_params(struct mp_image *img, struct mp_image_params *p) -{ - // Filters must absolutely set these correctly. - assert(img->w == p->w && img->h == p->h); - assert(img->imgfmt == p->imgfmt); - // Too many things don't set this correctly. - // If --colormatrix is used, decoder and filter chain disagree too. - // In general, it's probably more convenient to force these here, - // instead of requiring filters to set these correctly. - img->params = *p; -} - -// Get a new image for filter output, with size and pixel format according to -// the last vf_config call. -struct mp_image *vf_alloc_out_image(struct vf_instance *vf) -{ - struct mp_image_params *p = &vf->fmt_out; - assert(p->imgfmt); - struct mp_image *img = mp_image_pool_get(vf->out_pool, p->imgfmt, p->w, p->h); - if (img) - vf_fix_img_params(img, p); - return img; -} - -// Returns false on failure; then the image can't be written to. -bool vf_make_out_image_writeable(struct vf_instance *vf, struct mp_image *img) -{ - struct mp_image_params *p = &vf->fmt_out; - assert(p->imgfmt); - assert(p->imgfmt == img->imgfmt); - assert(p->w == img->w && p->h == img->h); - return mp_image_pool_make_writeable(vf->out_pool, img); -} - -//============================================================================ - -// The default callback assumes all formats are passed through. -static int vf_default_query_format(struct vf_instance *vf, unsigned int fmt) -{ - return vf_next_query_format(vf, fmt); -} - -void vf_print_filter_chain(struct vf_chain *c, int msglevel, - struct vf_instance *vf) -{ - if (!mp_msg_test(c->log, msglevel)) - return; - - for (vf_instance_t *f = c->first; f; f = f->next) { - char b[256] = {0}; - mp_snprintf_cat(b, sizeof(b), " [%s] ", f->full_name); - if (f->label) - mp_snprintf_cat(b, sizeof(b), "\"%s\" ", f->label); - mp_snprintf_cat(b, sizeof(b), "%s", mp_image_params_to_str(&f->fmt_out)); - if (f->autoinserted) - mp_snprintf_cat(b, sizeof(b), " [a]"); - if (f == vf) - mp_snprintf_cat(b, sizeof(b), " <---"); - mp_msg(c->log, msglevel, "%s\n", b); - } -} - -static struct vf_instance *vf_open(struct vf_chain *c, const char *name, - char **args) -{ - const char *lavfi_name = NULL; - char **lavfi_args = NULL; - struct m_obj_desc desc; - if (!m_obj_list_find(&desc, &vf_obj_list, bstr0(name))) { - if (!m_obj_list_find(&desc, &vf_obj_list, bstr0("lavfi-bridge"))) { - MP_ERR(c, "Couldn't find video filter '%s'.\n", name); - return NULL; - } - lavfi_name = name; - lavfi_args = args; - args = NULL; - if (strncmp(lavfi_name, "lavfi-", 6) == 0) - lavfi_name += 6; - } - vf_instance_t *vf = talloc_zero(NULL, struct vf_instance); - *vf = (vf_instance_t) { - .full_name = talloc_strdup(vf, name), - .info = desc.p, - .log = mp_log_new(vf, c->log, name), - .hwdec_devs = c->hwdec_devs, - .query_format = vf_default_query_format, - .out_pool = mp_image_pool_new(vf), - .chain = c, - }; - struct m_config *config = - m_config_from_obj_desc_and_args(vf, vf->log, c->global, &desc, - name, c->opts->vf_defs, args); - if (!config) - goto error; - if (lavfi_name) { - // Pass the filter arguments as proper sub-options to the bridge filter. - struct m_config_option *name_opt = m_config_get_co(config, bstr0("name")); - assert(name_opt); - assert(name_opt->opt->type == &m_option_type_string); - if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0) - goto error; - struct m_config_option *opts = m_config_get_co(config, bstr0("opts")); - assert(opts); - assert(opts->opt->type == &m_option_type_keyvalue_list); - if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0) - goto error; - vf->full_name = talloc_asprintf(vf, "%s (lavfi)", vf->full_name); - } - vf->priv = config->optstruct; - int retcode = vf->info->open(vf); - if (retcode < 1) - goto error; - return vf; - -error: - MP_ERR(c, "Creating filter '%s' failed.\n", name); - talloc_free(vf); - return NULL; -} - -static vf_instance_t *vf_open_filter(struct vf_chain *c, const char *name, - char **args) -{ - int i, l = 0; - for (i = 0; args && args[2 * i]; i++) - l += 1 + strlen(args[2 * i]) + 1 + strlen(args[2 * i + 1]); - l += strlen(name); - char *str = malloc(l + 1); - if (!str) - return NULL; - char *p = str; - p += sprintf(str, "%s", name); - for (i = 0; args && args[2 * i]; i++) - p += sprintf(p, " %s=%s", args[2 * i], args[2 * i + 1]); - MP_INFO(c, "Opening video filter: [%s]\n", str); - free(str); - return vf_open(c, name, args); -} - -void vf_remove_filter(struct vf_chain *c, struct vf_instance *vf) -{ - assert(vf != c->first && vf != c->last); // these are sentinels - struct vf_instance *prev = c->first; - while (prev && prev->next != vf) - prev = prev->next; - assert(prev); // not inserted - prev->next = vf->next; - vf_uninit_filter(vf); - c->initialized = 0; -} - -struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name, - char **args) -{ - struct vf_instance *vf = vf_open_filter(c, name, args); - if (vf) { - // Insert it before the last filter, which is the "out" pseudo-filter - // (But after the "in" pseudo-filter) - struct vf_instance **pprev = &c->first->next; - while (*pprev && (*pprev)->next) - pprev = &(*pprev)->next; - vf->next = *pprev ? *pprev : NULL; - *pprev = vf; - c->initialized = 0; - } - return vf; -} - -int vf_append_filter_list(struct vf_chain *c, struct m_obj_settings *list) -{ - for (int n = 0; list && list[n].name; n++) { - if (!list[n].enabled) - continue; - struct vf_instance *vf = - vf_append_filter(c, list[n].name, list[n].attribs); - if (vf) { - if (list[n].label) { - vf->label = talloc_strdup(vf, list[n].label); - } else { - for (int i = 0; i < 100; i++) { - char* label = talloc_asprintf(vf, "%s.%02d", list[n].name, i); - if (vf_find_by_label(c, label)) { - talloc_free(label); - } else { - vf->label = label; - break; - } - } - } - } - } - return 0; -} - -// Used by filters to add a filtered frame to the output queue. -// Ownership of img is transferred from caller to the filter chain. -void vf_add_output_frame(struct vf_instance *vf, struct mp_image *img) -{ - if (img) { - vf_fix_img_params(img, &vf->fmt_out); - MP_TARRAY_APPEND(vf, vf->out_queued, vf->num_out_queued, img); - } -} - -static bool vf_has_output_frame(struct vf_instance *vf) -{ - if (!vf->num_out_queued && vf->filter_out) { - if (vf->filter_out(vf) < 0) - MP_ERR(vf, "Error filtering frame.\n"); - } - return vf->num_out_queued > 0; -} - -static struct mp_image *vf_dequeue_output_frame(struct vf_instance *vf) -{ - struct mp_image *res = NULL; - if (vf_has_output_frame(vf)) { - res = vf->out_queued[0]; - MP_TARRAY_REMOVE_AT(vf->out_queued, vf->num_out_queued, 0); - } - return res; -} - -static int vf_do_filter(struct vf_instance *vf, struct mp_image *img) -{ - assert(vf->fmt_in.imgfmt); - if (img) - assert(mp_image_params_equal(&img->params, &vf->fmt_in)); - - if (vf->filter_ext) { - int r = vf->filter_ext(vf, img); - if (r < 0) - MP_ERR(vf, "Error filtering frame.\n"); - return r; - } else { - if (img) { - if (vf->filter) - img = vf->filter(vf, img); - vf_add_output_frame(vf, img); - } - return 0; - } -} - -// Input a frame into the filter chain. Ownership of img is transferred. -// 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; - } - assert(mp_image_params_equal(&img->params, &c->input_params)); - return vf_do_filter(c->first, img); -} - -// Similar to vf_output_frame(), but only ensure that the filter "until" has -// output, instead of the end of the filter chain. -static int vf_output_frame_until(struct vf_chain *c, struct vf_instance *until, - bool eof) -{ - if (until->num_out_queued) - return 1; - if (c->initialized < 1) - return -1; - 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) { - int r = vf_do_filter(cur, NULL); - if (r < 0) - return r; - } - if (vf_has_output_frame(cur)) - last = cur; - if (cur == until) - break; - } - if (!last) - return 0; - if (last == until) - return 1; - int r = vf_do_filter(last->next, vf_dequeue_output_frame(last)); - if (r < 0) - return r; - } -} - -// Output the next queued image (if any) from the full filter chain. -// The frame can be retrieved with vf_read_output_frame(). -// 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 -// returns: -1: error, 0: no output, 1: output available -int vf_output_frame(struct vf_chain *c, bool eof) -{ - return vf_output_frame_until(c, c->last, eof); -} - -struct mp_image *vf_read_output_frame(struct vf_chain *c) -{ - if (!c->last->num_out_queued) - vf_output_frame(c, false); - return vf_dequeue_output_frame(c->last); -} - -// Undo the previous vf_read_output_frame(). -void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img) -{ - struct vf_instance *vf = c->last; - MP_TARRAY_INSERT_AT(vf, vf->out_queued, vf->num_out_queued, 0, img); -} - -// Some filters (vf_vapoursynth) filter on separate threads, and may need new -// input from the decoder, even though the core does not need a new output image -// yet (this is required to get proper pipelining in the filter). If the filter -// needs new data, it will call c->wakeup_callback, which in turn causes the -// core to recheck the filter chain, calling this function. Each filter is asked -// whether it needs a frame (with vf->needs_input), and if so, it will try to -// feed it a new frame. If this fails, it will request a new frame from the -// core by returning 1. -// returns -1: error, 0: nothing needed, 1: add new frame with vf_filter_frame() -int vf_needs_input(struct vf_chain *c) -{ - struct vf_instance *prev = c->first; - for (struct vf_instance *cur = c->first; cur; cur = cur->next) { - while (cur->needs_input && cur->needs_input(cur)) { - // Get frames from preceding filters, or if there are none, - // request new frames from decoder. - int r = vf_output_frame_until(c, prev, false); - if (r < 1) - return r < 0 ? -1 : 1; - r = vf_do_filter(cur, vf_dequeue_output_frame(prev)); - if (r < 0) - return r; - } - prev = cur; - } - return 0; -} - -static void vf_forget_frames(struct vf_instance *vf) -{ - for (int n = 0; n < vf->num_out_queued; n++) - talloc_free(vf->out_queued[n]); - vf->num_out_queued = 0; -} - -static void vf_chain_forget_frames(struct vf_chain *c) -{ - for (struct vf_instance *cur = c->first; cur; cur = cur->next) - vf_forget_frames(cur); -} - -void vf_seek_reset(struct vf_chain *c) -{ - vf_control_all(c, VFCTRL_SEEK_RESET, NULL); - vf_chain_forget_frames(c); -} - -int vf_next_query_format(struct vf_instance *vf, unsigned int fmt) -{ - return fmt >= IMGFMT_START && fmt < IMGFMT_END - ? vf->last_outfmts[fmt - IMGFMT_START] : 0; -} - -// Mark accepted input formats in fmts[]. Note that ->query_format will -// typically (but not always) call vf_next_query_format() to check whether -// an output format is supported. -static void query_formats(uint8_t *fmts, struct vf_instance *vf) -{ - for (int n = IMGFMT_START; n < IMGFMT_END; n++) - fmts[n - IMGFMT_START] = vf->query_format(vf, n); -} - -static bool is_conv_filter(struct vf_instance *vf) -{ - return vf && (strcmp(vf->info->name, "convert") == 0 || vf->autoinserted); -} - -static const char *find_conv_filter(uint8_t *fmts_in, uint8_t *fmts_out) -{ - for (int n = 0; filter_list[n]; n++) { - if (filter_list[n]->test_conversion) { - for (int a = IMGFMT_START; a < IMGFMT_END; a++) { - for (int b = IMGFMT_START; b < IMGFMT_END; b++) { - if (fmts_in[a - IMGFMT_START] && fmts_out[b - IMGFMT_START] && - filter_list[n]->test_conversion(a, b)) - return filter_list[n]->name; - } - } - } - } - return "convert"; -} - -static void update_formats(struct vf_chain *c, struct vf_instance *vf, - uint8_t *fmts) -{ - if (vf->next) - update_formats(c, vf->next, vf->last_outfmts); - query_formats(fmts, vf); - bool has_in = false, has_out = false; - for (int n = IMGFMT_START; n < IMGFMT_END; n++) { - has_in |= !!fmts[n - IMGFMT_START]; - has_out |= !!vf->last_outfmts[n - IMGFMT_START]; - } - if (has_out && !has_in && !is_conv_filter(vf) && - !is_conv_filter(vf->next)) - { - // If there are output formats, but no input formats (meaning the - // filters after vf work, but vf can't output any format the filters - // after it accept), try to insert a conversion filter. - MP_INFO(c, "Using conversion filter.\n"); - // Determine which output formats the filter _could_ accept. For this - // to work after the conversion filter is inserted, it is assumed that - // conversion filters have a single set of in/output formats that can - // be converted to each other. - uint8_t out_formats[IMGFMT_END - IMGFMT_START]; - for (int n = IMGFMT_START; n < IMGFMT_END; n++) { - out_formats[n - IMGFMT_START] = vf->last_outfmts[n - IMGFMT_START]; - vf->last_outfmts[n - IMGFMT_START] = 1; - } - query_formats(fmts, vf); - const char *filter = find_conv_filter(fmts, out_formats); - char **args = NULL; - char *args_no_warn[] = {"warn", "no", NULL}; - if (strcmp(filter, "scale") == 0) - args = args_no_warn; - struct vf_instance *conv = vf_open(c, filter, args); - if (conv) { - conv->autoinserted = true; - conv->next = vf->next; - vf->next = conv; - update_formats(c, conv, vf->last_outfmts); - query_formats(fmts, vf); - } - } - for (int n = IMGFMT_START; n < IMGFMT_END; n++) - has_in |= !!fmts[n - IMGFMT_START]; - if (!has_in) { - // Pretend all out formats work. All this does it getting better - // error messages in some cases, so we can configure all filter - // until it fails, which will be visible in vf_print_filter_chain(). - for (int n = IMGFMT_START; n < IMGFMT_END; n++) - vf->last_outfmts[n - IMGFMT_START] = 1; - query_formats(fmts, vf); - } -} - -// Insert a conversion filter _after_ vf. -// vf needs to have been successfully configured, vf->next unconfigured but -// with formats negotiated. -static void auto_insert_conversion_filter_if_needed(struct vf_chain *c, - struct vf_instance *vf) -{ - if (!vf->next || vf->next->query_format(vf->next, vf->fmt_out.imgfmt) || - is_conv_filter(vf) || is_conv_filter(vf->next)) - return; - - MP_INFO(c, "Using conversion filter.\n"); - - uint8_t fmts[IMGFMT_END - IMGFMT_START]; - query_formats(fmts, vf->next); - - uint8_t out_formats[IMGFMT_END - IMGFMT_START]; - for (int n = IMGFMT_START; n < IMGFMT_END; n++) - out_formats[n - IMGFMT_START] = n == vf->fmt_out.imgfmt; - - const char *filter = find_conv_filter(out_formats, fmts); - char **args = NULL; - char *args_no_warn[] = {"warn", "no", NULL}; - if (strcmp(filter, "scale") == 0) - args = args_no_warn; - struct vf_instance *conv = vf_open(c, filter, args); - if (conv) { - conv->autoinserted = true; - conv->next = vf->next; - vf->next = conv; - update_formats(c, conv, vf->last_outfmts); - } -} - -static int vf_reconfig_wrapper(struct vf_instance *vf, - const struct mp_image_params *p) -{ - vf_forget_frames(vf); - if (vf->out_pool) - mp_image_pool_clear(vf->out_pool); - - if (!vf->query_format(vf, p->imgfmt)) - return -2; - - vf->fmt_out = vf->fmt_in = *p; - - if (!mp_image_params_valid(&vf->fmt_in)) - return -2; - - int r = 0; - if (vf->reconfig) - r = vf->reconfig(vf, &vf->fmt_in, &vf->fmt_out); - - if (!mp_image_params_equal(&vf->fmt_in, p)) - r = -2; - - if (!mp_image_params_valid(&vf->fmt_out)) - r = -2; - - // Fix csp in case of pixel format change - if (r >= 0) - mp_image_params_guess_csp(&vf->fmt_out); - - return r; -} - -int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params) -{ - int r = 0; - vf_seek_reset(c); - for (struct vf_instance *vf = c->first; vf; ) { - struct vf_instance *next = vf->next; - if (vf->autoinserted) - vf_remove_filter(c, vf); - vf = next; - } - c->input_params = *params; - c->first->fmt_in = *params; - struct mp_image_params cur = *params; - - uint8_t unused[IMGFMT_END - IMGFMT_START]; - update_formats(c, c->first, unused); - AVBufferRef *hwfctx = c->in_hwframes_ref; - struct vf_instance *failing = NULL; - for (struct vf_instance *vf = c->first; vf; vf = vf->next) { - av_buffer_unref(&vf->in_hwframes_ref); - av_buffer_unref(&vf->out_hwframes_ref); - vf->in_hwframes_ref = hwfctx ? av_buffer_ref(hwfctx) : NULL; - vf->out_hwframes_ref = hwfctx ? av_buffer_ref(hwfctx) : NULL; - r = vf_reconfig_wrapper(vf, &cur); - if (r < 0) { - failing = vf; - break; - } - cur = vf->fmt_out; - hwfctx = vf->out_hwframes_ref; - // Recheck if the new output format works with the following filters. - auto_insert_conversion_filter_if_needed(c, vf); - } - c->output_params = cur; - c->initialized = r < 0 ? -1 : 1; - int loglevel = r < 0 ? MSGL_WARN : MSGL_V; - if (r == -2) - MP_ERR(c, "Image formats incompatible or invalid.\n"); - mp_msg(c->log, loglevel, "Video filter chain:\n"); - vf_print_filter_chain(c, loglevel, failing); - if (r < 0) - c->output_params = (struct mp_image_params){0}; - return r; -} - -// Hack to get mp_image.hwctx before vf_reconfig() -void vf_set_proto_frame(struct vf_chain *c, struct mp_image *img) -{ - av_buffer_unref(&c->in_hwframes_ref); - c->in_hwframes_ref = img && img->hwctx ? av_buffer_ref(img->hwctx) : NULL; -} - -struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label) -{ - struct vf_instance *vf = c->first; - while (vf) { - if (vf->label && label && strcmp(vf->label, label) == 0) - return vf; - vf = vf->next; - } - return NULL; -} - -static void vf_uninit_filter(vf_instance_t *vf) -{ - av_buffer_unref(&vf->in_hwframes_ref); - av_buffer_unref(&vf->out_hwframes_ref); - if (vf->uninit) - vf->uninit(vf); - vf_forget_frames(vf); - talloc_free(vf); -} - -static int input_query_format(struct vf_instance *vf, unsigned int fmt) -{ - // Setting fmt_in is guaranteed by vf_reconfig(). - if (fmt == vf->fmt_in.imgfmt) - return vf_next_query_format(vf, fmt); - return 0; -} - -static int output_query_format(struct vf_instance *vf, unsigned int fmt) -{ - struct vf_chain *c = (void *)vf->priv; - if (fmt >= IMGFMT_START && fmt < IMGFMT_END) - return c->allowed_output_formats[fmt - IMGFMT_START]; - return 0; -} - -struct vf_chain *vf_new(struct mpv_global *global) -{ - struct vf_chain *c = talloc_ptrtype(NULL, c); - *c = (struct vf_chain){ - .opts = global->opts, - .log = mp_log_new(c, global->log, "!vf"), - .global = global, - }; - static const struct vf_info in = { .name = "in" }; - c->first = talloc(c, struct vf_instance); - *c->first = (struct vf_instance) { - .full_name = "in", - .log = c->log, - .info = &in, - .query_format = input_query_format, - }; - static const struct vf_info out = { .name = "out" }; - c->last = talloc(c, struct vf_instance); - *c->last = (struct vf_instance) { - .full_name = "out", - .log = c->log, - .info = &out, - .query_format = output_query_format, - .priv = (void *)c, - }; - c->first->next = c->last; - return c; -} - -void vf_destroy(struct vf_chain *c) -{ - if (!c) - return; - av_buffer_unref(&c->in_hwframes_ref); - while (c->first) { - vf_instance_t *vf = c->first; - c->first = vf->next; - vf_uninit_filter(vf); - } - vf_chain_forget_frames(c); - talloc_free(c); -} diff --git a/video/filter/vf.h b/video/filter/vf.h deleted file mode 100644 index 5146a4d15b..0000000000 --- a/video/filter/vf.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * This file is part of mpv. - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MPLAYER_VF_H -#define MPLAYER_VF_H - -#include <stdbool.h> - -#include "video/mp_image.h" -#include "common/common.h" - -struct MPOpts; -struct mpv_global; -struct vf_instance; -struct vf_priv_s; -struct m_obj_settings; - -typedef struct vf_info { - const char *description; - const char *name; - int (*open)(struct vf_instance *vf); - int priv_size; - const void *priv_defaults; - const struct m_option *options; - void (*print_help)(struct mp_log *log); - bool (*test_conversion)(int in, int out); -} vf_info_t; - -typedef struct vf_instance { - const vf_info_t *info; - char *full_name; - - // Initialize the filter. The filter must set *out to the same image - // params as the images the filter functions will return for the given - // *in format. - // Note that by default, only formats reported as supported by query_format - // will be allowed for *in. - // Returns >= 0 on success, < 0 on error. - int (*reconfig)(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out); - - int (*control)(struct vf_instance *vf, int request, void *data); - int (*query_format)(struct vf_instance *vf, unsigned int fmt); - - // Filter mpi and return the result. The input mpi reference is owned by - // the filter, the returned reference is owned by the caller. - // Return NULL if the output frame is skipped. - struct mp_image *(*filter)(struct vf_instance *vf, struct mp_image *mpi); - - // 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); - - // Produce an output frame. This is called after filter or filter_ext. - // You can add 0 or more frames with vf_add_output_frame(). (This allows - // distributing the filter load over time -> typically add at most 1 frame.) - // If this adds no frame (or is NULL), then the caller assumes that the - // filter needs new input. - // Return a negative value on error. (No more frames is not an error.) - // May be called multiple times, even if the filter gives no output. - int (*filter_out)(struct vf_instance *vf); - - // Optional function that checks whether the filter needs additional - // input. This is for filters with asynchronous behavior: they filter - // frames in the background, and to get good pipelining behavior, new - // data should be fed, even if the playback core doesn't need any yet. - bool (*needs_input)(struct vf_instance *vf); - - void (*uninit)(struct vf_instance *vf); - - char *label; - bool autoinserted; - - struct mp_image_params fmt_in, fmt_out; - - // This is a dirty hack. - struct AVBufferRef *in_hwframes_ref, *out_hwframes_ref; - - struct mp_image_pool *out_pool; - struct vf_priv_s *priv; - struct mp_log *log; - struct mp_hwdec_devices *hwdec_devs; - - struct mp_image **out_queued; - int num_out_queued; - - // Caches valid output formats. - uint8_t last_outfmts[IMGFMT_END - IMGFMT_START]; - - struct vf_chain *chain; - struct vf_instance *next; -} vf_instance_t; - -// A chain of video filters -struct vf_chain { - int initialized; // 0: no, 1: yes, -1: attempted to, but failed - - struct vf_instance *first, *last; - - struct mp_image_params input_params; - struct mp_image_params output_params; - uint8_t allowed_output_formats[IMGFMT_END - IMGFMT_START]; - - double container_fps; - double display_fps; - - struct mp_log *log; - struct MPOpts *opts; - struct mpv_global *global; - struct mp_hwdec_devices *hwdec_devs; - - // This is a dirty hack. - struct AVBufferRef *in_hwframes_ref; - - // Call when the filter chain wants new processing (for filters with - // asynchronous behavior) - must be immutable once filters are created, - // since they are supposed to call it from foreign threads. - void (*wakeup_callback)(void *ctx); - void *wakeup_callback_ctx; -}; - -enum vf_ctrl { - VFCTRL_SEEK_RESET = 1, // reset on picture and PTS discontinuities - VFCTRL_GET_METADATA, // Get frame metadata from lavfi filters (e.g., cropdetect) - /* Hack to make the OSD state object available to vf_sub which - * access OSD/subtitle state outside of normal OSD draw time. */ - VFCTRL_INIT_OSD, - VFCTRL_COMMAND, -}; - -struct vf_chain *vf_new(struct mpv_global *global); -void vf_destroy(struct vf_chain *c); -void vf_set_proto_frame(struct vf_chain *c, struct mp_image *img); -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); -int vf_output_frame(struct vf_chain *c, bool eof); -int vf_needs_input(struct vf_chain *c); -struct mp_image *vf_read_output_frame(struct vf_chain *c); -void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img); -void vf_seek_reset(struct vf_chain *c); -struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name, - char **args); -void vf_remove_filter(struct vf_chain *c, struct vf_instance *vf); -int vf_append_filter_list(struct vf_chain *c, struct m_obj_settings *list); -struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label); -void vf_print_filter_chain(struct vf_chain *c, int msglevel, - struct vf_instance *vf); - -int vf_send_command(struct vf_chain *c, char *label, char *cmd, char *arg); - -// Filter internal API -struct mp_image *vf_alloc_out_image(struct vf_instance *vf); -bool vf_make_out_image_writeable(struct vf_instance *vf, struct mp_image *img); -void vf_add_output_frame(struct vf_instance *vf, struct mp_image *img); - -// default wrappers: -int vf_next_query_format(struct vf_instance *vf, unsigned int fmt); - -#endif /* MPLAYER_VF_H */ diff --git a/video/filter/vf_convert.c b/video/filter/vf_convert.c deleted file mode 100644 index 7a7fdce228..0000000000 --- a/video/filter/vf_convert.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This file is part of mpv. - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <inttypes.h> -#include <stdarg.h> -#include <assert.h> - -#include <libswscale/swscale.h> - -#include "common/av_common.h" -#include "common/msg.h" - -#include "options/options.h" - -#include "video/img_format.h" -#include "video/mp_image.h" -#include "video/sws_utils.h" -#include "video/fmt-conversion.h" -#include "vf.h" - -struct vf_priv_s { - struct mp_sws_context *sws; -}; - -static int find_best_out(vf_instance_t *vf, int in_format) -{ - int best = 0; - for (int out_format = IMGFMT_START; out_format < IMGFMT_END; out_format++) { - if (!vf_next_query_format(vf, out_format)) - continue; - if (sws_isSupportedOutput(imgfmt2pixfmt(out_format)) < 1) - continue; - if (best) { - int candidate = mp_imgfmt_select_best(best, out_format, in_format); - if (candidate) - best = candidate; - } else { - best = out_format; - } - } - return best; -} - -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) -{ - unsigned int best = find_best_out(vf, in->imgfmt); - if (!best) { - MP_WARN(vf, "no supported output format found\n"); - return -1; - } - - *out = *in; - out->imgfmt = best; - - // If we convert from RGB to YUV, default to limited range. - if (mp_imgfmt_get_forced_csp(in->imgfmt) == MP_CSP_RGB && - mp_imgfmt_get_forced_csp(out->imgfmt) == MP_CSP_AUTO) - out->color.levels = MP_CSP_LEVELS_TV; - - mp_image_params_guess_csp(out); - - mp_sws_set_from_cmdline(vf->priv->sws, vf->chain->global); - vf->priv->sws->src = *in; - vf->priv->sws->dst = *out; - - if (mp_sws_reinit(vf->priv->sws) < 0) { - // error... - MP_WARN(vf, "Couldn't init libswscale for this setup\n"); - return -1; - } - return 0; -} - -static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) -{ - struct mp_image *dmpi = vf_alloc_out_image(vf); - if (!dmpi) - return NULL; - mp_image_copy_attributes(dmpi, mpi); - - mp_sws_scale(vf->priv->sws, dmpi, mpi); - - talloc_free(mpi); - return dmpi; -} - -static int query_format(struct vf_instance *vf, unsigned int fmt) -{ - if (IMGFMT_IS_HWACCEL(fmt) || sws_isSupportedInput(imgfmt2pixfmt(fmt)) < 1) - return 0; - return !!find_best_out(vf, fmt); -} - -static void uninit(struct vf_instance *vf) -{ -} - -static int vf_open(vf_instance_t *vf) -{ - vf->reconfig = reconfig; - vf->filter = filter; - vf->query_format = query_format; - vf->uninit = uninit; - vf->priv->sws = mp_sws_alloc(vf); - vf->priv->sws->log = vf->log; - return 1; -} - -const vf_info_t vf_info_convert = { - .description = "image format conversion with libswscale", - .name = "convert", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), -}; diff --git a/video/filter/vf_d3d11vpp.c b/video/filter/vf_d3d11vpp.c index 3be49ede80..fb96a44e65 100644 --- a/video/filter/vf_d3d11vpp.c +++ b/video/filter/vf_d3d11vpp.c @@ -25,9 +25,13 @@ #include "common/common.h" #include "osdep/timer.h" #include "osdep/windows_utils.h" -#include "vf.h" +#include "filters/f_autoconvert.h" +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "refqueue.h" #include "video/hwdec.h" +#include "video/mp_image.h" #include "video/mp_image_pool.h" // missing in MinGW @@ -38,8 +42,17 @@ #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE 0x10 #define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION 0x20 -struct vf_priv_s { +struct opts { + int deint_enabled; + int interlaced_only; + int mode; +}; + +struct priv { + struct opts *opts; + ID3D11Device *vo_dev; + const int *vo_formats; ID3D11DeviceContext *device_ctx; ID3D11VideoDevice *video_dev; @@ -61,10 +74,6 @@ struct vf_priv_s { struct mp_image_pool *pool; struct mp_refqueue *queue; - - int deint_enabled; - int interlaced_only; - int mode; }; static void release_tex(void *arg) @@ -76,8 +85,8 @@ static void release_tex(void *arg) static struct mp_image *alloc_pool(void *pctx, int fmt, int w, int h) { - struct vf_instance *vf = pctx; - struct vf_priv_s *p = vf->priv; + struct mp_filter *vf = pctx; + struct priv *p = vf->priv; HRESULT hr; ID3D11Texture2D *texture = NULL; @@ -100,7 +109,7 @@ static struct mp_image *alloc_pool(void *pctx, int fmt, int w, int h) if (!mpi) abort(); - mp_image_setfmt(mpi, p->out_params.imgfmt); + mp_image_setfmt(mpi, IMGFMT_D3D11); mp_image_set_size(mpi, w, h); mpi->params.hw_subfmt = p->out_params.hw_subfmt; @@ -110,29 +119,15 @@ static struct mp_image *alloc_pool(void *pctx, int fmt, int w, int h) return mpi; } -static void flush_frames(struct vf_instance *vf) +static void flush_frames(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; mp_refqueue_flush(p->queue); } -static int filter_ext(struct vf_instance *vf, struct mp_image *in) -{ - struct vf_priv_s *p = vf->priv; - - mp_refqueue_set_refs(p->queue, 0, 0); - mp_refqueue_set_mode(p->queue, - (p->deint_enabled ? MP_MODE_DEINT : 0) | - MP_MODE_OUTPUT_FIELDS | - (p->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); - - mp_refqueue_add_input(p->queue, in); - return 0; -} - -static void destroy_video_proc(struct vf_instance *vf) +static void destroy_video_proc(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; if (p->video_proc) ID3D11VideoProcessor_Release(p->video_proc); @@ -143,9 +138,9 @@ static void destroy_video_proc(struct vf_instance *vf) p->vp_enum = NULL; } -static int recreate_video_proc(struct vf_instance *vf) +static int recreate_video_proc(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; HRESULT hr; destroy_video_proc(vf); @@ -168,7 +163,7 @@ static int recreate_video_proc(struct vf_instance *vf) goto fail; MP_VERBOSE(vf, "Found %d rate conversion caps. Looking for caps=0x%x.\n", - (int)caps.RateConversionCapsCount, p->mode); + (int)caps.RateConversionCapsCount, p->opts->mode); int rindex = -1; for (int n = 0; n < caps.RateConversionCapsCount; n++) { @@ -178,7 +173,7 @@ static int recreate_video_proc(struct vf_instance *vf) if (FAILED(hr)) goto fail; MP_VERBOSE(vf, " - %d: 0x%08x\n", n, (unsigned)rcaps.ProcessorCaps); - if (rcaps.ProcessorCaps & p->mode) { + if (rcaps.ProcessorCaps & p->opts->mode) { MP_VERBOSE(vf, " (matching)\n"); if (rindex < 0) rindex = n; @@ -248,17 +243,19 @@ fail: return -1; } -static int render(struct vf_instance *vf) +static struct mp_image *render(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; int res = -1; HRESULT hr; ID3D11VideoProcessorInputView *in_view = NULL; ID3D11VideoProcessorOutputView *out_view = NULL; struct mp_image *in = NULL, *out = NULL; - out = mp_image_pool_get(p->pool, p->out_params.imgfmt, p->params.w, p->params.h); - if (!out) + out = mp_image_pool_get(p->pool, IMGFMT_D3D11, p->params.w, p->params.h); + if (!out) { + MP_WARN(vf, "failed to allocate frame\n"); goto cleanup; + } ID3D11Texture2D *d3d_out_tex = (void *)out->planes[0]; @@ -325,8 +322,10 @@ static int render(struct vf_instance *vf) (ID3D11Resource *)d3d_out_tex, p->vp_enum, &outdesc, &out_view); - if (FAILED(hr)) + if (FAILED(hr)) { + MP_ERR(vf, "Could not create ID3D11VideoProcessorOutputView\n"); goto cleanup; + } D3D11_VIDEO_PROCESSOR_STREAM stream = { .Enable = TRUE, @@ -346,87 +345,73 @@ cleanup: ID3D11VideoProcessorInputView_Release(in_view); if (out_view) ID3D11VideoProcessorOutputView_Release(out_view); - if (res >= 0) { - vf_add_output_frame(vf, out); - } else { - talloc_free(out); - } - mp_refqueue_next_field(p->queue); - return res; + if (res < 0) + TA_FREEP(&out); + return out; } -static int filter_out(struct vf_instance *vf) +static bool vo_supports(struct priv *p, int subfmt) { - struct vf_priv_s *p = vf->priv; - - if (!mp_refqueue_has_output(p->queue)) - return 0; - - // no filtering - if (!mp_refqueue_should_deint(p->queue) && !p->require_filtering) { - struct mp_image *in = mp_image_new_ref(mp_refqueue_get(p->queue, 0)); - if (!in) - return -1; - mp_image_set_params(in, &p->out_params); - vf_add_output_frame(vf, in); - mp_refqueue_next(p->queue); - return 0; + for (int n = 0; p->vo_formats && p->vo_formats[n]; n++) { + if (p->vo_formats[n] == subfmt) + return true; } - - return render(vf); + return false; } -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) +static void vf_d3d11vpp_process(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; - - flush_frames(vf); - talloc_free(p->pool); - p->pool = NULL; + struct priv *p = vf->priv; + + struct mp_image *in_fmt = mp_refqueue_execute_reinit(p->queue); + if (in_fmt) { + mp_image_pool_clear(p->pool); + + destroy_video_proc(vf); + + p->params = in_fmt->params; + p->out_params = p->params; + + if (vo_supports(p, IMGFMT_NV12)) { + p->out_params.hw_subfmt = IMGFMT_NV12; + p->out_format = DXGI_FORMAT_NV12; + p->out_shared = false; + p->out_rgb = false; + } else { + p->out_params.hw_subfmt = IMGFMT_RGB0; + p->out_format = DXGI_FORMAT_B8G8R8A8_UNORM; + p->out_shared = true; + p->out_rgb = true; + } + p->out_params.hw_flags = 0; - destroy_video_proc(vf); + p->require_filtering = p->params.hw_subfmt != p->out_params.hw_subfmt; + } - *out = *in; + if (!mp_refqueue_can_output(p->queue)) + return; - if (vf_next_query_format(vf, IMGFMT_D3D11VA) || - vf_next_query_format(vf, IMGFMT_D3D11NV12)) - { - out->imgfmt = vf_next_query_format(vf, IMGFMT_D3D11VA) - ? IMGFMT_D3D11VA : IMGFMT_D3D11NV12; - out->hw_subfmt = IMGFMT_NV12; - p->out_format = DXGI_FORMAT_NV12; - p->out_shared = false; - p->out_rgb = false; + if (!mp_refqueue_should_deint(p->queue) && !p->require_filtering) { + // no filtering + struct mp_image *in = mp_image_new_ref(mp_refqueue_get(p->queue, 0)); + if (!in) { + mp_filter_internal_mark_failed(vf); + return; + } + mp_refqueue_write_out_pin(p->queue, in); } else { - out->imgfmt = IMGFMT_D3D11RGB; - out->hw_subfmt = IMGFMT_RGB0; - p->out_format = DXGI_FORMAT_B8G8R8A8_UNORM; - p->out_shared = true; - p->out_rgb = true; + mp_refqueue_write_out_pin(p->queue, render(vf)); } - out->hw_flags = 0; - - p->require_filtering = in->hw_subfmt != out->hw_subfmt; - - p->params = *in; - p->out_params = *out; - - p->pool = mp_image_pool_new(vf); - mp_image_pool_set_allocator(p->pool, alloc_pool, vf); - mp_image_pool_set_lru(p->pool); - - return 0; } -static void uninit(struct vf_instance *vf) +static void uninit(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; destroy_video_proc(vf); flush_frames(vf); - mp_refqueue_free(p->queue); + talloc_free(p->queue); talloc_free(p->pool); if (p->video_ctx) @@ -442,69 +427,55 @@ static void uninit(struct vf_instance *vf) ID3D11Device_Release(p->vo_dev); } -static int query_format(struct vf_instance *vf, unsigned int imgfmt) +static const struct mp_filter_info vf_d3d11vpp_filter = { + .name = "d3d11vpp", + .process = vf_d3d11vpp_process, + .reset = flush_frames, + .destroy = uninit, + .priv_size = sizeof(struct priv), +}; + +static struct mp_filter *vf_d3d11vpp_create(struct mp_filter *parent, + void *options) { - if (imgfmt == IMGFMT_D3D11VA || - imgfmt == IMGFMT_D3D11NV12 || - imgfmt == IMGFMT_D3D11RGB) - { - return vf_next_query_format(vf, IMGFMT_D3D11VA) || - vf_next_query_format(vf, IMGFMT_D3D11NV12) || - vf_next_query_format(vf, IMGFMT_D3D11RGB); + struct mp_filter *f = mp_filter_create(parent, &vf_d3d11vpp_filter); + if (!f) { + talloc_free(options); + return NULL; } - return 0; -} -static bool test_conversion(int in, int out) -{ - return (in == IMGFMT_D3D11VA || - in == IMGFMT_D3D11NV12 || - in == IMGFMT_D3D11RGB) && - (out == IMGFMT_D3D11VA || - out == IMGFMT_D3D11NV12 || - out == IMGFMT_D3D11RGB); -} + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); -static int control(struct vf_instance *vf, int request, void* data) -{ - switch (request){ - case VFCTRL_SEEK_RESET: - flush_frames(vf); - return true; - default: - return CONTROL_UNKNOWN; - } -} + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); -static int vf_open(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; + // Special path for vf_d3d11_create_outconv(): disable all processing except + // possibly surface format conversions. + if (!p->opts) { + static const struct opts opts = {0}; + p->opts = (struct opts *)&opts; + } - if (!vf->hwdec_devs) - return 0; + p->queue = mp_refqueue_alloc(f); - vf->reconfig = reconfig; - vf->filter_ext = filter_ext; - vf->filter_out = filter_out; - vf->query_format = query_format; - vf->uninit = uninit; - vf->control = control; + struct mp_stream_info *info = mp_filter_find_stream_info(f); + if (!info || !info->hwdec_devs) + goto fail; - hwdec_devices_request_all(vf->hwdec_devs); - AVBufferRef *ref = - hwdec_devices_get_lavc(vf->hwdec_devs, AV_HWDEVICE_TYPE_D3D11VA); - if (!ref) - return 0; + hwdec_devices_request_all(info->hwdec_devs); - AVHWDeviceContext *hwctx = (void *)ref->data; - AVD3D11VADeviceContext *d3dctx = hwctx->hwctx; + struct mp_hwdec_ctx *hwctx = + hwdec_devices_get_by_lavc(info->hwdec_devs, AV_HWDEVICE_TYPE_D3D11VA); + if (!hwctx || !hwctx->av_device_ref) + goto fail; + AVHWDeviceContext *avhwctx = (void *)hwctx->av_device_ref->data; + AVD3D11VADeviceContext *d3dctx = avhwctx->hwctx; p->vo_dev = d3dctx->device; ID3D11Device_AddRef(p->vo_dev); - av_buffer_unref(&ref); - - p->queue = mp_refqueue_alloc(); + p->vo_formats = hwctx->supported_formats; HRESULT hr; @@ -521,14 +492,26 @@ static int vf_open(vf_instance_t *vf) if (FAILED(hr)) goto fail; - return 1; + p->pool = mp_image_pool_new(f); + mp_image_pool_set_allocator(p->pool, alloc_pool, f); + mp_image_pool_set_lru(p->pool); + + mp_refqueue_add_in_format(p->queue, IMGFMT_D3D11, 0); + + mp_refqueue_set_refs(p->queue, 0, 0); + mp_refqueue_set_mode(p->queue, + (p->opts->deint_enabled ? MP_MODE_DEINT : 0) | + MP_MODE_OUTPUT_FIELDS | + (p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); + + return f; fail: - uninit(vf); - return 0; + talloc_free(f); + return NULL; } -#define OPT_BASE_STRUCT struct vf_priv_s +#define OPT_BASE_STRUCT struct opts static const m_option_t vf_opts_fields[] = { OPT_FLAG("deint", deint_enabled, 0), OPT_FLAG("interlaced-only", interlaced_only, 0), @@ -542,16 +525,25 @@ static const m_option_t vf_opts_fields[] = { {0} }; -const vf_info_t vf_info_d3d11vpp = { - .description = "D3D11 Video Post-Process Filter", - .name = "d3d11vpp", - .test_conversion = test_conversion, - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .priv_defaults = &(const struct vf_priv_s) { - .deint_enabled = 1, - .interlaced_only = 1, - .mode = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB, +const struct mp_user_filter_entry vf_d3d11vpp = { + .desc = { + .description = "D3D11 Video Post-Process Filter", + .name = "d3d11vpp", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT) { + .deint_enabled = 1, + .interlaced_only = 1, + .mode = D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB, + }, + .options = vf_opts_fields, }, - .options = vf_opts_fields, + .create = vf_d3d11vpp_create, }; + +// Create a filter for the purpose of converting the sub-format for hwdec +// interops which are incapable of handling some formats (ANGLE). +struct mp_filter *vf_d3d11_create_outconv(struct mp_filter *parent) +{ + // options==NULL is normally not allowed, and specially handled. + return vf_d3d11vpp_create(parent, NULL); +} diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c index aab30855d6..48eb51a795 100644 --- a/video/filter/vf_format.c +++ b/video/filter/vf_format.c @@ -25,14 +25,21 @@ #include "common/msg.h" #include "common/common.h" - +#include "filters/f_autoconvert.h" +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "video/img_format.h" #include "video/mp_image.h" -#include "vf.h" #include "options/m_option.h" -struct vf_priv_s { +struct priv { + struct vf_format_opts *opts; + struct mp_pin *in_pin; +}; + +struct vf_format_opts { int fmt; int outfmt; int colormatrix; @@ -51,43 +58,29 @@ struct vf_priv_s { float spherical_ref_angles[3]; }; -static bool is_compatible(int fmt1, int fmt2) +static void vf_format_process(struct mp_filter *f) { - struct mp_imgfmt_desc d1 = mp_imgfmt_get_desc(fmt1); - struct mp_imgfmt_desc d2 = mp_imgfmt_get_desc(fmt2); - if (d1.num_planes < d2.num_planes) - return false; - if (!(d1.flags & MP_IMGFLAG_BYTE_ALIGNED) || - !(d2.flags & MP_IMGFLAG_BYTE_ALIGNED)) - return false; - for (int n = 0; n < MPMIN(d1.num_planes, d2.num_planes); n++) { - if (d1.bytes[n] != d2.bytes[n]) - return false; - if (d1.xs[n] != d2.xs[n] || d1.ys[n] != d2.ys[n]) - return false; - } - return true; -} + struct priv *priv = f->priv; + struct vf_format_opts *p = priv->opts; -static int query_format(struct vf_instance *vf, unsigned int fmt) -{ - if (fmt == vf->priv->fmt || !vf->priv->fmt) { - if (vf->priv->outfmt) { - if (!is_compatible(fmt, vf->priv->outfmt)) - return 0; - fmt = vf->priv->outfmt; - } - return vf_next_query_format(vf, fmt); - } - return 0; -} + if (!mp_pin_can_transfer_data(f->ppins[1], priv->in_pin)) + return; -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) -{ - struct vf_priv_s *p = vf->priv; + struct mp_frame frame = mp_pin_out_read(priv->in_pin); + + if (mp_frame_is_signaling(frame)) { + mp_pin_in_write(f->ppins[1], frame); + return; + } + if (frame.type != MP_FRAME_VIDEO) { + MP_ERR(f, "unsupported frame type\n"); + mp_frame_unref(&frame); + mp_filter_internal_mark_failed(f); + return; + } - *out = *in; + struct mp_image *img = frame.data; + struct mp_image_params *out = &img->params; if (p->outfmt) out->imgfmt = p->outfmt; @@ -98,8 +91,9 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in, if (p->primaries) out->color.primaries = p->primaries; if (p->gamma) { + enum mp_csp_trc in_gamma = p->gamma; out->color.gamma = p->gamma; - if (in->color.gamma != out->color.gamma) { + if (in_gamma != out->color.gamma) { // When changing the gamma function explicitly, also reset stuff // related to the gamma function since that information will almost // surely be false now and have to be re-inferred @@ -140,28 +134,47 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in, // Make sure the user-overrides are consistent (no RGB csp for YUV, etc.). mp_image_params_guess_csp(out); - return 0; + mp_pin_in_write(f->ppins[1], frame); } -static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) -{ - if (vf->priv->outfmt) - mp_image_setfmt(mpi, vf->priv->outfmt); - return mpi; -} +static const struct mp_filter_info vf_format_filter = { + .name = "format", + .process = vf_format_process, + .priv_size = sizeof(struct priv), +}; -static int vf_open(vf_instance_t *vf) +static struct mp_filter *vf_format_create(struct mp_filter *parent, void *options) { - vf->query_format = query_format; - vf->reconfig = reconfig; - vf->filter = filter; - return 1; + struct mp_filter *f = mp_filter_create(parent, &vf_format_filter); + if (!f) { + talloc_free(options); + return NULL; + } + + struct priv *priv = f->priv; + priv->opts = talloc_steal(priv, options); + + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) { + talloc_free(f); + return NULL; + } + + if (priv->opts->fmt) + mp_autoconvert_add_imgfmt(conv, priv->opts->fmt, 0); + + priv->in_pin = conv->f->pins[1]; + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + + return f; } -#define OPT_BASE_STRUCT struct vf_priv_s +#define OPT_BASE_STRUCT struct vf_format_opts static const m_option_t vf_opts_fields[] = { OPT_IMAGEFORMAT("fmt", fmt, 0), - OPT_IMAGEFORMAT("outfmt", outfmt, 0), OPT_CHOICE_C("colormatrix", colormatrix, 0, mp_csp_names), OPT_CHOICE_C("colorlevels", colorlevels, 0, mp_csp_levels_names), OPT_CHOICE_C("primaries", primaries, 0, mp_csp_prim_names), @@ -184,14 +197,16 @@ static const m_option_t vf_opts_fields[] = { {0} }; -const vf_info_t vf_info_format = { - .description = "force output format", - .name = "format", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .options = vf_opts_fields, - .priv_defaults = &(const struct vf_priv_s){ - .rotate = -1, - .spherical_ref_angles = {NAN, NAN, NAN}, +const struct mp_user_filter_entry vf_format = { + .desc = { + .description = "force output format", + .name = "format", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT){ + .rotate = -1, + .spherical_ref_angles = {NAN, NAN, NAN}, + }, + .options = vf_opts_fields, }, + .create = vf_format_create, }; diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c deleted file mode 100644 index 0cd3af8673..0000000000 --- a/video/filter/vf_lavfi.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * This file is part of mpv. - * - * Filter graph creation code taken from Libav avplay.c (LGPL 2.1 or later) - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <inttypes.h> -#include <stdarg.h> -#include <assert.h> - -#include <libavutil/avstring.h> -#include <libavutil/hwcontext.h> -#include <libavutil/mem.h> -#include <libavutil/mathematics.h> -#include <libavutil/rational.h> -#include <libavutil/pixdesc.h> -#include <libavutil/time.h> -#include <libavutil/error.h> -#include <libswscale/swscale.h> -#include <libavfilter/avfilter.h> -#include <libavfilter/buffersink.h> -#include <libavfilter/buffersrc.h> - -#include "config.h" -#include "common/av_common.h" -#include "common/msg.h" -#include "options/m_option.h" -#include "common/tags.h" - -#include "video/hwdec.h" -#include "video/img_format.h" -#include "video/mp_image.h" -#include "video/sws_utils.h" -#include "video/fmt-conversion.h" -#include "vf.h" - -// FFmpeg and Libav have slightly different APIs, just enough to cause us -// unnecessary pain. <Expletive deleted.> -#if LIBAVFILTER_VERSION_MICRO < 100 -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx) -#define avfilter_graph_send_command(a, b, c, d, e, f, g) -1 -#else -#define graph_parse(graph, filters, inputs, outputs, log_ctx) \ - avfilter_graph_parse_ptr(graph, filters, &(inputs), &(outputs), log_ctx) -#endif - -struct vf_priv_s { - // Single filter bridge, instead of a graph. - bool is_bridge; - - AVFilterGraph *graph; - AVFilterContext *in; - AVFilterContext *out; - bool eof; - - AVRational timebase_in; - AVRational timebase_out; - AVRational par_in; - - struct mp_tags *metadata; - - // for the lw wrapper - void *old_priv; - int (*lw_reconfig_cb)(struct vf_instance *vf, - struct mp_image_params *in, - struct mp_image_params *out); - - // options - char *cfg_graph; - int64_t cfg_sws_flags; - char **cfg_avopts; - - char *cfg_filter_name; - char **cfg_filter_opts; -}; - -static void destroy_graph(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - avfilter_graph_free(&p->graph); - p->in = p->out = NULL; - - if (p->metadata) { - talloc_free(p->metadata); - p->metadata = NULL; - } - - p->eof = false; -} - -static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) -{ - void *tmp = talloc_new(NULL); - struct vf_priv_s *p = vf->priv; - AVFilterContext *in = NULL, *out = NULL; - int ret; - - if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) { - MP_FATAL(vf, "lavfi: no filter graph set\n"); - return false; - } - - destroy_graph(vf); - - AVFilterGraph *graph = avfilter_graph_alloc(); - if (!graph) - goto error; - - if (mp_set_avopts(vf->log, graph, p->cfg_avopts) < 0) - goto error; - - AVFilterInOut *outputs = avfilter_inout_alloc(); - AVFilterInOut *inputs = avfilter_inout_alloc(); - if (!outputs || !inputs) - goto error; - - char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags); - graph->scale_sws_opts = av_strdup(sws_flags); - - in = avfilter_graph_alloc_filter(graph, avfilter_get_by_name("buffer"), "src"); - if (!in) - goto error; - - AVBufferSrcParameters *in_params = av_buffersrc_parameters_alloc(); - if (!in_params) - goto error; - - in_params->format = imgfmt2pixfmt(fmt->imgfmt); - in_params->time_base = AV_TIME_BASE_Q; - in_params->width = fmt->w; - in_params->height = fmt->h; - in_params->sample_aspect_ratio.num = fmt->p_w; - in_params->sample_aspect_ratio.den = fmt->p_h; - // Assume it's ignored for non-hwaccel formats. - in_params->hw_frames_ctx = vf->in_hwframes_ref; - - ret = av_buffersrc_parameters_set(in, in_params); - av_free(in_params); - if (ret < 0) - goto error; - - if (avfilter_init_str(in, NULL) < 0) - goto error; - - if (avfilter_graph_create_filter(&out, avfilter_get_by_name("buffersink"), - "out", NULL, NULL, graph) < 0) - goto error; - - if (p->is_bridge) { - AVFilterContext *filter = avfilter_graph_alloc_filter(graph, - avfilter_get_by_name(p->cfg_filter_name), "filter"); - if (!filter) - goto error; - - if (mp_set_avopts(vf->log, filter->priv, p->cfg_filter_opts) < 0) - goto error; - - if (avfilter_init_str(filter, NULL) < 0) - goto error; - - // Yep, we have to manually link those filters. - if (filter->nb_inputs != 1 || - avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_VIDEO || - filter->nb_outputs != 1 || - avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_VIDEO) - { - MP_ERR(vf, "The filter is required to have 1 video input pad and " - "1 video output pad.\n"); - goto error; - } - if (avfilter_link(in, 0, filter, 0) < 0 || - avfilter_link(filter, 0, out, 0) < 0) - { - MP_ERR(vf, "Failed to link filter.\n"); - goto error; - } - } else { - MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = in; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = out; - - if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) - goto error; - } - - if (vf->hwdec_devs) { - struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs); - for (int n = 0; n < graph->nb_filters; n++) { - AVFilterContext *filter = graph->filters[n]; - if (hwdec && hwdec->av_device_ref) - filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref); - } - } - - if (avfilter_graph_config(graph, NULL) < 0) - goto error; - - p->in = in; - p->out = out; - p->graph = graph; - - assert(out->nb_inputs == 1); - assert(in->nb_outputs == 1); - - talloc_free(tmp); - return true; - -error: - MP_FATAL(vf, "Can't configure libavfilter graph.\n"); - avfilter_graph_free(&graph); - talloc_free(tmp); - return false; -} - -static void reset(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; - struct mp_image_params *f = &vf->fmt_in; - if (p->graph && f->imgfmt) - recreate_graph(vf, f); -} - -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) -{ - struct vf_priv_s *p = vf->priv; - - *out = *in; // pass-through untouched flags - - if (vf->priv->lw_reconfig_cb) { - if (vf->priv->lw_reconfig_cb(vf, in, out) < 0) - return -1; - } - - if (!recreate_graph(vf, in)) - return -1; - - AVFilterLink *l_out = p->out->inputs[0]; - AVFilterLink *l_in = p->in->outputs[0]; - - p->timebase_in = l_in->time_base; - p->timebase_out = l_out->time_base; - - p->par_in = l_in->sample_aspect_ratio; - - out->w = l_out->w; - out->h = l_out->h; - out->p_w = l_out->sample_aspect_ratio.num; - out->p_h = l_out->sample_aspect_ratio.den; - out->imgfmt = pixfmt2imgfmt(l_out->format); - av_buffer_unref(&vf->out_hwframes_ref); -#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(6, 69, 100) && \ - LIBAVFILTER_VERSION_MICRO >= 100 - AVBufferRef *hw_frames_ctx = av_buffersink_get_hw_frames_ctx(p->out); -#else - AVBufferRef *hw_frames_ctx = l_out->hw_frames_ctx; -#endif - if (hw_frames_ctx) { - AVHWFramesContext *fctx = (void *)hw_frames_ctx->data; - out->hw_subfmt = pixfmt2imgfmt(fctx->sw_format); - vf->out_hwframes_ref = av_buffer_ref(hw_frames_ctx); - } - return 0; -} - -static int query_format(struct vf_instance *vf, unsigned int fmt) -{ - // Format negotiation is not possible with libavfilter. - return 1; -} - -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); - if (!frame) - return NULL; // OOM is (coincidentally) handled as EOF - frame->pts = pts; - frame->sample_aspect_ratio = p->par_in; - return frame; -} - -static struct mp_image *av_to_mp(struct vf_instance *vf, AVFrame *av_frame) -{ - struct vf_priv_s *p = vf->priv; - struct mp_image *img = mp_image_from_av_frame(av_frame); - if (!img) - return NULL; // OOM - img->pts = av_frame->pts == AV_NOPTS_VALUE ? - MP_NOPTS_VALUE : av_frame->pts * av_q2d(p->timebase_out); - av_frame_free(&av_frame); - return img; -} - -static void get_metadata_from_av_frame(struct vf_instance *vf, AVFrame *frame) -{ -#if LIBAVUTIL_VERSION_MICRO >= 100 - struct vf_priv_s *p = vf->priv; - if (!p->metadata) - p->metadata = talloc_zero(p, struct mp_tags); - - mp_tags_copy_from_av_dictionary(p->metadata, frame->metadata); -#endif -} - -static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) -{ - struct vf_priv_s *p = vf->priv; - - if (p->eof && mpi) { - // Once EOF is reached, libavfilter is "stuck" in the EOF state, and - // won't accept new input. Forcefully override it. This helps e.g. - // with cover art, where we always want to generate new output. - reset(vf); - } - - if (!p->graph) - return -1; - - if (!mpi) { - if (p->eof) - return 0; - p->eof = true; - } - - AVFrame *frame = mp_to_av(vf, mpi); - int r = av_buffersrc_add_frame(p->in, frame) < 0 ? -1 : 0; - av_frame_free(&frame); - - return r; -} - -static int filter_out(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - - AVFrame *frame = av_frame_alloc(); - int err = av_buffersink_get_frame(p->out, frame); - if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) { - // Not an error situation - no more output buffers in queue. - // AVERROR_EOF means we shouldn't even give the filter more - // input, but we don't handle that completely correctly. - av_frame_free(&frame); - p->eof |= err == AVERROR_EOF; - return 0; - } - if (err < 0) { - av_frame_free(&frame); - MP_ERR(vf, "libavfilter error: %d\n", err); - return -1; - } - - get_metadata_from_av_frame(vf, frame); - vf_add_output_frame(vf, av_to_mp(vf, frame)); - return 0; -} - -static int control(vf_instance_t *vf, int request, void *data) -{ - switch (request) { - case VFCTRL_SEEK_RESET: - reset(vf); - return CONTROL_OK; - case VFCTRL_COMMAND: { - if (!vf->priv->graph) - break; - char **args = data; - return avfilter_graph_send_command(vf->priv->graph, "all", - args[0], args[1], &(char){0}, 0, 0) - >= 0 ? CONTROL_OK : CONTROL_ERROR; - } - case VFCTRL_GET_METADATA: - if (vf->priv && vf->priv->metadata) { - *(struct mp_tags *)data = *vf->priv->metadata; - return CONTROL_OK; - } else { - return CONTROL_NA; - } - } - return CONTROL_UNKNOWN; -} - -static void uninit(struct vf_instance *vf) -{ - if (!vf->priv) - return; - destroy_graph(vf); -} - -static int vf_open(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; - - vf->reconfig = reconfig; - vf->filter_ext = filter_ext; - vf->filter_out = filter_out; - vf->filter = NULL; - vf->query_format = query_format; - vf->control = control; - vf->uninit = uninit; - - if (p->is_bridge) { - if (!p->cfg_filter_name) { - MP_ERR(vf, "Filter name not set!\n"); - return 0; - } - if (!avfilter_get_by_name(p->cfg_filter_name)) { - MP_ERR(vf, "libavfilter filter '%s' not found!\n", p->cfg_filter_name); - return 0; - } - } - - return 1; -} - -static bool is_single_video_only(const AVFilterPad *pads) -{ - int count = avfilter_pad_count(pads); - if (count != 1) - return false; - return avfilter_pad_get_type(pads, 0) == AVMEDIA_TYPE_VIDEO; -} - -// Does it have exactly one video input and one video output? -static bool is_usable(const AVFilter *filter) -{ - return is_single_video_only(filter->inputs) && - is_single_video_only(filter->outputs); -} - -static void print_help(struct mp_log *log) -{ - mp_info(log, "List of libavfilter filters:\n"); - for (const AVFilter *filter = avfilter_next(NULL); filter; - filter = avfilter_next(filter)) - { - if (is_usable(filter)) - mp_info(log, " %-16s %s\n", filter->name, filter->description); - } - mp_info(log, "\n" - "This lists video->video filters only. Refer to\n" - "\n" - " https://ffmpeg.org/ffmpeg-filters.html\n" - "\n" - "to see how to use each filter and what arguments each filter takes.\n" - "Also, be sure to quote the FFmpeg filter string properly, e.g.:\n" - "\n" - " \"--vf=lavfi=[gradfun=20:30]\"\n" - "\n" - "Otherwise, mpv and libavfilter syntax will conflict.\n" - "\n"); -} - -#define OPT_BASE_STRUCT struct vf_priv_s -static const m_option_t vf_opts_fields[] = { - OPT_STRING("graph", cfg_graph, M_OPT_MIN, .min = 1), - OPT_INT64("sws-flags", cfg_sws_flags, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} -}; - -const vf_info_t vf_info_lavfi = { - .description = "libavfilter bridge", - .name = "lavfi", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .priv_defaults = &(const struct vf_priv_s){ - .cfg_sws_flags = SWS_BICUBIC, - }, - .options = vf_opts_fields, - .print_help = print_help, -}; - -const vf_info_t vf_info_lavfi_bridge = { - .description = "libavfilter bridge (explicit options)", - .name = "lavfi-bridge", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .options = (const m_option_t[]) { - OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1), - OPT_KEYVALUELIST("opts", cfg_filter_opts, 0), - OPT_INT64("sws-flags", cfg_sws_flags, 0), - OPT_KEYVALUELIST("o", cfg_avopts, 0), - {0} - }, - .priv_defaults = &(const struct vf_priv_s){ - .is_bridge = true, - .cfg_sws_flags = SWS_BICUBIC, - }, - .print_help = print_help, -}; diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c index 065a8c66c7..8b2c1c83f0 100644 --- a/video/filter/vf_sub.c +++ b/video/filter/vf_sub.c @@ -29,11 +29,13 @@ #include "config.h" #include "common/msg.h" +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "options/options.h" - #include "video/img_format.h" #include "video/mp_image.h" -#include "vf.h" +#include "video/mp_image_pool.h" #include "sub/osd.h" #include "sub/dec_sub.h" @@ -41,112 +43,121 @@ #include "options/m_option.h" -struct vf_priv_s { - int opt_top_margin, opt_bottom_margin; - - int outh, outw; +struct vf_sub_opts { + int top_margin, bottom_margin; +}; - struct osd_state *osd; - struct mp_osd_res dim; +struct priv { + struct vf_sub_opts *opts; + struct mp_image_pool *pool; }; -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) +static void vf_sub_process(struct mp_filter *f) { - int width = in->w, height = in->h; - - vf->priv->outh = height + vf->priv->opt_top_margin + - vf->priv->opt_bottom_margin; - vf->priv->outw = width; - - vf->priv->dim = (struct mp_osd_res) { - .w = vf->priv->outw, - .h = vf->priv->outh, - .mt = vf->priv->opt_top_margin, - .mb = vf->priv->opt_bottom_margin, - .display_par = in->p_w / (double)in->p_h, - }; + struct priv *priv = f->priv; - *out = *in; - out->w = vf->priv->outw; - out->h = vf->priv->outh; - return 0; -} + if (!mp_pin_can_transfer_data(f->ppins[1], f->ppins[0])) + return; -static void prepare_image(struct vf_instance *vf, struct mp_image *dmpi, - struct mp_image *mpi) -{ - int y1 = MP_ALIGN_DOWN(vf->priv->opt_top_margin, mpi->fmt.align_y); - int y2 = MP_ALIGN_DOWN(y1 + mpi->h, mpi->fmt.align_y); - struct mp_image cropped = *dmpi; - mp_image_crop(&cropped, 0, y1, mpi->w, y1 + mpi->h); - mp_image_copy(&cropped, mpi); - mp_image_clear(dmpi, 0, 0, dmpi->w, y1); - mp_image_clear(dmpi, 0, y2, dmpi->w, vf->priv->outh); -} + struct mp_frame frame = mp_pin_out_read(f->ppins[0]); -static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) -{ - struct vf_priv_s *priv = vf->priv; - struct osd_state *osd = priv->osd; + if (mp_frame_is_signaling(frame)) { + mp_pin_in_write(f->ppins[1], frame); + return; + } + + struct mp_stream_info *info = mp_filter_find_stream_info(f); + struct osd_state *osd = info ? info->osd : NULL; + + if (!osd) + goto error; + + osd_set_render_subs_in_filter(osd, true); + + if (frame.type != MP_FRAME_VIDEO) + goto error; + + struct mp_image *mpi = frame.data; - if (vf->priv->opt_top_margin || vf->priv->opt_bottom_margin) { - struct mp_image *dmpi = vf_alloc_out_image(vf); + if (!mp_sws_supported_format(mpi->imgfmt)) + goto error; + + struct mp_osd_res dim = { + .w = mpi->w, + .h = mpi->h + priv->opts->top_margin + priv->opts->bottom_margin, + .mt = priv->opts->top_margin, + .mb = priv->opts->bottom_margin, + .display_par = mpi->params.p_w / (double)mpi->params.p_h, + }; + + if (dim.w != mpi->w || dim.h != mpi->h) { + struct mp_image *dmpi = + mp_image_pool_get(priv->pool, mpi->imgfmt, dim.w, dim.h); if (!dmpi) - return NULL; + goto error; mp_image_copy_attributes(dmpi, mpi); - prepare_image(vf, dmpi, mpi); - talloc_free(mpi); + int y1 = MP_ALIGN_DOWN(priv->opts->top_margin, mpi->fmt.align_y); + int y2 = MP_ALIGN_DOWN(y1 + mpi->h, mpi->fmt.align_y); + struct mp_image cropped = *dmpi; + mp_image_crop(&cropped, 0, y1, mpi->w, y1 + mpi->h); + mp_image_copy(&cropped, mpi); + mp_image_clear(dmpi, 0, 0, dmpi->w, y1); + mp_image_clear(dmpi, 0, y2, dmpi->w, dim.h); + mp_frame_unref(&frame); mpi = dmpi; + frame = (struct mp_frame){MP_FRAME_VIDEO, mpi}; } - if (!osd) - return mpi; + osd_draw_on_image_p(osd, dim, mpi->pts, OSD_DRAW_SUB_FILTER, priv->pool, mpi); - osd_draw_on_image_p(osd, priv->dim, mpi->pts, OSD_DRAW_SUB_FILTER, - vf->out_pool, mpi); + mp_pin_in_write(f->ppins[1], frame); + return; - return mpi; +error: + MP_ERR(f, "unsupported format, missing OSD, or failed allocation\n"); + mp_frame_unref(&frame); + mp_filter_internal_mark_failed(f); } -static int query_format(struct vf_instance *vf, unsigned int fmt) -{ - if (!mp_sws_supported_format(fmt)) - return 0; - return vf_next_query_format(vf, fmt); -} +static const struct mp_filter_info vf_sub_filter = { + .name = "sub", + .process = vf_sub_process, + .priv_size = sizeof(struct priv), +}; -static int control(vf_instance_t *vf, int request, void *data) +static struct mp_filter *vf_sub_create(struct mp_filter *parent, void *options) { - switch (request) { - case VFCTRL_INIT_OSD: - vf->priv->osd = data; - return CONTROL_TRUE; + struct mp_filter *f = mp_filter_create(parent, &vf_sub_filter); + if (!f) { + talloc_free(options); + return NULL; } - return CONTROL_UNKNOWN; -} -static int vf_open(vf_instance_t *vf) -{ - MP_WARN(vf, "This filter is deprecated and will be removed (no replacement)\n"); - vf->reconfig = reconfig; - vf->query_format = query_format; - vf->control = control; - vf->filter = filter; - return 1; + MP_WARN(f, "This filter is deprecated and will be removed (no replacement)\n"); + + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + struct priv *priv = f->priv; + priv->opts = talloc_steal(priv, options); + priv->pool = mp_image_pool_new(priv); + + return f; } -#define OPT_BASE_STRUCT struct vf_priv_s +#define OPT_BASE_STRUCT struct vf_sub_opts static const m_option_t vf_opts_fields[] = { - OPT_INTRANGE("bottom-margin", opt_bottom_margin, 0, 0, 2000), - OPT_INTRANGE("top-margin", opt_top_margin, 0, 0, 2000), + OPT_INTRANGE("bottom-margin", bottom_margin, 0, 0, 2000), + OPT_INTRANGE("top-margin", top_margin, 0, 0, 2000), {0} }; -const vf_info_t vf_info_sub = { - .description = "Render subtitles", - .name = "sub", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .options = vf_opts_fields, +const struct mp_user_filter_entry vf_sub = { + .desc = { + .description = "Render subtitles", + .name = "sub", + .priv_size = sizeof(OPT_BASE_STRUCT), + .options = vf_opts_fields, + }, + .create = vf_sub_create, }; diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c index b5aad7abbf..4de09794e6 100644 --- a/video/filter/vf_vapoursynth.c +++ b/video/filter/vf_vapoursynth.c @@ -34,13 +34,27 @@ #include "common/msg.h" #include "options/m_option.h" #include "options/path.h" - +#include "filters/f_autoconvert.h" +#include "filters/f_utils.h" +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "video/img_format.h" #include "video/mp_image.h" #include "video/sws_utils.h" -#include "vf.h" -struct vf_priv_s { +struct vapoursynth_opts { + char *file; + int maxbuffer; + int maxrequests; + + const struct script_driver *drv; +}; + +struct priv { + struct mp_log *log; + struct vapoursynth_opts *opts; + VSCore *vscore; const VSAPI *vsapi; VSNodeRef *out_node; @@ -56,16 +70,20 @@ struct vf_priv_s { VSMap **gc_map; int num_gc_map; + struct mp_filter *f; + struct mp_pin *in_pin; + + // Format for which VS is currently configured. struct mp_image_params fmt_in; pthread_mutex_t lock; pthread_cond_t wakeup; // --- the following members are all protected by lock - struct mp_image *next_image;// used to compute frame duration of oldest image struct mp_image **buffered; // oldest image first int num_buffered; int in_frameno; // frame number of buffered[0] (the oldest) + int requested_frameno; // last frame number for which we woke up core int out_frameno; // frame number of first requested/ready frame double out_pts; // pts corresponding to first requested/ready frame struct mp_image **requested;// frame callback results (can point to dummy_img) @@ -74,25 +92,25 @@ struct vf_priv_s { bool failed; // frame callback returned with an error bool shutdown; // ask node to return bool eof; // drain remaining data - int64_t frames_sent; + int64_t frames_sent; // total nr. of frames ever added to input queue bool initializing; // filters are being built bool in_node_active; // node might still be called - - // --- options - char *cfg_file; - int cfg_maxbuffer; - int cfg_maxrequests; }; // priv->requested[n] points to this if a request for frame n is in-progress static const struct mp_image dummy_img; +// or if a request failed during EOF/reinit draining +static const struct mp_image dummy_img_eof; + +static void destroy_vs(struct priv *p); +static int reinit_vs(struct priv *p); struct script_driver { - int (*init)(struct vf_instance *vf); // first time init - void (*uninit)(struct vf_instance *vf); // last time uninit - int (*load_core)(struct vf_instance *vf); // make vsapi/vscore available - int (*load)(struct vf_instance *vf, VSMap *vars); // also set p->out_node - void (*unload)(struct vf_instance *vf); // unload script and maybe vs + int (*init)(struct priv *p); // first time init + void (*uninit)(struct priv *p); // last time uninit + int (*load_core)(struct priv *p); // make vsapi/vscore available + int (*load)(struct priv *p, VSMap *vars); // also sets p->out_node + void (*unload)(struct priv *p); // unload script and maybe vs }; struct mpvs_fmt { @@ -166,7 +184,7 @@ static int mp_from_vs(VSPresetFormat vs) return 0; } -static void copy_mp_to_vs_frame_props_map(struct vf_priv_s *p, VSMap *map, +static void copy_mp_to_vs_frame_props_map(struct priv *p, VSMap *map, struct mp_image *img) { struct mp_image_params *params = &img->params; @@ -197,7 +215,7 @@ static void copy_mp_to_vs_frame_props_map(struct vf_priv_s *p, VSMap *map, p->vsapi->propSetInt(map, "_FieldBased", field, 0); } -static int set_vs_frame_props(struct vf_priv_s *p, VSFrameRef *frame, +static int set_vs_frame_props(struct priv *p, VSFrameRef *frame, struct mp_image *img, int dur_num, int dur_den) { VSMap *map = p->vsapi->getFramePropsRW(frame); @@ -209,14 +227,14 @@ static int set_vs_frame_props(struct vf_priv_s *p, VSFrameRef *frame, return 0; } -static VSFrameRef *alloc_vs_frame(struct vf_priv_s *p, struct mp_image_params *fmt) +static VSFrameRef *alloc_vs_frame(struct priv *p, struct mp_image_params *fmt) { const VSFormat *vsfmt = p->vsapi->getFormatPreset(mp_to_vs(fmt->imgfmt), p->vscore); return p->vsapi->newVideoFrame(vsfmt, fmt->w, fmt->h, NULL, p->vscore); } -static struct mp_image map_vs_frame(struct vf_priv_s *p, const VSFrameRef *ref, +static struct mp_image map_vs_frame(struct priv *p, const VSFrameRef *ref, bool w) { const VSFormat *fmt = p->vsapi->getFrameFormat(ref); @@ -238,7 +256,7 @@ static struct mp_image map_vs_frame(struct vf_priv_s *p, const VSFrameRef *ref, return img; } -static void drain_oldest_buffered_frame(struct vf_priv_s *p) +static void drain_oldest_buffered_frame(struct priv *p) { if (!p->num_buffered) return; @@ -252,185 +270,170 @@ static void drain_oldest_buffered_frame(struct vf_priv_s *p) static void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n, VSNodeRef *node, const char *errorMsg) { - struct vf_instance *vf = userData; - struct vf_priv_s *p = vf->priv; + struct priv *p = userData; pthread_mutex_lock(&p->lock); // If these assertions fail, n is an unrequested frame (or filtered twice). assert(n >= p->out_frameno && n < p->out_frameno + p->max_requests); int index = n - p->out_frameno; - MP_TRACE(vf, "filtered frame %d (%d)\n", n, index); + MP_TRACE(p, "filtered frame %d (%d)\n", n, index); assert(p->requested[index] == &dummy_img); struct mp_image *res = NULL; if (f) { struct mp_image img = map_vs_frame(p, f, false); - img.pts = MP_NOPTS_VALUE; + img.pkt_duration = -1; const VSMap *map = p->vsapi->getFramePropsRO(f); if (map) { int err1, err2; int num = p->vsapi->propGetInt(map, "_DurationNum", 0, &err1); int den = p->vsapi->propGetInt(map, "_DurationDen", 0, &err2); if (!err1 && !err2) - img.pts = num / (double)den; // abusing pts for frame length + img.pkt_duration = num / (double)den; } - if (img.pts == MP_NOPTS_VALUE) - MP_ERR(vf, "No PTS after filter at frame %d!\n", n); + if (img.pkt_duration < 0) + MP_ERR(p, "No PTS after filter at frame %d!\n", n); res = mp_image_new_copy(&img); p->vsapi->freeFrame(f); } - if (!res) { - p->failed = true; - MP_ERR(vf, "Filter error at frame %d: %s\n", n, errorMsg); + if (!res && !p->shutdown) { + if (p->eof) { + res = (struct mp_image *)&dummy_img_eof; + } else { + p->failed = true; + MP_ERR(p, "Filter error at frame %d: %s\n", n, errorMsg); + } } p->requested[index] = res; pthread_cond_broadcast(&p->wakeup); pthread_mutex_unlock(&p->lock); + mp_filter_wakeup(p->f); } -static bool locked_need_input(struct vf_instance *vf) +static void vf_vapoursynth_process(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; - return p->num_buffered < MP_TALLOC_AVAIL(p->buffered); -} + struct priv *p = f->priv; -// Return true if progress was made. -static bool locked_read_output(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - bool r = false; + pthread_mutex_lock(&p->lock); - // Move finished frames from the request slots to the vf output queue. - while (p->requested[0] && p->requested[0] != &dummy_img) { - struct mp_image *out = p->requested[0]; - if (out->pts != MP_NOPTS_VALUE) { - double duration = out->pts; - out->pts = p->out_pts; - p->out_pts += duration; - } - vf_add_output_frame(vf, out); - for (int n = 0; n < p->max_requests - 1; n++) - p->requested[n] = p->requested[n + 1]; - p->requested[p->max_requests - 1] = NULL; - p->out_frameno++; - r = true; + if (p->failed) { + // Not sure what we do on errors, but at least don't deadlock. + MP_ERR(f, "failed, no action taken\n"); + mp_filter_internal_mark_failed(f); + goto done; } - // Don't request frames if we haven't sent any input yet. - if (p->num_buffered + p->in_frameno == 0) - return r; - - // Request new future frames as far as possible. - for (int n = 0; n < p->max_requests; n++) { - if (!p->requested[n]) { - // Note: this assumes getFrameAsync() will never call - // infiltGetFrame (if it does, we would deadlock) - p->requested[n] = (struct mp_image *)&dummy_img; - p->failed = false; - MP_TRACE(vf, "requesting frame %d (%d)\n", p->out_frameno + n, n); - p->vsapi->getFrameAsync(p->out_frameno + n, p->out_node, - vs_frame_done, vf); + // Read input and pass it to the input queue VS reads. + if (p->num_buffered < MP_TALLOC_AVAIL(p->buffered) && !p->eof) { + // Note: this requests new input frames even if no output was ever + // requested. Normally this is not how mp_filter works, but since VS + // works asynchronously, it's probably ok. + struct mp_frame frame = mp_pin_out_read(p->in_pin); + if (frame.type == MP_FRAME_EOF) { + if (p->out_node) { + MP_VERBOSE(p, "initiate EOF\n"); + p->eof = true; + pthread_cond_broadcast(&p->wakeup); + } else if (mp_pin_in_needs_data(f->ppins[1])) { + MP_VERBOSE(p, "return EOF\n"); + mp_pin_in_write(f->ppins[1], frame); + frame = MP_NO_FRAME; + } + // Keep it until we can propagate it. + mp_pin_out_unread(p->in_pin, frame); + } else if (frame.type == MP_FRAME_VIDEO) { + struct mp_image *mpi = frame.data; + // Init VS script, or reinit it to change video format. (This + // includes derived parameters we pass manually to the script.) + if (!p->out_node || mpi->imgfmt != p->fmt_in.imgfmt || + mpi->w != p->fmt_in.w || mpi->h != p->fmt_in.h || + mpi->params.p_w != p->fmt_in.p_w || + mpi->params.p_h != p->fmt_in.p_h) + { + if (p->out_node) { + // Drain still buffered frames. + MP_VERBOSE(p, "draining VS for format change\n"); + mp_pin_out_unread(p->in_pin, frame); + p->eof = true; + pthread_cond_broadcast(&p->wakeup); + mp_filter_internal_mark_progress(f); + goto done; + } + pthread_mutex_unlock(&p->lock); + if (p->out_node) + destroy_vs(p); + p->fmt_in = mpi->params; + if (reinit_vs(p) < 0) { + MP_ERR(p, "could not init VS\n"); + mp_frame_unref(&frame); + return; + } + } + if (p->out_pts == MP_NOPTS_VALUE) + p->out_pts = mpi->pts; + p->frames_sent++; + p->buffered[p->num_buffered++] = mpi; + pthread_cond_broadcast(&p->wakeup); + } else if (frame.type != MP_FRAME_NONE) { + MP_ERR(p, "discarding unknown frame type\n"); + goto done; } } - return r; -} + // Read output and return them from the VS output queue. + if (mp_pin_in_needs_data(f->ppins[1]) && p->requested[0] && + p->requested[0] != &dummy_img && + p->requested[0] != &dummy_img_eof) + { + struct mp_image *out = p->requested[0]; -static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) -{ - struct vf_priv_s *p = vf->priv; - int ret = 0; - bool eof = !mpi; + out->pts = p->out_pts; + if (p->out_pts != MP_NOPTS_VALUE && out->pkt_duration >= 0) + p->out_pts += out->pkt_duration; - if (!p->out_node) { - talloc_free(mpi); - return -1; - } + mp_pin_in_write(f->ppins[1], MAKE_FRAME(MP_FRAME_VIDEO, out)); - MPSWAP(struct mp_image *, p->next_image, mpi); - - if (mpi) { - // Turn PTS into frame duration (the pts field is abused for storing it) - if (p->out_pts == MP_NOPTS_VALUE) - p->out_pts = mpi->pts; - mpi->pts = p->next_image ? p->next_image->pts - mpi->pts : 0; + for (int n = 0; n < p->max_requests - 1; n++) + p->requested[n] = p->requested[n + 1]; + p->requested[p->max_requests - 1] = NULL; + p->out_frameno++; } - // Try to get new frames until we get rid of the input mpi. - pthread_mutex_lock(&p->lock); - while (1) { - // Not sure what we do on errors, but at least don't deadlock. - if (p->failed) { - p->failed = false; - talloc_free(mpi); - ret = -1; - break; - } - - // Make the input frame available to infiltGetFrame(). - if (mpi && locked_need_input(vf)) { - p->frames_sent++; - p->buffered[p->num_buffered++] = talloc_steal(p->buffered, mpi); - mpi = NULL; - pthread_cond_broadcast(&p->wakeup); - } - - locked_read_output(vf); - - if (!mpi) { - if (eof && p->frames_sent && !p->eof) { - MP_VERBOSE(vf, "input EOF\n"); - p->eof = true; - pthread_cond_broadcast(&p->wakeup); - } - break; - } - pthread_cond_wait(&p->wakeup, &p->lock); + // This happens on EOF draining and format changes. + if (p->requested[0] == &dummy_img_eof) { + MP_VERBOSE(p, "finishing up\n"); + assert(p->eof); + pthread_mutex_unlock(&p->lock); + destroy_vs(p); + mp_filter_internal_mark_progress(f); + return; } - pthread_mutex_unlock(&p->lock); - return ret; -} -// Fetch 1 outout frame, or 0 if we probably need new input. -static int filter_out(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - int ret = 0; - pthread_mutex_lock(&p->lock); - while (1) { - if (p->failed) { - ret = -1; - break; + // Don't request frames if we haven't sent any input yet. + if (p->frames_sent && p->out_node) { + // Request new future frames as far as possible. + for (int n = 0; n < p->max_requests; n++) { + if (!p->requested[n]) { + // Note: this assumes getFrameAsync() will never call + // infiltGetFrame (if it does, we would deadlock) + p->requested[n] = (struct mp_image *)&dummy_img; + p->failed = false; + MP_TRACE(p, "requesting frame %d (%d)\n", p->out_frameno + n, n); + p->vsapi->getFrameAsync(p->out_frameno + n, p->out_node, + vs_frame_done, p); + } } - if (locked_read_output(vf)) - break; - // If the VS filter wants new input, there's no guarantee that we can - // actually finish any time soon without feeding new input. - if (!p->eof && locked_need_input(vf)) - break; - pthread_cond_wait(&p->wakeup, &p->lock); } - pthread_mutex_unlock(&p->lock); - return ret; -} -static bool needs_input(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - bool r = false; - pthread_mutex_lock(&p->lock); - locked_read_output(vf); - r = vf->num_out_queued < p->max_requests && locked_need_input(vf); +done: pthread_mutex_unlock(&p->lock); - return r; } static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) { - struct vf_instance *vf = *instanceData; - struct vf_priv_s *p = vf->priv; + struct priv *p = *instanceData; // The number of frames of our input node is obviously unknown. The user // could for example seek any time, randomly "ending" the clip. // This specific value was suggested by the VapourSynth developer. @@ -458,30 +461,29 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { - struct vf_instance *vf = *instanceData; - struct vf_priv_s *p = vf->priv; + struct priv *p = *instanceData; VSFrameRef *ret = NULL; pthread_mutex_lock(&p->lock); - MP_TRACE(vf, "VS asking for frame %d (at %d)\n", frameno, p->in_frameno); + MP_TRACE(p, "VS asking for frame %d (at %d)\n", frameno, p->in_frameno); while (1) { if (p->shutdown) { - p->vsapi->setFilterError("EOF or filter reinit/uninit", frameCtx); - MP_DBG(vf, "returning error on EOF/reset\n"); + p->vsapi->setFilterError("EOF or filter reset/uninit", frameCtx); + MP_DBG(p, "returning error on reset/uninit\n"); break; } if (p->initializing) { - MP_WARN(vf, "Frame requested during init! This is unsupported.\n" + MP_WARN(p, "Frame requested during init! This is unsupported.\n" "Returning black dummy frame with 0 duration.\n"); - ret = alloc_vs_frame(p, &vf->fmt_in); + ret = alloc_vs_frame(p, &p->fmt_in); if (!ret) { p->vsapi->setFilterError("Could not allocate VS frame", frameCtx); break; } struct mp_image vsframe = map_vs_frame(p, ret, true); - mp_image_clear(&vsframe, 0, 0, vf->fmt_in.w, vf->fmt_in.h); + mp_image_clear(&vsframe, 0, 0, p->fmt_in.w, p->fmt_in.h); struct mp_image dummy = {0}; - mp_image_set_params(&dummy, &vf->fmt_in); + mp_image_set_params(&dummy, &p->fmt_in); set_vs_frame_props(p, ret, &dummy, 0, 1); break; } @@ -491,7 +493,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, "Frame %d requested, but only have frames starting from %d. " "Try increasing the buffered-frames suboption.", frameno, p->in_frameno); - MP_FATAL(vf, "%s\n", msg); + MP_FATAL(p, "%s\n", msg); p->vsapi->setFilterError(msg, frameCtx); break; } @@ -501,20 +503,23 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, if (p->num_buffered) { drain_oldest_buffered_frame(p); pthread_cond_broadcast(&p->wakeup); - if (vf->chain->wakeup_callback) - vf->chain->wakeup_callback(vf->chain->wakeup_callback_ctx); + mp_filter_wakeup(p->f); continue; } } if (frameno >= p->in_frameno + p->num_buffered) { - // If we think EOF was reached, don't wait for new input, and assume - // the VS filter has reached EOF. + // If there won't be any new frames, abort the request. if (p->eof) { - p->shutdown = true; - continue; + p->vsapi->setFilterError("EOF or filter EOF/reinit", frameCtx); + MP_DBG(p, "returning error on EOF/reinit\n"); + break; } - } - if (frameno < p->in_frameno + p->num_buffered) { + // Request more frames. + if (p->requested_frameno <= p->in_frameno + p->num_buffered) { + p->requested_frameno = p->in_frameno + p->num_buffered + 1; + mp_filter_wakeup(p->f); + } + } else { struct mp_image *img = p->buffered[frameno - p->in_frameno]; ret = alloc_vs_frame(p, &img->params); if (!ret) { @@ -524,7 +529,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, struct mp_image vsframe = map_vs_frame(p, ret, true); mp_image_copy(&vsframe, img); int res = 1e6; - int dur = img->pts * res + 0.5; + int dur = img->pkt_duration * res + 0.5; set_vs_frame_props(p, ret, img, dur, res); break; } @@ -537,8 +542,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason, static void VS_CC infiltFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { - struct vf_instance *vf = instanceData; - struct vf_priv_s *p = vf->priv; + struct priv *p = instanceData; pthread_mutex_lock(&p->lock); p->in_node_active = false; @@ -548,7 +552,7 @@ static void VS_CC infiltFree(void *instanceData, VSCore *core, const VSAPI *vsap // number of getAsyncFrame calls in progress // must be called with p->lock held -static int num_requested(struct vf_priv_s *p) +static int num_requested(struct priv *p) { int r = 0; for (int n = 0; n < p->max_requests; n++) @@ -556,11 +560,12 @@ static int num_requested(struct vf_priv_s *p) return r; } -static void destroy_vs(struct vf_instance *vf) +static void destroy_vs(struct priv *p) { - struct vf_priv_s *p = vf->priv; + if (!p->out_node && !p->initializing) + return; - MP_DBG(vf, "destroying VS filters\n"); + MP_DBG(p, "destroying VS filters\n"); // Wait until our frame callbacks return. pthread_mutex_lock(&p->lock); @@ -571,7 +576,7 @@ static void destroy_vs(struct vf_instance *vf) pthread_cond_wait(&p->wakeup, &p->lock); pthread_mutex_unlock(&p->lock); - MP_DBG(vf, "all requests terminated\n"); + MP_DBG(p, "all requests terminated\n"); if (p->in_node) p->vsapi->freeNode(p->in_node); @@ -579,7 +584,7 @@ static void destroy_vs(struct vf_instance *vf) p->vsapi->freeNode(p->out_node); p->in_node = p->out_node = NULL; - p->drv->unload(vf); + p->drv->unload(p); assert(!p->in_node_active); assert(num_requested(p) == 0); // async callback didn't return? @@ -588,34 +593,42 @@ static void destroy_vs(struct vf_instance *vf) p->eof = false; p->frames_sent = 0; // Kill filtered images that weren't returned yet - for (int n = 0; n < p->max_requests; n++) - mp_image_unrefp(&p->requested[n]); + for (int n = 0; n < p->max_requests; n++) { + if (p->requested[n] != &dummy_img_eof) + mp_image_unrefp(&p->requested[n]); + p->requested[n] = NULL; + } // Kill queued frames too for (int n = 0; n < p->num_buffered; n++) talloc_free(p->buffered[n]); p->num_buffered = 0; - talloc_free(p->next_image); - p->next_image = NULL; p->out_pts = MP_NOPTS_VALUE; p->out_frameno = p->in_frameno = 0; + p->requested_frameno = 0; p->failed = false; - MP_DBG(vf, "uninitialized.\n"); + MP_DBG(p, "uninitialized.\n"); } -static int reinit_vs(struct vf_instance *vf) +static int reinit_vs(struct priv *p) { - struct vf_priv_s *p = vf->priv; VSMap *vars = NULL, *in = NULL, *out = NULL; int res = -1; - destroy_vs(vf); + destroy_vs(p); + + MP_DBG(p, "initializing...\n"); + + struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p->fmt_in.imgfmt); + if (p->fmt_in.w % desc.align_x || p->fmt_in.h % desc.align_y) { + MP_FATAL(p, "VapourSynth does not allow unaligned/cropped video sizes.\n"); + return -1; + } - MP_DBG(vf, "initializing...\n"); p->initializing = true; - if (p->drv->load_core(vf) < 0 || !p->vsapi || !p->vscore) { - MP_FATAL(vf, "Could not get vapoursynth API handle.\n"); + if (p->drv->load_core(p) < 0 || !p->vsapi || !p->vscore) { + MP_FATAL(p, "Could not get vapoursynth API handle.\n"); goto error; } @@ -626,11 +639,11 @@ static int reinit_vs(struct vf_instance *vf) goto error; p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame, - infiltFree, fmSerial, 0, vf, p->vscore); + infiltFree, fmSerial, 0, p, p->vscore); int vserr; p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr); if (!p->in_node) { - MP_FATAL(vf, "Could not get our own input node.\n"); + MP_FATAL(p, "Could not get our own input node.\n"); goto error; } @@ -642,26 +655,36 @@ static int reinit_vs(struct vf_instance *vf) p->vsapi->propSetInt(vars, "video_in_dw", d_w, 0); p->vsapi->propSetInt(vars, "video_in_dh", d_h, 0); - p->vsapi->propSetFloat(vars, "container_fps", vf->chain->container_fps, 0); - p->vsapi->propSetFloat(vars, "display_fps", vf->chain->display_fps, 0); - if (p->drv->load(vf, vars) < 0) + struct mp_stream_info *info = mp_filter_find_stream_info(p->f); + double container_fps = 0; + double display_fps = 0; + if (info) { + if (info->get_container_fps) + container_fps = info->get_container_fps(info); + if (info->get_display_fps) + display_fps = info->get_display_fps(info); + } + p->vsapi->propSetFloat(vars, "container_fps", container_fps, 0); + p->vsapi->propSetFloat(vars, "display_fps", display_fps, 0); + + if (p->drv->load(p, vars) < 0) goto error; if (!p->out_node) { - MP_FATAL(vf, "Could not get script output node.\n"); + MP_FATAL(p, "Could not get script output node.\n"); goto error; } const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); - if (!isConstantFormat(vi)) { - MP_FATAL(vf, "Video format is required to be constant.\n"); + if (!mp_from_vs(vi->format->id)) { + MP_FATAL(p, "Unsupported output format.\n"); goto error; } pthread_mutex_lock(&p->lock); p->initializing = false; pthread_mutex_unlock(&p->lock); - MP_DBG(vf, "initialized.\n"); + MP_DBG(p, "initialized.\n"); res = 0; error: if (p->vsapi) { @@ -670,104 +693,110 @@ error: p->vsapi->freeMap(vars); } if (res < 0) - destroy_vs(vf); + destroy_vs(p); return res; } -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) +static void vf_vapoursynth_reset(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; + struct priv *p = f->priv; - *out = *in; - p->fmt_in = *in; + destroy_vs(p); +} - if (reinit_vs(vf) < 0) - return -1; +static void vf_vapoursynth_destroy(struct mp_filter *f) +{ + struct priv *p = f->priv; - const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node); - out->w = vi->width; - out->h = vi->height; - out->imgfmt = mp_from_vs(vi->format->id); - if (!out->imgfmt) { - MP_FATAL(vf, "Unsupported output format.\n"); - destroy_vs(vf); - return -1; - } + destroy_vs(p); + p->drv->uninit(p); - struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(in->imgfmt); - if (in->w % desc.align_x || in->h % desc.align_y) { - MP_FATAL(vf, "VapourSynth does not allow unaligned/cropped video sizes.\n"); - destroy_vs(vf); - return -1; - } + pthread_cond_destroy(&p->wakeup); + pthread_mutex_destroy(&p->lock); - return 0; + mp_filter_free_children(f); } -static int query_format(struct vf_instance *vf, unsigned int fmt) -{ - return mp_to_vs(fmt) != pfNone; -} +static const struct mp_filter_info vf_vapoursynth_filter = { + .name = "vapoursynth", + .process = vf_vapoursynth_process, + .reset = vf_vapoursynth_reset, + .destroy = vf_vapoursynth_destroy, + .priv_size = sizeof(struct priv), +}; -static int control(vf_instance_t *vf, int request, void *data) +static struct mp_filter *vf_vapoursynth_create(struct mp_filter *parent, + void *options) { - struct vf_priv_s *p = vf->priv; - switch (request) { - case VFCTRL_SEEK_RESET: - if (p->out_node && reinit_vs(vf) < 0) - return CONTROL_ERROR; - return CONTROL_OK; + struct mp_filter *f = mp_filter_create(parent, &vf_vapoursynth_filter); + if (!f) { + talloc_free(options); + return NULL; } - return CONTROL_UNKNOWN; -} -static void uninit(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; + // In theory, we could allow multiple inputs and outputs, but since this + // wrapper is for --vf only, we don't. + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); - destroy_vs(vf); - p->drv->uninit(vf); - - pthread_cond_destroy(&p->wakeup); - pthread_mutex_destroy(&p->lock); -} -static int vf_open(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; - if (p->drv->init(vf) < 0) - return 0; - if (!p->cfg_file || !p->cfg_file[0]) { - MP_FATAL(vf, "'file' parameter must be set.\n"); - return 0; - } - talloc_steal(vf, p->cfg_file); - p->cfg_file = mp_get_user_path(vf, vf->chain->global, p->cfg_file); + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); + p->log = f->log; + p->drv = p->opts->drv; + p->f = f; pthread_mutex_init(&p->lock, NULL); pthread_cond_init(&p->wakeup, NULL); - vf->reconfig = reconfig; - vf->filter_ext = filter_ext; - vf->filter_out = filter_out; - vf->needs_input = needs_input; - vf->query_format = query_format; - vf->control = control; - vf->uninit = uninit; - p->max_requests = p->cfg_maxrequests; + + if (!p->opts->file || !p->opts->file[0]) { + MP_FATAL(p, "'file' parameter must be set.\n"); + goto error; + } + talloc_steal(p, p->opts->file); + p->opts->file = mp_get_user_path(p, f->global, p->opts->file); + + p->max_requests = p->opts->maxrequests; if (p->max_requests < 0) p->max_requests = av_cpu_count(); - MP_VERBOSE(vf, "using %d concurrent requests.\n", p->max_requests); - int maxbuffer = p->cfg_maxbuffer * p->max_requests; - p->buffered = talloc_array(vf, struct mp_image *, maxbuffer); - p->requested = talloc_zero_array(vf, struct mp_image *, p->max_requests); - return 1; + MP_VERBOSE(p, "using %d concurrent requests.\n", p->max_requests); + int maxbuffer = p->opts->maxbuffer * p->max_requests; + p->buffered = talloc_array(p, struct mp_image *, maxbuffer); + p->requested = talloc_zero_array(p, struct mp_image *, p->max_requests); + + struct mp_autoconvert *conv = mp_autoconvert_create(f); + if (!conv) + goto error; + + for (int n = 0; mpvs_fmt_table[n].bits; n++) { + int imgfmt = mp_from_vs(mpvs_fmt_table[n].vs); + if (imgfmt) + mp_autoconvert_add_imgfmt(conv, imgfmt, 0); + } + + struct mp_filter *dur = mp_compute_frame_duration_create(f); + if (!dur) + goto error; + + mp_pin_connect(conv->f->pins[0], f->ppins[0]); + mp_pin_connect(dur->pins[0], conv->f->pins[1]); + p->in_pin = dur->pins[1]; + + if (p->drv->init(p) < 0) + goto error; + + return f; + +error: + talloc_free(f); + return NULL; } -#define OPT_BASE_STRUCT struct vf_priv_s + +#define OPT_BASE_STRUCT struct vapoursynth_opts static const m_option_t vf_opts_fields[] = { - OPT_STRING("file", cfg_file, M_OPT_FILE), - OPT_INTRANGE("buffered-frames", cfg_maxbuffer, 0, 1, 9999, OPTDEF_INT(4)), - OPT_CHOICE_OR_INT("concurrent-frames", cfg_maxrequests, 0, 1, 99, + OPT_STRING("file", file, M_OPT_FILE), + OPT_INTRANGE("buffered-frames", maxbuffer, 0, 1, 9999, OPTDEF_INT(4)), + OPT_CHOICE_OR_INT("concurrent-frames", maxrequests, 0, 1, 99, ({"auto", -1}), OPTDEF_INT(-1)), {0} }; @@ -776,24 +805,22 @@ static const m_option_t vf_opts_fields[] = { #include <VSScript.h> -static int drv_vss_init(struct vf_instance *vf) +static int drv_vss_init(struct priv *p) { if (!vsscript_init()) { - MP_FATAL(vf, "Could not initialize VapourSynth scripting.\n"); + MP_FATAL(p, "Could not initialize VapourSynth scripting.\n"); return -1; } return 0; } -static void drv_vss_uninit(struct vf_instance *vf) +static void drv_vss_uninit(struct priv *p) { vsscript_finalize(); } -static int drv_vss_load_core(struct vf_instance *vf) +static int drv_vss_load_core(struct priv *p) { - struct vf_priv_s *p = vf->priv; - // First load an empty script to get a VSScript, so that we get the vsapi // and vscore. if (vsscript_createScript(&p->se)) @@ -803,24 +830,20 @@ static int drv_vss_load_core(struct vf_instance *vf) return 0; } -static int drv_vss_load(struct vf_instance *vf, VSMap *vars) +static int drv_vss_load(struct priv *p, VSMap *vars) { - struct vf_priv_s *p = vf->priv; - vsscript_setVariable(p->se, vars); - if (vsscript_evaluateFile(&p->se, p->cfg_file, 0)) { - MP_FATAL(vf, "Script evaluation failed:\n%s\n", vsscript_getError(p->se)); + if (vsscript_evaluateFile(&p->se, p->opts->file, 0)) { + MP_FATAL(p, "Script evaluation failed:\n%s\n", vsscript_getError(p->se)); return -1; } p->out_node = vsscript_getOutput(p->se, 0); return 0; } -static void drv_vss_unload(struct vf_instance *vf) +static void drv_vss_unload(struct priv *p) { - struct vf_priv_s *p = vf->priv; - if (p->se) vsscript_freeScript(p->se); p->se = NULL; @@ -836,19 +859,17 @@ static const struct script_driver drv_vss = { .unload = drv_vss_unload, }; -static int vf_open_vss(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; - p->drv = &drv_vss; - return vf_open(vf); -} - -const vf_info_t vf_info_vapoursynth = { - .description = "VapourSynth bridge (Python)", - .name = "vapoursynth", - .open = vf_open_vss, - .priv_size = sizeof(struct vf_priv_s), - .options = vf_opts_fields, +const struct mp_user_filter_entry vf_vapoursynth = { + .desc = { + .description = "VapourSynth bridge (Python)", + .name = "vapoursynth", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT){ + .drv = &drv_vss, + }, + .options = vf_opts_fields, + }, + .create = vf_vapoursynth_create, }; #endif @@ -875,9 +896,8 @@ static int mp_cpcall (lua_State *L, lua_CFunction func, void *ud) #define FUCKYOUOHGODWHY lua_pushglobaltable #endif -static int drv_lazy_init(struct vf_instance *vf) +static int drv_lazy_init(struct priv *p) { - struct vf_priv_s *p = vf->priv; p->ls = luaL_newstate(); if (!p->ls) return -1; @@ -885,38 +905,36 @@ static int drv_lazy_init(struct vf_instance *vf) p->vsapi = getVapourSynthAPI(VAPOURSYNTH_API_VERSION); p->vscore = p->vsapi ? p->vsapi->createCore(0) : NULL; if (!p->vscore) { - MP_FATAL(vf, "Could not load VapourSynth.\n"); + MP_FATAL(p, "Could not load VapourSynth.\n"); lua_close(p->ls); return -1; } return 0; } -static void drv_lazy_uninit(struct vf_instance *vf) +static void drv_lazy_uninit(struct priv *p) { - struct vf_priv_s *p = vf->priv; lua_close(p->ls); p->vsapi->freeCore(p->vscore); } -static int drv_lazy_load_core(struct vf_instance *vf) +static int drv_lazy_load_core(struct priv *p) { // not needed return 0; } -static struct vf_instance *get_vf(lua_State *L) +static struct priv *get_priv(lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, "p"); // p - struct vf_instance *vf = lua_touserdata(L, -1); // p + struct priv *p = lua_touserdata(L, -1); // p lua_pop(L, 1); // - - return vf; + return p; } static void vsmap_to_table(lua_State *L, int index, VSMap *map) { - struct vf_instance *vf = get_vf(L); - struct vf_priv_s *p = vf->priv; + struct priv *p = get_priv(L); const VSAPI *vsapi = p->vsapi; for (int n = 0; n < vsapi->propNumKeys(map); n++) { const char *key = vsapi->propGetKey(map, n); @@ -943,8 +961,7 @@ static void vsmap_to_table(lua_State *L, int index, VSMap *map) static VSMap *table_to_vsmap(lua_State *L, int index) { - struct vf_instance *vf = get_vf(L); - struct vf_priv_s *p = vf->priv; + struct priv *p = get_priv(L); const VSAPI *vsapi = p->vsapi; assert(index > 0); VSMap *map = vsapi->createMap(); @@ -989,8 +1006,7 @@ static VSMap *table_to_vsmap(lua_State *L, int index) static int l_invoke(lua_State *L) { - struct vf_instance *vf = get_vf(L); - struct vf_priv_s *p = vf->priv; + struct priv *p = get_priv(L); const VSAPI *vsapi = p->vsapi; VSPlugin *plugin = vsapi->getPluginByNs(luaL_checkstring(L, 1), p->vscore); @@ -1013,7 +1029,7 @@ static int l_invoke(lua_State *L) } struct load_ctx { - struct vf_instance *vf; + struct priv *p; VSMap *vars; int status; }; @@ -1022,18 +1038,17 @@ static int load_stuff(lua_State *L) { struct load_ctx *ctx = lua_touserdata(L, -1); lua_pop(L, 1); // - - struct vf_instance *vf = ctx->vf; - struct vf_priv_s *p = vf->priv; + struct priv *p = ctx->p; // setup stuff; should be idempotent - lua_pushlightuserdata(L, vf); + lua_pushlightuserdata(L, p); lua_setfield(L, LUA_REGISTRYINDEX, "p"); // - lua_pushcfunction(L, l_invoke); lua_setglobal(L, "invoke"); FUCKYOUOHGODWHY(L); vsmap_to_table(L, lua_gettop(L), ctx->vars); - if (luaL_dofile(L, p->cfg_file)) + if (luaL_dofile(L, p->opts->file)) lua_error(L); lua_pop(L, 1); @@ -1044,12 +1059,11 @@ static int load_stuff(lua_State *L) return 0; } -static int drv_lazy_load(struct vf_instance *vf, VSMap *vars) +static int drv_lazy_load(struct priv *p, VSMap *vars) { - struct vf_priv_s *p = vf->priv; - struct load_ctx ctx = {vf, vars, 0}; + struct load_ctx ctx = {p, vars, 0}; if (mp_cpcall(p->ls, load_stuff, &ctx)) { - MP_FATAL(vf, "filter creation failed: %s\n", lua_tostring(p->ls, -1)); + MP_FATAL(p, "filter creation failed: %s\n", lua_tostring(p->ls, -1)); lua_pop(p->ls, 1); ctx.status = -1; } @@ -1057,10 +1071,8 @@ static int drv_lazy_load(struct vf_instance *vf, VSMap *vars) return ctx.status; } -static void drv_lazy_unload(struct vf_instance *vf) +static void drv_lazy_unload(struct priv *p) { - struct vf_priv_s *p = vf->priv; - for (int n = 0; n < p->num_gc_noderef; n++) { VSNodeRef *ref = p->gc_noderef[n]; if (ref) @@ -1083,19 +1095,17 @@ static const struct script_driver drv_lazy = { .unload = drv_lazy_unload, }; -static int vf_open_lazy(vf_instance_t *vf) -{ - struct vf_priv_s *p = vf->priv; - p->drv = &drv_lazy; - return vf_open(vf); -} - -const vf_info_t vf_info_vapoursynth_lazy = { - .description = "VapourSynth bridge (Lua)", - .name = "vapoursynth-lazy", - .open = vf_open_lazy, - .priv_size = sizeof(struct vf_priv_s), - .options = vf_opts_fields, +const struct mp_user_filter_entry vf_vapoursynth_lazy = { + .desc = { + .description = "VapourSynth bridge (Lua)", + .name = "vapoursynth-lazy", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT){ + .drv = &drv_lazy, + }, + .options = vf_opts_fields, + }, + .create = vf_vapoursynth_create, }; #endif diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c index edee556232..608f1eea6f 100644 --- a/video/filter/vf_vavpp.c +++ b/video/filter/vf_vavpp.c @@ -25,8 +25,11 @@ #include "config.h" #include "options/options.h" -#include "vf.h" +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "refqueue.h" + #include "video/fmt-conversion.h" #include "video/vaapi.h" #include "video/hwdec.h" @@ -47,10 +50,14 @@ struct pipeline { struct surface_refs forward, backward; }; -struct vf_priv_s { +struct opts { int deint_type; int interlaced_only; int reversal_bug; +}; + +struct priv { + struct opts *opts; bool do_deint; VABufferID buffers[VAProcFilterCount]; int num_buffers; @@ -65,15 +72,7 @@ struct vf_priv_s { struct mp_refqueue *queue; }; -static const struct vf_priv_s vf_priv_default = { - .config = VA_INVALID_ID, - .context = VA_INVALID_ID, - .deint_type = 2, - .interlaced_only = 1, - .reversal_bug = 1, -}; - -static void add_surfaces(struct vf_priv_s *p, struct surface_refs *refs, int dir) +static void add_surfaces(struct priv *p, struct surface_refs *refs, int dir) { for (int n = 0; n < refs->max_surfaces; n++) { struct mp_image *s = mp_refqueue_get(p->queue, (1 + n) * dir); @@ -96,18 +95,18 @@ static const int deint_algorithm[] = { [5] = VAProcDeinterlacingMotionCompensated, }; -static void flush_frames(struct vf_instance *vf) +static void flush_frames(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; + struct priv *p = f->priv; mp_refqueue_flush(p->queue); } -static void update_pipeline(struct vf_instance *vf) +static void update_pipeline(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; VABufferID *filters = p->buffers; int num_filters = p->num_buffers; - if (p->deint_type && !p->do_deint) { + if (p->opts->deint_type && !p->do_deint) { filters++; num_filters--; } @@ -133,7 +132,7 @@ static void update_pipeline(struct vf_instance *vf) p->pipe.num_output_colors = caps.num_output_color_standards; p->pipe.forward.max_surfaces = caps.num_forward_references; p->pipe.backward.max_surfaces = caps.num_backward_references; - if (p->reversal_bug) { + if (p->opts->reversal_bug) { int max = MPMAX(caps.num_forward_references, caps.num_backward_references); mp_refqueue_set_refs(p->queue, max, max); } else { @@ -142,8 +141,8 @@ static void update_pipeline(struct vf_instance *vf) } 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)); + (p->opts->deint_type >= 2 ? MP_MODE_OUTPUT_FIELDS : 0) | + (p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0)); return; nodeint: @@ -151,9 +150,25 @@ nodeint: mp_refqueue_set_mode(p->queue, 0); } -static struct mp_image *alloc_out(struct vf_instance *vf) +static struct mp_image *alloc_out(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; + + struct mp_image *fmt = mp_refqueue_get_format(p->queue); + if (!fmt || !fmt->hwctx) + return NULL; + + AVHWFramesContext *hw_frames = (void *)fmt->hwctx->data; + // VAAPI requires the full surface size to match for input and output. + int src_w = hw_frames->width; + int src_h = hw_frames->height; + + if (!mp_update_av_hw_frames_pool(&p->hw_pool, p->av_device_ref, + IMGFMT_VAAPI, IMGFMT_NV12, src_w, src_h)) + { + MP_ERR(vf, "Failed to create hw pool.\n"); + return NULL; + } AVFrame *av_frame = av_frame_alloc(); if (!av_frame) @@ -169,13 +184,13 @@ static struct mp_image *alloc_out(struct vf_instance *vf) MP_ERR(vf, "Unknown error.\n"); return NULL; } - mp_image_set_size(img, vf->fmt_in.w, vf->fmt_in.h); + mp_image_set_size(img, fmt->w, fmt->h); return img; } -static struct mp_image *render(struct vf_instance *vf) +static struct mp_image *render(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; struct mp_image *in = mp_refqueue_get(p->queue, 0); struct mp_image *img = NULL; @@ -184,7 +199,7 @@ static struct mp_image *render(struct vf_instance *vf) VABufferID buffer = VA_INVALID_ID; VASurfaceID in_id = va_surface_id(in); - if (!p->pipe.filters || in_id == VA_INVALID_ID || !p->hw_pool) + if (!p->pipe.filters || in_id == VA_INVALID_ID) goto cleanup; img = alloc_out(vf); @@ -243,7 +258,7 @@ static struct mp_image *render(struct vf_instance *vf) param->filters = p->pipe.filters; param->num_filters = p->pipe.num_filters; - int dir = p->reversal_bug ? -1 : 1; + int dir = p->opts->reversal_bug ? -1 : 1; add_surfaces(p, &p->pipe.forward, 1 * dir); param->forward_references = p->pipe.forward.surfaces; @@ -277,108 +292,29 @@ cleanup: return NULL; } -static struct mp_image *upload(struct vf_instance *vf, struct mp_image *in) -{ - // Since we do no scaling or csp conversion, we can allocate an output - // surface for input too. - struct mp_image *out = alloc_out(vf); - if (!out) - return NULL; - if (!mp_image_hw_upload(out, in)) { - talloc_free(out); - return NULL; - } - mp_image_copy_attributes(out, in); - return out; -} - -static int filter_ext(struct vf_instance *vf, struct mp_image *in) +static void vf_vavpp_process(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; - - update_pipeline(vf); + struct priv *p = f->priv; - if (in && in->imgfmt != IMGFMT_VAAPI) { - struct mp_image *tmp = upload(vf, in); - talloc_free(in); - in = tmp; - if (!in) - return -1; - } - - mp_refqueue_add_input(p->queue, in); - return 0; -} + update_pipeline(f); -static int filter_out(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; + mp_refqueue_execute_reinit(p->queue); - if (!mp_refqueue_has_output(p->queue)) - return 0; + if (!mp_refqueue_can_output(p->queue)) + return; - // no filtering if (!p->pipe.num_filters || !mp_refqueue_should_deint(p->queue)) { + // no filtering 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; -} - -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) -{ - struct vf_priv_s *p = vf->priv; - - flush_frames(vf); - av_buffer_unref(&p->hw_pool); - - p->params = *in; - *out = *in; - - int src_w = in->w; - int src_h = in->h; - - if (in->imgfmt == IMGFMT_VAAPI) { - if (!vf->in_hwframes_ref) - return -1; - AVHWFramesContext *hw_frames = (void *)vf->in_hwframes_ref->data; - // VAAPI requires the full surface size to match for input and output. - src_w = hw_frames->width; - src_h = hw_frames->height; + mp_refqueue_write_out_pin(p->queue, mp_image_new_ref(in)); } else { - out->imgfmt = IMGFMT_VAAPI; - out->hw_subfmt = IMGFMT_NV12; - } - - p->hw_pool = av_hwframe_ctx_alloc(p->av_device_ref); - if (!p->hw_pool) - return -1; - AVHWFramesContext *hw_frames = (void *)p->hw_pool->data; - hw_frames->format = AV_PIX_FMT_VAAPI; - hw_frames->sw_format = imgfmt2pixfmt(out->hw_subfmt); - hw_frames->width = src_w; - hw_frames->height = src_h; - if (av_hwframe_ctx_init(p->hw_pool) < 0) { - MP_ERR(vf, "Failed to initialize libavutil vaapi frames pool.\n"); - av_buffer_unref(&p->hw_pool); - return -1; + mp_refqueue_write_out_pin(p->queue, render(f)); } - - return 0; } -static void uninit(struct vf_instance *vf) +static void uninit(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; for (int i = 0; i < p->num_buffers; i++) vaDestroyBuffer(p->display, p->buffers[i]); if (p->context != VA_INVALID_ID) @@ -387,41 +323,23 @@ static void uninit(struct vf_instance *vf) vaDestroyConfig(p->display, p->config); av_buffer_unref(&p->hw_pool); flush_frames(vf); - mp_refqueue_free(p->queue); + talloc_free(p->queue); av_buffer_unref(&p->av_device_ref); } -static int query_format(struct vf_instance *vf, unsigned int imgfmt) -{ - if (imgfmt == IMGFMT_VAAPI || imgfmt == IMGFMT_NV12 || imgfmt == IMGFMT_420P) - return vf_next_query_format(vf, IMGFMT_VAAPI); - return 0; -} - -static int control(struct vf_instance *vf, int request, void* data) -{ - switch (request){ - case VFCTRL_SEEK_RESET: - flush_frames(vf); - return true; - default: - return CONTROL_UNKNOWN; - } -} - -static int va_query_filter_caps(struct vf_instance *vf, VAProcFilterType type, +static int va_query_filter_caps(struct mp_filter *vf, VAProcFilterType type, void *caps, unsigned int count) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; VAStatus status = vaQueryVideoProcFilterCaps(p->display, p->context, type, caps, &count); return CHECK_VA_STATUS(vf, "vaQueryVideoProcFilterCaps()") ? count : 0; } -static VABufferID va_create_filter_buffer(struct vf_instance *vf, int bytes, +static VABufferID va_create_filter_buffer(struct mp_filter *vf, int bytes, int num, void *data) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; VABufferID buffer; VAStatus status = vaCreateBuffer(p->display, p->context, VAProcFilterParameterBufferType, @@ -429,9 +347,9 @@ static VABufferID va_create_filter_buffer(struct vf_instance *vf, int bytes, return CHECK_VA_STATUS(vf, "vaCreateBuffer()") ? buffer : VA_INVALID_ID; } -static bool initialize(struct vf_instance *vf) +static bool initialize(struct mp_filter *vf) { - struct vf_priv_s *p = vf->priv; + struct priv *p = vf->priv; VAStatus status; VAConfigID config; @@ -458,14 +376,15 @@ static bool initialize(struct vf_instance *vf) buffers[i] = VA_INVALID_ID; for (int i = 0; i < num_filters; i++) { if (filters[i] == VAProcFilterDeinterlacing) { - if (p->deint_type < 1) + if (p->opts->deint_type < 1) continue; VAProcFilterCapDeinterlacing caps[VAProcDeinterlacingCount]; int num = va_query_filter_caps(vf, VAProcFilterDeinterlacing, caps, VAProcDeinterlacingCount); if (!num) continue; - VAProcDeinterlacingType algorithm = deint_algorithm[p->deint_type]; + VAProcDeinterlacingType algorithm = + deint_algorithm[p->opts->deint_type]; for (int n=0; n < num; n++) { // find the algorithm if (caps[n].type != algorithm) continue; @@ -482,47 +401,59 @@ static bool initialize(struct vf_instance *vf) p->num_buffers = 0; if (buffers[VAProcFilterDeinterlacing] != VA_INVALID_ID) p->buffers[p->num_buffers++] = buffers[VAProcFilterDeinterlacing]; - p->do_deint = !!p->deint_type; + p->do_deint = !!p->opts->deint_type; // next filters: p->buffers[p->num_buffers++] = buffers[next_filter]; return true; } -static int vf_open(vf_instance_t *vf) +static const struct mp_filter_info vf_vavpp_filter = { + .name = "vavpp", + .process = vf_vavpp_process, + .reset = flush_frames, + .destroy = uninit, + .priv_size = sizeof(struct priv), +}; + +static struct mp_filter *vf_vavpp_create(struct mp_filter *parent, void *options) { - struct vf_priv_s *p = vf->priv; - - if (!vf->hwdec_devs) - return 0; - - vf->reconfig = reconfig; - vf->filter_ext = filter_ext; - vf->filter_out = filter_out; - vf->query_format = query_format; - vf->uninit = uninit; - vf->control = control; - - p->queue = mp_refqueue_alloc(); - - hwdec_devices_request_all(vf->hwdec_devs); - p->av_device_ref = - hwdec_devices_get_lavc(vf->hwdec_devs, AV_HWDEVICE_TYPE_VAAPI); - if (!p->av_device_ref) { - uninit(vf); - return 0; + struct mp_filter *f = mp_filter_create(parent, &vf_vavpp_filter); + if (!f) { + talloc_free(options); + return NULL; } + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); + + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); + p->config = VA_INVALID_ID; + p->context = VA_INVALID_ID; + + p->queue = mp_refqueue_alloc(f); + + p->av_device_ref = mp_filter_load_hwdec_device(f, AV_HWDEVICE_TYPE_VAAPI); + if (!p->av_device_ref) + goto error; + AVHWDeviceContext *hwctx = (void *)p->av_device_ref->data; AVVAAPIDeviceContext *vactx = hwctx->hwctx; p->display = vactx->display; - if (initialize(vf)) - return true; - uninit(vf); - return false; + mp_refqueue_add_in_format(p->queue, IMGFMT_VAAPI, 0); + + if (!initialize(f)) + goto error; + + return f; + +error: + talloc_free(f); + return NULL; } -#define OPT_BASE_STRUCT struct vf_priv_s +#define OPT_BASE_STRUCT struct opts static const m_option_t vf_opts_fields[] = { OPT_CHOICE("deint", deint_type, 0, // The values must match with deint_algorithm[]. @@ -537,11 +468,17 @@ static const m_option_t vf_opts_fields[] = { {0} }; -const vf_info_t vf_info_vaapi = { - .description = "VA-API Video Post-Process Filter", - .name = "vavpp", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .priv_defaults = &vf_priv_default, - .options = vf_opts_fields, +const struct mp_user_filter_entry vf_vavpp = { + .desc = { + .description = "VA-API Video Post-Process Filter", + .name = "vavpp", + .priv_size = sizeof(OPT_BASE_STRUCT), + .priv_defaults = &(const OPT_BASE_STRUCT){ + .deint_type = 2, + .interlaced_only = 1, + .reversal_bug = 1, + }, + .options = vf_opts_fields, + }, + .create = vf_vavpp_create, }; diff --git a/video/filter/vf_vdpaupp.c b/video/filter/vf_vdpaupp.c index 391dc9e6b1..3b10e13421 100644 --- a/video/filter/vf_vdpaupp.c +++ b/video/filter/vf_vdpaupp.c @@ -26,62 +26,34 @@ #include "common/common.h" #include "common/msg.h" #include "options/m_option.h" - +#include "filters/filter.h" +#include "filters/filter_internal.h" +#include "filters/user_filters.h" #include "video/img_format.h" #include "video/mp_image.h" #include "video/hwdec.h" #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 // processing on the final rendering process in the VO. -struct vf_priv_s { - struct mp_vdpau_ctx *ctx; - struct mp_refqueue *queue; - - int def_deintmode; +struct opts { int deint_enabled; int interlaced_only; struct mp_vdpau_mixer_opts opts; }; -static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) -{ - struct vf_priv_s *p = vf->priv; - - 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; -} +struct priv { + struct opts *opts; + struct mp_vdpau_ctx *ctx; + struct mp_refqueue *queue; + struct mp_pin *in_pin; +}; -static VdpVideoSurface ref_field(struct vf_priv_s *p, +static VdpVideoSurface ref_field(struct priv *p, struct mp_vdpau_mixer_frame *frame, int pos) { struct mp_image *mpi = mp_image_new_ref(mp_refqueue_get_field(p->queue, pos)); @@ -91,17 +63,19 @@ static VdpVideoSurface ref_field(struct vf_priv_s *p, return (uintptr_t)mpi->planes[3]; } -static int filter_out(struct vf_instance *vf) +static void vf_vdpaupp_process(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; + struct priv *p = f->priv; - if (!mp_refqueue_has_output(p->queue)) - return 0; + mp_refqueue_execute_reinit(p->queue); + + if (!mp_refqueue_can_output(p->queue)) + return; struct mp_image *mpi = mp_vdpau_mixed_frame_create(mp_refqueue_get_field(p->queue, 0)); if (!mpi) - return -1; // OOM + return; // OOM struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(mpi); if (!mp_refqueue_should_deint(p->queue)) { @@ -117,72 +91,52 @@ static int filter_out(struct vf_instance *vf) frame->past[0] = ref_field(p, frame, -1); frame->past[1] = ref_field(p, frame, -2); - frame->opts = p->opts; + frame->opts = p->opts->opts; mpi->planes[3] = (void *)(uintptr_t)frame->current; - mp_refqueue_next_field(p->queue); + mpi->params.hw_subfmt = 0; // force mixer - vf_add_output_frame(vf, mpi); - return 0; + mp_refqueue_write_out_pin(p->queue, mpi); } -static int reconfig(struct vf_instance *vf, struct mp_image_params *in, - struct mp_image_params *out) +static void vf_vdpaupp_reset(struct mp_filter *f) { - struct vf_priv_s *p = vf->priv; + struct priv *p = f->priv; mp_refqueue_flush(p->queue); - *out = *in; - out->imgfmt = IMGFMT_VDPAU; - out->hw_subfmt = 0; - return 0; } -static int query_format(struct vf_instance *vf, unsigned int fmt) +static void vf_vdpaupp_destroy(struct mp_filter *f) { - if (fmt == IMGFMT_VDPAU || mp_vdpau_get_format(fmt, NULL, NULL)) - return vf_next_query_format(vf, IMGFMT_VDPAU); - return 0; + struct priv *p = f->priv; + talloc_free(p->queue); } -static int control(vf_instance_t *vf, int request, void *data) -{ - struct vf_priv_s *p = vf->priv; - - switch (request) { - case VFCTRL_SEEK_RESET: - mp_refqueue_flush(p->queue); - return CONTROL_OK; - } - return CONTROL_UNKNOWN; -} - -static void uninit(struct vf_instance *vf) -{ - struct vf_priv_s *p = vf->priv; - - mp_refqueue_free(p->queue); -} +static const struct mp_filter_info vf_vdpaupp_filter = { + .name = "vdpaupp", + .process = vf_vdpaupp_process, + .reset = vf_vdpaupp_reset, + .destroy = vf_vdpaupp_destroy, + .priv_size = sizeof(struct priv), +}; -static int vf_open(vf_instance_t *vf) +static struct mp_filter *vf_vdpaupp_create(struct mp_filter *parent, void *options) { - struct vf_priv_s *p = vf->priv; + struct mp_filter *f = mp_filter_create(parent, &vf_vdpaupp_filter); + if (!f) { + talloc_free(options); + return NULL; + } - if (!vf->hwdec_devs) - return 0; + mp_filter_add_pin(f, MP_PIN_IN, "in"); + mp_filter_add_pin(f, MP_PIN_OUT, "out"); - vf->reconfig = reconfig; - vf->filter_ext = filter_ext; - vf->filter_out = filter_out; - vf->query_format = query_format; - vf->control = control; - vf->uninit = uninit; + struct priv *p = f->priv; + p->opts = talloc_steal(p, options); - p->queue = mp_refqueue_alloc(); + p->queue = mp_refqueue_alloc(f); - hwdec_devices_request_all(vf->hwdec_devs); - AVBufferRef *ref = - hwdec_devices_get_lavc(vf->hwdec_devs, AV_HWDEVICE_TYPE_VDPAU); + AVBufferRef *ref = mp_filter_load_hwdec_device(f, AV_HWDEVICE_TYPE_VDPAU); if (!ref) goto error; p->ctx = mp_vdpau_get_ctx_from_av(ref); @@ -190,18 +144,29 @@ static int vf_open(vf_instance_t *vf) if (!p->ctx) goto error; - p->def_deintmode = p->opts.deint; - if (!p->deint_enabled) - p->opts.deint = 0; + if (!p->opts->deint_enabled) + p->opts->opts.deint = 0; - return 1; + if (p->opts->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->opts->deint_enabled ? MP_MODE_DEINT : 0) | + (p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0) | + (p->opts->opts.deint >= 2 ? MP_MODE_OUTPUT_FIELDS : 0)); + + mp_refqueue_add_in_format(p->queue, IMGFMT_VDPAU, 0); + + return f; error: - uninit(vf); - return 0; + talloc_free(f); + return NULL; } -#define OPT_BASE_STRUCT struct vf_priv_s +#define OPT_BASE_STRUCT struct opts static const m_option_t vf_opts_fields[] = { OPT_CHOICE("deint-mode", opts.deint, 0, ({"first-field", 1}, @@ -219,10 +184,12 @@ static const m_option_t vf_opts_fields[] = { {0} }; -const vf_info_t vf_info_vdpaupp = { - .description = "vdpau postprocessing", - .name = "vdpaupp", - .open = vf_open, - .priv_size = sizeof(struct vf_priv_s), - .options = vf_opts_fields, +const struct mp_user_filter_entry vf_vdpaupp = { + .desc = { + .description = "vdpau postprocessing", + .name = "vdpaupp", + .priv_size = sizeof(OPT_BASE_STRUCT), + .options = vf_opts_fields, + }, + .create = vf_vdpaupp_create, }; |