From patchwork Sat Jul 6 21:08:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Oltmanns X-Patchwork-Id: 50378 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a59:cc64:0:b0:482:c625:d099 with SMTP id k4csp5035342vqv; Sat, 6 Jul 2024 14:08:42 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCUzYLz278T5fYWlRX7SF4B+vfqH1RNIh8xaow62y6VtIE475Ap4gCgVuAspMNyAFTgU1kh5lvN259w1QYuykHW0zhKn1aU75v78QA== X-Google-Smtp-Source: AGHT+IHN2+hLYBKbl82TI8SYFjSG/U35pVt6FMEs8DW+tfo8bS2OdlIf07/kilKWSBW0iRrg16p7 X-Received: by 2002:a19:5f5e:0:b0:52c:dccd:39aa with SMTP id 2adb3069b0e04-52ea06fc236mr5118146e87.67.1720300119812; Sat, 06 Jul 2024 14:08:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1720300119; cv=none; d=google.com; s=arc-20160816; b=zFVayHyVodwnxsTdq2sMOdEDsEHa9T6U0ZZ4eg5+tDrKLeT1KIp34JqzNQywSg/8oP i0kUifB8jv6NDuug+j0J5PCGcSqASgbWVcklfekyQ34G/f1l3BW2p/2xL6nrQOo5v+9g jdaCbo4Pi98b9sWb05slcrfpv6sGlFKmP8xq0AiwPM1eWVodjn3S+tN7nSD4Ik9VlAGs NanD0XuRjqTmXD2eMUsFlG9SXhq4dq4wZZo1P00wfw2gYr5zwMLtghdBR7JkQJiWouKz MgtlM+dI+0acsyAz1WMS6kTLiGJvDsHwKnWpmyCBEGK4FXMMKRPswnEEzlvlVT3rQdCk Nqdw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:from:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :ui-outboundreport:content-language:to:user-agent:mime-version:date :message-id:delivered-to; bh=6soKUYXASQjyuNKG3FUZwsDb5WCZnDXzwOYXz7KN7jQ=; fh=fcJVYuKBz42Tmr+GF9+673T/g5dDxXNShPnqZaNJEIc=; b=wLxx4ZxEMzpQ1MBhV7lPbWDKWAdzsgBhNp/IBx4hCVQrJJJWr65U/NYCBHirvlCZ74 vG3h5OHW0St8eTVdsdv8cvRYK0jxFPZ2xCTBTxqdZIJfShxT/yzyuh27EfnA+3doBy2G Ut8tn78Dj+L/0CVKuREJ2ET63OTq/Zz5sZHZOhjIhUUyWqOxQWra6qCaFzU5FBfJXlIJ hRl67pGU0qz/6+WTDZtEnEC3cK00ni1FuxXfZDPihXSePt+oC7FplUPGFfrtRyb7r+9o WII7pubLeBuKOW6VPOcsP/Z1dZFDbAMis6E7roihGMnJ1+GLsdumz+SnEfCQXWMoGPC7 D2NQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 2adb3069b0e04-52e7ab31bd2si5859585e87.507.2024.07.06.14.08.38; Sat, 06 Jul 2024 14:08:39 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 45D0768D83C; Sun, 7 Jul 2024 00:08:35 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mout.gmx.net (mout.gmx.net [212.227.15.15]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9484268D8D6 for ; Sun, 7 Jul 2024 00:08:26 +0300 (EEST) X-UI-Sender-Class: 724b4f7f-cbec-4199-ad4e-598c01a50d3a Received: from [192.168.11.12] ([85.16.197.80]) by mail.gmx.net (mrgmx005 [212.227.17.190]) with ESMTPSA (Nemesis) id 1MV67o-1ssHt92EkQ-00PNbN for ; Sat, 06 Jul 2024 23:08:25 +0200 Message-ID: <8c6c11a6-bc55-41a0-9f98-262c60f63ec8@gmx.net> Date: Sat, 6 Jul 2024 23:08:25 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird To: ffmpeg-devel@ffmpeg.org Content-Language: de-DE, en-US X-Provags-ID: V03:K1:7ocT7KYdSyxAFOZ+oMILm/zphgj65wkh6P1ltl/J7WzuZm41BaX 5ubjMThPpPphciy0VrLBc3prJh9MWiHL+6Hw+q05pgHjY9R69+AbKpP7GNscVaPKBg2Rxnx /wPRG9Q3x1zzDrJw3LbPhjHASQtdzBM7yaS0CRjz+Lac/NU1z5TIiD7UoHG+evb+7E5Eamq 40A+Fn5039AdSHqczpTuA== X-Spam-Flag: NO UI-OutboundReport: notjunk:1;M01:P0:cbgmslOniec=;IpU9BtrRQy/k865KudkhQFvymow 3Xj0V6BUJx90klfqgH/fgyu3NRiebCM07aDsNP67RH5YUebeYB5Cl7LxygfxlI/iCS8xgzyUe /dR2ZrP84Kb8lGtXg3wzhWAPX1zbaTXBsq6LM0K1WWDI1zpxzWYWidY8qiyXWOvD3kpwg2Fc9 NWooil4ziokwN1N5eAYuen5gK0BwUuzhPupI16PTabh7+KbpuLwZULXXl6bBn3+9/+qdbkn2X XqU3mBtkByMpY0Jqmp/JZDIgknVkN5P5+EdLCRcgiqHGN9IlG0BWhg40AAqxo5i9I9GAe8WD0 K4v1rQveIBbyDBZtsQMxc/a+g6s+UORRDUMX8uuU1Juk/n98oiF3XAvo8mVgtIOwVf7NmfS33 zX1Xd47TVBfMi1QOkJr3ciW7RJHVFCB1gAOemc3CAqIYOG/Csq0u7I+Nx3kXLI6qyMebIT0Wu 4xwzrwefWU9gg1Be0t3J8LyWOiDlHe/JLCNzHME0ptN8bpdB48ufTs9Cbzs9JhIVYz63kQjUL NhJSl2Fips/YM/o34tXOGJC25EDRcHVSZU8bSoJ4JIByRRxGucFaHEtpmnvREGpOda+hf1vKY ULOsTRhej4hupixDnxYgoCUWCzPJCxVEYcHepsaBrbtOAdFCZjWL/RXDhJqE+wHmg7i36QXVC +HKMZ7hh8nSNBpQMUbCvBa9A+vl7AEIUNF0JjeHE+yRQPyGOXgbkwSsciMfzde+twkHaanVnb p6lrh+tiD3umorvkz1EerGiXmdImVJ8aKD9IeZW5TLRHlwaFF1ixm42++kdLG0uDQxgV+/S6J MtfPOdHwIJubKfY3hWyRpaUrsmYbyIZ2BlsC4WUXZV3eU= Subject: [FFmpeg-devel] [PATCH v2] libavformat/vapoursynth: Update to API version 4, load library at runtime X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Stefan Oltmanns via ffmpeg-devel From: Stefan Oltmanns Reply-To: FFmpeg development discussions and patches Cc: Stefan Oltmanns Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: nLrOWrTITOEt 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 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