From: Stefan Oltmanns via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: Stefan Oltmanns <stefan-oltmanns@gmx.net> Subject: [FFmpeg-devel] [PATCH v2] libavformat/vapoursynth: Update to API version 4, load library at runtime Date: Sat, 6 Jul 2024 23:08:25 +0200 Message-ID: <8c6c11a6-bc55-41a0-9f98-262c60f63ec8@gmx.net> (raw) [-- Attachment #1: Type: text/plain, Size: 2159 bytes --] Hello, this is revised patch, to sum up the changes: 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. Changes from first patch: -Separated the Windows-specific function for getting the DLL location from the platform-specific includes -It is not enabled by default in configure -The header files are not included anymore I would like to include the header files for this reason: While most Linux distributions ship ffmpeg, only very few contain VapourSynth. Therefore ffmpeg won't be compiled with VapourSynth support by these distributions, because no VapourSynth headers. Including the headers in ffmpeg would mean they can compile with VapourSynth support and if a user decided to install VapourSynth from somewhere else or compile it by himself, ffmpeg support would be ready and no need for the user to install another ffmpeg or compile one. I'm not sure what the rules for shipping include files are, as there are a few 3rd-party include files in ffmpeg. License is not an issue (Vapourynth is LGPL v2.1 or later like ffmpeg). 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-avformat-vapoursynth-Update-to-API-version-4-load-li.patch --] [-- Type: text/x-patch, Size: 12682 bytes --] From 759b097865953ee66949ecbcdadbebfad623c29a Mon Sep 17 00:00:00 2001 From: Stefan Oltmanns <stefan-oltmanns@gmx.net> Date: Sat, 6 Jul 2024 22:56:53 +0200 Subject: [PATCH] avformat/vapoursynth: Update to API version 4, load library at runtime Signed-off-by: Stefan Oltmanns <stefan-oltmanns@gmx.net> --- configure | 3 +- libavformat/vapoursynth.c | 171 +++++++++++++++++++++++++++++--------- 2 files changed, 136 insertions(+), 38 deletions(-) diff --git a/configure b/configure index b28221f258..e43f3827ec 100755 --- a/configure +++ b/configure @@ -3575,6 +3575,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" @@ -7080,7 +7081,7 @@ 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 +enabled vapoursynth && require_headers "vapoursynth/VSScript4.h vapoursynth/VapourSynth4.h" if enabled gcrypt; then diff --git a/libavformat/vapoursynth.c b/libavformat/vapoursynth.c index 8a2519e19a..9c82f8f3b8 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" @@ -40,19 +37,46 @@ #include "demux.h" #include "internal.h" +/* Platform-specific directives. */ +#ifdef _WIN32 + #include <windows.h> + #include "compat/w32dlfcn.h" + #include "libavutil/wchar_filename.h" + #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 + +#include <vapoursynth/VSScript4.h> + 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 +94,72 @@ static const AVOption options[] = { {NULL} }; +static VSScriptLibrary vs_script_library; + +static int vs_atexit_called = 0; + +static av_cold void vs_atexit_handler(void); + +#ifdef _WIN32 +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; +} +#endif + +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 +167,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 +188,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 +210,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 +257,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 +275,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 +331,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 +353,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 +373,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 +408,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 +436,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 +478,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 +507,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; -- 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".
next reply other threads:[~2024-07-06 21:08 UTC|newest] Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-07-06 21:08 Stefan Oltmanns via ffmpeg-devel [this message] 2024-07-18 9:37 ` Stefan Oltmanns via ffmpeg-devel 2024-07-18 11:25 ` Anton Khirnov 2024-07-18 15:38 ` Stefan Oltmanns via ffmpeg-devel 2024-07-22 6:57 ` Anton Khirnov 2024-07-23 0:24 ` Stefan Oltmanns via ffmpeg-devel 2024-07-18 11:08 ` Ramiro Polla 2024-07-18 12:48 ` Stefan Oltmanns via ffmpeg-devel 2024-07-18 13:04 ` Ramiro Polla 2024-07-18 13:41 ` Stefan Oltmanns via ffmpeg-devel 2024-07-18 14:21 ` Ramiro Polla 2024-07-18 14:53 ` Stefan Oltmanns via ffmpeg-devel 2024-07-19 17:05 ` Stephen Hutchinson 2024-07-18 15:23 ` epirat07 2024-07-21 22:08 ` Stefan Oltmanns via ffmpeg-devel 2024-07-21 22:15 ` Hendrik Leppkes 2024-07-22 0:42 ` Stefan Oltmanns via ffmpeg-devel 2024-07-22 6:36 ` Hendrik Leppkes 2024-07-22 12:13 ` Ramiro Polla 2024-07-22 13:41 ` Hendrik Leppkes 2024-07-22 18:28 ` Ramiro Polla 2024-07-22 13:52 ` Stefan Oltmanns via ffmpeg-devel
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=8c6c11a6-bc55-41a0-9f98-262c60f63ec8@gmx.net \ --to=ffmpeg-devel@ffmpeg.org \ --cc=stefan-oltmanns@gmx.net \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
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