summaryrefslogtreecommitdiffstats
path: root/video/filter
diff options
context:
space:
mode:
Diffstat (limited to 'video/filter')
-rw-r--r--video/filter/refqueue.c4
-rw-r--r--video/filter/vf_gpu.c117
-rw-r--r--video/filter/vf_gpu.h35
-rw-r--r--video/filter/vf_gpu_egl.c107
-rw-r--r--video/filter/vf_gpu_vulkan.c115
-rw-r--r--video/filter/vf_vapoursynth.c297
6 files changed, 434 insertions, 241 deletions
diff --git a/video/filter/refqueue.c b/video/filter/refqueue.c
index f5124087c6..031feb8c96 100644
--- a/video/filter/refqueue.c
+++ b/video/filter/refqueue.c
@@ -119,10 +119,10 @@ bool mp_refqueue_is_top_field(struct mp_refqueue *q)
{
if (!mp_refqueue_has_output(q))
return false;
-
+
bool tff = q->field_parity == MP_FIELD_PARITY_TFF;
bool bff = q->field_parity == MP_FIELD_PARITY_BFF;
- bool ret = (!!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field
+ bool ret = (!!(q->queue[q->pos]->fields & MP_IMGFIELD_TOP_FIRST) ^ q->second_field
&& !tff && !bff); // Default parity
ret = ret || (tff && !q->second_field); // Check if top field is forced
ret = ret || (bff && q->second_field); // Check if bottom field is forced
diff --git a/video/filter/vf_gpu.c b/video/filter/vf_gpu.c
index e19faaef42..d2ba990a00 100644
--- a/video/filter/vf_gpu.c
+++ b/video/filter/vf_gpu.c
@@ -24,95 +24,49 @@
#include "options/options.h"
#include "video/out/aspect.h"
#include "video/out/gpu/video.h"
-#include "video/out/opengl/egl_helpers.h"
-#include "video/out/opengl/ra_gl.h"
-
-struct offscreen_ctx {
- struct mp_log *log;
- struct ra *ra;
- void *priv;
-
- void (*set_context)(struct offscreen_ctx *ctx, bool enable);
-};
-
-struct gl_offscreen_ctx {
- GL gl;
- EGLDisplay egl_display;
- EGLContext egl_context;
+#include "video/filter/vf_gpu.h"
+
+extern const struct offscreen_context offscreen_vk;
+extern const struct offscreen_context offscreen_egl;
+
+static const struct offscreen_context *contexts[] = {
+#if HAVE_EGL
+ &offscreen_egl,
+#endif
+#if HAVE_VULKAN
+ &offscreen_vk,
+#endif
};
-static void gl_ctx_destroy(void *p)
+static inline OPT_STRING_VALIDATE_FUNC(offscreen_ctx_validate_api)
{
- struct offscreen_ctx *ctx = p;
- struct gl_offscreen_ctx *gl = ctx->priv;
-
- ra_free(&ctx->ra);
-
- if (gl->egl_context)
- eglDestroyContext(gl->egl_display, gl->egl_context);
+ struct bstr param = bstr0(*value);
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (bstr_equals0(param, contexts[i]->api))
+ return 1;
+ }
+ return M_OPT_INVALID;
}
-static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable)
+static int offscreen_ctx_api_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name)
{
- struct gl_offscreen_ctx *gl = ctx->priv;
- EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT;
-
- if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c))
- MP_ERR(ctx, "Could not make EGL context current.\n");
+ mp_info(log, "GPU APIs (offscreen contexts):\n");
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++)
+ mp_info(log, " %s\n", contexts[i]->api);
+ return M_OPT_EXIT;
}
-static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global,
- struct mp_log *log)
+static struct offscreen_ctx *offscreen_ctx_create(struct mpv_global *global,
+ struct mp_log *log,
+ const char *api)
{
- struct offscreen_ctx *ctx = talloc_zero(NULL, struct offscreen_ctx);
- struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx);
- talloc_set_destructor(ctx, gl_ctx_destroy);
- *ctx = (struct offscreen_ctx){
- .log = log,
- .priv = gl,
- .set_context = gl_ctx_set_context,
- };
-
- // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default
- // display" is at all.
- gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(gl->egl_display, NULL, NULL)) {
- MP_ERR(ctx, "Could not initialize EGL.\n");
- goto error;
+ for (int i = 0; i < MP_ARRAY_SIZE(contexts); i++) {
+ if (api && strcmp(contexts[i]->api, api) != 0)
+ continue;
+ mp_info(log, "Creating offscreen GPU context '%s'\n", contexts[i]->api);
+ return contexts[i]->offscreen_ctx_create(global, log);
}
-
- // Unfortunately, mpegl_create_context() is entangled with ra_ctx.
- // Fortunately, it does not need much, and we can provide a stub.
- struct ra_ctx ractx = {
- .log = ctx->log,
- .global = global,
- };
- EGLConfig config;
- if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config))
- {
- MP_ERR(ctx, "Could not create EGL context.\n");
- goto error;
- }
-
- if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- gl->egl_context))
- {
- MP_ERR(ctx, "Could not make EGL context current.\n");
- goto error;
- }
-
- mpegl_load_functions(&gl->gl, ctx->log);
- ctx->ra = ra_create_gl(&gl->gl, ctx->log);
-
- if (!ctx->ra)
- goto error;
-
- gl_ctx_set_context(ctx, false);
-
- return ctx;
-
-error:
- talloc_free(ctx);
return NULL;
}
@@ -124,6 +78,7 @@ static void offscreen_ctx_set_current(struct offscreen_ctx *ctx, bool enable)
struct gpu_opts {
int w, h;
+ char *api;
};
struct priv {
@@ -327,7 +282,7 @@ static struct mp_filter *gpu_create(struct mp_filter *parent, void *options)
priv->vo_opts_cache = m_config_cache_alloc(f, f->global, &vo_sub_opts);
priv->vo_opts = priv->vo_opts_cache->opts;
- priv->ctx = gl_offscreen_ctx_create(f->global, f->log);
+ priv->ctx = offscreen_ctx_create(f->global, f->log, priv->opts->api);
if (!priv->ctx) {
MP_FATAL(f, "Could not create offscreen ra context.\n");
goto error;
@@ -368,6 +323,8 @@ const struct mp_user_filter_entry vf_gpu = {
.options = (const struct m_option[]){
{"w", OPT_INT(w)},
{"h", OPT_INT(h)},
+ {"api", OPT_STRING_VALIDATE(api, offscreen_ctx_validate_api),
+ .help = offscreen_ctx_api_help},
{0}
},
},
diff --git a/video/filter/vf_gpu.h b/video/filter/vf_gpu.h
new file mode 100644
index 0000000000..2cc9a16eea
--- /dev/null
+++ b/video/filter/vf_gpu.h
@@ -0,0 +1,35 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+#include "common/common.h"
+#include "common/global.h"
+
+struct offscreen_ctx {
+ struct mp_log *log;
+ struct ra *ra;
+ void *priv;
+
+ void (*set_context)(struct offscreen_ctx *ctx, bool enable);
+};
+
+struct offscreen_context {
+ const char *api;
+ struct offscreen_ctx *(*offscreen_ctx_create)(struct mpv_global *,
+ struct mp_log *);
+};
diff --git a/video/filter/vf_gpu_egl.c b/video/filter/vf_gpu_egl.c
new file mode 100644
index 0000000000..cd90a6e06a
--- /dev/null
+++ b/video/filter/vf_gpu_egl.c
@@ -0,0 +1,107 @@
+/*
+ * 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 "common/common.h"
+#include "video/filter/vf_gpu.h"
+#include "video/out/opengl/egl_helpers.h"
+#include "video/out/opengl/ra_gl.h"
+
+struct gl_offscreen_ctx {
+ GL gl;
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+};
+
+static void gl_ctx_destroy(void *p)
+{
+ struct offscreen_ctx *ctx = p;
+ struct gl_offscreen_ctx *gl = ctx->priv;
+
+ ra_free(&ctx->ra);
+
+ if (gl->egl_context)
+ eglDestroyContext(gl->egl_display, gl->egl_context);
+}
+
+static void gl_ctx_set_context(struct offscreen_ctx *ctx, bool enable)
+{
+ struct gl_offscreen_ctx *gl = ctx->priv;
+ EGLContext c = enable ? gl->egl_context : EGL_NO_CONTEXT;
+
+ if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, c))
+ MP_ERR(ctx, "Could not make EGL context current.\n");
+}
+
+static struct offscreen_ctx *gl_offscreen_ctx_create(struct mpv_global *global,
+ struct mp_log *log)
+{
+ struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx);
+ struct gl_offscreen_ctx *gl = talloc_zero(ctx, struct gl_offscreen_ctx);
+ talloc_set_destructor(ctx, gl_ctx_destroy);
+ *ctx = (struct offscreen_ctx){
+ .log = log,
+ .priv = gl,
+ .set_context = gl_ctx_set_context,
+ };
+
+ // This appears to work with Mesa. EGL 1.5 doesn't specify what a "default
+ // display" is at all.
+ gl->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(gl->egl_display, NULL, NULL)) {
+ MP_ERR(ctx, "Could not initialize EGL.\n");
+ goto error;
+ }
+
+ // Unfortunately, mpegl_create_context() is entangled with ra_ctx.
+ // Fortunately, it does not need much, and we can provide a stub.
+ struct ra_ctx ractx = {
+ .log = ctx->log,
+ .global = global,
+ };
+ EGLConfig config;
+ if (!mpegl_create_context(&ractx, gl->egl_display, &gl->egl_context, &config))
+ {
+ MP_ERR(ctx, "Could not create EGL context.\n");
+ goto error;
+ }
+
+ if (!eglMakeCurrent(gl->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ gl->egl_context))
+ {
+ MP_ERR(ctx, "Could not make EGL context current.\n");
+ goto error;
+ }
+
+ mpegl_load_functions(&gl->gl, ctx->log);
+ ctx->ra = ra_create_gl(&gl->gl, ctx->log);
+
+ if (!ctx->ra)
+ goto error;
+
+ gl_ctx_set_context(ctx, false);
+
+ return ctx;
+
+error:
+ talloc_free(ctx);
+ return NULL;
+}
+
+const struct offscreen_context offscreen_egl = {
+ .api = "egl",
+ .offscreen_ctx_create = gl_offscreen_ctx_create
+};
diff --git a/video/filter/vf_gpu_vulkan.c b/video/filter/vf_gpu_vulkan.c
new file mode 100644
index 0000000000..d477fd153a
--- /dev/null
+++ b/video/filter/vf_gpu_vulkan.c
@@ -0,0 +1,115 @@
+/*
+ * 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 "options/m_config.h"
+#include "video/filter/vf_gpu.h"
+#include "video/out/placebo/ra_pl.h"
+#include "video/out/placebo/utils.h"
+#include "video/out/vulkan/context.h"
+#include "video/out/vulkan/utils.h"
+
+struct vk_offscreen_ctx {
+ struct ra_ctx *ractx;
+ struct mpvk_ctx *vk;
+};
+
+extern const struct m_sub_options vulkan_conf;
+
+static void vk_ctx_destroy(void *p)
+{
+ struct offscreen_ctx *ctx = p;
+ struct vk_offscreen_ctx *vkctx = ctx->priv;
+ struct ra_ctx *ractx = vkctx->ractx;
+ struct mpvk_ctx *vk = vkctx->vk;
+
+ if (ractx->ra) {
+ pl_gpu_finish(vk->gpu);
+ ractx->ra->fns->destroy(ctx->ra);
+ ractx->ra = NULL;
+ ctx->ra = NULL;
+ }
+
+ vk->gpu = NULL;
+ pl_vulkan_destroy(&vk->vulkan);
+ mpvk_uninit(vk);
+ talloc_free(vk);
+ talloc_free(ractx);
+}
+
+static struct offscreen_ctx *vk_offscreen_ctx_create(struct mpv_global *global,
+ struct mp_log *log)
+{
+ struct offscreen_ctx *ctx = talloc(NULL, struct offscreen_ctx);
+ talloc_set_destructor(ctx, vk_ctx_destroy);
+ *ctx = (struct offscreen_ctx){
+ .log = log,
+ };
+
+ struct ra_ctx *ractx = talloc_zero(ctx, struct ra_ctx);
+ struct mpvk_ctx *vk = talloc_zero(ctx, struct mpvk_ctx);
+ ractx->log = ctx->log;
+ ractx->global = global;
+
+ vk->pllog = mppl_log_create(ctx, log);
+ if (!vk->pllog)
+ goto error;
+
+ struct pl_vk_inst_params pl_vk_params = {0};
+ struct ra_ctx_opts *ctx_opts = mp_get_config_group(NULL, global, &ra_ctx_conf);
+ pl_vk_params.debug = ctx_opts->debug;
+ talloc_free(ctx_opts);
+ mppl_log_set_probing(vk->pllog, true);
+ vk->vkinst = pl_vk_inst_create(vk->pllog, &pl_vk_params);
+ mppl_log_set_probing(vk->pllog, false);
+ if (!vk->vkinst)
+ goto error;
+
+ struct vulkan_opts *vk_opts = mp_get_config_group(NULL, global, &vulkan_conf);
+ vk->vulkan = mppl_create_vulkan(vk_opts, vk->vkinst, vk->pllog, VK_NULL_HANDLE);
+ talloc_free(vk_opts);
+ if (!vk->vulkan)
+ goto error;
+
+ vk->gpu = vk->vulkan->gpu;
+ ractx->ra = ra_create_pl(vk->gpu, ractx->log);
+ if (!ractx->ra)
+ goto error;
+
+ struct vk_offscreen_ctx *vkctx = talloc(ctx, struct vk_offscreen_ctx);
+ *vkctx = (struct vk_offscreen_ctx){
+ .ractx = ractx,
+ .vk = vk
+ };
+
+ ctx->ra = ractx->ra;
+ ctx->priv = vkctx;
+
+ return ctx;
+
+error:
+ pl_vulkan_destroy(&vk->vulkan);
+ mpvk_uninit(vk);
+ talloc_free(vk);
+ talloc_free(ractx);
+ talloc_free(ctx);
+ return NULL;
+}
+
+const struct offscreen_context offscreen_vk = {
+ .api = "vulkan",
+ .offscreen_ctx_create = vk_offscreen_ctx_create
+};
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index 0b798c8daf..c0d4fdb474 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -22,8 +22,8 @@
#include <limits.h>
#include <assert.h>
-#include <VapourSynth.h>
-#include <VSHelper.h>
+#include <VapourSynth4.h>
+#include <VSScript4.h>
#include <libavutil/rational.h>
#include <libavutil/cpu.h>
@@ -46,6 +46,7 @@ struct vapoursynth_opts {
char *file;
int maxbuffer;
int maxrequests;
+ char *user_data;
const struct script_driver *drv;
};
@@ -57,13 +58,13 @@ struct priv {
VSCore *vscore;
const VSAPI *vsapi;
- VSNodeRef *out_node;
- VSNodeRef *in_node;
+ VSNode *out_node;
+ VSNode *in_node;
const struct script_driver *drv;
// drv_vss
- bool vs_initialized;
- struct VSScript *se;
+ const VSSCRIPTAPI *vs_script_api;
+ VSScript *vs_script;
struct mp_filter *f;
struct mp_pin *in_pin;
@@ -108,72 +109,50 @@ struct script_driver {
void (*unload)(struct priv *p); // unload script and maybe vs
};
-struct mpvs_fmt {
- VSPresetFormat vs;
- int bits, xs, ys;
-};
-
-static const struct mpvs_fmt mpvs_fmt_table[] = {
- {pfYUV420P8, 8, 1, 1},
- {pfYUV420P9, 9, 1, 1},
- {pfYUV420P10, 10, 1, 1},
- {pfYUV420P16, 16, 1, 1},
- {pfYUV422P8, 8, 1, 0},
- {pfYUV422P9, 9, 1, 0},
- {pfYUV422P10, 10, 1, 0},
- {pfYUV422P16, 16, 1, 0},
- {pfYUV410P8, 8, 2, 2},
- {pfYUV411P8, 8, 2, 0},
- {pfYUV440P8, 8, 0, 1},
- {pfYUV444P8, 8, 0, 0},
- {pfYUV444P9, 9, 0, 0},
- {pfYUV444P10, 10, 0, 0},
- {pfYUV444P16, 16, 0, 0},
- {pfNone}
-};
-
-static bool compare_fmt(int imgfmt, const struct mpvs_fmt *vs)
-{
- struct mp_regular_imgfmt rfmt;
- if (!mp_get_regular_imgfmt(&rfmt, imgfmt))
- return false;
- if (rfmt.component_pad > 0)
- return false;
- if (rfmt.chroma_xs != vs->xs || rfmt.chroma_ys != vs->ys)
+static bool get_valid_mp_regular_imgfmt(struct mp_regular_imgfmt *reg_fmt, int imgfmt) {
+ if (!mp_get_regular_imgfmt(reg_fmt, imgfmt))
return false;
- if (rfmt.component_size * 8 + rfmt.component_pad != vs->bits)
+ if (reg_fmt->component_pad > 0)
return false;
- if (rfmt.num_planes != 3)
+ if (reg_fmt->num_planes != 3)
return false;
for (int n = 0; n < 3; n++) {
- if (rfmt.planes[n].num_components != 1)
+ if (reg_fmt->planes[n].num_components != 1)
return false;
- if (rfmt.planes[n].components[0] != n + 1)
+ if (reg_fmt->planes[n].components[0] != n + 1)
return false;
}
return true;
}
-static VSPresetFormat mp_to_vs(int imgfmt)
+static bool mp_to_vs(struct priv *p, VSVideoFormat *vsfmt, int imgfmt)
{
- for (int n = 0; mpvs_fmt_table[n].bits; n++) {
- const struct mpvs_fmt *vsentry = &mpvs_fmt_table[n];
- if (compare_fmt(imgfmt, vsentry))
- return vsentry->vs;
- }
- return pfNone;
+ struct mp_regular_imgfmt reg_fmt;
+ if (!get_valid_mp_regular_imgfmt(&reg_fmt, imgfmt))
+ return false;
+
+ int rfmt_bits = reg_fmt.component_size * 8 + reg_fmt.component_pad;
+ return p->vsapi->queryVideoFormat(vsfmt, cfYUV,
+ reg_fmt.component_type == MP_COMPONENT_TYPE_FLOAT ? stFloat : stInteger,
+ rfmt_bits, reg_fmt.chroma_xs, reg_fmt.chroma_ys, p->vscore);
}
-static int mp_from_vs(VSPresetFormat vs)
+static int mp_from_vs(const VSVideoFormat *vsfmt)
{
- for (int n = 0; mpvs_fmt_table[n].bits; n++) {
- const struct mpvs_fmt *vsentry = &mpvs_fmt_table[n];
- if (vsentry->vs == vs) {
- for (int imgfmt = IMGFMT_START; imgfmt < IMGFMT_END; imgfmt++) {
- if (compare_fmt(imgfmt, vsentry))
- return imgfmt;
+ if (vsfmt->colorFamily == cfYUV) {
+ for (int imgfmt = IMGFMT_START + 1; imgfmt < IMGFMT_END; imgfmt++) {
+ struct mp_regular_imgfmt reg_fmt;
+ if (!get_valid_mp_regular_imgfmt(&reg_fmt, imgfmt))
+ continue;
+
+ int rfmt_bits = reg_fmt.component_size * 8 + reg_fmt.component_pad;
+ if ((reg_fmt.component_type == MP_COMPONENT_TYPE_FLOAT) == (vsfmt->sampleType == stFloat) &&
+ rfmt_bits == vsfmt->bitsPerSample &&
+ reg_fmt.chroma_xs == vsfmt->subSamplingW &&
+ reg_fmt.chroma_ys == vsfmt->subSamplingH)
+ {
+ return imgfmt;
}
- break;
}
}
return 0;
@@ -183,18 +162,19 @@ 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;
- p->vsapi->propSetInt(map, "_SARNum", params->p_w, 0);
- p->vsapi->propSetInt(map, "_SARDen", params->p_h, 0);
+ p->vsapi->mapSetInt(map, "_SARNum", params->p_w, 0);
+ p->vsapi->mapSetInt(map, "_SARDen", params->p_h, 0);
if (params->repr.levels) {
- p->vsapi->propSetInt(map, "_ColorRange",
+ p->vsapi->mapSetInt(map, "_ColorRange",
params->repr.levels == PL_COLOR_LEVELS_LIMITED, 0);
}
// The docs explicitly say it uses libavcodec values.
- p->vsapi->propSetInt(map, "_ColorSpace",
+ p->vsapi->mapSetInt(map, "_ColorSpace",
pl_system_to_av(params->repr.sys), 0);
if (params->chroma_location) {
- p->vsapi->propSetInt(map, "_ChromaLocation",
- params->chroma_location == PL_CHROMA_CENTER, 0);
+ // 0=left, 1=center, 2=topleft, 3=top, 4=bottomleft, 5=bottom.
+ p->vsapi->mapSetInt(map, "_ChromaLocation",
+ params->chroma_location - 1, 0);
}
char pict_type = 0;
switch (img->pict_type) {
@@ -203,45 +183,53 @@ static void copy_mp_to_vs_frame_props_map(struct priv *p, VSMap *map,
case 3: pict_type = 'B'; break;
}
if (pict_type)
- p->vsapi->propSetData(map, "_PictType", &pict_type, 1, 0);
+ p->vsapi->mapSetData(map, "_PictType", &pict_type, 1, dtUtf8, 0);
int field = 0;
if (img->fields & MP_IMGFIELD_INTERLACED)
field = img->fields & MP_IMGFIELD_TOP_FIRST ? 2 : 1;
- p->vsapi->propSetInt(map, "_FieldBased", field, 0);
+ p->vsapi->mapSetInt(map, "_FieldBased", field, 0);
+
+ // Don't increase the reference count. It is not intended to be read externally,
+ // and we know it will be alive when we retrieve it.
+ p->vsapi->mapSetData(map, "_MP_IMAGE", (const char *)img, sizeof(*img), dtBinary, 0);
}
-static int set_vs_frame_props(struct priv *p, VSFrameRef *frame,
+static int set_vs_frame_props(struct priv *p, VSFrame *frame,
struct mp_image *img, int dur_num, int dur_den)
{
- VSMap *map = p->vsapi->getFramePropsRW(frame);
+ VSMap *map = p->vsapi->getFramePropertiesRW(frame);
if (!map)
return -1;
- p->vsapi->propSetInt(map, "_DurationNum", dur_num, 0);
- p->vsapi->propSetInt(map, "_DurationDen", dur_den, 0);
+ p->vsapi->mapSetInt(map, "_DurationNum", dur_num, 0);
+ p->vsapi->mapSetInt(map, "_DurationDen", dur_den, 0);
copy_mp_to_vs_frame_props_map(p, map, img);
return 0;
}
-static VSFrameRef *alloc_vs_frame(struct priv *p, struct mp_image_params *fmt)
+static VSFrame *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);
+ VSVideoFormat vsfmt;
+ if (mp_to_vs(p, &vsfmt, fmt->imgfmt))
+ return p->vsapi->newVideoFrame(&vsfmt, fmt->w, fmt->h, NULL, p->vscore);
+
+ return NULL;
}
-static struct mp_image map_vs_frame(struct priv *p, const VSFrameRef *ref,
- bool w)
+static struct mp_image map_vs_frame(struct priv *p, const VSFrame *ref,
+ bool w, struct mp_image *ref_image)
{
- const VSFormat *fmt = p->vsapi->getFrameFormat(ref);
+ const VSVideoFormat *fmt = p->vsapi->getVideoFrameFormat(ref);
struct mp_image img = {0};
- mp_image_setfmt(&img, mp_from_vs(fmt->id));
+ if (ref_image)
+ img = *ref_image;
+ mp_image_setfmt(&img, mp_from_vs(fmt));
mp_image_set_size(&img, p->vsapi->getFrameWidth(ref, 0),
p->vsapi->getFrameHeight(ref, 0));
for (int n = 0; n < img.num_planes; n++) {
if (w) {
- img.planes[n] = p->vsapi->getWritePtr((VSFrameRef *)ref, n);
+ img.planes[n] = p->vsapi->getWritePtr((VSFrame *)ref, n);
} else {
img.planes[n] = (uint8_t *)p->vsapi->getReadPtr(ref, n);
}
@@ -262,24 +250,30 @@ static void drain_oldest_buffered_frame(struct priv *p)
p->in_frameno++;
}
-static void VS_CC vs_frame_done(void *userData, const VSFrameRef *f, int n,
- VSNodeRef *node, const char *errorMsg)
+static void VS_CC vs_frame_done(void *userData, const VSFrame *f, int n,
+ VSNode *node, const char *errorMsg)
{
struct priv *p = userData;
struct mp_image *res = NULL;
if (f) {
- struct mp_image img = map_vs_frame(p, f, false);
- struct mp_image dummy = {.params = p->fmt_in};
- if (p->fmt_in.w != img.w || p->fmt_in.h != img.h)
- dummy.params.crop = (struct mp_rect){0, 0, img.w, img.h};
- mp_image_copy_attributes(&img, &dummy);
+ const VSMap *map = p->vsapi->getFramePropertiesRO(f);
+ if (!map)
+ MP_ERR(p, "Failed to get frame properties!");
+ struct mp_image *mpi = NULL;
+ if (map) {
+ mpi = (void *)p->vsapi->mapGetData(map, "_MP_IMAGE", 0, NULL);
+ if (!mpi)
+ MP_ERR(p, "Failed to get mp_image attributes!");
+ }
+ struct mp_image img = map_vs_frame(p, f, false, mpi);
img.pkt_duration = -1;
- const VSMap *map = p->vsapi->getFramePropsRO(f);
+ if (mpi && (mpi->params.w != img.w || mpi->params.h != img.h))
+ img.params.crop = (struct mp_rect){0, 0, img.w, img.h};
if (map) {
int err1, err2;
- int num = p->vsapi->propGetInt(map, "_DurationNum", 0, &err1);
- int den = p->vsapi->propGetInt(map, "_DurationDen", 0, &err2);
+ int num = p->vsapi->mapGetInt(map, "_DurationNum", 0, &err1);
+ int den = p->vsapi->mapGetInt(map, "_DurationDen", 0, &err2);
if (!err1 && !err2)
img.pkt_duration = num / (double)den;
}
@@ -440,39 +434,13 @@ done:
mp_mutex_unlock(&p->lock);
}
-static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
- VSNode *node, VSCore *core, const VSAPI *vsapi)
-{
- 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.
- int enough_for_everyone = INT_MAX / 16;
-
- // Note: this is called from createFilter, so no need for locking.
-
- VSVideoInfo fmt = {
- .format = p->vsapi->getFormatPreset(mp_to_vs(p->fmt_in.imgfmt), p->vscore),
- .width = p->fmt_in.w,
- .height = p->fmt_in.h,
- .numFrames = enough_for_everyone,
- };
- if (!fmt.format) {
- p->vsapi->setError(out, "Unsupported input format.\n");
- return;
- }
-
- p->vsapi->setVideoInfo(&fmt, 1, node);
- p->in_node_active = true;
-}
-
-static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
- void **instanceData, void **frameData,
+static const VSFrame *VS_CC infiltGetFrame(int frameno, int activationReason,
+ void *instanceData, void **frameData,
VSFrameContext *frameCtx, VSCore *core,
const VSAPI *vsapi)
{
- struct priv *p = *instanceData;
- VSFrameRef *ret = NULL;
+ struct priv *p = instanceData;
+ VSFrame *ret = NULL;
mp_mutex_lock(&p->lock);
MP_TRACE(p, "VS asking for frame %d (at %d)\n", frameno, p->in_frameno);
@@ -490,7 +458,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
p->vsapi->setFilterError("Could not allocate VS frame", frameCtx);
break;
}
- struct mp_image vsframe = map_vs_frame(p, ret, true);
+ struct mp_image vsframe = map_vs_frame(p, ret, true, NULL);
mp_image_clear(&vsframe, 0, 0, p->fmt_in.w, p->fmt_in.h);
struct mp_image dummy = {0};
mp_image_set_params(&dummy, &p->fmt_in);
@@ -538,7 +506,7 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
}
mp_mutex_unlock(&p->lock);
- struct mp_image vsframe = map_vs_frame(p, ret, true);
+ struct mp_image vsframe = map_vs_frame(p, ret, true, NULL);
mp_image_copy(&vsframe, img);
int res = 1e6;
int dur = img->pkt_duration * res + 0.5;
@@ -624,7 +592,7 @@ static void destroy_vs(struct priv *p)
static int reinit_vs(struct priv *p, struct mp_image *input)
{
- VSMap *vars = NULL, *in = NULL, *out = NULL;
+ VSMap *vars = NULL;
int res = -1;
destroy_vs(p);
@@ -645,29 +613,41 @@ static int reinit_vs(struct priv *p, struct mp_image *input)
goto error;
}
- in = p->vsapi->createMap();
- out = p->vsapi->createMap();
- vars = p->vsapi->createMap();
- if (!in || !out || !vars)
+ // 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.
+ int enough_for_everyone = INT_MAX / 16;
+
+ VSVideoInfo vi_in = {
+ .width = p->fmt_in.w,
+ .height = p->fmt_in.h,
+ .numFrames = enough_for_everyone,
+ };
+ if (!mp_to_vs(p, &vi_in.format, p->fmt_in.imgfmt)) {
+ MP_FATAL(p, "Unsupported input format.\n");
goto error;
+ }
- p->vsapi->createFilter(in, out, "Input", infiltInit, infiltGetFrame,
- infiltFree, fmSerial, 0, p, p->vscore);
- int vserr;
- p->in_node = p->vsapi->propGetNode(out, "clip", 0, &vserr);
+ p->in_node = p->vsapi->createVideoFilter2("Input", &vi_in, infiltGetFrame, infiltFree,
+ fmParallel, NULL, 0, p, p->vscore);
if (!p->in_node) {
MP_FATAL(p, "Could not get our own input node.\n");
goto error;
}
+ p->in_node_active = true;
+
+ vars = p->vsapi->createMap();
+ if (!vars)
+ goto error;
- if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0))
+ if (p->vsapi->mapSetNode(vars, "video_in", p->in_node, 0))
goto error;
int d_w, d_h;
mp_image_params_get_dsize(&p->fmt_in, &d_w, &d_h);
- p->vsapi->propSetInt(vars, "video_in_dw", d_w, 0);
- p->vsapi->propSetInt(vars, "video_in_dh", d_h, 0);
+ p->vsapi->mapSetInt(vars, "video_in_dw", d_w, 0);
+ p->vsapi->mapSetInt(vars, "video_in_dh", d_h, 0);
struct mp_stream_info *info = mp_filter_find_stream_info(p->f);
double container_fps = input->nominal_fps;
@@ -683,9 +663,10 @@ static int reinit_vs(struct priv *p, struct mp_image *input)
display_res[1] = tmp[1];
}
}
- p->vsapi->propSetFloat(vars, "container_fps", container_fps, 0);
- p->vsapi->propSetFloat(vars, "display_fps", display_fps, 0);
- p->vsapi->propSetIntArray(vars, "display_res", display_res, 2);
+ p->vsapi->mapSetFloat(vars, "container_fps", container_fps, 0);
+ p->vsapi->mapSetFloat(vars, "display_fps", display_fps, 0);
+ p->vsapi->mapSetIntArray(vars, "display_res", display_res, 2);
+ p->vsapi->mapSetData(vars, "user_data", p->opts->user_data, -1, dtUtf8, 0);
if (p->drv->load(p, vars) < 0)
goto error;
@@ -694,8 +675,8 @@ static int reinit_vs(struct priv *p, struct mp_image *input)
goto error;
}
- const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
- if (!mp_from_vs(vi->format->id)) {
+ const VSVideoInfo *vi_out = p->vsapi->getVideoInfo(p->out_node);
+ if (!mp_from_vs(&vi_out->format)) {
MP_FATAL(p, "Unsupported output format.\n");
goto error;
}
@@ -707,8 +688,6 @@ static int reinit_vs(struct priv *p, struct mp_image *input)
res = 0;
error:
if (p->vsapi) {
- p->vsapi->freeMap(in);
- p->vsapi->freeMap(out);
p->vsapi->freeMap(vars);
}
if (res < 0)
@@ -785,9 +764,11 @@ static struct mp_filter *vf_vapoursynth_create(struct mp_filter *parent,
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)
+ for (int imgfmt = IMGFMT_START + 1; imgfmt < IMGFMT_END; imgfmt++) {
+ // due to the lack of access to VapourSynth at this point, the formats
+ // added to autoconvert is a superset of what's actually needed
+ struct mp_regular_imgfmt reg_fmt;
+ if (get_valid_mp_regular_imgfmt(&reg_fmt, imgfmt))
mp_autoconvert_add_imgfmt(conv, imgfmt, 0);
}
@@ -817,58 +798,56 @@ static const m_option_t vf_opts_fields[] = {
OPTDEF_INT(4)},
{"concurrent-frames", OPT_CHOICE(maxrequests, {"auto", -1}),
M_RANGE(1, 99), OPTDEF_INT(-1)},
+ {"user-data", OPT_STRING(user_data), OPTDEF_STR("")},
{0}
};
-#include <VSScript.h>
-
static int drv_vss_init(struct priv *p)
{
- if (!vsscript_init()) {
+ p->vs_script_api = getVSScriptAPI(VSSCRIPT_API_VERSION);
+ if (!p->vs_script_api) {
MP_FATAL(p, "Could not initialize VapourSynth scripting.\n");
return -1;
}
- p->vs_initialized = true;
return 0;
}
static void drv_vss_uninit(struct priv *p)
{
- if (p->vs_initialized)
- vsscript_finalize();
- p->vs_initialized = false;
+ p->vs_script_api = NULL;
}
static int drv_vss_load_core(struct priv *p)
{
// First load an empty script to get a VSScript, so that we get the vsapi
// and vscore.
- if (vsscript_createScript(&p->se))
+ p->vs_script = p->vs_script_api->createScript(NULL);
+ if (!p->vs_script)
return -1;
- p->vsapi = vsscript_getVSApi();
- p->vscore = vsscript_getCore(p->se);
+ p->vsapi = p->vs_script_api->getVSAPI(VAPOURSYNTH_API_VERSION);
+ p->vscore = p->vs_script_api->getCore(p->vs_script);
return 0;
}
static int drv_vss_load(struct priv *p, VSMap *vars)
{
- vsscript_setVariable(p->se, vars);
+ p->vs_script_api->setVariables(p->vs_script, vars);
- if (vsscript_evaluateFile(&p->se, p->script_path, 0)) {
- MP_FATAL(p, "Script evaluation failed:\n%s\n", vsscript_getError(p->se));
+ if (p->vs_script_api->evaluateFile(p->vs_script, p->script_path)) {
+ MP_FATAL(p, "Script evaluation failed:\n%s\n", p->vs_script_api->getError(p->v