Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
@ 2024-06-22  1:37 Stefan Oltmanns via ffmpeg-devel
  2024-06-22  6:27 ` Stephen Hutchinson
  2024-06-25  9:10 ` Anton Khirnov
  0 siblings, 2 replies; 7+ messages in thread
From: Stefan Oltmanns via ffmpeg-devel @ 2024-06-22  1:37 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Stefan Oltmanns

[-- Attachment #1: Type: text/plain, Size: 1574 bytes --]

Hello,

this is my first patch, I hope I got all the formalities correct.

The current VapourSynth implementation is rarely used, as it links the
VapourSynth library at build time, making the resulting build unable to
run when VapourSynth is not installed. Therefore barely anyone compiles
with VapourSynth activated.

I changed it, so that it loads the library at runtime when a VapourSynth
script should be opened, just like AviSynth does.
On Windows the DLL from VapourSynth is not installed in the system
directory, but the location is stored in the Registry. Therefore I added
some code to read that information from the registry.

As the V4 API is designed with dynamic loading in mind (only a single
import), I updated the implementation to V4 (changes are mostly
superficial, no structural changes). The V4 API is already several years
old, fully supported since R55 released in 2021.

I copied the two needed header files directly in a vapoursynth.h,
removing the need to install VapourSynth on the build machine
(VapourSynth is also LGPL 2.1 or later, so no license issue). I updated
the configure so that it checks for the ability to load libraries at
runtime for VapourSynth, just like AviSynth and activate it if not disabled.

make fate runs without any issue. I tested VapourSynth input scripts
with various color formats on different platforms:

Ubuntu 22.04
macOS 13 (x86_64)
macOS 13 (arm64)
Windows 10 (msys2/gcc)

It compiles on these platforms without any warning and runs without any
issues.

Best regards
Stefan

[-- Attachment #2: 0001-libavformat-vapoursynth-Update-to-API-version-4-load.patch --]
[-- Type: text/x-patch, Size: 48872 bytes --]

From c13faf46f5d210c676b237f855d56a9b8a0a127d Mon Sep 17 00:00:00 2001
From: John Doe <johndoe@example.com>
Date: Sat, 22 Jun 2024 02:53:07 +0200
Subject: [PATCH] libavformat/vapoursynth: Update to API version 4, load
 library at runtime

The VapourSynth V4 API is designed for loading at runtime instead of linking to it during build time,
this is now done in ffmpeg, similar to how AviSynth support is implemented.
This allows building with VapourSynth support enabled,
but ffmpeg still working fine without VapourSynth installed on target machine.
---
 configure                 |   7 +-
 libavformat/vapoursynth.c | 167 +++++++---
 libavformat/vapoursynth.h | 635 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 768 insertions(+), 41 deletions(-)
 create mode 100644 libavformat/vapoursynth.h

diff --git a/configure b/configure
index 3bca638459..a54d2976f5 100755
--- a/configure
+++ b/configure
@@ -333,7 +333,7 @@ External library support:
   --disable-sdl2           disable sdl2 [autodetect]
   --disable-securetransport disable Secure Transport, needed for TLS support
                            on OSX if openssl and gnutls are not used [autodetect]
-  --enable-vapoursynth     enable VapourSynth demuxer [no]
+  --disable-vapoursynth    disable VapourSynth demuxer [autodetect]
   --disable-xlib           disable xlib [autodetect]
   --disable-zlib           disable zlib [autodetect]
 
@@ -1853,6 +1853,7 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST="
     sdl2
     securetransport
     sndio
+    vapoursynth
     xlib
     zlib
 "
@@ -1981,7 +1982,6 @@ EXTERNAL_LIBRARY_LIST="
     opengl
     openssl
     pocketsphinx
-    vapoursynth
 "
 
 HWACCEL_AUTODETECT_LIBRARY_LIST="
@@ -3572,6 +3572,7 @@ libxevd_decoder_deps="libxevd"
 libxeve_encoder_deps="libxeve"
 libxvid_encoder_deps="libxvid"
 libzvbi_teletext_decoder_deps="libzvbi"
+vapoursynth_deps_any="libdl LoadLibrary"
 vapoursynth_demuxer_deps="vapoursynth"
 videotoolbox_suggest="coreservices"
 videotoolbox_deps="corefoundation coremedia corevideo"
@@ -7068,8 +7069,6 @@ enabled rkmpp             && { require_pkg_config rkmpp rockchip_mpp  rockchip/r
                                { enabled libdrm ||
                                  die "ERROR: rkmpp requires --enable-libdrm"; }
                              }
-enabled vapoursynth       && require_pkg_config vapoursynth "vapoursynth-script >= 42" VSScript.h vsscript_init
-
 
 if enabled gcrypt; then
     GCRYPT_CONFIG="${cross_prefix}libgcrypt-config"
diff --git a/libavformat/vapoursynth.c b/libavformat/vapoursynth.c
index 8a2519e19a..8cf309002b 100644
--- a/libavformat/vapoursynth.c
+++ b/libavformat/vapoursynth.c
@@ -25,9 +25,6 @@
 
 #include <limits.h>
 
-#include <VapourSynth.h>
-#include <VSScript.h>
-
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/eval.h"
@@ -39,20 +36,65 @@
 #include "avformat.h"
 #include "demux.h"
 #include "internal.h"
+#include "vapoursynth.h"
+
+/* Platform-specific directives. */
+#ifdef _WIN32
+  #include <windows.h>
+  #include "compat/w32dlfcn.h"
+  #include "libavutil/wchar_filename.h"
+  static av_cold char* get_vs_script_dll_name(void) {
+       LONG r;
+       WCHAR vss_path[512];
+       char *vss_path_utf8;
+       DWORD buf_size = sizeof(vss_path) - 2;
+       r = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\VapourSynth",
+                        L"VSScriptDLL", RRF_RT_REG_SZ, NULL,
+                        &vss_path, &buf_size);
+       if (r == ERROR_SUCCESS && wchartoutf8(vss_path, &vss_path_utf8) == 0)
+           return vss_path_utf8;
+       r = RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\VapourSynth",
+                        L"VSScriptDLL", RRF_RT_REG_SZ, NULL,
+                        &vss_path, &buf_size);
+       if (r == ERROR_SUCCESS && wchartoutf8(vss_path, &vss_path_utf8) == 0)
+           return vss_path_utf8;
+       if (wchartoutf8(L"VSScript.dll", &vss_path_utf8) == 0)
+           return vss_path_utf8;
+       return 0;
+  }
+  #undef EXTERN_C
+  #define VSSCRIPT_LIB "VSScript.dll"
+  #define VS_DLOPEN() ({ void *handle = NULL; \
+                        char *dll_name = get_vs_script_dll_name(); \
+                        handle = dlopen(dll_name, RTLD_NOW | RTLD_GLOBAL); \
+                        av_free(dll_name); \
+                        handle; })
+#else
+  #include <dlfcn.h>
+  #define VSSCRIPT_NAME "libvapoursynth-script"
+  #define VSSCRIPT_LIB VSSCRIPT_NAME SLIBSUF
+  #define VS_DLOPEN() dlopen(VSSCRIPT_LIB, RTLD_NOW | RTLD_GLOBAL)
+#endif
 
 struct VSState {
     VSScript *vss;
 };
 
