From fdcb0ee75f2716aaf27e01a0d4a1153bb290d419 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sun, 3 Jul 2022 17:49:46 +0200 Subject: [PATCH 01/15] meson: Fix compliation with avisynth enabled Still crashes when loading a video. --- meson.build | 1 + src/meson.build | 1 + 2 files changed, 2 insertions(+) diff --git a/meson.build b/meson.build index 433ca01eef..c5a9dd7a05 100644 --- a/meson.build +++ b/meson.build @@ -222,6 +222,7 @@ endforeach if host_machine.system() == 'windows' and get_option('avisynth').enabled() conf.set('WITH_AVISYNTH', 1) # bundled separately with installer + deps += cc.find_library('avifil32', required: true) endif if host_machine.system() == 'windows' and not get_option('directsound').disabled() diff --git a/src/meson.build b/src/meson.build index 72587d366c..9592cf6e0a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -174,6 +174,7 @@ elif host_machine.system() == 'windows' 'avisynth_wrap.cpp', 'font_file_lister_gdi.cpp', # 'libass_gdi_fontselect.cpp', + 'audio_provider_avs.cpp', 'video_provider_avs.cpp', ) From e240a5fdc2f0d461c69519bfaed0385f3c3ccba4 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 5 Jul 2022 01:12:10 +0200 Subject: [PATCH 02/15] avisynth: Update headers and fix playback - Build the latest AviSynth headers using CMake, as proposed in #134 - Add and initialize AVS_Linkage to fix video loading Video and audio playback now works in most cases, but still crashes for some files. --- .gitignore | 1 + meson.build | 8 + src/avisynth.h | 751 -------------------------------------- src/avisynth_wrap.cpp | 5 + subprojects/avisynth.wrap | 6 + 5 files changed, 20 insertions(+), 751 deletions(-) delete mode 100644 src/avisynth.h create mode 100644 subprojects/avisynth.wrap diff --git a/.gitignore b/.gitignore index a2d7c9b868..7d31eeed47 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ tools/repack-thes-dict.dSYM # Meson build*/ +subprojects/avisynth subprojects/boost*/ subprojects/cairo* subprojects/ffmpeg diff --git a/meson.build b/meson.build index c5a9dd7a05..04bc31331e 100644 --- a/meson.build +++ b/meson.build @@ -223,6 +223,14 @@ endforeach if host_machine.system() == 'windows' and get_option('avisynth').enabled() conf.set('WITH_AVISYNTH', 1) # bundled separately with installer deps += cc.find_library('avifil32', required: true) + + avs_opt = cmake.subproject_options() + avs_opt.add_cmake_defines({ + 'HEADERS_ONLY': true + }) + + avs = cmake.subproject('avisynth', options: avs_opt) + deps_inc += avs.include_directories('AviSynth-Headers') endif if host_machine.system() == 'windows' and not get_option('directsound').disabled() diff --git a/src/avisynth.h b/src/avisynth.h deleted file mode 100644 index 7e15f21336..0000000000 --- a/src/avisynth.h +++ /dev/null @@ -1,751 +0,0 @@ -// Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. -// http://www.avisynth.org - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// Linking Avisynth statically or dynamically with other modules is making a -// combined work based on Avisynth. Thus, the terms and conditions of the GNU -// General Public License cover the whole combination. -// -// As a special exception, the copyright holders of Avisynth give you -// permission to link Avisynth with independent modules that communicate with -// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license -// terms of these independent modules, and to copy and distribute the -// resulting combined work under terms of your choice, provided that -// every copy of the combined work is accompanied by a complete copy of -// the source code of Avisynth (the version of Avisynth used to produce the -// combined work), being distributed under the terms of the GNU General -// Public License plus this exception. An independent module is a module -// which is not derived from or based on Avisynth, such as 3rd-party filters, -// import and export plugins, or graphical user interfaces. - - - - - -#ifndef __AVISYNTH_H__ -#define __AVISYNTH_H__ - -enum { AVISYNTH_INTERFACE_VERSION = 3 }; - - -/* Define all types necessary for interfacing with avisynth.dll - Moved from internal.h */ - -// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc. -#include - -// COM interface macros -#include - - -// Raster types used by VirtualDub & Avisynth -#define in64 (__int64)(unsigned short) -typedef unsigned long Pixel; // this will break on 64-bit machines! -typedef unsigned long Pixel32; -typedef unsigned char Pixel8; -typedef long PixCoord; -typedef long PixDim; -typedef long PixOffset; - - -/* Compiler-specific crap */ - -// Tell MSVC to stop precompiling here -#ifdef _MSC_VER - #pragma hdrstop -#endif - -// Set up debugging macros for MS compilers; for others, step down to the -// standard interface -#ifdef _MSC_VER - #include -#else - #define _RPT0(a,b) ((void)0) - #define _RPT1(a,b,c) ((void)0) - #define _RPT2(a,b,c,d) ((void)0) - #define _RPT3(a,b,c,d,e) ((void)0) - #define _RPT4(a,b,c,d,e,f) ((void)0) - - #define _ASSERTE(x) assert(x) - #include -#endif - - - -// I had problems with Premiere wanting 1-byte alignment for its structures, -// so I now set the Avisynth struct alignment explicitly here. -#pragma pack(push,8) - -#define FRAME_ALIGN 16 -// Default frame alignment is 16 bytes, to help P4, when using SSE2 - -// The VideoInfo struct holds global information about a clip (i.e. -// information that does not depend on the frame number). The GetVideoInfo -// method in IClip returns this struct. - -// Audio Sample information -typedef float SFLOAT; - -enum {SAMPLE_INT8 = 1<<0, - SAMPLE_INT16 = 1<<1, - SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware. - SAMPLE_INT32 = 1<<3, - SAMPLE_FLOAT = 1<<4}; - -enum { - PLANAR_Y=1<<0, - PLANAR_U=1<<1, - PLANAR_V=1<<2, - PLANAR_ALIGNED=1<<3, - PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED, - PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED, - PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED, - }; - -struct VideoInfo { - int width, height; // width=0 means no video - unsigned fps_numerator, fps_denominator; - int num_frames; - // This is more extensible than previous versions. More properties can be added seeminglesly. - - // Colorspace properties. - enum { - CS_BGR = 1<<28, - CS_YUV = 1<<29, - CS_INTERLEAVED = 1<<30, - CS_PLANAR = 1<<31 - }; - - // Specific colorformats - enum { CS_UNKNOWN = 0, - CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED, - CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED, - CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED, - CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR, // y-v-u, planar - CS_I420 = 1<<4 | CS_YUV | CS_PLANAR, // y-u-v, planar - CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR // same as above - }; - int pixel_type; // changed to int as of 2.5 - - - int audio_samples_per_second; // 0 means no audio - int sample_type; // as of 2.5 - __int64 num_audio_samples; // changed as of 2.5 - int nchannels; // as of 2.5 - - // Imagetype properties - - int image_type; - - enum { - IT_BFF = 1<<0, - IT_TFF = 1<<1, - IT_FIELDBASED = 1<<2 - }; - - // useful functions of the above - bool HasVideo() const { return (width!=0); } - bool HasAudio() const { return (audio_samples_per_second!=0); } - bool IsRGB() const { return !!(pixel_type&CS_BGR); } - bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties - bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; } - bool IsYUV() const { return !!(pixel_type&CS_YUV ); } - bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; } - bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); } - bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); } - bool Is(int property) const { return ((pixel_type & property)==property ); } - bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); } - bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); } - bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF|IT_TFF))); } - bool IsBFF() const { return !!(image_type & IT_BFF); } - bool IsTFF() const { return !!(image_type & IT_TFF); } - - bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); } // Don't use this - int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); } // Will not work on planar images, but will return only luma planes - int RowSize() const { return BytesFromPixels(width); } // Also only returns first plane on planar images - int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p; } return height * ((RowSize()+3) & ~3); } - __int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; } - int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator)/((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; } - __int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; } - __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); } - int AudioChannels() const { return nchannels; } - int SampleType() const{ return sample_type;} - bool IsSampleType(int testtype) const{ return !!(sample_type&testtype);} - int SamplesPerSecond() const { return audio_samples_per_second; } - int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();} - void SetFieldBased(bool isfieldbased) { if (isfieldbased) image_type|=IT_FIELDBASED; else image_type&=~IT_FIELDBASED; } - void Set(int property) { image_type|=property; } - void Clear(int property) { image_type&=~property; } - - int BitsPerPixel() const { - switch (pixel_type) { - case CS_BGR24: - return 24; - case CS_BGR32: - return 32; - case CS_YUY2: - return 16; - case CS_YV12: - case CS_I420: - return 12; - default: - return 0; - } - } - int BytesPerChannelSample() const { - switch (sample_type) { - case SAMPLE_INT8: - return sizeof(signed char); - case SAMPLE_INT16: - return sizeof(signed short); - case SAMPLE_INT24: - return 3; - case SAMPLE_INT32: - return sizeof(signed int); - case SAMPLE_FLOAT: - return sizeof(SFLOAT); - default: - _ASSERTE("Sample type not recognized!"); - return 0; - } - } - - // useful mutator - void SetFPS(unsigned numerator, unsigned denominator) { - if ((numerator == 0) || (denominator == 0)) { - fps_numerator = 0; - fps_denominator = 1; - } - else { - unsigned x=numerator, y=denominator; - while (y) { // find gcd - unsigned t = x%y; x = y; y = t; - } - fps_numerator = numerator/x; - fps_denominator = denominator/x; - } - } - - // Range protected multiply-divide of FPS - void MulDivFPS(unsigned multiplier, unsigned divisor) { - unsigned __int64 numerator = UInt32x32To64(fps_numerator, multiplier); - unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor); - - unsigned __int64 x=numerator, y=denominator; - while (y) { // find gcd - unsigned __int64 t = x%y; x = y; y = t; - } - numerator /= x; // normalize - denominator /= x; - - unsigned __int64 temp = numerator | denominator; // Just looking top bit - unsigned u = 0; - while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2 - temp = Int64ShrlMod32(temp, 1); - u++; - } - if (u) { // Scale to fit - const unsigned round = 1 << (u-1); - SetFPS( (unsigned)Int64ShrlMod32(numerator + round, u), - (unsigned)Int64ShrlMod32(denominator + round, u) ); - } - else { - fps_numerator = (unsigned)numerator; - fps_denominator = (unsigned)denominator; - } - } - - // Test for same colorspace - bool IsSameColorspace(const VideoInfo& vi) const { - if (vi.pixel_type == pixel_type) return TRUE; - if (IsYV12() && vi.IsYV12()) return TRUE; - return FALSE; - } - -}; - - - - -// VideoFrameBuffer holds information about a memory block which is used -// for video data. For efficiency, instances of this class are not deleted -// when the refcount reaches zero; instead they're stored in a linked list -// to be reused. The instances are deleted when the corresponding AVS -// file is closed. - -class VideoFrameBuffer { - BYTE* const data; - const int data_size; - // sequence_number is incremented every time the buffer is changed, so - // that stale views can tell they're no longer valid. - long sequence_number; - - friend class VideoFrame; - friend class Cache; - friend class ScriptEnvironment; - long refcount; - -public: - VideoFrameBuffer(int size); - VideoFrameBuffer(); - ~VideoFrameBuffer(); - - const BYTE* GetReadPtr() const { return data; } - BYTE* GetWritePtr() { ++sequence_number; return data; } - int GetDataSize() { return data_size; } - int GetSequenceNumber() { return sequence_number; } - int GetRefcount() { return refcount; } -}; - - -class IClip; -class PClip; -class PVideoFrame; -class IScriptEnvironment; -class AVSValue; - - -// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new -// is overloaded to recycle class instances. - -class VideoFrame { - int refcount; - VideoFrameBuffer* const vfb; - const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture. - - friend class PVideoFrame; - void AddRef() { InterlockedIncrement((long *)&refcount); } - void Release() { if (refcount==1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); } - - friend class ScriptEnvironment; - friend class Cache; - - VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height); - VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV); - - void* operator new(size_t size); -// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard! -public: - int GetPitch() const { return pitch; } - int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; } - int GetRowSize() const { return row_size; } - int GetRowSize(int plane) const { - switch (plane) { - case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0; - case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED: - if (pitchUV) { - int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize - if (r<=pitchUV) - return r; - return row_size>>1; - } else return 0; - case PLANAR_Y_ALIGNED: - int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize - if (r<=pitch) - return r; - return row_size; - } - return row_size; } - int GetHeight() const { return height; } - int GetHeight(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; } - - // generally you shouldn't use these three - VideoFrameBuffer* GetFrameBuffer() const { return vfb; } - int GetOffset() const { return offset; } - int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; } - - // in plugins use env->SubFrame() - VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const; - VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const; - - - const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; } - const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); } - - bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); } - - BYTE* GetWritePtr() const { - if (vfb->GetRefcount()>1) { - _ASSERT(FALSE); - //throw AvisynthError("Internal Error - refcount was more than one!"); - } - return IsWritable() ? (vfb->GetWritePtr() + offset) : 0; - } - - BYTE* GetWritePtr(int plane) const { - if (plane==PLANAR_Y) { - if (vfb->GetRefcount()>1) { - _ASSERT(FALSE); -// throw AvisynthError("Internal Error - refcount was more than one!"); - } - return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0; - } - return vfb->data + GetOffset(plane); - } - - ~VideoFrame() { InterlockedDecrement(&vfb->refcount); } -}; - -enum { - CACHE_NOTHING=0, - CACHE_RANGE=1, - CACHE_ALL=2, - CACHE_AUDIO=3, - CACHE_AUDIO_NONE=4 - }; - -// Base class for all filters. -class IClip { - friend class PClip; - friend class AVSValue; - int refcnt; - void AddRef() { InterlockedIncrement((long *)&refcnt); } - void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; } -public: - IClip() : refcnt(0) {} - - virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; } - - virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0; - virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame - virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples - virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter. - virtual const VideoInfo& __stdcall GetVideoInfo() = 0; - virtual __stdcall ~IClip() {} -}; - - -// smart pointer to IClip -class PClip { - - IClip* p; - - IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; } - friend class AVSValue; - friend class VideoFrame; - - void Init(IClip* x) { - if (x) x->AddRef(); - p=x; - } - void Set(IClip* x) { - if (x) x->AddRef(); - if (p) p->Release(); - p=x; - } - -public: - PClip() { p = 0; } - PClip(const PClip& x) { Init(x.p); } - PClip(IClip* x) { Init(x); } - void operator=(IClip* x) { Set(x); } - void operator=(const PClip& x) { Set(x.p); } - - IClip* operator->() const { return p; } - - // useful in conditional expressions - operator void*() const { return p; } - bool operator!() const { return !p; } - - ~PClip() { if (p) p->Release(); } -}; - - -// smart pointer to VideoFrame -class PVideoFrame { - - VideoFrame* p; - - void Init(VideoFrame* x) { - if (x) x->AddRef(); - p=x; - } - void Set(VideoFrame* x) { - if (x) x->AddRef(); - if (p) p->Release(); - p=x; - } - -public: - PVideoFrame() { p = 0; } - PVideoFrame(const PVideoFrame& x) { Init(x.p); } - PVideoFrame(VideoFrame* x) { Init(x); } - void operator=(VideoFrame* x) { Set(x); } - void operator=(const PVideoFrame& x) { Set(x.p); } - - VideoFrame* operator->() const { return p; } - - // for conditional expressions - operator void*() const { return p; } - bool operator!() const { return !p; } - - ~PVideoFrame() { if (p) p->Release();} -}; - - -class AVSValue { -public: - - AVSValue() { type = 'v'; } - AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); } - AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); } - AVSValue(bool b) { type = 'b'; boolean = b; } - AVSValue(int i) { type = 'i'; integer = i; } -// AVSValue(__int64 l) { type = 'l'; longlong = l; } - AVSValue(float f) { type = 'f'; floating_pt = f; } - AVSValue(double f) { type = 'f'; floating_pt = float(f); } - AVSValue(const char* s) { type = 's'; string = s; } - AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; } - AVSValue(const AVSValue& v) { Assign(&v, true); } - - ~AVSValue() { if (IsClip() && clip) clip->Release(); } - AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; } - - // Note that we transparently allow 'int' to be treated as 'float'. - // There are no int<->bool conversions, though. - - bool Defined() const { return type != 'v'; } - bool IsClip() const { return type == 'c'; } - bool IsBool() const { return type == 'b'; } - bool IsInt() const { return type == 'i'; } -// bool IsLong() const { return (type == 'l'|| type == 'i'); } - bool IsFloat() const { return type == 'f' || type == 'i'; } - bool IsString() const { return type == 's'; } - bool IsArray() const { return type == 'a'; } - - PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; } - bool AsBool() const { _ASSERTE(IsBool()); return boolean; } - int AsInt() const { _ASSERTE(IsInt()); return integer; } -// int AsLong() const { _ASSERTE(IsLong()); return longlong; } - const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; } - double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; } - - bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; } - int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; } - double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; } - const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; } - - int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; } - - const AVSValue& operator[](int index) const { - _ASSERTE(IsArray() && index>=0 && index=0 && indexIsClip() && src->clip) - src->clip->AddRef(); - if (!init && IsClip() && clip) - clip->Release(); - // make sure this copies the whole struct! - //((__int32*)this)[0] = ((__int32*)src)[0]; - //((__int32*)this)[1] = ((__int32*)src)[1]; - memcpy(this, src, sizeof(AVSValue)); - } -}; - - -// instantiable null filter -class GenericVideoFilter : public IClip { -protected: - PClip child; - VideoInfo vi; -public: - GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); } - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); } - void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); } - const VideoInfo& __stdcall GetVideoInfo() { return vi; } - bool __stdcall GetParity(int n) { return child->GetParity(n); } - void __stdcall SetCacheHints(int cachehints,int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter. -}; - - -class AvisynthError /* exception */ { -public: - const char* const msg; - AvisynthError(const char* _msg) : msg(_msg) {} -}; - - - - -/* Helper classes useful to plugin authors */ - -class AlignPlanar : public GenericVideoFilter -{ -public: - AlignPlanar(PClip _clip); - static PClip Create(PClip clip); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); -}; - - - -class FillBorder : public GenericVideoFilter -{ -public: - FillBorder(PClip _clip); - static PClip Create(PClip clip); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); -}; - - - -class ConvertAudio : public GenericVideoFilter -/** - * Helper class to convert audio to any format - **/ -{ -public: - ConvertAudio(PClip _clip, int prefered_format); - void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env); - void __stdcall SetCacheHints(int cachehints,int frame_range); // We do pass cache requests upwards, to the cache! - - static PClip Create(PClip clip, int sample_type, int prefered_type); - static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_24bit(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*); - static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*); - virtual ~ConvertAudio(); - -private: - void convertToFloat(char* inbuf, float* outbuf, char sample_type, int count); - void convertToFloat_3DN(char* inbuf, float* outbuf, char sample_type, int count); - void convertToFloat_SSE(char* inbuf, float* outbuf, char sample_type, int count); - void convertToFloat_SSE2(char* inbuf, float* outbuf, char sample_type, int count); - void convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count); - void convertFromFloat_3DN(float* inbuf, void* outbuf, char sample_type, int count); - void convertFromFloat_SSE(float* inbuf, void* outbuf, char sample_type, int count); - void convertFromFloat_SSE2(float* inbuf, void* outbuf, char sample_type, int count); - - __inline int Saturate_int8(float n); - __inline short Saturate_int16(float n); - __inline int Saturate_int24(float n); - __inline int Saturate_int32(float n); - - char src_format; - char dst_format; - int src_bps; - char *tempbuffer; - SFLOAT *floatbuffer; - int tempbuffer_size; -}; - - -// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. -enum { - /* slowest CPU to support extension */ - CPUF_FORCE = 0x01, // N/A - CPUF_FPU = 0x02, // 386/486DX - CPUF_MMX = 0x04, // P55C, K6, PII - CPUF_INTEGER_SSE = 0x08, // PIII, Athlon - CPUF_SSE = 0x10, // PIII, Athlon XP/MP - CPUF_SSE2 = 0x20, // PIV, Hammer - CPUF_3DNOW = 0x40, // K6-2 - CPUF_3DNOW_EXT = 0x80, // Athlon - CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer - // will have anyway) - CPUF_SSE3 = 0x100, // Some P4 & Athlon 64. -}; -#define MAX_INT 0x7fffffff -#define MIN_INT -0x7fffffff - - - -class IScriptEnvironment { -public: - virtual __stdcall ~IScriptEnvironment() {} - - virtual /*static*/ long __stdcall GetCPUFlags() = 0; - - virtual char* __stdcall SaveString(const char* s, int length = -1) = 0; - virtual char* __stdcall Sprintf(const char* fmt, ...) = 0; - // note: val is really a va_list; I hope everyone typedefs va_list to a pointer - virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0; - - __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0; - - class NotFound /*exception*/ {}; // thrown by Invoke and GetVar - - typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env); - - virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0; - virtual bool __stdcall FunctionExists(const char* name) = 0; - virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0; - - virtual AVSValue __stdcall GetVar(const char* name) = 0; - virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0; - virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0; - - virtual void __stdcall PushContext(int level=0) = 0; - virtual void __stdcall PopContext() = 0; - - // align should be 4 or 8 - virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0; - - virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0; - - virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0; - - typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env); - virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0; - - virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0; - - virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0; - - virtual int __stdcall SetMemoryMax(int mem) = 0; - - virtual int __stdcall SetWorkingDir(const char * newdir) = 0; - - virtual void* __stdcall ManageCache(int key, void* data) = 0; - - enum PlanarChromaAlignmentMode { - PlanarChromaAlignmentOff, - PlanarChromaAlignmentOn, - PlanarChromaAlignmentTest }; - - virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0; - - virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0; -}; - - -// avisynth.dll exports this; it's a way to use it as a library, without -// writing an AVS script or without going through AVIFile. -IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION); - - -#pragma pack(pop) - -#endif //__AVISYNTH_H__ - diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index d76a377d28..bbbcc0a3a5 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -47,6 +47,8 @@ namespace { IScriptEnvironment *env = nullptr; std::mutex AviSynthMutex; } +// This needs to be visible so Avisynth sees it +const AVS_Linkage *AVS_linkage = nullptr; typedef IScriptEnvironment* __stdcall FUNC(int); @@ -70,6 +72,8 @@ AviSynthWrapper::AviSynthWrapper() { if (!env) throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?"); + AVS_linkage = env->GetAVSLinkage(); + // Set memory limit const int memoryMax = OPT_GET("Provider/Avisynth/Memory Max")->GetInt(); if (memoryMax) @@ -80,6 +84,7 @@ AviSynthWrapper::AviSynthWrapper() { AviSynthWrapper::~AviSynthWrapper() { if (!--avs_refcount) { delete env; + AVS_linkage = nullptr; FreeLibrary(hLib); } } diff --git a/subprojects/avisynth.wrap b/subprojects/avisynth.wrap new file mode 100644 index 0000000000..6bb2f8aa4d --- /dev/null +++ b/subprojects/avisynth.wrap @@ -0,0 +1,6 @@ +[wrap-git] +url = https://github.com/AviSynth/AviSynthPlus.git +revision = v3.7.2 + +[provide] +avisynth = avisynth_dep From 635503affe88b946ab21029248f27f1e76ed1f5e Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Mon, 4 Jul 2022 22:22:01 +0200 Subject: [PATCH 03/15] meson: Add files in manifest as respack.py input This makes meson detect changes to files like default_config.json and rebuild libresrc.cpp when necessary. --- meson.build | 2 +- src/libresrc/meson.build | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 04bc31331e..39cc7de006 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('Aegisub', ['c', 'cpp'], license: 'BSD-3-Clause', - meson_version: '>=0.56.1', + meson_version: '>=0.57.0', default_options: ['cpp_std=c++14', 'buildtype=debugoptimized'], version: '3.2.2') diff --git a/src/libresrc/meson.build b/src/libresrc/meson.build index d4be467d74..e4589f191a 100644 --- a/src/libresrc/meson.build +++ b/src/libresrc/meson.build @@ -1,3 +1,5 @@ +fs = import('fs') + respack = find_program(meson.project_source_root() / 'tools/respack.py') resrc = [ @@ -12,17 +14,25 @@ conf_platform_json = configure_file(input: 'default_config_platform.json.in', configuration: conf_platform) if host_machine.system() == 'darwin' - resrc += custom_target('default_config.{cpp,h}', - command: [respack, '@INPUT0@', '@OUTPUT@'], - input: [files('manifest_osx.respack'), conf_platform_json], - output: ['default_config.cpp', 'default_config.h']) + resmanifest = 'manifest_osx.respack' else - resrc += custom_target('default_config.{cpp,h}', - command: [respack, '@INPUT0@', '@OUTPUT@'], - input: [files('manifest.respack'), conf_platform_json], - output: ['default_config.cpp', 'default_config.h']) + resmanifest = 'manifest.respack' endif +resmanifest_files = [conf_platform_json] +# Filter out the files we've generated ourselves +foreach rfile : fs.read(resmanifest).strip().split('\n') + rfile_s = rfile.strip() + if fs.is_file(rfile_s) + resmanifest_files += files(rfile_s) + endif +endforeach + +resrc += custom_target('default_config.{cpp,h}', + command: [respack, '@INPUT0@', '@OUTPUT@'], + input: [files(resmanifest), resmanifest_files], + output: ['default_config.cpp', 'default_config.h']) + libresrc_inc = include_directories('.') libresrc = static_library('resrc', 'libresrc.cpp', resrc, include_directories: deps_inc, dependencies: deps) From e644227e955ed460b6d41dca9b42346ff5d04d8a Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 5 Jul 2022 02:01:35 +0200 Subject: [PATCH 04/15] avisynth: Remove option to allow ancient versions - This refers to AviSynth < 2.5.6, which is from before 2005 - With the current setup (using GetAVSLinkage, these versions wouldn't work anyway) - The latest AviSynthPlus is bundled with the installer anyway --- src/avisynth_wrap.cpp | 6 +----- src/libresrc/default_config.json | 3 +-- src/libresrc/osx/default_config.json | 3 +-- src/preferences.cpp | 2 -- src/project.cpp | 1 - 5 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index bbbcc0a3a5..fd2246f778 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -63,11 +63,7 @@ AviSynthWrapper::AviSynthWrapper() { if (!CreateScriptEnv) throw AvisynthError("Failed to get address of CreateScriptEnv from avisynth.dll"); - // Require Avisynth 2.5.6+? - if (OPT_GET("Provider/Avisynth/Allow Ancient")->GetBool()) - env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION-1); - else - env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION); + env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION); if (!env) throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?"); diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json index 318f8d3eef..f83002436a 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -335,8 +335,7 @@ } }, "Avisynth" : { - "Allow Ancient" : false, - "Memory Max" : 64 + "Memory Max" : 128 }, "FFmpegSource" : { "Cache" : { diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index 59f2ed05f5..66cecd520c 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -335,8 +335,7 @@ } }, "Avisynth" : { - "Allow Ancient" : false, - "Memory Max" : 64 + "Memory Max" : 128 }, "FFmpegSource" : { "Cache" : { diff --git a/src/preferences.cpp b/src/preferences.cpp index f6320dd75d..38f1ac2852 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -436,8 +436,6 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) { #ifdef WITH_AVISYNTH auto avisynth = p->PageSizer("Avisynth"); - p->OptionAdd(avisynth, _("Allow pre-2.56a Avisynth"), "Provider/Avisynth/Allow Ancient"); - p->CellSkip(avisynth); p->OptionAdd(avisynth, _("Avisynth memory limit"), "Provider/Avisynth/Memory Max"); #endif diff --git a/src/project.cpp b/src/project.cpp index e01dd3749f..6bd79abf4f 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -53,7 +53,6 @@ Project::Project(agi::Context *c) : context(c) { OPT_SUB("Audio/Cache/Type", &Project::ReloadAudio, this); OPT_SUB("Audio/Provider", &Project::ReloadAudio, this); OPT_SUB("Provider/Audio/FFmpegSource/Decode Error Handling", &Project::ReloadAudio, this); - OPT_SUB("Provider/Avisynth/Allow Ancient", &Project::ReloadVideo, this); OPT_SUB("Provider/Avisynth/Memory Max", &Project::ReloadVideo, this); OPT_SUB("Provider/Video/FFmpegSource/Decoding Threads", &Project::ReloadVideo, this); OPT_SUB("Provider/Video/FFmpegSource/Unsafe Seeking", &Project::ReloadVideo, this); From 21591b3e885c037b7a93eafdf624fa6bf70e8406 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Thu, 7 Jul 2022 10:33:36 +0200 Subject: [PATCH 05/15] meson: Use depend_files for files in respack manifest --- src/libresrc/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libresrc/meson.build b/src/libresrc/meson.build index e4589f191a..0b94147363 100644 --- a/src/libresrc/meson.build +++ b/src/libresrc/meson.build @@ -30,7 +30,8 @@ endforeach resrc += custom_target('default_config.{cpp,h}', command: [respack, '@INPUT0@', '@OUTPUT@'], - input: [files(resmanifest), resmanifest_files], + input: [files(resmanifest)], + depend_files: resmanifest_files, output: ['default_config.cpp', 'default_config.h']) From 691ab820b8dcec37154dc584c6f5cfac095edd16 Mon Sep 17 00:00:00 2001 From: wangqr Date: Tue, 13 Jul 2021 10:32:40 -0400 Subject: [PATCH 06/15] Handle exceptions from AviSynthWrapper ctor --- src/audio_provider_avs.cpp | 5 ++++- src/video_provider_avs.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp index b94cb4df4c..35981b1d5d 100644 --- a/src/audio_provider_avs.cpp +++ b/src/audio_provider_avs.cpp @@ -63,7 +63,7 @@ class AvisynthAudioProvider final : public agi::AudioProvider { bool NeedsCache() const override { return true; } }; -AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) { +AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) try { agi::acs::CheckFileRead(filename); std::lock_guard lock(avs_wrapper.GetMutex()); @@ -100,6 +100,9 @@ AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) { throw agi::AudioProviderError("Avisynth error: " + errmsg); } } +catch (AvisynthError& err) { + throw agi::AudioProviderError("Avisynth error: " + std::string(err.msg)); +} void AvisynthAudioProvider::LoadFromClip(AVSValue clip) { // Check if it has audio diff --git a/src/video_provider_avs.cpp b/src/video_provider_avs.cpp index dabcdaa68d..7c779e800e 100644 --- a/src/video_provider_avs.cpp +++ b/src/video_provider_avs.cpp @@ -93,7 +93,7 @@ class AvisynthVideoProvider: public VideoProvider { bool HasAudio() const override { return has_audio; } }; -AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) { +AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try { agi::acs::CheckFileRead(filename); std::lock_guard lock(avs.GetMutex()); @@ -182,6 +182,9 @@ AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std: throw VideoOpenError("Avisynth error: " + std::string(err.msg)); } } +catch (AvisynthError const& err) { + throw VideoProviderError("Avisynth error: " + std::string(err.msg)); +} void AvisynthVideoProvider::Init(std::string const& colormatrix) { auto script = source_clip; From e2a49b3313d9961d58b3e0366fe59001302d1b5c Mon Sep 17 00:00:00 2001 From: wangqr Date: Sat, 2 Nov 2019 22:26:02 -0400 Subject: [PATCH 07/15] Avisynth audio provider: add option to allow no downmix --- src/audio_provider_avs.cpp | 12 +++++++----- src/preferences.cpp | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp index 35981b1d5d..d249aa3d38 100644 --- a/src/audio_provider_avs.cpp +++ b/src/audio_provider_avs.cpp @@ -110,12 +110,14 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue clip) { if (!vi.HasAudio()) throw agi::AudioDataNotFound("No audio found."); IScriptEnvironment *env = avs_wrapper.GetEnv(); + AVSValue script; // Convert to one channel - AVSValue script = env->Invoke(OPT_GET("Audio/Downmixer")->GetString().c_str(), clip); + if (OPT_GET("Audio/Downmixer")->GetString() != "None") + script = env->Invoke(OPT_GET("Audio/Downmixer")->GetString().c_str(), clip); + else + script = clip; - // Convert to 16 bits per sample - script = env->Invoke("ConvertAudioTo16bit", script); vi = script.AsClip()->GetVideoInfo(); // Convert sample rate @@ -135,8 +137,8 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue clip) { channels = vi.AudioChannels(); decoded_samples = num_samples = vi.num_audio_samples; sample_rate = vi.SamplesPerSecond(); - bytes_per_sample = vi.BytesPerAudioSample(); - float_samples = false; + bytes_per_sample = vi.BytesPerChannelSample(); + float_samples = vi.IsSampleType(SAMPLE_FLOAT); this->clip = tempclip; } diff --git a/src/preferences.cpp b/src/preferences.cpp index 38f1ac2852..07a21a4203 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -387,8 +387,8 @@ void Advanced_Audio(wxTreebook *book, Preferences *parent) { #ifdef WITH_AVISYNTH auto avisynth = p->PageSizer("Avisynth"); - const wxString adm_arr[3] = { "ConvertToMono", "GetLeftChannel", "GetRightChannel" }; - wxArrayString adm_choice(3, adm_arr); + const wxString adm_arr[4] = { "None", "ConvertToMono", "GetLeftChannel", "GetRightChannel" }; + wxArrayString adm_choice(4, adm_arr); p->OptionChoice(avisynth, _("Avisynth down-mixer"), adm_choice, "Audio/Downmixer"); p->OptionAdd(avisynth, _("Force sample rate"), "Provider/Audio/AVS/Sample Rate"); #endif From fe285678a397d28f791dfdbad8c1b64401788b6a Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Fri, 6 Mar 2020 00:15:07 -0500 Subject: [PATCH 08/15] avisynth: Allow compilation on Linux Co-authored-by: wangqr --- meson.build | 8 ++++-- src/avisynth_wrap.cpp | 40 ++++++++++++++++++++++++---- src/libresrc/default_config.json | 2 +- src/libresrc/osx/default_config.json | 2 +- src/meson.build | 7 ++--- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/meson.build b/meson.build index 39cc7de006..50ec01dded 100644 --- a/meson.build +++ b/meson.build @@ -220,9 +220,9 @@ foreach dep: [ endif endforeach -if host_machine.system() == 'windows' and get_option('avisynth').enabled() +if get_option('avisynth').enabled() conf.set('WITH_AVISYNTH', 1) # bundled separately with installer - deps += cc.find_library('avifil32', required: true) + dep_avail += 'AviSynth' avs_opt = cmake.subproject_options() avs_opt.add_cmake_defines({ @@ -231,6 +231,10 @@ if host_machine.system() == 'windows' and get_option('avisynth').enabled() avs = cmake.subproject('avisynth', options: avs_opt) deps_inc += avs.include_directories('AviSynth-Headers') + + if host_machine.system() == 'windows' + deps += cc.find_library('avifil32', required: true) + endif endif if host_machine.system() == 'windows' and not get_option('directsound').disabled() diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index fd2246f778..a3b6a3de6c 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -40,10 +40,24 @@ #include +#ifndef _WIN32 +#include +#endif + +#ifdef _WIN32 +#define AVISYNTH_SO "avisynth.dll" +#else +#define AVISYNTH_SO "libavisynth.so" +#endif + // Allocate storage for and initialise static members namespace { int avs_refcount = 0; +#ifdef _WIN32 HINSTANCE hLib = nullptr; +#else + void* hLib = nullptr; +#endif IScriptEnvironment *env = nullptr; std::mutex AviSynthMutex; } @@ -54,14 +68,26 @@ typedef IScriptEnvironment* __stdcall FUNC(int); AviSynthWrapper::AviSynthWrapper() { if (!avs_refcount++) { - hLib = LoadLibrary(L"avisynth.dll"); +#ifdef _WIN32 +#define CONCATENATE(x, y) x ## y +#define _Lstr(x) CONCATENATE(L, x) + hLib = LoadLibraryW(_Lstr(AVISYNTH_SO)); +#undef _Lstr +#undef CONCATENATE +#else + hLib = dlopen(AVISYNTH_SO, RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND); +#endif if (!hLib) - throw AvisynthError("Could not load avisynth.dll"); + throw AvisynthError("Could not load " AVISYNTH_SO); - FUNC *CreateScriptEnv = (FUNC*)GetProcAddress(hLib, "CreateScriptEnvironment"); +#ifdef _WIN32 + FUNC* CreateScriptEnv = (FUNC*)GetProcAddress(hLib, "CreateScriptEnvironment"); +#else + FUNC* CreateScriptEnv = (FUNC*)dlsym(hLib, "CreateScriptEnvironment"); +#endif if (!CreateScriptEnv) - throw AvisynthError("Failed to get address of CreateScriptEnv from avisynth.dll"); + throw AvisynthError("Failed to get address of CreateScriptEnv from " AVISYNTH_SO); env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION); @@ -80,8 +106,12 @@ AviSynthWrapper::AviSynthWrapper() { AviSynthWrapper::~AviSynthWrapper() { if (!--avs_refcount) { delete env; - AVS_linkage = nullptr; +#ifdef _WIN32 FreeLibrary(hLib); +#else + dlclose(hLib); +#endif + AVS_linkage = nullptr; } } diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json index f83002436a..fbc98dc2eb 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -335,7 +335,7 @@ } }, "Avisynth" : { - "Memory Max" : 128 + "Memory Max" : 1024 }, "FFmpegSource" : { "Cache" : { diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index 66cecd520c..7d0cb9b6a0 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -335,7 +335,7 @@ } }, "Avisynth" : { - "Memory Max" : 128 + "Memory Max" : 1024 }, "FFmpegSource" : { "Cache" : { diff --git a/src/meson.build b/src/meson.build index 9592cf6e0a..b5791e558e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -171,11 +171,8 @@ if host_machine.system() == 'darwin' ) elif host_machine.system() == 'windows' aegisub_src += files( - 'avisynth_wrap.cpp', 'font_file_lister_gdi.cpp', # 'libass_gdi_fontselect.cpp', - 'audio_provider_avs.cpp', - 'video_provider_avs.cpp', ) if cc.has_header('wingdi.h') @@ -238,6 +235,10 @@ opt_src = [ 'video_provider_ffmpegsource.cpp', 'ffmpegsource_common.cpp']], + ['AviSynth', ['avisynth_wrap.cpp', + 'audio_provider_avs.cpp', + 'video_provider_avs.cpp']], + ['Hunspell', 'spellchecker_hunspell.cpp'], ] From ce1b3a0158c5c6407e58d8ff46f7e5d30f397fa6 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 9 Aug 2022 02:43:43 +0200 Subject: [PATCH 09/15] avisynth: Validate downmix option Because... let's maybe not let users make Aegisub call arbitrary symbols in avisynth.dll/so just by editing the config.json. --- src/audio_provider_avs.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp index d249aa3d38..0daa77e8e2 100644 --- a/src/audio_provider_avs.cpp +++ b/src/audio_provider_avs.cpp @@ -113,8 +113,9 @@ void AvisynthAudioProvider::LoadFromClip(AVSValue clip) { AVSValue script; // Convert to one channel - if (OPT_GET("Audio/Downmixer")->GetString() != "None") - script = env->Invoke(OPT_GET("Audio/Downmixer")->GetString().c_str(), clip); + std::string downmixtype = OPT_GET("Audio/Downmixer")->GetString(); + if (downmixtype == "ConvertToMono" || downmixtype == "GetLeftChannel" || downmixtype == "GetRightChannel") + script = env->Invoke(downmixtype.c_str(), clip); else script = clip; From ad443dd1186226f9542e85524c2dda656a2efff6 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 13 Aug 2022 22:53:58 +0200 Subject: [PATCH 10/15] avisynth: Properly destruct the IScriptEnvironment Starting with AVISYNTH_INTERFACE_VERSION=5, this is how script environments should be deleted. The previous code was causing crashes when unloading AviSynth in certain scenarios, such as when failing to open a file due to an incorrect path. --- src/avisynth_wrap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index a3b6a3de6c..eb957fa3cd 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -105,7 +105,7 @@ AviSynthWrapper::AviSynthWrapper() { AviSynthWrapper::~AviSynthWrapper() { if (!--avs_refcount) { - delete env; + env->DeleteScriptEnvironment(); #ifdef _WIN32 FreeLibrary(hLib); #else From fe77a1a0b8c29a9fa2d0c45f6779b4a6459872f3 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Tue, 27 Sep 2022 22:59:01 +0200 Subject: [PATCH 11/15] avisynth: Check if file exists before creating provider Since FileNotFound exceptions don't abort the provider search, opening an invalid path would show errors such as "avisynth not found" when Avisynth wasn't installed, even if Avisynth wasn't selected as the video provider. --- src/audio_provider_avs.cpp | 3 +-- src/video_provider_avs.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp index 0daa77e8e2..fff0ed823a 100644 --- a/src/audio_provider_avs.cpp +++ b/src/audio_provider_avs.cpp @@ -64,8 +64,6 @@ class AvisynthAudioProvider final : public agi::AudioProvider { }; AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) try { - agi::acs::CheckFileRead(filename); - std::lock_guard lock(avs_wrapper.GetMutex()); try { @@ -150,6 +148,7 @@ void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) } std::unique_ptr CreateAvisynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) { + agi::acs::CheckFileRead(file); return agi::make_unique(file); } #endif diff --git a/src/video_provider_avs.cpp b/src/video_provider_avs.cpp index 7c779e800e..2633106759 100644 --- a/src/video_provider_avs.cpp +++ b/src/video_provider_avs.cpp @@ -94,8 +94,6 @@ class AvisynthVideoProvider: public VideoProvider { }; AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try { - agi::acs::CheckFileRead(filename); - std::lock_guard lock(avs.GetMutex()); #ifdef _WIN32 @@ -326,6 +324,7 @@ void AvisynthVideoProvider::GetFrame(int n, VideoFrame &out) { namespace agi { class BackgroundRunner; } std::unique_ptr CreateAvisynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) { + agi::acs::CheckFileRead(path); return agi::make_unique(path, colormatrix); } #endif // HAVE_AVISYNTH From f5a730fa45343faa157af1c988c98e5badff7a6d Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 14 Jan 2023 00:13:47 +0100 Subject: [PATCH 12/15] avisynth: Only increase refcount when fully initialized When Avisynth is not installed or not functional, this would otherwise cause a crash when trying to initialize Avisynth more than once, since after the first time the refcount would have been incrased anyway. --- src/avisynth_wrap.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index eb957fa3cd..1dc5d71b2f 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -67,7 +67,7 @@ const AVS_Linkage *AVS_linkage = nullptr; typedef IScriptEnvironment* __stdcall FUNC(int); AviSynthWrapper::AviSynthWrapper() { - if (!avs_refcount++) { + if (!avs_refcount){ #ifdef _WIN32 #define CONCATENATE(x, y) x ## y #define _Lstr(x) CONCATENATE(L, x) @@ -94,6 +94,8 @@ AviSynthWrapper::AviSynthWrapper() { if (!env) throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?"); + avs_refcount++; + AVS_linkage = env->GetAVSLinkage(); // Set memory limit From 6704a8e57d669e7fdf12992dcf496c848f42b262 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Wed, 8 Feb 2023 17:41:31 +0100 Subject: [PATCH 13/15] avisynth: Remove file access checks These are broken in some edge cases, such as smb mounts on Windows. Paired with d96fc1f70da9e0b01b394ebf25083949a0da759c. --- src/audio_provider_avs.cpp | 1 - src/video_provider_avs.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp index fff0ed823a..b5b1d0916e 100644 --- a/src/audio_provider_avs.cpp +++ b/src/audio_provider_avs.cpp @@ -148,7 +148,6 @@ void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) } std::unique_ptr CreateAvisynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) { - agi::acs::CheckFileRead(file); return agi::make_unique(file); } #endif diff --git a/src/video_provider_avs.cpp b/src/video_provider_avs.cpp index 2633106759..0c824f264d 100644 --- a/src/video_provider_avs.cpp +++ b/src/video_provider_avs.cpp @@ -324,7 +324,6 @@ void AvisynthVideoProvider::GetFrame(int n, VideoFrame &out) { namespace agi { class BackgroundRunner; } std::unique_ptr CreateAvisynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) { - agi::acs::CheckFileRead(path); return agi::make_unique(path, colormatrix); } #endif // HAVE_AVISYNTH From a9eed184c292e06131761d5a54fc7c01001b8ea4 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sun, 15 Oct 2023 22:58:23 +0200 Subject: [PATCH 14/15] Revert "avisynth: Only increase refcount when fully initialized" This reverts commit f5a730fa45343faa157af1c988c98e5badff7a6d. --- src/avisynth_wrap.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index 1dc5d71b2f..eb957fa3cd 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -67,7 +67,7 @@ const AVS_Linkage *AVS_linkage = nullptr; typedef IScriptEnvironment* __stdcall FUNC(int); AviSynthWrapper::AviSynthWrapper() { - if (!avs_refcount){ + if (!avs_refcount++) { #ifdef _WIN32 #define CONCATENATE(x, y) x ## y #define _Lstr(x) CONCATENATE(L, x) @@ -94,8 +94,6 @@ AviSynthWrapper::AviSynthWrapper() { if (!env) throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?"); - avs_refcount++; - AVS_linkage = env->GetAVSLinkage(); // Set memory limit From 858f4acf3544e7938ac3c6878fb098b3b4fc386f Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sun, 15 Oct 2023 23:08:39 +0200 Subject: [PATCH 15/15] avisynth: Decrease refcount again when constructor fails Proper fix to the issue that f5a730fa45343faa157af1c988c98e5badff7a6d was trying to fix. Fixes arch1t3cht/Aegisub#61 . --- src/avisynth_wrap.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp index eb957fa3cd..3473efd941 100644 --- a/src/avisynth_wrap.cpp +++ b/src/avisynth_wrap.cpp @@ -53,6 +53,7 @@ // Allocate storage for and initialise static members namespace { int avs_refcount = 0; + bool failed = false; #ifdef _WIN32 HINSTANCE hLib = nullptr; #else @@ -66,7 +67,7 @@ const AVS_Linkage *AVS_linkage = nullptr; typedef IScriptEnvironment* __stdcall FUNC(int); -AviSynthWrapper::AviSynthWrapper() { +AviSynthWrapper::AviSynthWrapper() try { if (!avs_refcount++) { #ifdef _WIN32 #define CONCATENATE(x, y) x ## y @@ -101,6 +102,9 @@ AviSynthWrapper::AviSynthWrapper() { if (memoryMax) env->SetMemoryMax(memoryMax); } +} catch (AvisynthError const&) { + avs_refcount--; + throw; } AviSynthWrapper::~AviSynthWrapper() {