summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/d3d.c3
-rw-r--r--video/filter/refqueue.c192
-rw-r--r--video/filter/refqueue.h18
-rw-r--r--video/filter/vf.c797
-rw-r--r--video/filter/vf.h179
-rw-r--r--video/filter/vf_convert.c133
-rw-r--r--video/filter/vf_d3d11vpp.c318
-rw-r--r--video/filter/vf_format.c135
-rw-r--r--video/filter/vf_lavfi.c517
-rw-r--r--video/filter/vf_sub.c177
-rw-r--r--video/filter/vf_vapoursynth.c682
-rw-r--r--video/filter/vf_vavpp.c291
-rw-r--r--video/filter/vf_vdpaupp.c175
-rw-r--r--video/fmt-conversion.c3
-rw-r--r--video/hwdec.c25
-rw-r--r--video/hwdec.h10
-rw-r--r--video/img_format.c10
-rw-r--r--video/img_format.h9
-rw-r--r--video/out/d3d11/hwdec_d3d11va.c5
-rw-r--r--video/out/opengl/hwdec_d3d11egl.c5
-rw-r--r--video/out/opengl/hwdec_d3d11eglrgb.c10
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);
-