From 759b097865953ee66949ecbcdadbebfad623c29a Mon Sep 17 00:00:00 2001 From: Stefan Oltmanns 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 --- 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 -#include -#include - #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 + #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 + #define VSSCRIPT_NAME "libvapoursynth-script" + #define VSSCRIPT_LIB VSSCRIPT_NAME SLIBSUF + #define VS_DLOPEN() dlopen(VSSCRIPT_LIB, RTLD_NOW | RTLD_GLOBAL) +#endif + +#include + 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