diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/d3d.c | 3 | ||||
-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 | ||||
-rw-r--r-- | video/fmt-conversion.c | 3 | ||||
-rw-r--r-- | video/hwdec.c | 25 | ||||
-rw-r--r-- | video/hwdec.h | 10 | ||||
-rw-r--r-- | video/img_format.c | 10 | ||||
-rw-r--r-- | video/img_format.h | 9 | ||||
-rw-r--r-- | video/out/d3d11/hwdec_d3d11va.c | 5 | ||||
-rw-r--r-- | video/out/opengl/hwdec_d3d11egl.c | 5 | ||||
-rw-r--r-- | video/out/opengl/hwdec_d3d11eglrgb.c | 10 |
21 files changed, 1078 insertions, 2616 deletions
diff --git a/video/d3d.c b/video/d3d.c index 8f04dcd0d6..b7a644dcfd 100644 --- a/video/d3d.c +++ b/video/d3d.c @@ -119,9 +119,6 @@ static void d3d11_complete_image_params(struct mp_image *img) // According to hwcontex_d3d11va.h, this means DXGI_FORMAT_420_OPAQUE. img->params.hw_flags = hw_frames->sw_format == AV_PIX_FMT_YUV420P ? MP_IMAGE_HW_FLAG_OPAQUE : 0; - - if (img->params.hw_subfmt == IMGFMT_NV12) - mp_image_setfmt(img, IMGFMT_D3D11NV12); } static struct AVBufferRef *d3d11_create_standalone(struct mpv_global *global, 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); - |