+typedef const VSSCRIPTAPI *(*VSScriptGetAPIFunc)(int version);
+
+typedef struct VSScriptLibrary {
+    void *library;
+    const VSSCRIPTAPI *vssapi;
+} VSScriptLibrary;
+
 typedef struct VSContext {
     const AVClass *class;
 
     AVBufferRef *vss_state;
 
     const VSAPI *vsapi;
-    VSCore *vscore;
 
-    VSNodeRef *outnode;
+    VSNode *outnode;
     int is_cfr;
     int current_frame;
 
@@ -70,19 +112,50 @@ static const AVOption options[] = {
     {NULL}
 };
 
+static VSScriptLibrary vs_script_library;
+
+static int vs_atexit_called = 0;
+
+static av_cold void vs_atexit_handler(void);
+
+static av_cold int vs_load_library(void)
+{
+    VSScriptGetAPIFunc get_vs_script_api;
+    vs_script_library.library = VS_DLOPEN();
+    if (!vs_script_library.library)
+        return -1;
+    get_vs_script_api = (VSScriptGetAPIFunc)dlsym(vs_script_library.library,
+                                                  "getVSScriptAPI");
+    if (!get_vs_script_api) {
+        dlclose(vs_script_library.library);
+        return -2;
+    }
+    vs_script_library.vssapi = get_vs_script_api(VSSCRIPT_API_VERSION);
+    if (!vs_script_library.vssapi) {
+        dlclose(vs_script_library.library);
+        return -3;
+    }
+    atexit(vs_atexit_handler);
+    return 0;
+}
+
 static void free_vss_state(void *opaque, uint8_t *data)
 {
     struct VSState *vss = opaque;
 
     if (vss->vss) {
-        vsscript_freeScript(vss->vss);
-        vsscript_finalize();
+        vs_script_library.vssapi->freeScript(vss->vss);
     }
 }
 
 static av_cold int read_close_vs(AVFormatContext *s)
 {
-    VSContext *vs = s->priv_data;
+    VSContext *vs;
+
+    if (vs_atexit_called)
+        return 0;
+
+    vs = s->priv_data;
 
     if (vs->outnode)
         vs->vsapi->freeNode(vs->outnode);
@@ -90,12 +163,17 @@ static av_cold int read_close_vs(AVFormatContext *s)
     av_buffer_unref(&vs->vss_state);
 
     vs->vsapi = NULL;
-    vs->vscore = NULL;
     vs->outnode = NULL;
 
     return 0;
 }
 
+static av_cold void vs_atexit_handler(void)
+{
+    dlclose(vs_script_library.library);
+    vs_atexit_called = 1;
+}
+
 static av_cold int is_native_endian(enum AVPixelFormat pixfmt)
 {
     enum AVPixelFormat other = av_pix_fmt_swap_endianness(pixfmt);
@@ -106,7 +184,7 @@ static av_cold int is_native_endian(enum AVPixelFormat pixfmt)
     return pd && (!!HAVE_BIGENDIAN == !!(pd->flags & AV_PIX_FMT_FLAG_BE));
 }
 
-static av_cold enum AVPixelFormat match_pixfmt(const VSFormat *vsf, int c_order[4])
+static av_cold enum AVPixelFormat match_pixfmt(const VSVideoFormat *vsf, int c_order[4])
 {
     static const int yuv_order[4] = {0, 1, 2, 0};
     static const int rgb_order[4] = {1, 2, 0, 0};
@@ -128,13 +206,12 @@ static av_cold enum AVPixelFormat match_pixfmt(const VSFormat *vsf, int c_order[
             pd->log2_chroma_h != vsf->subSamplingH)
             continue;
 
-        is_rgb = vsf->colorFamily == cmRGB;
+        is_rgb = vsf->colorFamily == cfRGB;
         if (is_rgb != !!(pd->flags & AV_PIX_FMT_FLAG_RGB))
             continue;
 
-        is_yuv = vsf->colorFamily == cmYUV ||
-                 vsf->colorFamily == cmYCoCg ||
-                 vsf->colorFamily == cmGray;
+        is_yuv = vsf->colorFamily == cfYUV ||
+                 vsf->colorFamily == cfGray;
         if (!is_rgb && !is_yuv)
             continue;
 
@@ -176,6 +253,7 @@ static av_cold int read_header_vs(AVFormatContext *s)
     int64_t sz = avio_size(pb);
     char *buf = NULL;
     char dummy;
+    char vsfmt[32];
     const VSVideoInfo *info;
     struct VSState *vss_state;
     int err = 0;
@@ -193,16 +271,30 @@ static av_cold int read_header_vs(AVFormatContext *s)
         goto done;
     }
 
-    if (!vsscript_init()) {
-        av_log(s, AV_LOG_ERROR, "Failed to initialize VSScript (possibly PYTHONPATH not set).\n");
+    if (err = vs_load_library()) {
+        if (err == -1) av_log(s, AV_LOG_ERROR, "Could not open " VSSCRIPT_LIB
+                              ". Check VapourSynth installation.\n");
+        else if (err == -2) av_log(s, AV_LOG_ERROR,
+                                   "Could not load VapourSynth library. "
+                                   "VapourSynth installation may be outdated "
+                                   "or broken.\n");
+        else if (err == -3) av_log(s, AV_LOG_ERROR,
+                                   "Failed to initialize VSScript "
+                                   "(possibly PYTHONPATH not set).\n");
         err = AVERROR_EXTERNAL;
         goto done;
     }
 
-    if (vsscript_createScript(&vss_state->vss)) {
+    if (!(vs->vsapi = vs_script_library.vssapi->getVSAPI(VAPOURSYNTH_API_VERSION))) {
+        av_log(s, AV_LOG_ERROR, "Could not get VSAPI. "
+                                "Check VapourSynth installation.\n");
+        err = AVERROR_EXTERNAL;
+        goto done;
+    }
+
+    if (!(vss_state->vss = vs_script_library.vssapi->createScript(NULL))) {
         av_log(s, AV_LOG_ERROR, "Failed to create script instance.\n");
         err = AVERROR_EXTERNAL;
-        vsscript_finalize();
         goto done;
     }
 
@@ -235,17 +327,14 @@ static av_cold int read_header_vs(AVFormatContext *s)
     }
 
     buf[sz] = '\0';
-    if (vsscript_evaluateScript(&vss_state->vss, buf, s->url, 0)) {
-        const char *msg = vsscript_getError(vss_state->vss);
+    if (vs_script_library.vssapi->evaluateBuffer(vss_state->vss, buf, s->url)) {
+        const char *msg = vs_script_library.vssapi->getError(vss_state->vss);
         av_log(s, AV_LOG_ERROR, "Failed to parse script: %s\n", msg ? msg : "(unknown)");
         err = AVERROR_EXTERNAL;
         goto done;
     }
 
-    vs->vsapi = vsscript_getVSApi();
-    vs->vscore = vsscript_getCore(vss_state->vss);
-
-    vs->outnode = vsscript_getOutput(vss_state->vss, 0);
+    vs->outnode = vs_script_library.vssapi->getOutputNode(vss_state->vss, 0);
     if (!vs->outnode) {
         av_log(s, AV_LOG_ERROR, "Could not get script output node.\n");
         err = AVERROR_EXTERNAL;
@@ -260,7 +349,7 @@ static av_cold int read_header_vs(AVFormatContext *s)
 
     info = vs->vsapi->getVideoInfo(vs->outnode);
 
-    if (!info->format || !info->width || !info->height) {
+    if (!info->format.colorFamily || !info->width || !info->height) {
         av_log(s, AV_LOG_ERROR, "Non-constant input format not supported.\n");
         err = AVERROR_PATCHWELCOME;
         goto done;
@@ -280,18 +369,22 @@ static av_cold int read_header_vs(AVFormatContext *s)
     st->codecpar->codec_id = AV_CODEC_ID_WRAPPED_AVFRAME;
     st->codecpar->width = info->width;
     st->codecpar->height = info->height;
-    st->codecpar->format = match_pixfmt(info->format, vs->c_order);
+    st->codecpar->format = match_pixfmt(&info->format, vs->c_order);
 
     if (st->codecpar->format == AV_PIX_FMT_NONE) {
-        av_log(s, AV_LOG_ERROR, "Unsupported VS pixel format %s\n", info->format->name);
+        if(vs->vsapi->getVideoFormatName(&info->format, vsfmt))
+            av_log(s, AV_LOG_ERROR, "Unsupported VS pixel format %s\n", vsfmt);
+        else
+            av_log(s, AV_LOG_ERROR, "Unsupported VS pixel format\n");
         err = AVERROR_EXTERNAL;
         goto done;
     }
-    av_log(s, AV_LOG_VERBOSE, "VS format %s -> pixfmt %s\n", info->format->name,
-           av_get_pix_fmt_name(st->codecpar->format));
-
-    if (info->format->colorFamily == cmYCoCg)
-        st->codecpar->color_space = AVCOL_SPC_YCGCO;
+    if (vs->vsapi->getVideoFormatName(&info->format, vsfmt))
+        av_log(s, AV_LOG_VERBOSE, "VS format %s -> pixfmt %s\n",
+               vsfmt, av_get_pix_fmt_name(st->codecpar->format));
+    else
+        av_log(s, AV_LOG_VERBOSE, "VS format -> pixfmt %s\n",
+               av_get_pix_fmt_name(st->codecpar->format));
 
 done:
     av_free(buf);
@@ -311,13 +404,13 @@ static int get_vs_prop_int(AVFormatContext *s, const VSMap *map, const char *nam
     int64_t res;
     int err = 1;
 
-    res = vs->vsapi->propGetInt(map, name, 0, &err);
+    res = vs->vsapi->mapGetInt(map, name, 0, &err);
     return err || res < INT_MIN || res > INT_MAX ? def : res;
 }
 
 struct vsframe_ref_data {
     const VSAPI *vsapi;
-    const VSFrameRef *frame;
+    const VSFrame *frame;
     AVBufferRef *vss_state;
 };
 
@@ -339,7 +432,7 @@ static int read_packet_vs(AVFormatContext *s, AVPacket *pkt)
     AVStream *st = s->streams[0];
     AVFrame *frame = NULL;
     char vserr[80];
-    const VSFrameRef *vsframe;
+    const VSFrame *vsframe;
     const VSVideoInfo *info = vs->vsapi->getVideoInfo(vs->outnode);
     const VSMap *props;
     const AVPixFmtDescriptor *desc;
@@ -381,7 +474,7 @@ static int read_packet_vs(AVFormatContext *s, AVPacket *pkt)
         goto end;
     }
 
-    props = vs->vsapi->getFramePropsRO(vsframe);
+    props = vs->vsapi->getFramePropertiesRO(vsframe);
 
     frame = av_frame_alloc();
     if (!frame) {
@@ -410,7 +503,7 @@ static int read_packet_vs(AVFormatContext *s, AVPacket *pkt)
 
     desc = av_pix_fmt_desc_get(frame->format);
 
-    for (i = 0; i < info->format->numPlanes; i++) {
+    for (i = 0; i < info->format.numPlanes; i++) {
         int p = vs->c_order[i];
         ptrdiff_t plane_h = frame->height;
 
diff --git a/libavformat/vapoursynth.h b/libavformat/vapoursynth.h
new file mode 100644
index 0000000000..5b5bb276ee
--- /dev/null
+++ b/libavformat/vapoursynth.h
@@ -0,0 +1,635 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+* @file
+* VapourSynth headers
+*
+* Contains the two needed header files taken from VapourSynth.
+*/
+
+
+
+/* VapourSynth4.h */
+/*
+* Copyright (c) 2012-2021 Fredrik Mellbin
+*
+* This file is part of VapourSynth.
+*
+* VapourSynth 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.
+*
+* VapourSynth 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 VapourSynth; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef VAPOURSYNTH4_H
+#define VAPOURSYNTH4_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define VS_MAKE_VERSION(major, minor) (((major) << 16) | (minor))
+#define VAPOURSYNTH_API_MAJOR 4
+#if defined(VS_USE_LATEST_API) || defined(VS_USE_API_41)
+#define VAPOURSYNTH_API_MINOR 1
+#else
+#define VAPOURSYNTH_API_MINOR 0
+#endif
+#define VAPOURSYNTH_API_VERSION VS_MAKE_VERSION(VAPOURSYNTH_API_MAJOR, VAPOURSYNTH_API_MINOR)
+
+#define VS_AUDIO_FRAME_SAMPLES 3072
+
+/* Convenience for C++ users. */
+#ifdef __cplusplus
+#    define VS_EXTERN_C extern "C"
+#    if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+#        define VS_NOEXCEPT noexcept
+#    else
+#        define VS_NOEXCEPT
+#    endif
+#else
+#    define VS_EXTERN_C
+#    define VS_NOEXCEPT
+#endif
+
+#if defined(_WIN32) && !defined(_WIN64)
+#    define VS_CC __stdcall
+#else
+#    define VS_CC
+#endif
+
+/* And now for some symbol hide-and-seek... */
+#if defined(_WIN32) /* Windows being special */
+#    define VS_EXTERNAL_API(ret) VS_EXTERN_C __declspec(dllexport) ret VS_CC
+#elif defined(__GNUC__) && __GNUC__ >= 4
+#    define VS_EXTERNAL_API(ret) VS_EXTERN_C __attribute__((visibility("default"))) ret VS_CC
+#else
+#    define VS_EXTERNAL_API(ret) VS_EXTERN_C ret VS_CC
+#endif
+
+#if !defined(VS_CORE_EXPORTS) && defined(_WIN32)
+#    define VS_API(ret) VS_EXTERN_C __declspec(dllimport) ret VS_CC
+#else
+#    define VS_API(ret) VS_EXTERNAL_API(ret)
+#endif
+
+typedef struct VSFrame VSFrame;
+typedef struct VSNode VSNode;
+typedef struct VSCore VSCore;
+typedef struct VSPlugin VSPlugin;
+typedef struct VSPluginFunction VSPluginFunction;
+typedef struct VSFunction VSFunction;
+typedef struct VSMap VSMap;
+typedef struct VSLogHandle VSLogHandle;
+typedef struct VSFrameContext VSFrameContext;
+typedef struct VSPLUGINAPI VSPLUGINAPI;
+typedef struct VSAPI VSAPI;
+
+typedef enum VSColorFamily {
+    cfUndefined = 0,
+    cfGray      = 1,
+    cfRGB       = 2,
+    cfYUV       = 3
+} VSColorFamily;
+
+typedef enum VSSampleType {
+    stInteger = 0,
+    stFloat = 1
+} VSSampleType;
+
+#define VS_MAKE_VIDEO_ID(colorFamily, sampleType, bitsPerSample, subSamplingW, subSamplingH) ((colorFamily << 28) | (sampleType << 24) | (bitsPerSample << 16) | (subSamplingW << 8) | (subSamplingH << 0))
+
+typedef enum VSPresetVideoFormat {
+    pfNone = 0,
+
+    pfGray8 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 8, 0, 0),
+    pfGray9 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 9, 0, 0),
+    pfGray10 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 10, 0, 0),
+    pfGray12 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 12, 0, 0),
+    pfGray14 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 14, 0, 0),
+    pfGray16 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 16, 0, 0),
+    pfGray32 = VS_MAKE_VIDEO_ID(cfGray, stInteger, 32, 0, 0),
+
+    pfGrayH = VS_MAKE_VIDEO_ID(cfGray, stFloat, 16, 0, 0),
+    pfGrayS = VS_MAKE_VIDEO_ID(cfGray, stFloat, 32, 0, 0),
+
+    pfYUV410P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 2, 2),
+    pfYUV411P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 2, 0),
+    pfYUV440P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 0, 1),
+
+    pfYUV420P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 1, 1),
+    pfYUV422P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 1, 0),
+    pfYUV444P8 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 8, 0, 0),
+
+    pfYUV420P9 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 9, 1, 1),
+    pfYUV422P9 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 9, 1, 0),
+    pfYUV444P9 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 9, 0, 0),
+
+    pfYUV420P10 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 10, 1, 1),
+    pfYUV422P10 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 10, 1, 0),
+    pfYUV444P10 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 10, 0, 0),
+
+    pfYUV420P12 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 12, 1, 1),
+    pfYUV422P12 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 12, 1, 0),
+    pfYUV444P12 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 12, 0, 0),
+
+    pfYUV420P14 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 14, 1, 1),
+    pfYUV422P14 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 14, 1, 0),
+    pfYUV444P14 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 14, 0, 0),
+
+    pfYUV420P16 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 16, 1, 1),
+    pfYUV422P16 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 16, 1, 0),
+    pfYUV444P16 = VS_MAKE_VIDEO_ID(cfYUV, stInteger, 16, 0, 0),
+
+    pfYUV444PH = VS_MAKE_VIDEO_ID(cfYUV, stFloat, 16, 0, 0),
+    pfYUV444PS = VS_MAKE_VIDEO_ID(cfYUV, stFloat, 32, 0, 0),
+
+    pfRGB24 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 8, 0, 0),
+    pfRGB27 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 9, 0, 0),
+    pfRGB30 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 10, 0, 0),
+    pfRGB36 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 12, 0, 0),
+    pfRGB42 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 14, 0, 0),
+    pfRGB48 = VS_MAKE_VIDEO_ID(cfRGB, stInteger, 16, 0, 0),
+
+    pfRGBH = VS_MAKE_VIDEO_ID(cfRGB, stFloat, 16, 0, 0),
+    pfRGBS = VS_MAKE_VIDEO_ID(cfRGB, stFloat, 32, 0, 0),
+} VSPresetVideoFormat;
+
+#undef VS_MAKE_VIDEO_ID
+
+
+typedef enum VSFilterMode {
+    fmParallel = 0, /* completely parallel execution */
+    fmParallelRequests = 1, /* for filters that are serial in nature but can request one or more frames they need in advance */
+    fmUnordered = 2, /* for filters that modify their internal state every request like source filters that read a file */
+    fmFrameState = 3 /* DO NOT USE UNLESS ABSOLUTELY NECESSARY, for compatibility with external code that can only keep the processing state of a single frame at a time */
+} VSFilterMode;
+
+typedef enum VSMediaType {
+    mtVideo = 1,
+    mtAudio = 2
+} VSMediaType;
+
+typedef struct VSVideoFormat {
+    int colorFamily; /* see VSColorFamily */
+    int sampleType; /* see VSSampleType */
+    int bitsPerSample; /* number of significant bits */
+    int bytesPerSample; /* actual storage is always in a power of 2 and the smallest possible that can fit the number of bits used per sample */
+
+    int subSamplingW; /* log2 subsampling factor, applied to second and third plane */
+    int subSamplingH; /* log2 subsampling factor, applied to second and third plane */
+
+    int numPlanes; /* implicit from colorFamily */
+} VSVideoFormat;
+
+typedef enum VSAudioChannels {
+    acFrontLeft           = 0,
+    acFrontRight          = 1,
+    acFrontCenter         = 2,
+    acLowFrequency        = 3,
+    acBackLeft            = 4,
+    acBackRight           = 5,
+    acFrontLeftOFCenter   = 6,
+    acFrontRightOFCenter  = 7,
+    acBackCenter          = 8,
+    acSideLeft            = 9,
+    acSideRight           = 10,
+    acTopCenter           = 11,
+    acTopFrontLeft        = 12,
+    acTopFrontCenter      = 13,
+    acTopFrontRight       = 14,
+    acTopBackLeft         = 15,
+    acTopBackCenter       = 16,
+    acTopBackRight        = 17,
+    acStereoLeft          = 29,
+    acStereoRight         = 30,
+    acWideLeft            = 31,
+    acWideRight           = 32,
+    acSurroundDirectLeft  = 33,
+    acSurroundDirectRight = 34,
+    acLowFrequency2       = 35
+} VSAudioChannels;
+
+typedef struct VSAudioFormat {
+    int sampleType;
+    int bitsPerSample;
+    int bytesPerSample; /* implicit from bitsPerSample */
+    int numChannels; /* implicit from channelLayout */
+    uint64_t channelLayout;
+} VSAudioFormat;
+
+typedef enum VSPropertyType {
+    ptUnset = 0,
+    ptInt = 1,
+    ptFloat = 2,
+    ptData = 3,
+    ptFunction = 4,
+    ptVideoNode = 5,
+    ptAudioNode = 6,
+    ptVideoFrame = 7,
+    ptAudioFrame = 8
+} VSPropertyType;
+
+typedef enum VSMapPropertyError {
+    peSuccess = 0,
+    peUnset   = 1, /* no key exists */
+    peType    = 2, /* key exists but not of a compatible type */
+    peIndex   = 4, /* index out of bounds */
+    peError   = 3  /* map has error state set */
+} VSMapPropertyError;
+
+typedef enum VSMapAppendMode {
+    maReplace = 0,
+    maAppend  = 1
+} VSMapAppendMode;
+
+typedef struct VSCoreInfo {
+    const char *versionString;
+    int core;
+    int api;
+    int numThreads;
+    int64_t maxFramebufferSize;
+    int64_t usedFramebufferSize;
+} VSCoreInfo;
+
+typedef struct VSVideoInfo {
+    VSVideoFormat format;
+    int64_t fpsNum;
+    int64_t fpsDen;
+    int width;
+    int height;
+    int numFrames;
+} VSVideoInfo;
+
+typedef struct VSAudioInfo {
+    VSAudioFormat format;
+    int sampleRate;
+    int64_t numSamples;
+    int numFrames; /* the total number of audio frames needed to hold numSamples, implicit from numSamples when calling createAudioFilter */
+} VSAudioInfo;
+
+typedef enum VSActivationReason {
+    arInitial = 0,
+    arAllFramesReady = 1,
+    arError = -1
+} VSActivationReason;
+
+typedef enum VSMessageType {
+    mtDebug = 0,
+    mtInformation = 1, 
+    mtWarning = 2,
+    mtCritical = 3,
+    mtFatal = 4 /* also terminates the process, should generally not be used by normal filters */
+} VSMessageType;
+
+typedef enum VSCoreCreationFlags {
+    ccfEnableGraphInspection = 1,
+    ccfDisableAutoLoading = 2,
+    ccfDisableLibraryUnloading = 4
+} VSCoreCreationFlags;
+
+typedef enum VSPluginConfigFlags {
+    pcModifiable = 1
+} VSPluginConfigFlags;
+
+typedef enum VSDataTypeHint {
+    dtUnknown = -1,
+    dtBinary = 0,
+    dtUtf8 = 1
+} VSDataTypeHint;
+
+typedef enum VSRequestPattern {
+    rpGeneral = 0, /* General pattern */
+    rpNoFrameReuse = 1, /* When requesting all output frames from the filter no frame will be requested more than once from this input clip, never requests frames beyond the end of the clip */
+    rpStrictSpatial = 2 /* Always (and only) requests frame n from the input clip when generating output frame n, never requests frames beyond the end of the clip */
+#if VAPOURSYNTH_API_MINOR >= 1   
+    ,
+    rpFrameReuseLastOnly = 3 /* Added in API 4.1, This modes is basically identical rpNoFrameReuse except that it hints the last frame may be requested multiple times */
+#endif
+} VSRequestPattern;
+
+typedef enum VSCacheMode {
+    cmAuto = -1,
+    cmForceDisable = 0,
+    cmForceEnable = 1
+} VSCacheMode;
+
+/* Core entry point */
+typedef const VSAPI *(VS_CC *VSGetVapourSynthAPI)(int version);
+
+/* Plugin, function and filter related */
+typedef void (VS_CC *VSPublicFunction)(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi);
+typedef void (VS_CC *VSInitPlugin)(VSPlugin *plugin, const VSPLUGINAPI *vspapi);
+typedef void (VS_CC *VSFreeFunctionData)(void *userData);
+typedef const VSFrame *(VS_CC *VSFilterGetFrame)(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi);
+typedef void (VS_CC *VSFilterFree)(void *instanceData, VSCore *core, const VSAPI *vsapi);
+
+/* Other */
+typedef void (VS_CC *VSFrameDoneCallback)(void *userData, const VSFrame *f, int n, VSNode *node, const char *errorMsg);
+typedef void (VS_CC *VSLogHandler)(int msgType, const char *msg, void *userData);
+typedef void (VS_CC *VSLogHandlerFree)(void *userData);
+
+typedef struct VSPLUGINAPI {
+    int (VS_CC *getAPIVersion)(void) VS_NOEXCEPT; /* returns VAPOURSYNTH_API_VERSION of the library */
+    int (VS_CC *configPlugin)(const char *identifier, const char *pluginNamespace, const char *name, int pluginVersion, int apiVersion, int flags, VSPlugin *plugin) VS_NOEXCEPT; /* use the VS_MAKE_VERSION macro for pluginVersion */
+    int (VS_CC *registerFunction)(const char *name, const char *args, const char *returnType, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin) VS_NOEXCEPT; /* non-zero return value on success  */
+} VSPLUGINAPI;
+
+typedef struct VSFilterDependency {
+    VSNode *source;
+    int requestPattern; /* VSRequestPattern */
+} VSFilterDependency;
+
+struct VSAPI {
+    /* Audio and video filter related including nodes */
+    void (VS_CC *createVideoFilter)(VSMap *out, const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; /* output nodes are appended to the clip key in the out map */
+    VSNode *(VS_CC *createVideoFilter2)(const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; /* same as createVideoFilter but returns a pointer to the VSNode directly or NULL on failure */
+    void (VS_CC *createAudioFilter)(VSMap *out, const char *name, const VSAudioInfo *ai, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; /* output nodes are appended to the clip key in the out map */
+    VSNode *(VS_CC *createAudioFilter2)(const char *name, const VSAudioInfo *ai, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; /* same as createAudioFilter but returns a pointer to the VSNode directly or NULL on failure */
+    int (VS_CC *setLinearFilter)(VSNode *node) VS_NOEXCEPT; /* Use right after create*Filter*, sets the correct cache mode for using the cacheFrame API and returns the recommended upper number of additional frames to cache per request */
+    void (VS_CC *setCacheMode)(VSNode *node, int mode) VS_NOEXCEPT; /* VSCacheMode, changing the cache mode also resets all options to their default */
+    void (VS_CC *setCacheOptions)(VSNode *node, int fixedSize, int maxSize, int maxHistorySize) VS_NOEXCEPT; /* passing -1 means no change */
+
+    void (VS_CC *freeNode)(VSNode *node) VS_NOEXCEPT;
+    VSNode *(VS_CC *addNodeRef)(VSNode *node) VS_NOEXCEPT;
+    int (VS_CC *getNodeType)(VSNode *node) VS_NOEXCEPT; /* returns VSMediaType */
+    const VSVideoInfo *(VS_CC *getVideoInfo)(VSNode *node) VS_NOEXCEPT;
+    const VSAudioInfo *(VS_CC *getAudioInfo)(VSNode *node) VS_NOEXCEPT;
+
+    /* Frame related functions */
+    VSFrame *(VS_CC *newVideoFrame)(const VSVideoFormat *format, int width, int height, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT;
+    VSFrame *(VS_CC *newVideoFrame2)(const VSVideoFormat *format, int width, int height, const VSFrame **planeSrc, const int *planes, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT; /* same as newVideoFrame but allows the specified planes to be effectively copied from the source frames */
+    VSFrame *(VS_CC *newAudioFrame)(const VSAudioFormat *format, int numSamples, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT;
+    VSFrame *(VS_CC *newAudioFrame2)(const VSAudioFormat *format, int numSamples, const VSFrame **channelSrc, const int *channels, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT; /* same as newAudioFrame but allows the specified channels to be effectively copied from the source frames */
+    void (VS_CC *freeFrame)(const VSFrame *f) VS_NOEXCEPT;
+    const VSFrame *(VS_CC *addFrameRef)(const VSFrame *f) VS_NOEXCEPT;
+    VSFrame *(VS_CC *copyFrame)(const VSFrame *f, VSCore *core) VS_NOEXCEPT;
+    const VSMap *(VS_CC *getFramePropertiesRO)(const VSFrame *f) VS_NOEXCEPT;
+    VSMap *(VS_CC *getFramePropertiesRW)(VSFrame *f) VS_NOEXCEPT;
+
+    ptrdiff_t (VS_CC *getStride)(const VSFrame *f, int plane) VS_NOEXCEPT;
+    const uint8_t *(VS_CC *getReadPtr)(const VSFrame *f, int plane) VS_NOEXCEPT;
+    uint8_t *(VS_CC *getWritePtr)(VSFrame *f, int plane) VS_NOEXCEPT; /* calling this function invalidates previously gotten read pointers to the same frame */
+
+    const VSVideoFormat *(VS_CC *getVideoFrameFormat)(const VSFrame *f) VS_NOEXCEPT;
+    const VSAudioFormat *(VS_CC *getAudioFrameFormat)(const VSFrame *f) VS_NOEXCEPT;
+    int (VS_CC *getFrameType)(const VSFrame *f) VS_NOEXCEPT; /* returns VSMediaType */
+    int (VS_CC *getFrameWidth)(const VSFrame *f, int plane) VS_NOEXCEPT;
+    int (VS_CC *getFrameHeight)(const VSFrame *f, int plane) VS_NOEXCEPT;
+    int (VS_CC *getFrameLength)(const VSFrame *f) VS_NOEXCEPT; /* returns the number of samples for audio frames */
+
+    /* General format functions  */
+    int (VS_CC *getVideoFormatName)(const VSVideoFormat *format, char *buffer) VS_NOEXCEPT; /* up to 32 characters including terminating null may be written to the buffer, non-zero return value on success */
+    int (VS_CC *getAudioFormatName)(const VSAudioFormat *format, char *buffer) VS_NOEXCEPT; /* up to 32 characters including terminating null may be written to the buffer, non-zero return value on success */
+    int (VS_CC *queryVideoFormat)(VSVideoFormat *format, int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT; /* non-zero return value on success */
+    int (VS_CC *queryAudioFormat)(VSAudioFormat *format, int sampleType, int bitsPerSample, uint64_t channelLayout, VSCore *core) VS_NOEXCEPT; /* non-zero return value on success */
+    uint32_t (VS_CC *queryVideoFormatID)(int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT; /* returns 0 on failure */
+    int (VS_CC *getVideoFormatByID)(VSVideoFormat *format, uint32_t id, VSCore *core) VS_NOEXCEPT; /* non-zero return value on success */
+
+    /* Frame request and filter getframe functions */
+    const VSFrame *(VS_CC *getFrame)(int n, VSNode *node, char *errorMsg, int bufSize) VS_NOEXCEPT; /* only for external applications using the core as a library or for requesting frames in a filter constructor, do not use inside a filter's getframe function */
+    void (VS_CC *getFrameAsync)(int n, VSNode *node, VSFrameDoneCallback callback, void *userData) VS_NOEXCEPT; /* only for external applications using the core as a library or for requesting frames in a filter constructor, do not use inside a filter's getframe function */
+    const VSFrame *(VS_CC *getFrameFilter)(int n, VSNode *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */
+    void (VS_CC *requestFrameFilter)(int n, VSNode *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */
+    void (VS_CC *releaseFrameEarly)(VSNode *node, int n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function, unless this function is called a requested frame is kept in memory until the end of processing the current frame */
+    void (VS_CC *cacheFrame)(const VSFrame *frame, int n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* used to store intermediate frames in cache, useful for filters where random access is slow, must call setLinearFilter on the node before using or the result is undefined  */
+    void (VS_CC *setFilterError)(const char *errorMessage, VSFrameContext *frameCtx) VS_NOEXCEPT; /* used to signal errors in the filter getframe function */
+
+    /* External functions */
+    VSFunction *(VS_CC *createFunction)(VSPublicFunction func, void *userData, VSFreeFunctionData free, VSCore *core) VS_NOEXCEPT;
+    void (VS_CC *freeFunction)(VSFunction *f) VS_NOEXCEPT;
+    VSFunction *(VS_CC *addFunctionRef)(VSFunction *f) VS_NOEXCEPT;
+    void (VS_CC *callFunction)(VSFunction *func, const VSMap *in, VSMap *out) VS_NOEXCEPT;
+
+    /* Map and property access functions */
+    VSMap *(VS_CC *createMap)(void) VS_NOEXCEPT;
+    void (VS_CC *freeMap)(VSMap *map) VS_NOEXCEPT;
+    void (VS_CC *clearMap)(VSMap *map) VS_NOEXCEPT;
+    void (VS_CC *copyMap)(const VSMap *src, VSMap *dst) VS_NOEXCEPT; /* copies all values in src to dst, if a key already exists in dst it's replaced */
+
+    void (VS_CC *mapSetError)(VSMap *map, const char *errorMessage) VS_NOEXCEPT; /* used to signal errors outside filter getframe function */
+    const char *(VS_CC *mapGetError)(const VSMap *map) VS_NOEXCEPT; /* used to query errors, returns 0 if no error */
+
+    int (VS_CC *mapNumKeys)(const VSMap *map) VS_NOEXCEPT;
+    const char *(VS_CC *mapGetKey)(const VSMap *map, int index) VS_NOEXCEPT;
+    int (VS_CC *mapDeleteKey)(VSMap *map, const char *key) VS_NOEXCEPT;
+    int (VS_CC *mapNumElements)(const VSMap *map, const char *key) VS_NOEXCEPT; /* returns -1 if a key doesn't exist */
+    int (VS_CC *mapGetType)(const VSMap *map, const char *key) VS_NOEXCEPT; /* returns VSPropertyType */
+    int (VS_CC *mapSetEmpty)(VSMap *map, const char *key, int type) VS_NOEXCEPT;
+
+    int64_t (VS_CC *mapGetInt)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapGetIntSaturated)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    const int64_t *(VS_CC *mapGetIntArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapSetInt)(VSMap *map, const char *key, int64_t i, int append) VS_NOEXCEPT;
+    int (VS_CC *mapSetIntArray)(VSMap *map, const char *key, const int64_t *i, int size) VS_NOEXCEPT;
+
+    double (VS_CC *mapGetFloat)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    float (VS_CC *mapGetFloatSaturated)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    const double *(VS_CC *mapGetFloatArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapSetFloat)(VSMap *map, const char *key, double d, int append) VS_NOEXCEPT;
+    int (VS_CC *mapSetFloatArray)(VSMap *map, const char *key, const double *d, int size) VS_NOEXCEPT;
+
+    const char *(VS_CC *mapGetData)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapGetDataSize)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapGetDataTypeHint)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; /* returns VSDataTypeHint */
+    int (VS_CC *mapSetData)(VSMap *map, const char *key, const char *data, int size, int type, int append) VS_NOEXCEPT;
+
+    VSNode *(VS_CC *mapGetNode)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapSetNode)(VSMap *map, const char *key, VSNode *node, int append) VS_NOEXCEPT; /* returns 0 on success */
+    int (VS_CC *mapConsumeNode)(VSMap *map, const char *key, VSNode *node, int append) VS_NOEXCEPT; /* always consumes the reference, even on error */
+
+    const VSFrame *(VS_CC *mapGetFrame)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapSetFrame)(VSMap *map, const char *key, const VSFrame *f, int append) VS_NOEXCEPT; /* returns 0 on success */
+    int (VS_CC *mapConsumeFrame)(VSMap *map, const char *key, const VSFrame *f, int append) VS_NOEXCEPT; /* always consumes the reference, even on error */
+
+    VSFunction *(VS_CC *mapGetFunction)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT;
+    int (VS_CC *mapSetFunction)(VSMap *map, const char *key, VSFunction *func, int append) VS_NOEXCEPT; /* returns 0 on success */
+    int (VS_CC *mapConsumeFunction)(VSMap *map, const char *key, VSFunction *func, int append) VS_NOEXCEPT; /* always consumes the reference, even on error */
+
+    /* Plugin and plugin function related */
+    int (VS_CC *registerFunction)(const char *name, const char *args, const char *returnType, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin) VS_NOEXCEPT; /* non-zero return value on success  */
+    VSPlugin *(VS_CC *getPluginByID)(const char *identifier, VSCore *core) VS_NOEXCEPT;
+    VSPlugin *(VS_CC *getPluginByNamespace)(const char *ns, VSCore *core) VS_NOEXCEPT;
+    VSPlugin *(VS_CC *getNextPlugin)(VSPlugin *plugin, VSCore *core) VS_NOEXCEPT; /* pass NULL to get the first plugin  */
+    const char *(VS_CC *getPluginName)(VSPlugin *plugin) VS_NOEXCEPT;
+    const char *(VS_CC *getPluginID)(VSPlugin *plugin) VS_NOEXCEPT;
+    const char *(VS_CC *getPluginNamespace)(VSPlugin *plugin) VS_NOEXCEPT;
+    VSPluginFunction *(VS_CC *getNextPluginFunction)(VSPluginFunction *func, VSPlugin *plugin) VS_NOEXCEPT; /* pass NULL to get the first plugin function  */
+    VSPluginFunction *(VS_CC *getPluginFunctionByName)(const char *name, VSPlugin *plugin) VS_NOEXCEPT;
+    const char *(VS_CC *getPluginFunctionName)(VSPluginFunction *func) VS_NOEXCEPT;
+    const char *(VS_CC *getPluginFunctionArguments)(VSPluginFunction *func) VS_NOEXCEPT; /* returns an argument format string */
+    const char *(VS_CC *getPluginFunctionReturnType)(VSPluginFunction *func) VS_NOEXCEPT; /* returns an argument format string */
+    const char *(VS_CC *getPluginPath)(const VSPlugin *plugin) VS_NOEXCEPT; /* the full path to the loaded library file containing the plugin entry point */
+    int (VS_CC *getPluginVersion)(const VSPlugin *plugin) VS_NOEXCEPT;
+    VSMap *(VS_CC *invoke)(VSPlugin *plugin, const char *name, const VSMap *args) VS_NOEXCEPT; /* user must free the returned VSMap */
+
+    /* Core and information */
+    VSCore *(VS_CC *createCore)(int flags) VS_NOEXCEPT; /* flags uses the VSCoreCreationFlags enum */
+    void (VS_CC *freeCore)(VSCore *core) VS_NOEXCEPT; /* only call this function after all node, frame and function references belonging to the core have been freed */
+    int64_t (VS_CC *setMaxCacheSize)(int64_t bytes, VSCore *core) VS_NOEXCEPT; /* the total cache size at which vapoursynth more aggressively tries to reclaim memory, it is not a hard limit */
+    int (VS_CC *setThreadCount)(int threads, VSCore *core) VS_NOEXCEPT; /* setting threads to 0 means automatic detection */
+    void (VS_CC *getCoreInfo)(VSCore *core, VSCoreInfo *info) VS_NOEXCEPT;
+    int (VS_CC *getAPIVersion)(void) VS_NOEXCEPT;
+
+    /* Message handler */
+    void (VS_CC *logMessage)(int msgType, const char *msg, VSCore *core) VS_NOEXCEPT;
+    VSLogHandle *(VS_CC *addLogHandler)(VSLogHandler handler, VSLogHandlerFree free, void *userData, VSCore *core) VS_NOEXCEPT; /* free and userData can be NULL, returns a handle that can be passed to removeLogHandler */
+    int (VS_CC *removeLogHandler)(VSLogHandle *handle, VSCore *core) VS_NOEXCEPT; /* returns non-zero if successfully removed */
+    
+    /* Added in API 4.1, mostly graph and node inspection, PLEASE DON'T USE INSIDE FILTERS */
+#if VAPOURSYNTH_API_MINOR >= 1
+    /* Additional cache management to free memory */
+    void (VS_CC *clearNodeCache)(VSNode *node) VS_NOEXCEPT; /* clears the cache of the specified node */
+    void (VS_CC *clearCoreCaches)(VSCore *core) VS_NOEXCEPT; /* clears all caches belonging to the specified core */
+
+    /* Basic node information */
+    const char *(VS_CC *getNodeName)(VSNode *node) VS_NOEXCEPT; /* the name passed to create*Filter */
+    int (VS_CC *getNodeFilterMode)(VSNode *node) VS_NOEXCEPT; /* returns VSFilterMode */
+    int (VS_CC *getNumNodeDependencies)(VSNode *node) VS_NOEXCEPT;
+    const VSFilterDependency *(VS_CC *getNodeDependency)(VSNode *node, int index) VS_NOEXCEPT;
+
+    /* Node timing functions */
+    int (VS_CC *getCoreNodeTiming)(VSCore *core) VS_NOEXCEPT; /* non-zero when filter timing is enabled */
+    void (VS_CC *setCoreNodeTiming)(VSCore *core, int enable) VS_NOEXCEPT; /* non-zero enables filter timing, note that disabling simply stops the counters from incrementing */
+    int64_t (VS_CC *getNodeProcessingTime)(VSNode *node, int reset) VS_NOEXCEPT; /* time spent processing frames in nanoseconds, reset sets the counter to 0 again */
+    int64_t (VS_CC *getFreedNodeProcessingTime)(VSCore *core, int reset) VS_NOEXCEPT; /* time spent processing frames in nanoseconds in all destroyed nodes, reset sets the counter to 0 again */
+
+#if defined(VS_GRAPH_API)
+    /* !!! Experimental/expensive graph information, these function require both the major and minor version to match exactly when using them !!!
+     * 
+     * These functions only exist to retrieve internal details for debug purposes and graph visualization
+     * They will only only work properly when used on a core created with ccfEnableGraphInspection and are
+     * not safe to use concurrently with frame requests or other API functions. Because of this they are
+     * unsuitable for use in plugins and filters.
+     */
+
+    const char *(VS_CC *getNodeCreationFunctionName)(VSNode *node, int level) VS_NOEXCEPT; /* level=0 returns the name of the function that created the filter, specifying a higher level will retrieve the function above that invoked it or NULL if a non-existent level is requested */
+    const VSMap *(VS_CC *getNodeCreationFunctionArguments)(VSNode *node, int level) VS_NOEXCEPT; /* level=0 returns a copy of the arguments passed to the function that created the filter, returns NULL if a non-existent level is requested */
+#endif
+#endif
+};
+
+VS_API(const VSAPI *) getVapourSynthAPI(int version) VS_NOEXCEPT;
+
+#endif /* VAPOURSYNTH4_H */
+
+/* VSScript4.h */
+/*
+* Copyright (c) 2013-2020 Fredrik Mellbin
+*
+* This file is part of VapourSynth.
+*
+* VapourSynth 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.
+*
+* VapourSynth 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 VapourSynth; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef VSSCRIPT4_H
+#define VSSCRIPT4_H
+
+#define VSSCRIPT_API_MAJOR 4
+#define VSSCRIPT_API_MINOR 1
+#define VSSCRIPT_API_VERSION VS_MAKE_VERSION(VSSCRIPT_API_MAJOR, VSSCRIPT_API_MINOR)
+
+typedef struct VSScript VSScript;
+typedef struct VSSCRIPTAPI VSSCRIPTAPI;
+
+struct VSSCRIPTAPI {
+    /* Returns the highest supported VSSCRIPT_API_VERSION */
+    int (VS_CC *getAPIVersion)(void) VS_NOEXCEPT;
+
+    /* Convenience function for retrieving a VSAPI pointer without having to use the VapourSynth library. Always pass VAPOURSYNTH_API_VERSION */
+    const VSAPI *(VS_CC *getVSAPI)(int version) VS_NOEXCEPT;
+
+    /* 
+    * Providing a pre-created core is useful for setting core creation flags, log callbacks, preload specific plugins and many other things.
+    * You must create a VSScript object before evaluating a script. Always takes ownership of the core even on failure. Returns NULL on failure.
+    * Pass NULL to have a core automatically created with the default options.
+    */
+    VSScript *(VS_CC *createScript)(VSCore *core) VS_NOEXCEPT;
+
+    /* The core is valid as long as the environment exists, return NULL on error */
+    VSCore *(VS_CC *getCore)(VSScript *handle) VS_NOEXCEPT;
+
+    /*
+    * Evaluates a script passed in the buffer argument. The scriptFilename is only used for display purposes. in Python
+    * it means that the main module won't be unnamed in error messages.
+    * 
+    * Returns 0 on success.
+    * 
+    * Note that calling any function other than getError() and freeScript() on a VSScript object in the error state
+    * will result in undefined behavior.
+    */
+    int (VS_CC *evaluateBuffer)(VSScript *handle, const char *buffer, const char *scriptFilename) VS_NOEXCEPT;
+
+    /* Convenience version of the above function that loads the script from scriptFilename and passes as the buffer to evaluateBuffer */
+    int (VS_CC *evaluateFile)(VSScript *handle, const char *scriptFilename) VS_NOEXCEPT;
+
+    /* Returns NULL on success, otherwise an error message */
+    const char *(VS_CC *getError)(VSScript *handle) VS_NOEXCEPT;
+
+    /* Returns the script's reported exit code */
+    int (VS_CC *getExitCode)(VSScript *handle) VS_NOEXCEPT;
+
+    /* Fetches a variable of any VSMap storable type set in a script. It is stored in the key with the same name in dst. Returns 0 on success. */
+    int (VS_CC *getVariable)(VSScript *handle, const char *name, VSMap *dst) VS_NOEXCEPT;
+
+    /* Sets all keys in the provided VSMap as variables in the script. Returns 0 on success. */
+    int (VS_CC *setVariables)(VSScript *handle, const VSMap *vars) VS_NOEXCEPT;
+
+    /*
+    * The returned nodes must be freed using freeNode() before calling freeScript() since they may depend on data in the VSScript
+    * environment. Returns NULL if no node was set as output in the script. Index 0 is used by default in scripts and other
+    * values are rarely used.
+    */
+    VSNode *(VS_CC *getOutputNode)(VSScript *handle, int index) VS_NOEXCEPT;
+    VSNode *(VS_CC *getOutputAlphaNode)(VSScript *handle, int index) VS_NOEXCEPT;
+    int (VS_CC *getAltOutputMode)(VSScript *handle, int index) VS_NOEXCEPT;
+
+    void (VS_CC *freeScript)(VSScript *handle) VS_NOEXCEPT;
+
+    /*
+    * Set whether or not the working directory is temporarily changed to the same
+    * location as the script file when evaluateFile is called. Off by default.
+    */
+    void (VS_CC *evalSetWorkingDir)(VSScript *handle, int setCWD) VS_NOEXCEPT;
+
+};
+
+VS_API(const VSSCRIPTAPI *) getVSScriptAPI(int version) VS_NOEXCEPT;
+
+#endif /* VSSCRIPT4_H */
-- 
2.34.1


[-- Attachment #3: Type: text/plain, Size: 251 bytes --]

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-22  1:37 [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime Stefan Oltmanns via ffmpeg-devel
@ 2024-06-22  6:27 ` Stephen Hutchinson
  2024-06-22 10:02   ` Stefan Oltmanns via ffmpeg-devel
  2024-06-25  9:10 ` Anton Khirnov
  1 sibling, 1 reply; 7+ messages in thread
From: Stephen Hutchinson @ 2024-06-22  6:27 UTC (permalink / raw)
  To: ffmpeg-devel

On 6/21/24 9:37 PM, Stefan Oltmanns via ffmpeg-devel wrote:
> The current VapourSynth implementation is rarely used, as it links the
> VapourSynth library at build time, making the resulting build unable to
> run when VapourSynth is not installed. Therefore barely anyone compiles
> with VapourSynth activated.
> 

How many distros package VapourSynth yet don't enable it in FFmpeg vs.
both not packaging it and not enabling it?  VapourSynth not being
available as a package in the distro's repository is a far more likely
reason than linking is.

Switching to dynamic loading would absolutely make the experience on or
building for Windows smoother, and would remove the need to rebuild
FFmpeg when VapourSynth updates, outside of future API changes the
demuxer would need to actively account for.

> I changed it, so that it loads the library at runtime when a VapourSynth
> script should be opened, just like AviSynth does.
> On Windows the DLL from VapourSynth is not installed in the system
> directory, but the location is stored in the Registry. Therefore I added
> some code to read that information from the registry.
> 

That function is in the wrong place.

> I copied the two needed header files directly in a vapoursynth.h,
> removing the need to install VapourSynth on the build machine
> (VapourSynth is also LGPL 2.1 or later, so no license issue). I updated
> the configure so that it checks for the ability to load libraries at
> runtime for VapourSynth, just like AviSynth and activate it if not 
> disabled.
> 

Including local copies of the headers in compat/ wasn't acceptable for
AviSynth (and were removed as soon as it was no longer necessary for 
there to be an OS distinction between what headers were being used),
it's not going to be acceptable for this either.

And setting it up as autodetect seems like overreach.  I don't know if
there's any actual written rule about which libraries to autodetect and
which ones require explicit enabling, but most of the autodetected ones
thus far appear to be OS-level or otherwise foundational libraries, not
libraries for a singular media format.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-22  6:27 ` Stephen Hutchinson
@ 2024-06-22 10:02   ` Stefan Oltmanns via ffmpeg-devel
  2024-06-22 18:23     ` Stephen Hutchinson
  0 siblings, 1 reply; 7+ messages in thread
From: Stefan Oltmanns via ffmpeg-devel @ 2024-06-22 10:02 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Stefan Oltmanns

Am 22.06.24 um 08:27 schrieb Stephen Hutchinson:
> On 6/21/24 9:37 PM, Stefan Oltmanns via ffmpeg-devel wrote:
>> The current VapourSynth implementation is rarely used, as it links the
>> VapourSynth library at build time, making the resulting build unable to
>> run when VapourSynth is not installed. Therefore barely anyone compiles
>> with VapourSynth activated.
>>
>
> How many distros package VapourSynth yet don't enable it in FFmpeg vs.
> both not packaging it and not enabling it?  VapourSynth not being
> available as a package in the distro's repository is a far more likely
> reason than linking is.
>

I don't know the extact reason, but VapourSynth is not just a library
like avisynth, but an application that uses Python, meaning a lot of
dependencies.
Additionally VapourSynth can nowadays be installed using pip, so no
reason to package it for distros anymore.

> Switching to dynamic loading would absolutely make the experience on or
> building for Windows smoother, and would remove the need to rebuild
> FFmpeg when VapourSynth updates, outside of future API changes the
> demuxer would need to actively account for.
>

Exactly

>> I changed it, so that it loads the library at runtime when a VapourSynth
>> script should be opened, just like AviSynth does.
>> On Windows the DLL from VapourSynth is not installed in the system
>> directory, but the location is stored in the Registry. Therefore I added
>> some code to read that information from the registry.
>>
>
> That function is in the wrong place.
>


I thought that to, should it be in /compat as w32registry.h or something
like that?

But what exactly should it contain?

I could make a function that would be used like
  get_w32_regsitry_str(HKEY_CURRENT_USER, L"SOFTWARE\\VapourSynth",
                       L"VSScriptDLL");
That would return a utf8 string on success and NULL on failure

But that would still contain 3 Windows-specific constants in the
function call. Also is it useful to write the literals as utf-8 just for
them to be converted to wchar again or should it just take WCHAR and
return utf8?
Or shoulds the entire function be located in
/compat/vapoursynth/w32_vsscript_dll.h as "get_w32_vsscript_dll" or
something similar?

>> I copied the two needed header files directly in a vapoursynth.h,
>> removing the need to install VapourSynth on the build machine
>> (VapourSynth is also LGPL 2.1 or later, so no license issue). I updated
>> the configure so that it checks for the ability to load libraries at
>> runtime for VapourSynth, just like AviSynth and activate it if not
>> disabled.
>>
>
> Including local copies of the headers in compat/ wasn't acceptable for
> AviSynth (and were removed as soon as it was no longer necessary for
> there to be an OS distinction between what headers were being used),
> it's not going to be acceptable for this either.

As distros probably won't package VapourSynth as it can be installed
using pip, they probably won't build ffmpeg with VapourSynth support,
because they don't have the headers on the build system.
Even when someone decides to build ffmpeg for themself with VapourSynth
support, it will fail unless they have manually build VapourSynth,
because pip won't install any header.
That was my motivation to include those headers. Is there a specific
rule when it is allowed to include headers for external libaries?

>
> And setting it up as autodetect seems like overreach.  I don't know if
> there's any actual written rule about which libraries to autodetect and
> which ones require explicit enabling, but most of the autodetected ones
> thus far appear to be OS-level or otherwise foundational libraries, not
> libraries for a singular media format.

I did not find any rule on that. I made it autodetect, because I saw no
negative impact (same license and no dependency at runtime).

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-22 10:02   ` Stefan Oltmanns via ffmpeg-devel
@ 2024-06-22 18:23     ` Stephen Hutchinson
  2024-06-22 19:22       ` Stefan Oltmanns via ffmpeg-devel
  0 siblings, 1 reply; 7+ messages in thread
From: Stephen Hutchinson @ 2024-06-22 18:23 UTC (permalink / raw)
  To: ffmpeg-devel

On 6/22/24 6:02 AM, Stefan Oltmanns via ffmpeg-devel wrote:
> I don't know the extact reason, but VapourSynth is not just a library
> like avisynth, but an application that uses Python, meaning a lot of
> dependencies.

If we want to be technical, then yes, VapourSynth is just a library,
with bindings to integrate it into Python.  Lua bindings used to exist
in mpv, but were removed several years ago; I don't know if those
still exist somewhere externally.  Maybe someone's tried
writing Ruby or JS bindings for it, crazier things have happened.

Python is the thing that provides the ability to execute scripts,
not VapourSynth/VSScript[.dll|.so|.dylib] itself.

> Additionally VapourSynth can nowadays be installed using pip, so no
> reason to package it for distros anymore.
> 

 > As distros probably won't package VapourSynth as it can be installed
 > using pip, they probably won't build ffmpeg with VapourSynth support,
 > because they don't have the headers on the build system.
 > Even when someone decides to build ffmpeg for themself with VapourSynth
 > support, it will fail unless they have manually build VapourSynth,
 > because pip won't install any header.
 > That was my motivation to include those headers. Is there a specific
 > rule when it is allowed to include headers for external libaries?
 >

Distros don't really care about whether something is installable via
pip before providing their own packages, and this was even acknowledged
by upstream Python/pip with the recent move to the
venv/--break-system-packages thing.  What a distro decides to package
or not package in conjunction with enabling support for it in FFmpeg
relates directly to their native repository system.

This would also apply to a project like libdovi; if it's going to be
provided and enabled in a distro's FFmpeg package, they aren't going
to take the stance of 'users will just install it through their Rust
environment first' - the distro will have to provide a package for it
for the distro's FFmpeg package to enable it.



>>> I changed it, so that it loads the library at runtime when a VapourSynth
>>> script should be opened, just like AviSynth does.
>>> On Windows the DLL from VapourSynth is not installed in the system
>>> directory, but the location is stored in the Registry. Therefore I added
>>> some code to read that information from the registry.
>>>
>>
>> That function is in the wrong place.
>>
> 
> 
> I thought that to, should it be in /compat as w32registry.h or something
> like that?
  >
> But what exactly should it contain?
> 
> I could make a function that would be used like
>   get_w32_regsitry_str(HKEY_CURRENT_USER, L"SOFTWARE\\VapourSynth",
>                        L"VSScriptDLL");
> That would return a utf8 string on success and NULL on failure
> 

I would say either unify the logic inside of vs_load_library with the
minimum of Windows-specific things still cordoned off behind an ifdef,
or move the function (again, still behind an ifdef) to just above where
vs_load_library is.  It was mainly that putting it inside a big block
of header #includes isn't the right place for that.

Have you tested whether simply adding the directory VapourSynth.dll
and VSScript.dll reside in to the %PATH% gets around the need to
read locations from the registry?  Without adding it to the %PATH%,
you could use Windows' own DLL loading rules to test it (just plop
ffmpeg.exe down in the same directory and run it when navigated into
that directory).

> But that would still contain 3 Windows-specific constants in the
> function call. Also is it useful to write the literals as utf-8 just for
> them to be converted to wchar again or should it just take WCHAR and
> return utf8?
> Or shoulds the entire function be located in
> /compat/vapoursynth/w32_vsscript_dll.h as "get_w32_vsscript_dll" or
> something similar?
> 

I'm definitely not the person to ask about the text encoding stuff.

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-22 18:23     ` Stephen Hutchinson
@ 2024-06-22 19:22       ` Stefan Oltmanns via ffmpeg-devel
  0 siblings, 0 replies; 7+ messages in thread
From: Stefan Oltmanns via ffmpeg-devel @ 2024-06-22 19:22 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Stefan Oltmanns

Am 22.06.24 um 20:23 schrieb Stephen Hutchinson:
> On 6/22/24 6:02 AM, Stefan Oltmanns via ffmpeg-devel wrote:
>> I don't know the extact reason, but VapourSynth is not just a library
>> like avisynth, but an application that uses Python, meaning a lot of
>> dependencies.
>
> If we want to be technical, then yes, VapourSynth is just a library,
> with bindings to integrate it into Python.  Lua bindings used to exist
> in mpv, but were removed several years ago; I don't know if those
> still exist somewhere externally.  Maybe someone's tried
> writing Ruby or JS bindings for it, crazier things have happened.
>
> Python is the thing that provides the ability to execute scripts,
> not VapourSynth/VSScript[.dll|.so|.dylib] itself.
>

Technically yes, but a library that is not mostly self-contained like
for example libx264, but one with massive dependencies to Python. That's
why you might not want to link to VapourSynth even if you have packaged it.

>> Additionally VapourSynth can nowadays be installed using pip, so no
>> reason to package it for distros anymore.
>>
>
>  > As distros probably won't package VapourSynth as it can be installed
>  > using pip, they probably won't build ffmpeg with VapourSynth support,
>  > because they don't have the headers on the build system.
>  > Even when someone decides to build ffmpeg for themself with VapourSynth
>  > support, it will fail unless they have manually build VapourSynth,
>  > because pip won't install any header.
>  > That was my motivation to include those headers. Is there a specific
>  > rule when it is allowed to include headers for external libaries?
>  >
>
> Distros don't really care about whether something is installable via
> pip before providing their own packages, and this was even acknowledged
> by upstream Python/pip with the recent move to the
> venv/--break-system-packages thing.  What a distro decides to package
> or not package in conjunction with enabling support for it in FFmpeg
> relates directly to their native repository system.
>
> This would also apply to a project like libdovi; if it's going to be
> provided and enabled in a distro's FFmpeg package, they aren't going
> to take the stance of 'users will just install it through their Rust
> environment first' - the distro will have to provide a package for it
> for the distro's FFmpeg package to enable it.
>

What I mean before a distro will include something there needs to be a
need for that and a maintainer for that package. If people will just use
pip no one will ask for that package to be included in the distro and
it's difficult to find someone to maintain it.
The pip vs. distro packet manager is in my experience mostly a result of
an application that is provided by the distro that has Python
dependencies to something like numpy. Later the user wants to install a
application with pip, but that has a dependency to a newer numpy
version. Then the solutions are what you have described.


>>>> I changed it, so that it loads the library at runtime when a
>>>> VapourSynth
>>>> script should be opened, just like AviSynth does.
>>>> On Windows the DLL from VapourSynth is not installed in the system
>>>> directory, but the location is stored in the Registry. Therefore I
>>>> added
>>>> some code to read that information from the registry.
>>>>
>>>
>>> That function is in the wrong place.
>>>
>>
>>
>> I thought that to, should it be in /compat as w32registry.h or something
>> like that?
>   >
>> But what exactly should it contain?
>>
>> I could make a function that would be used like
>>   get_w32_regsitry_str(HKEY_CURRENT_USER, L"SOFTWARE\\VapourSynth",
>>                        L"VSScriptDLL");
>> That would return a utf8 string on success and NULL on failure
>>
>
> I would say either unify the logic inside of vs_load_library with the
> minimum of Windows-specific things still cordoned off behind an ifdef,
> or move the function (again, still behind an ifdef) to just above where
> vs_load_library is.  It was mainly that putting it inside a big block
> of header #includes isn't the right place for that.

Ah ok, I see. I made it like this to not have platform-specific code at
different places, but if it's preferred at another place, that's fine.

>
> Have you tested whether simply adding the directory VapourSynth.dll
> and VSScript.dll reside in to the %PATH% gets around the need to
> read locations from the registry?  Without adding it to the %PATH%,
> you could use Windows' own DLL loading rules to test it (just plop
> ffmpeg.exe down in the same directory and run it when navigated into
> that directory).

Yes, that works and is fallback when no registry entries are found (for
portable packages). But when a user has installed VapourSynth and wants
to use ffmpeg with it it's quite inconvenient to force him to copy the
DLL somewhere else.
It might also create a problem: When the version of VSScript.dll does
not match the version of the installed Vapoursynth (because it was
updated) that could cause a problem.
Reading the registry entry to load the VSScript.dll is the way to go as
recommended by the VapourSynth auhtor.

>
>> But that would still contain 3 Windows-specific constants in the
>> function call. Also is it useful to write the literals as utf-8 just for
>> them to be converted to wchar again or should it just take WCHAR and
>> return utf8?
>> Or shoulds the entire function be located in
>> /compat/vapoursynth/w32_vsscript_dll.h as "get_w32_vsscript_dll" or
>> something similar?
>>
>
> I'm definitely not the person to ask about the text encoding stuff.

When I just move the code somewhere else that's not an issue. If there
should be a generalized function to read from the registry that would be
something to decide.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-22  1:37 [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime Stefan Oltmanns via ffmpeg-devel
  2024-06-22  6:27 ` Stephen Hutchinson
@ 2024-06-25  9:10 ` Anton Khirnov
  2024-06-25  9:17   ` Paul B Mahol
  1 sibling, 1 reply; 7+ messages in thread
From: Anton Khirnov @ 2024-06-25  9:10 UTC (permalink / raw)
  To: FFmpeg development discussions and patches; +Cc: Stefan Oltmanns

Quoting Stefan Oltmanns via ffmpeg-devel (2024-06-22 03:37:03)
> Hello,
> 
> this is my first patch, I hope I got all the formalities correct.
> 
> The current VapourSynth implementation is rarely used, as it links the
> VapourSynth library at build time, making the resulting build unable to
> run when VapourSynth is not installed. Therefore barely anyone compiles
> with VapourSynth activated.
> 
> I changed it, so that it loads the library at runtime when a VapourSynth
> script should be opened, just like AviSynth does.
> On Windows the DLL from VapourSynth is not installed in the system
> directory, but the location is stored in the Registry. Therefore I added
> some code to read that information from the registry.
> 
> As the V4 API is designed with dynamic loading in mind (only a single
> import), I updated the implementation to V4 (changes are mostly
> superficial, no structural changes). The V4 API is already several years
> old, fully supported since R55 released in 2021.
> 
> I copied the two needed header files directly in a vapoursynth.h,
> removing the need to install VapourSynth on the build machine
> (VapourSynth is also LGPL 2.1 or later, so no license issue). I updated
> the configure so that it checks for the ability to load libraries at
> runtime for VapourSynth, just like AviSynth and activate it if not disabled.
> 
> make fate runs without any issue. I tested VapourSynth input scripts
> with various color formats on different platforms:
> 
> Ubuntu 22.04
> macOS 13 (x86_64)
> macOS 13 (arm64)
> Windows 10 (msys2/gcc)
> 
> It compiles on these platforms without any warning and runs without any
> issues.

Sorry, this is entirely unacceptable.
Especially the part instaling atexit handlers and bundling downstream
headers, but overall we prefer to avoid dlopen.

-- 
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime
  2024-06-25  9:10 ` Anton Khirnov
@ 2024-06-25  9:17   ` Paul B Mahol
  0 siblings, 0 replies; 7+ messages in thread
From: Paul B Mahol @ 2024-06-25  9:17 UTC (permalink / raw)
  To: FFmpeg development discussions and patches, Stefan Oltmanns

On Tue, Jun 25, 2024 at 11:10 AM Anton Khirnov <anton@khirnov.net> wrote:

> Quoting Stefan Oltmanns via ffmpeg-devel (2024-06-22 03:37:03)
> > Hello,
> >
> > this is my first patch, I hope I got all the formalities correct.
> >
> > The current VapourSynth implementation is rarely used, as it links the
> > VapourSynth library at build time, making the resulting build unable to
> > run when VapourSynth is not installed. Therefore barely anyone compiles
> > with VapourSynth activated.
> >
> > I changed it, so that it loads the library at runtime when a VapourSynth
> > script should be opened, just like AviSynth does.
> > On Windows the DLL from VapourSynth is not installed in the system
> > directory, but the location is stored in the Registry. Therefore I added
> > some code to read that information from the registry.
> >
> > As the V4 API is designed with dynamic loading in mind (only a single
> > import), I updated the implementation to V4 (changes are mostly
> > superficial, no structural changes). The V4 API is already several years
> > old, fully supported since R55 released in 2021.
> >
> > I copied the two needed header files directly in a vapoursynth.h,
> > removing the need to install VapourSynth on the build machine
> > (VapourSynth is also LGPL 2.1 or later, so no license issue). I updated
> > the configure so that it checks for the ability to load libraries at
> > runtime for VapourSynth, just like AviSynth and activate it if not
> disabled.
> >
> > make fate runs without any issue. I tested VapourSynth input scripts
> > with various color formats on different platforms:
> >
> > Ubuntu 22.04
> > macOS 13 (x86_64)
> > macOS 13 (arm64)
> > Windows 10 (msys2/gcc)
> >
> > It compiles on these platforms without any warning and runs without any
> > issues.
>
> Sorry, this is entirely unacceptable.
> Especially the part instaling atexit handlers and bundling downstream
> headers, but overall we prefer to avoid dlopen.
>

Dunno what 'we' means here.

$ grep dlopen */*.c
libavcodec/amfenc.c:    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW |
RTLD_LOCAL);
libavcodec/mediacodec_wrapper.c:    format->libmedia =
dlopen("libmediandk.so", RTLD_NOW);
libavcodec/mediacodec_wrapper.c:    codec->libmedia = dlopen(lib_name,
RTLD_NOW);
libavcodec/mfenc.c:    c->library = dlopen("mfplat.dll", 0);
libavcodec/omx.c:        s->lib2 = dlopen(libname2, RTLD_NOW | RTLD_GLOBAL);
libavcodec/omx.c:    s->lib = dlopen(libname, RTLD_NOW | RTLD_GLOBAL);
libavfilter/af_ladspa.c:        ret = dlopen(path, RTLD_LOCAL|RTLD_NOW);
libavfilter/af_ladspa.c:        s->dl_handle = dlopen(s->dl_name,
RTLD_LOCAL|RTLD_NOW);
libavfilter/vf_frei0r.c:    *handle_ptr = dlopen(path, RTLD_NOW|RTLD_LOCAL);
libavfilter/vf_telecine.c: * @file telecine filter, heavily based from
mpv-player:TOOLS/vf_dlopen/telecine.c by
libavfilter/vsrc_ddagrab.c:    user32_module = dlopen("user32.dll", 0);
libavformat/avisynth.c:    avs_library.library = dlopen(AVISYNTH_LIB,
RTLD_NOW | RTLD_LOCAL);
libavutil/hwcontext_d3d11va.c:    d3dlib  = dlopen("d3d11.dll", 0);
libavutil/hwcontext_d3d11va.c:    dxgilib = dlopen("dxgi.dll", 0);
libavutil/hwcontext_d3d12va.c:    priv->d3d12lib = dlopen("d3d12.dll", 0);
libavutil/hwcontext_d3d12va.c:    priv->dxgilib  = dlopen("dxgi.dll", 0);
libavutil/hwcontext_dxva2.c:    priv->d3dlib = dlopen("d3d9.dll", 0);
libavutil/hwcontext_dxva2.c:    priv->dxva2lib = dlopen("dxva2.dll", 0);
libavutil/hwcontext_mediacodec.c:    s->libmedia = dlopen("libmediandk.so",
RTLD_NOW);
libavutil/hwcontext_vaapi.c:            HANDLE dxgi = dlopen("dxgi.dll", 0);
libavutil/hwcontext_vulkan.c:        p->libvulkan = dlopen(lib_names[i],
RTLD_NOW | RTLD_LOCAL);
libavutil/macos_kperf.c:    av_assert0(kperf =
dlopen("/System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf",
RTLD_LAZY));




>
> --
> Anton Khirnov
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2024-06-25  9:18 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-22  1:37 [FFmpeg-devel] [PATCH] libavformat/vapoursynth: Update to API version 4, load library at runtime Stefan Oltmanns via ffmpeg-devel
2024-06-22  6:27 ` Stephen Hutchinson
2024-06-22 10:02   ` Stefan Oltmanns via ffmpeg-devel
2024-06-22 18:23     ` Stephen Hutchinson
2024-06-22 19:22       ` Stefan Oltmanns via ffmpeg-devel
2024-06-25  9:10 ` Anton Khirnov
2024-06-25  9:17   ` Paul B Mahol

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git