├── .gitmodules ├── README.md ├── ipc ├── logging.h ├── video_types.h ├── win32util.cpp ├── video_types.cpp ├── logging.cpp ├── win32util.h ├── ipc_types.h ├── ipc_client.h ├── ipc_types.cpp ├── ipc_commands.h ├── ipc_commands.cpp └── ipc_client.cpp ├── msvc ├── avshost_native │ ├── avshost_native.vcxproj.filters │ └── avshost_native.vcxproj ├── avsproxy │ ├── avsproxy.vcxproj.filters │ └── avsproxy.vcxproj ├── ipc32 │ ├── ipc32.vcxproj.filters │ └── ipc32.vcxproj ├── ipc64 │ ├── ipc64.vcxproj.filters │ └── ipc64.vcxproj └── avsproxy.sln ├── avshost_native ├── avshost.h ├── main.cpp ├── avshost.cpp └── avisynth_2.6.h ├── .gitattributes ├── .gitignore ├── COPYING └── avsproxy └── avsproxy.cpp /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vsxx"] 2 | path = vsxx 3 | url = https://github.com/sekrit-twc/vsxx.git 4 | [submodule "libp2p"] 5 | path = libp2p 6 | url = https://github.com/sekrit-twc/libp2p.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Avisynth 32-bit proxy 2 | 3 | Embed 32-bit Avisynth 2.6 or Avisynth+ environment within 64-bit VapourSynth. 4 | 5 | avsw.Eval(string script, clip[] "clips", string[] "clip_names", string "avisynth", string "slave", string "slave_log") 6 | 7 | * **script** - Avisynth script fragment 8 | * **clips** - VapourSynth clips ("nodes") to inject into Avisynth environment 9 | * **clip_names** - Avisynth variable name corresponding to injected clip 10 | * **avisynth** - Path to Avisynth DLL. The default uses the process DLL search path. 11 | * **slave** - Path to avshost_native.exe slave process. The plugin path is searched by default. 12 | * **slave_log** - Log file for slave process. 13 | 14 | The function returns the result of the Avisynth script, which may be an integer, float, string, or clip. If the result is a clip, the name of the return value is "clip", otherwise it is "result". 15 | 16 | ## Examples 17 | import vapoursynth as vs 18 | 19 | core = vs.get_core() 20 | 21 | red = core.std.BlankClip(color=[255, 0, 0]) 22 | green = core.std.BlankClip(color=[0, 255, 0]) 23 | # Before executing the Avisynth script, "r" and "g" are set to the bound clips. 24 | c = core.avsw.Eval("Merge(r, g)", clips=[red, green], clip_names=["r", "g"]) 25 | c.set_output() 26 | -------------------------------------------------------------------------------- /ipc/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IPC_LOGGING_H_ 4 | #define IPC_LOGGING_H_ 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | const char *ipc_filename(const char *path); 14 | 15 | void ipc_set_log_handler(void (*func)(const char *, va_list), void (*wfunc)(const wchar_t *, va_list)); 16 | 17 | void ipc_log_stderr(const char *fmt, va_list va); 18 | void ipc_wlog_stderr(const wchar_t *fmt, va_list va); 19 | 20 | void ipc_log(const char *fmt, ...); 21 | void ipc_wlog(const wchar_t *fmt, ...); 22 | 23 | void ipc_log_current_exception(); 24 | 25 | #ifndef _DEBUG 26 | #define ipc_filename(x) "" 27 | #endif 28 | 29 | #define ipc_log(x, ...) (ipc_log)("[%s @ %s:%d] " x, __func__, ipc_filename(__FILE__), __LINE__, __VA_ARGS__) 30 | #define ipc_log0(x) (ipc_log)("[%s @ %s:%d] " x, __func__, ipc_filename(__FILE__), __LINE__) 31 | 32 | #define ipc_wlog(x, ...) (ipc_wlog)(L"[%S @ %S:%d] " x, __func__, ipc_filename(__FILE__), __LINE__, __VA_ARGS__) 33 | #define ipc_wlog0(x) (ipc_wlog)("L[%S @ %S:%d] " x, __func__, ipc_filename(__FILE__), __LINE__) 34 | 35 | #define ipc_log_current_exception() do { ipc_log0(""); (ipc_log_current_exception)(); } while (0) 36 | 37 | #ifdef __cplusplus 38 | } // extern "C" 39 | #endif 40 | 41 | #endif /* IPC_LOGGING_H_ */ 42 | -------------------------------------------------------------------------------- /msvc/avshost_native/avshost_native.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /ipc/video_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IPC_VIDEO_TYPES_H_ 4 | #define IPC_VIDEO_TYPES_H_ 5 | 6 | #include 7 | #include 8 | 9 | namespace ipc { 10 | 11 | struct alignas(4) VideoInfo { 12 | enum { 13 | RGB = 0, 14 | YUV = 1, 15 | GRAY = 2, 16 | RGB24 = 3, 17 | RGB32 = 4, 18 | YUY2 = 5, 19 | }; 20 | 21 | int32_t width; 22 | int32_t height; 23 | uint32_t fps_num; 24 | uint32_t fps_den; 25 | int32_t num_frames; 26 | 27 | int8_t color_family; 28 | int8_t subsample_w; 29 | int8_t subsample_h; 30 | }; 31 | 32 | struct alignas(4) VideoFrameRequest { 33 | uint32_t clip_id; 34 | int32_t frame_number; 35 | }; 36 | 37 | struct alignas(4) VideoFrame { 38 | VideoFrameRequest request; 39 | uint32_t heap_offset; 40 | int32_t stride[4]; 41 | int32_t height[4]; 42 | }; 43 | 44 | struct alignas(4) Clip { 45 | uint32_t clip_id; 46 | VideoInfo vi; 47 | }; 48 | 49 | struct alignas(8) Value { 50 | enum { 51 | CLIP = 'c', 52 | BOOL_ = 'b', 53 | INT = 'i', 54 | FLOAT = 'f', 55 | STRING = 's', 56 | }; 57 | 58 | int8_t type; 59 | 60 | union { 61 | Clip c; 62 | int8_t b; 63 | int64_t i; 64 | double f; 65 | uint32_t s; // Heap pointer. 66 | }; 67 | }; 68 | 69 | 70 | // String functions. 71 | size_t deserialize_str(char *dst, const void *src, size_t buf_size) noexcept; 72 | size_t serialize_str(void *dst, const char *src, size_t len = -1) noexcept; 73 | 74 | size_t deserialize_wstr(wchar_t *dst, const void *src, size_t buf_size) noexcept; 75 | size_t serialize_wstr(void *dst, const wchar_t *src, size_t len = -1) noexcept; 76 | 77 | } // namespace ipc 78 | 79 | #endif // IPC_VIDEO_TYPES_H_ 80 | -------------------------------------------------------------------------------- /msvc/avsproxy/avsproxy.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {35d6a7aa-b110-4234-9d63-9d8f0e806a3e} 18 | 19 | 20 | {b1e06c37-0d5e-4b31-8d7e-1e52f926a0a8} 21 | 22 | 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files\vsxx 29 | 30 | 31 | 32 | 33 | Header Files\vsxx 34 | 35 | 36 | Header Files\vsxx 37 | 38 | 39 | Header Files\vsxx 40 | 41 | 42 | Header Files\vsxx 43 | 44 | 45 | -------------------------------------------------------------------------------- /ipc/win32util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "win32util.h" 6 | 7 | namespace win32 { 8 | 9 | static_assert(std::is_same::value, "type mismatch"); 10 | static_assert(std::is_same::value, "type mismatch"); 11 | static_assert(std::is_same::value, "type mismatch"); 12 | 13 | static_assert(detail::INFINITE_ == INFINITE, "constant mismatch"); 14 | 15 | 16 | namespace { 17 | const int unused_ = []() { assert(detail::invalid_handle() == INVALID_HANDLE_VALUE); return 0; }(); 18 | } // namespace 19 | 20 | 21 | namespace detail { 22 | 23 | void CloseHandleDeleter::operator()(handle h) { ::CloseHandle(h.h); } 24 | void UnmapViewOfFileDeleter::operator()(void *ptr) { ::UnmapViewOfFile(ptr); } 25 | void FreeLibraryDeleter::operator()(::HMODULE ptr) { ::FreeLibrary(ptr); } 26 | 27 | void TerminateProcessDeleter::operator()(handle h) 28 | { 29 | ::TerminateProcess(h.h, 0); 30 | ::CloseHandle(h.h); 31 | } 32 | 33 | } // namespace detail 34 | 35 | 36 | void trap_error(const char *msg) 37 | { 38 | std::error_code code{ static_cast(::GetLastError()), std::system_category() }; 39 | throw std::system_error{ code, msg }; 40 | } 41 | 42 | 43 | MutexGuard::MutexGuard(::HANDLE handle, ::DWORD timeout) : m_handle{ handle } 44 | { 45 | DWORD result = ::WaitForSingleObject(handle, timeout); 46 | switch (result) { 47 | case WAIT_OBJECT_0: 48 | return; 49 | case WAIT_ABANDONED: 50 | ::SetLastError(ERROR_ABANDONED_WAIT_0); 51 | trap_error("remote process abandoned mutex"); 52 | break; 53 | case WAIT_TIMEOUT: 54 | ::SetLastError(ERROR_TIMEOUT); 55 | trap_error(); 56 | break; 57 | case WAIT_FAILED: 58 | trap_error("failed to acquire mutex"); 59 | break; 60 | default: 61 | ::SetLastError(ERROR_UNIDENTIFIED_ERROR); 62 | trap_error("unknown error while waiting on mutex"); 63 | break; 64 | } 65 | } 66 | 67 | MutexGuard::~MutexGuard() { ::ReleaseMutex(m_handle); } 68 | 69 | } // namespace win32 70 | -------------------------------------------------------------------------------- /ipc/video_types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "video_types.h" 5 | 6 | namespace ipc { 7 | 8 | namespace { 9 | 10 | constexpr size_t LEN_UNKNOWN = ~static_cast(0); 11 | 12 | template 13 | size_t LEN_MAX = (UINT32_MAX - sizeof(uint32_t) - sizeof(T)) / sizeof(T); 14 | 15 | template 16 | size_t deserialize_chars(T *dst, const void *src, size_t buf_size) 17 | { 18 | if (buf_size < sizeof(uint32_t) + sizeof(T)) 19 | return LEN_UNKNOWN; 20 | 21 | size_t len = *static_cast(src); 22 | if (len > LEN_MAX) 23 | return LEN_UNKNOWN; 24 | if (buf_size < sizeof(uint32_t) + len * sizeof(T) + sizeof(T)) 25 | return LEN_UNKNOWN; 26 | 27 | if (dst) { 28 | const T *p = reinterpret_cast(static_cast(src) + sizeof(uint32_t)); 29 | std::memcpy(dst, p, len * sizeof(T)); 30 | dst[len] = '\0'; 31 | } 32 | 33 | return len; 34 | } 35 | 36 | template 37 | size_t serialize_chars(void *dst, const T *src, size_t len) 38 | { 39 | if (len == LEN_UNKNOWN) 40 | len = std::char_traits::length(src); 41 | if (len > LEN_MAX) 42 | len = 0; 43 | 44 | size_t total_size = sizeof(uint32_t) + len * sizeof(T) + sizeof(T); 45 | 46 | if (dst) { 47 | *static_cast(dst) = static_cast(len); 48 | 49 | T *p = reinterpret_cast(static_cast(dst) + sizeof(uint32_t)); 50 | std::memcpy(p, src, len * sizeof(T)); 51 | p[len] = '\0'; 52 | } 53 | 54 | return total_size; 55 | } 56 | 57 | } // namespace 58 | 59 | 60 | size_t deserialize_str(char *dst, const void *src, size_t buf_size) noexcept 61 | { 62 | return deserialize_chars(dst, src, buf_size); 63 | } 64 | 65 | size_t serialize_str(void *dst, const char *src, size_t len) noexcept 66 | { 67 | return serialize_chars(dst, src, len); 68 | } 69 | 70 | size_t deserialize_wstr(wchar_t *dst, const void *src, size_t buf_size) noexcept 71 | { 72 | return deserialize_chars(dst, src, buf_size); 73 | } 74 | 75 | size_t serialize_wstr(void *dst, const wchar_t *src, size_t len) noexcept 76 | { 77 | return serialize_chars(dst, src, len); 78 | } 79 | 80 | } // namespace ipc 81 | -------------------------------------------------------------------------------- /msvc/ipc32/ipc32.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | -------------------------------------------------------------------------------- /msvc/ipc64/ipc64.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | -------------------------------------------------------------------------------- /avshost_native/avshost.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef AVSHOST_H_ 4 | #define AVSHOST_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ipc/ipc_commands.h" 11 | #include "ipc/win32util.h" 12 | 13 | class IScriptEnvironment; 14 | 15 | class IClip; 16 | class PClip; 17 | 18 | class AVSValue; 19 | 20 | 21 | namespace ipc_client { 22 | 23 | class IPCClient; 24 | 25 | } // namespace ipc_client 26 | 27 | 28 | namespace avs { 29 | 30 | class Cache; 31 | class VirtualClip; 32 | 33 | class AvisynthError_ : public std::runtime_error { 34 | using std::runtime_error::runtime_error; 35 | }; 36 | 37 | class AvisynthHost : public ipc_client::CommandObserver { 38 | typedef ::IScriptEnvironment *(__stdcall *create_script_env)(int); 39 | 40 | struct IScriptEnvironmentDeleter { 41 | void operator()(::IScriptEnvironment *env); 42 | }; 43 | 44 | class PClip_ { 45 | union { 46 | IClip *i; 47 | unsigned char x[sizeof(IClip *)]; 48 | } m_val; 49 | public: 50 | PClip_(); 51 | PClip_(const ::PClip &p); 52 | PClip_(const PClip_ &other); 53 | 54 | ~PClip_(); 55 | 56 | PClip_ &operator=(const PClip_ &other); 57 | 58 | PClip &get() { return *reinterpret_cast<::PClip *>(m_val.x); } 59 | const PClip &get() const { return *reinterpret_cast(m_val.x); } 60 | }; 61 | 62 | ipc_client::IPCClient *m_client; 63 | win32::unique_module m_library; 64 | create_script_env m_create_script_env; 65 | std::unique_ptr<::IScriptEnvironment, IScriptEnvironmentDeleter> m_env; 66 | 67 | std::unique_ptr m_cache; 68 | std::unordered_map m_remote_clips; 69 | std::unordered_map m_local_clips; 70 | uint32_t m_local_clip_id; 71 | 72 | int observe(std::unique_ptr c) override; 73 | int observe(std::unique_ptr c) override; 74 | int observe(std::unique_ptr c) override; 75 | int observe(std::unique_ptr c) override; 76 | int observe(std::unique_ptr c) override; 77 | int observe(std::unique_ptr c) override; 78 | int observe(std::unique_ptr c) override; 79 | 80 | void send_avsvalue(uint32_t response_id, const ::AVSValue &avs_value); 81 | 82 | void send_err(uint32_t response_id); 83 | public: 84 | explicit AvisynthHost(ipc_client::IPCClient *client); 85 | 86 | ~AvisynthHost(); 87 | }; 88 | 89 | } // namespace avs 90 | 91 | #endif // AVSHOST_H_ 92 | -------------------------------------------------------------------------------- /ipc/logging.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ipc_client.h" 10 | #include "logging.h" 11 | 12 | #undef ipc_filename 13 | #undef ipc_log 14 | #undef ipc_wlog 15 | #undef ipc_log_current_exception 16 | 17 | namespace { 18 | 19 | #ifdef _DEBUG 20 | std::atomic g_log_handler{ ipc_log_stderr }; 21 | std::atomic g_wlog_handler{ ipc_wlog_stderr }; 22 | #else 23 | std::atomic g_log_handler{}; 24 | std::atomic g_wlog_handler{}; 25 | #endif 26 | 27 | } // namespace 28 | 29 | 30 | const char *ipc_filename(const char *path) 31 | { 32 | const char *fslash = std::strrchr(path, '/'); 33 | const char *bslash = std::strrchr(path, '\\'); 34 | 35 | if (fslash && bslash) { 36 | fslash = fslash < bslash ? fslash : bslash; 37 | bslash = nullptr; 38 | } 39 | 40 | return fslash ? fslash + 1 : bslash ? bslash + 1 : path; 41 | } 42 | 43 | void ipc_set_log_handler(void (*func)(const char *, va_list), void (*wfunc)(const wchar_t *, va_list)) 44 | { 45 | g_log_handler = func; 46 | g_wlog_handler = wfunc; 47 | } 48 | 49 | void ipc_log_stderr(const char *fmt, va_list va) 50 | { 51 | std::vfprintf(stderr, fmt, va); 52 | } 53 | 54 | void ipc_wlog_stderr(const wchar_t *fmt, va_list va) 55 | { 56 | std::vfwprintf(stderr, fmt, va); 57 | } 58 | 59 | void ipc_log(const char *fmt, ...) 60 | { 61 | va_list va; 62 | va_start(va, fmt); 63 | auto func = g_log_handler.load(); 64 | if (func) 65 | func(fmt, va); 66 | va_end(va); 67 | } 68 | 69 | void ipc_wlog(const wchar_t *fmt, ...) 70 | { 71 | va_list va; 72 | va_start(va, fmt); 73 | auto func = g_wlog_handler.load(); 74 | if (func) 75 | func(fmt, va); 76 | va_end(va); 77 | } 78 | 79 | void ipc_log_current_exception() 80 | { 81 | try { 82 | throw; 83 | } catch (const ipc_client::IPCError &e) { 84 | ipc_log("IPC error: %s\n", e.what()); 85 | 86 | if (e.cause()) { 87 | ipc_log("cause: "); 88 | 89 | try { 90 | std::rethrow_exception(e.cause()); 91 | } catch (...) { 92 | ipc_log_current_exception(); 93 | } 94 | } 95 | } catch (const std::system_error &e) { 96 | ipc_log("system error %d: %s\n", e.code().value(), e.what()); 97 | } catch (const std::exception &e) { 98 | ipc_log("%s: %s\n", typeid(e).name(), e.what()); 99 | } catch (...) { 100 | ipc_log("unknown exception\n"); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ipc/win32util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IPC_WIN32UTIL_H_ 4 | #define IPC_WIN32UTIL_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct HINSTANCE__; 11 | 12 | namespace win32 { 13 | namespace detail { 14 | typedef unsigned long DWORD; 15 | 16 | typedef void *HANDLE; 17 | typedef HINSTANCE__ *HMODULE; 18 | 19 | constexpr DWORD INFINITE_ = ~static_cast(0); 20 | constexpr uintptr_t INVALID_HANDLE_VALUE_ = ~static_cast(0); 21 | 22 | inline HANDLE invalid_handle() { return reinterpret_cast(INVALID_HANDLE_VALUE_); } 23 | 24 | // Pointer class handling the two invalid states of HANDLE. 25 | struct handle { 26 | HANDLE h; 27 | 28 | handle(HANDLE h = nullptr) : h{ h } {} 29 | 30 | explicit operator bool() const { return h && h != invalid_handle(); } 31 | 32 | friend bool operator==(handle lhs, handle rhs) { return lhs.h == rhs.h || (!lhs && !rhs); } 33 | friend bool operator==(handle lhs, std::nullptr_t rhs) { return !lhs; } 34 | friend bool operator==(std::nullptr_t lhs, handle rhs) { return !rhs; } 35 | 36 | friend bool operator!=(handle lhs, handle rhs) { return !(lhs == rhs); } 37 | friend bool operator!=(handle lhs, std::nullptr_t rhs) { return !!lhs; } 38 | friend bool operator!=(std::nullptr_t lhs, handle rhs) { return !!rhs; } 39 | }; 40 | 41 | struct CloseHandleDeleter { 42 | typedef handle pointer; 43 | void operator()(handle h); 44 | }; 45 | 46 | struct UnmapViewOfFileDeleter { 47 | void operator()(void *ptr); 48 | }; 49 | 50 | struct FreeLibraryDeleter { 51 | void operator()(HMODULE ptr); 52 | }; 53 | 54 | class TerminateProcessDeleter { 55 | typedef handle pointer; 56 | void operator()(handle h); 57 | }; 58 | 59 | } // namespace detail 60 | 61 | 62 | // Call GetLastError and throw a std::system_error. 63 | [[noreturn]] inline void trap_error(const char *msg = ""); 64 | 65 | // Handle-based smart pointers. 66 | typedef std::unique_ptr unique_handle; 67 | typedef std::unique_ptr unique_file_view; 68 | typedef std::unique_ptr unique_module; 69 | 70 | // Automatically terminates process. 71 | typedef std::unique_ptr unique_process; 72 | 73 | // std::lock_guard analogue for Win32 mutexes. 74 | class MutexGuard { 75 | detail::HANDLE m_handle; 76 | public: 77 | MutexGuard(detail::HANDLE h, detail::DWORD timeout = detail::INFINITE_); 78 | 79 | ~MutexGuard(); 80 | 81 | MutexGuard(const MutexGuard &) = delete; 82 | MutexGuard(MutexGuard &&) = delete; 83 | 84 | MutexGuard &operator=(const MutexGuard &) = delete; 85 | MutexGuard &operator=(MutexGuard &&) = delete; 86 | }; 87 | 88 | } // namespace win32 89 | 90 | #endif // IPC_WIN32UTIL_H_ 91 | -------------------------------------------------------------------------------- /ipc/ipc_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IPC_IPC_TYPES_H_ 4 | #define IPC_IPC_TYPES_H_ 5 | 6 | #include 7 | 8 | namespace ipc { 9 | 10 | // IPC protocol version. 11 | constexpr int32_t VERSION = 0; 12 | 13 | // Offset representing a null pointer in the IPC heap. 14 | constexpr uint32_t NULL_OFFSET = ~static_cast(0); 15 | 16 | 17 | // Header of the IPC shared memory. It must be present at offset 0. 18 | struct alignas(64) SharedMemoryHeader { 19 | int8_t magic[4] = { 'a', 'v', 's', 'w' }; 20 | // Size of the entire shared memory region. 21 | uint32_t size = 0; 22 | // IPC protocol version. 23 | int32_t version = VERSION; 24 | // Offset from SharedMemoryHeader to the master->slave queue. 25 | uint32_t master_queue_offset = NULL_OFFSET; 26 | // Offset from SharedMemoryHeader to the slave->master queue. 27 | uint32_t slave_queue_offset = NULL_OFFSET; 28 | // Offset from SharedMemoryHeader to the IPC heap. 29 | uint32_t heap_offset = NULL_OFFSET; 30 | }; 31 | 32 | // Unidirectional command queue. The queue buffer immediately follows. 33 | struct alignas(64) Queue { 34 | int8_t magic[4] = { 'c', 'm', 'd', 'q' }; 35 | // Size of the queue and subsequent command buffer. 36 | uint32_t size = 0; 37 | // Win32 event used by the reader to wait for commands. 38 | uint32_t event_handle = 0; 39 | // Win32 mutex protecting the queue. 40 | uint32_t mutex_handle = 0; 41 | // Offset from Queue to the command buffer. 42 | uint32_t buffer_offset = sizeof(Queue); 43 | // Number of bytes in use in the buffer, up to (size - sizeof(Queue)). 44 | uint32_t buffer_usage = 0; 45 | // Offset from command buffer to reader position. 46 | uint32_t read_pos = 0; 47 | // Offset from command buffer to writer position. 48 | uint32_t write_pos = 0; 49 | }; 50 | 51 | // Command object in queue. The command payload immediately follows. 52 | struct alignas(8) Command { 53 | int8_t magic[4] = { 'c', 'm', 'd', 'x' }; 54 | // Size of the command and subsequent payload. 55 | uint32_t size = 0; 56 | // ID generated by the sender used to wait for a response. 57 | uint32_t transaction_id; 58 | // ID generated by the recipient that resulted in the command. 59 | uint32_t response_id; 60 | // Command type. Defined in ipc_client.h. 61 | int32_t type; 62 | }; 63 | 64 | // Heap for memory allocation. The heap buffer immediately follows. 65 | struct alignas(64) Heap { 66 | int8_t magic[4] = { 'h', 'e', 'a', 'p' }; 67 | // Size of the heap and subsequent buffer. 68 | uint32_t size = 0; 69 | // Win32 mutex proteccting the heap. 70 | uint32_t mutex_handle = 0; 71 | // Offset from Heap to buffer. 72 | uint32_t buffer_offset = sizeof(Heap); 73 | // Number of bytes allocated, up to (size - sizeof(Heap)). 74 | uint32_t buffer_usage = 0; 75 | // Hint offset from base of buffer to a free block. 76 | uint32_t last_free_offset = 0; 77 | }; 78 | 79 | 80 | // Heap block is allocated. 81 | constexpr uint32_t HEAP_FLAG_ALLOCATED = 1; 82 | 83 | struct alignas(64) HeapNode { 84 | int8_t magic[4] = { 'm', 'e', 'm', 'z' }; 85 | uint32_t prev_node_offset = NULL_OFFSET; 86 | uint32_t next_node_offset = NULL_OFFSET; 87 | uint32_t flags = 0; 88 | }; 89 | 90 | 91 | // Read available commands from queue. The buffer must be at least 92 | // (queue->buffer_usage) bytes. The caller must be holding the queue mutex. 93 | void queue_read(Queue *queue, void *buf); 94 | 95 | // Write commands into queue. The caller must be holding the queue mutex. 96 | void queue_write(Queue *queue, const void *buf, uint32_t size); 97 | 98 | // Allocate a block from heap. The caller must be holding the heap mutex. 99 | HeapNode *heap_alloc(Heap *heap, uint32_t size); 100 | 101 | // Return a block to heap. The caller must be holding the heap mutex. 102 | void heap_free(Heap *heap, HeapNode *node); 103 | 104 | 105 | inline bool check_fourcc(const int8_t lhs[4], const char rhs[]) 106 | { 107 | return lhs[0] == rhs[0] && lhs[1] == rhs[1] && lhs[2] == rhs[2] && lhs[3] == rhs[3]; 108 | } 109 | 110 | template 111 | T *offset_to_pointer(U *base, uint32_t offset) 112 | { 113 | return (T *)((unsigned char *)base + offset); 114 | } 115 | 116 | inline uint32_t pointer_to_offset(const void *base, const void *ptr) 117 | { 118 | return static_cast(static_cast(ptr) - static_cast(base)); 119 | } 120 | 121 | } // namespace ipc 122 | 123 | #endif // IPC_IPC_TYPES_H_ 124 | -------------------------------------------------------------------------------- /msvc/ipc32/ipc32.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15.0 15 | {FA633448-2C1C-4655-9976-D637390CE8C5} 16 | ipc32 17 | 10.0 18 | 19 | 20 | 21 | StaticLibrary 22 | true 23 | v143 24 | Unicode 25 | 26 | 27 | StaticLibrary 28 | false 29 | v143 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Level3 49 | Disabled 50 | true 51 | true 52 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 53 | 54 | 55 | 56 | 57 | Level3 58 | MaxSpeed 59 | true 60 | true 61 | true 62 | true 63 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 64 | 65 | 66 | true 67 | true 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /ipc/ipc_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "win32util.h" 15 | 16 | namespace ipc { 17 | 18 | struct Queue; 19 | struct Heap; 20 | 21 | } // namespace ipc 22 | 23 | 24 | namespace ipc_client { 25 | 26 | class Command; 27 | 28 | class IPCError : public std::runtime_error { 29 | std::exception_ptr m_cause; 30 | public: 31 | using std::runtime_error::runtime_error; 32 | 33 | IPCError(std::exception_ptr cause, const std::string &what_arg) : 34 | std::runtime_error{ what_arg }, 35 | m_cause{ cause } 36 | {} 37 | 38 | IPCError(std::exception_ptr cause, const char *what_arg) : 39 | std::runtime_error{ what_arg }, 40 | m_cause{ cause } 41 | {} 42 | 43 | std::exception_ptr cause() const noexcept { return m_cause; } 44 | }; 45 | 46 | class IPCHeapFull : public IPCError { 47 | size_t m_alloc; 48 | size_t m_free; 49 | public: 50 | IPCHeapFull(size_t alloc, size_t free) : IPCError{ "heap full" }, m_alloc { alloc }, m_free{ free } {} 51 | 52 | size_t alloc() const { return m_alloc; } 53 | size_t free() const { return m_free; } 54 | }; 55 | 56 | 57 | class IPCClient { 58 | public: 59 | // Called from the receiver thread. Throwing exceptions will terminate the 60 | // session. The parameter will be null if the response could not be 61 | // deserialized or the session ended. 62 | typedef std::function)> callback_type; 63 | private: 64 | struct master_tag {}; 65 | struct slave_tag {}; 66 | 67 | // IPC control structures. 68 | win32::unique_handle m_shmem_handle; 69 | win32::unique_file_view m_shmem; 70 | 71 | ipc::Queue *m_master_queue; 72 | win32::unique_handle m_master_event; 73 | win32::unique_handle m_master_mutex; 74 | 75 | ipc::Queue *m_slave_queue; 76 | win32::unique_handle m_slave_event; 77 | win32::unique_handle m_slave_mutex; 78 | 79 | ipc::Heap *m_heap; 80 | win32::unique_handle m_heap_mutex; 81 | 82 | win32::detail::HANDLE m_remote_process; 83 | bool m_master; 84 | 85 | // Transaction state. 86 | std::unordered_map m_callbacks; 87 | callback_type m_default_cb; 88 | std::mutex m_worker_mutex; 89 | std::atomic_uint32_t m_transaction_id; 90 | std::atomic_bool m_kill_flag; 91 | 92 | std::unique_ptr m_recv_thread; 93 | std::exception_ptr m_recv_exception; 94 | 95 | ipc::Queue *send_queue() const { return m_master ? m_master_queue : m_slave_queue; } 96 | win32::detail::HANDLE send_event() const { return m_master ? m_master_event.get().h : m_slave_event.get().h; } 97 | win32::detail::HANDLE send_mutex() const { return m_master ? m_master_mutex.get().h : m_slave_mutex.get().h; } 98 | 99 | ipc::Queue *recv_queue() const { return m_master ? m_slave_queue : m_master_queue; } 100 | win32::detail::HANDLE recv_event() const { return m_master ? m_slave_event.get().h : m_master_event.get().h; } 101 | win32::detail::HANDLE recv_mutex() const { return m_master ? m_slave_mutex.get().h : m_master_mutex.get().h; } 102 | 103 | explicit IPCClient(bool master); 104 | 105 | uint32_t next_transaction_id(); 106 | 107 | void recv_thread_func(); 108 | public: 109 | static master_tag master() { return{}; } 110 | static slave_tag slave() { return{}; } 111 | 112 | // Allocate IPC context and start slave process. 113 | IPCClient(master_tag, const wchar_t *slave_path); 114 | 115 | // Connect to master process. 116 | IPCClient(slave_tag, win32::detail::HANDLE master_process, win32::detail::HANDLE shmem_handle, size_t shmem_size); 117 | 118 | IPCClient(const IPCClient &) = delete; 119 | IPCClient(IPCClient &&) = delete; 120 | 121 | ~IPCClient(); 122 | 123 | IPCClient &operator=(const IPCClient &) = delete; 124 | IPCClient &operator=(IPCClient &&) = delete; 125 | 126 | // Begin receiving commands. The client can only be started once. 127 | void start(callback_type default_cb); 128 | 129 | // Stop receiving commands. If a fatal communication error had previously 130 | // occurred, any exception generated will be raised here. 131 | void stop(); 132 | 133 | // Heap interface. 134 | uint32_t pointer_to_offset(void *ptr) const; 135 | void *offset_to_pointer(uint32_t off) const; 136 | 137 | void *allocate(size_t size); 138 | void deallocate(void *ptr); 139 | 140 | // Send a command with an optional callback. The callback will be invoked 141 | // from the command receiver thread. Raises any prior exceptions. 142 | void send_async(std::unique_ptr command, callback_type cb = nullptr); 143 | 144 | // Send a command and wait for the result. Synchronous commands can not be 145 | // sent from the command receiver thread. Raises any prior exceptions. 146 | std::unique_ptr send_sync(std::unique_ptr command); 147 | }; 148 | 149 | } // namespace ipc_client 150 | -------------------------------------------------------------------------------- /avshost_native/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ipc/ipc_client.h" 13 | #include "ipc/ipc_commands.h" 14 | #include "ipc/logging.h" 15 | #include "ipc/win32util.h" 16 | #include "avshost.h" 17 | 18 | namespace { 19 | 20 | class Session : ipc_client::CommandObserver { 21 | static std::FILE *s_log_file; 22 | 23 | ipc_client::IPCClient *m_client; 24 | avs::AvisynthHost m_avs; 25 | std::mutex m_mutex; 26 | std::condition_variable m_cond; 27 | std::deque> m_queue; 28 | std::atomic_bool m_exit_flag; 29 | 30 | static void log_to_file(const char *fmt, va_list va) 31 | { 32 | std::vfprintf(s_log_file, fmt, va); 33 | } 34 | 35 | static void log_to_file(const wchar_t *fmt, va_list va) 36 | { 37 | std::vfwprintf(s_log_file, fmt, va); 38 | } 39 | 40 | int observe(std::unique_ptr c) override 41 | { 42 | if (!s_log_file && (s_log_file = _wfopen(c->arg().c_str(), L"w"))) { 43 | std::setvbuf(s_log_file, nullptr, _IONBF, 0); 44 | ipc_set_log_handler(log_to_file, log_to_file); 45 | } 46 | 47 | c->deallocate_heap_resources(m_client); 48 | return 0; 49 | } 50 | 51 | #define AVS_OBSERVE(T) int observe(std::unique_ptr c) override { return m_avs.dispatch(std::move(c)); } 52 | AVS_OBSERVE(ipc_client::CommandLoadAvisynth) 53 | AVS_OBSERVE(ipc_client::CommandNewScriptEnv) 54 | AVS_OBSERVE(ipc_client::CommandGetScriptVar) 55 | AVS_OBSERVE(ipc_client::CommandSetScriptVar) 56 | AVS_OBSERVE(ipc_client::CommandEvalScript) 57 | AVS_OBSERVE(ipc_client::CommandGetFrame) 58 | AVS_OBSERVE(ipc_client::CommandSetFrame) 59 | #undef AVS_OBSERVE 60 | 61 | void queue_command(std::unique_ptr command) 62 | { 63 | std::unique_lock lock{ m_mutex }; 64 | 65 | if (command) 66 | m_queue.emplace_back(std::move(command)); 67 | else 68 | m_exit_flag = true; 69 | 70 | lock.unlock(); 71 | m_cond.notify_all(); 72 | } 73 | 74 | void send_ack(uint32_t response_id) 75 | { 76 | if (response_id == ipc_client::INVALID_TRANSACTION) 77 | return; 78 | 79 | auto response = std::make_unique(); 80 | response->set_response_id(response_id); 81 | m_client->send_async(std::move(response)); 82 | } 83 | 84 | void send_err(uint32_t response_id) 85 | { 86 | if (response_id == ipc_client::INVALID_TRANSACTION) 87 | return; 88 | 89 | auto response = std::make_unique(); 90 | response->set_response_id(response_id); 91 | m_client->send_async(std::move(response)); 92 | } 93 | public: 94 | explicit Session(ipc_client::IPCClient *client) : 95 | m_client{ client }, 96 | m_avs{ client }, 97 | m_exit_flag {} 98 | {} 99 | 100 | Session(const Session &) = delete; 101 | Session(Session &&) = delete; 102 | 103 | Session &operator=(const Session &) = delete; 104 | Session &operator=(Session &&) = delete; 105 | 106 | void run_loop() 107 | { 108 | m_client->start(std::bind(&Session::queue_command, this, std::placeholders::_1)); 109 | 110 | while (true) { 111 | std::unique_lock lock{ m_mutex }; 112 | m_cond.wait(lock, [&]() { return m_exit_flag || !m_queue.empty(); }); 113 | 114 | if (m_exit_flag) { 115 | ipc_log0("exit after broken connection\n"); 116 | break; 117 | } 118 | 119 | std::unique_ptr command = std::move(m_queue.front()); 120 | m_queue.pop_front(); 121 | lock.unlock(); 122 | 123 | uint32_t transaction_id = command->transaction_id(); 124 | 125 | try { 126 | int ret = dispatch(std::move(command)); 127 | if (!ret && transaction_id != ipc_client::INVALID_TRANSACTION) 128 | send_ack(transaction_id); 129 | } catch (const ipc_client::IPCError &) { 130 | throw; 131 | } catch (...) { 132 | send_err(transaction_id); 133 | ipc_log_current_exception(); 134 | } 135 | } 136 | } 137 | }; 138 | 139 | std::FILE *Session::s_log_file{}; 140 | 141 | } // namespace 142 | 143 | 144 | int wmain(int argc, wchar_t **argv) 145 | { 146 | for (int i = 0; i < argc; ++i) { 147 | ipc_wlog("argv[%d]: %s\n", i, argv[i]); 148 | } 149 | 150 | if (argc != 4) 151 | return 1; 152 | 153 | try { 154 | unsigned long parent_pid = std::stoul(argv[1]); 155 | ::HANDLE shmem_handle = ULongToHandle(std::stoul(argv[2])); 156 | unsigned long shmem_size = std::stoul(argv[3]); 157 | 158 | win32::unique_handle parent_process{ ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, parent_pid) }; 159 | if (!parent_process) 160 | win32::trap_error("error connecting to master process"); 161 | 162 | auto client = std::make_unique(ipc_client::IPCClient::slave(), parent_process.get().h, shmem_handle, shmem_size); 163 | Session session{ client.get() }; 164 | session.run_loop(); 165 | } catch (...) { 166 | ipc_log_current_exception(); 167 | throw; 168 | } 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /msvc/avshost_native/avshost_native.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15.0 15 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69} 16 | avshostnative 17 | 10.0 18 | 19 | 20 | 21 | Application 22 | true 23 | v143 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v143 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Level3 49 | Disabled 50 | true 51 | true 52 | $(ProjectDir)..\..;C:\Program Files (x86)\AviSynth+\FilterSDK\include 53 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 54 | 55 | 56 | $(OutDir) 57 | ipc32.lib;%(AdditionalDependencies) 58 | true 59 | 60 | 61 | 62 | 63 | Level3 64 | MaxSpeed 65 | true 66 | true 67 | true 68 | true 69 | $(ProjectDir)..\..;C:\Program Files (x86)\AviSynth+\FilterSDK\include 70 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 71 | 72 | 73 | true 74 | true 75 | $(OutDir) 76 | ipc32.lib;%(AdditionalDependencies) 77 | true 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | false 87 | false 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /msvc/avsproxy.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32616.157 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avsproxy", "avsproxy\avsproxy.vcxproj", "{552A627B-59C7-44FA-96AA-110766783B83}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD} = {0955E601-DABB-442D-AFFF-EB4B2BF331FD} 9 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F} = {65E94A7A-365E-4CCE-8999-B1E475C02A3F} 10 | EndProjectSection 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avshost_native", "avshost_native\avshost_native.vcxproj", "{00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}" 13 | ProjectSection(ProjectDependencies) = postProject 14 | {FA633448-2C1C-4655-9976-D637390CE8C5} = {FA633448-2C1C-4655-9976-D637390CE8C5} 15 | EndProjectSection 16 | EndProject 17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ipc32", "ipc32\ipc32.vcxproj", "{FA633448-2C1C-4655-9976-D637390CE8C5}" 18 | EndProject 19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ipc64", "ipc64\ipc64.vcxproj", "{65E94A7A-365E-4CCE-8999-B1E475C02A3F}" 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libp2p", "libp2p", "{688CBAB8-6F09-4C76-9F38-743FCC437360}" 22 | EndProject 23 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libp2p_simd", "..\libp2p\_msvc\libp2p_simd\libp2p_simd.vcxproj", "{0955E601-DABB-442D-AFFF-EB4B2BF331FD}" 24 | EndProject 25 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "p2p_sources", "..\libp2p\_msvc\p2p_sources\p2p_sources.vcxitems", "{899DD8F7-6CC3-46D1-8D5D-5AFB20072FC1}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|x64 = Debug|x64 30 | Debug|x86 = Debug|x86 31 | Release|x64 = Release|x64 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {552A627B-59C7-44FA-96AA-110766783B83}.Debug|x64.ActiveCfg = Debug|x64 36 | {552A627B-59C7-44FA-96AA-110766783B83}.Debug|x64.Build.0 = Debug|x64 37 | {552A627B-59C7-44FA-96AA-110766783B83}.Debug|x86.ActiveCfg = Debug|Win32 38 | {552A627B-59C7-44FA-96AA-110766783B83}.Debug|x86.Build.0 = Debug|Win32 39 | {552A627B-59C7-44FA-96AA-110766783B83}.Release|x64.ActiveCfg = Release|x64 40 | {552A627B-59C7-44FA-96AA-110766783B83}.Release|x64.Build.0 = Release|x64 41 | {552A627B-59C7-44FA-96AA-110766783B83}.Release|x86.ActiveCfg = Release|Win32 42 | {552A627B-59C7-44FA-96AA-110766783B83}.Release|x86.Build.0 = Release|Win32 43 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Debug|x64.ActiveCfg = Debug|Win32 44 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Debug|x64.Build.0 = Debug|Win32 45 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Debug|x86.ActiveCfg = Debug|Win32 46 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Debug|x86.Build.0 = Debug|Win32 47 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Release|x64.ActiveCfg = Release|Win32 48 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Release|x64.Build.0 = Release|Win32 49 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Release|x86.ActiveCfg = Release|Win32 50 | {00433F94-D81F-4A12-B5CF-EAE9AF1BCE69}.Release|x86.Build.0 = Release|Win32 51 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Debug|x64.ActiveCfg = Debug|Win32 52 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Debug|x64.Build.0 = Debug|Win32 53 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Debug|x86.ActiveCfg = Debug|Win32 54 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Debug|x86.Build.0 = Debug|Win32 55 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Release|x64.ActiveCfg = Release|Win32 56 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Release|x64.Build.0 = Release|Win32 57 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Release|x86.ActiveCfg = Release|Win32 58 | {FA633448-2C1C-4655-9976-D637390CE8C5}.Release|x86.Build.0 = Release|Win32 59 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Debug|x64.ActiveCfg = Debug|x64 60 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Debug|x64.Build.0 = Debug|x64 61 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Debug|x86.ActiveCfg = Debug|Win32 62 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Debug|x86.Build.0 = Debug|Win32 63 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Release|x64.ActiveCfg = Release|x64 64 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Release|x64.Build.0 = Release|x64 65 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Release|x86.ActiveCfg = Release|Win32 66 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F}.Release|x86.Build.0 = Release|Win32 67 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Debug|x64.ActiveCfg = Debug|x64 68 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Debug|x64.Build.0 = Debug|x64 69 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Debug|x86.ActiveCfg = Debug|Win32 70 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Debug|x86.Build.0 = Debug|Win32 71 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Release|x64.ActiveCfg = Release|x64 72 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Release|x64.Build.0 = Release|x64 73 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Release|x86.ActiveCfg = Release|Win32 74 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD}.Release|x86.Build.0 = Release|Win32 75 | EndGlobalSection 76 | GlobalSection(SolutionProperties) = preSolution 77 | HideSolutionNode = FALSE 78 | EndGlobalSection 79 | GlobalSection(NestedProjects) = preSolution 80 | {0955E601-DABB-442D-AFFF-EB4B2BF331FD} = {688CBAB8-6F09-4C76-9F38-743FCC437360} 81 | EndGlobalSection 82 | GlobalSection(ExtensibilityGlobals) = postSolution 83 | SolutionGuid = {417B92DB-F06B-4497-B91E-B73045D8615D} 84 | EndGlobalSection 85 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 86 | ..\libp2p\_msvc\p2p_sources\p2p_sources.vcxitems*{0955e601-dabb-442d-afff-eb4b2bf331fd}*SharedItemsImports = 4 87 | ..\libp2p\_msvc\p2p_sources\p2p_sources.vcxitems*{899dd8f7-6cc3-46d1-8d5d-5afb20072fc1}*SharedItemsImports = 9 88 | EndGlobalSection 89 | EndGlobal 90 | -------------------------------------------------------------------------------- /ipc/ipc_types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ipc_types.h" 6 | 7 | namespace ipc { 8 | 9 | namespace { 10 | 11 | void split_heap_node(void *heap_base, HeapNode *node, uint32_t size) 12 | { 13 | uint32_t node_offset = pointer_to_offset(heap_base, node); 14 | uint32_t alloc_size = size; 15 | 16 | if (alloc_size % alignof(HeapNode)) 17 | alloc_size += alignof(HeapNode) - alloc_size % alignof(HeapNode); 18 | 19 | HeapNode *next = new (offset_to_pointer(heap_base, node_offset + alloc_size)) HeapNode{}; 20 | next->prev_node_offset = node_offset; 21 | next->next_node_offset = node->next_node_offset; 22 | 23 | node->next_node_offset = node_offset + alloc_size; 24 | } 25 | 26 | } // namespace 27 | 28 | 29 | void queue_read(Queue *queue, void *buf) 30 | { 31 | unsigned char *queue_base = offset_to_pointer(queue, queue->buffer_offset); 32 | uint32_t capacity = queue->size - queue->buffer_offset; 33 | 34 | if (queue->buffer_usage <= capacity - queue->read_pos) { 35 | std::memcpy(buf, queue_base + queue->read_pos, queue->buffer_usage); 36 | queue->read_pos += queue->buffer_usage; 37 | } else { 38 | uint32_t read_first = capacity - queue->read_pos; 39 | std::memcpy(buf, queue_base + queue->read_pos, read_first); 40 | 41 | buf = offset_to_pointer(buf, read_first); 42 | std::memcpy(buf, queue_base, queue->buffer_usage - read_first); 43 | 44 | queue->read_pos = queue->buffer_usage - read_first; 45 | } 46 | 47 | queue->buffer_usage = 0; 48 | } 49 | 50 | void queue_write(Queue *queue, const void *buf, uint32_t size) 51 | { 52 | unsigned char *queue_base = offset_to_pointer(queue, queue->buffer_offset); 53 | uint32_t capacity = queue->size - queue->buffer_offset; 54 | 55 | assert(size <= capacity - queue->buffer_usage); 56 | 57 | if (size <= capacity - queue->write_pos) { 58 | std::memcpy(queue_base + queue->write_pos, buf, size); 59 | queue->write_pos += size; 60 | } else { 61 | uint32_t write_first = capacity - queue->write_pos; 62 | std::memcpy(queue_base + queue->write_pos, buf, write_first); 63 | 64 | buf = offset_to_pointer(buf, write_first); 65 | std::memcpy(queue_base, buf, size - write_first); 66 | 67 | queue->write_pos = size - write_first; 68 | } 69 | 70 | queue->buffer_usage += size; 71 | } 72 | 73 | HeapNode *heap_alloc(Heap *heap, uint32_t size) 74 | { 75 | void *heap_base = offset_to_pointer(heap, heap->buffer_offset); 76 | uint32_t capacity = heap->size - heap->buffer_offset; 77 | 78 | if (size > capacity - sizeof(HeapNode)) 79 | return nullptr; 80 | 81 | size += sizeof(HeapNode); 82 | if (size > capacity - heap->buffer_usage) 83 | return nullptr; 84 | 85 | // Try the hint first. 86 | HeapNode *node = static_cast(heap_base); 87 | if (heap->last_free_offset != NULL_OFFSET) 88 | node = offset_to_pointer(heap_base, heap->last_free_offset); 89 | 90 | // Forward scan. 91 | HeapNode *initial = node; 92 | 93 | while (true) { 94 | assert(check_fourcc(node->magic, "memz")); 95 | 96 | uint32_t node_offset = pointer_to_offset(heap_base, node); 97 | uint32_t node_real_next = node->next_node_offset == NULL_OFFSET ? capacity : node->next_node_offset; 98 | uint32_t node_size = node_real_next - node_offset; 99 | 100 | if (!(node->flags & HEAP_FLAG_ALLOCATED) && size < node_size) { 101 | if (node_size - size >= 4096U) { 102 | split_heap_node(heap_base, node, size); 103 | node_real_next = node->next_node_offset; 104 | } 105 | 106 | node->flags |= HEAP_FLAG_ALLOCATED; 107 | heap->buffer_usage += node_real_next - node_offset; 108 | return node; 109 | } 110 | 111 | if (node->next_node_offset == NULL_OFFSET) 112 | break; 113 | 114 | node = offset_to_pointer(heap_base, node->next_node_offset); 115 | } 116 | 117 | // Reverse scan. 118 | if (initial->prev_node_offset == NULL_OFFSET) 119 | return nullptr; 120 | 121 | node = offset_to_pointer(heap_base, initial->prev_node_offset); 122 | 123 | while (true) { 124 | assert(check_fourcc(node->magic, "memz")); 125 | 126 | uint32_t node_offset = pointer_to_offset(heap_base, node); 127 | uint32_t node_real_next = node->next_node_offset == NULL_OFFSET ? capacity : node->next_node_offset; 128 | uint32_t node_size = node_real_next - node_offset; 129 | 130 | if (!(node->flags & HEAP_FLAG_ALLOCATED) && size < node_size) { 131 | if (node_size - size >= 4096U) 132 | split_heap_node(heap_base, node, size); 133 | 134 | node->flags |= HEAP_FLAG_ALLOCATED; 135 | return node; 136 | } 137 | 138 | if (node->prev_node_offset == NULL_OFFSET) 139 | break; 140 | 141 | node = offset_to_pointer(heap_base, node->prev_node_offset); 142 | } 143 | 144 | return nullptr; 145 | } 146 | 147 | void heap_free(Heap *heap, HeapNode *node) 148 | { 149 | assert(check_fourcc(node->magic, "memz")); 150 | assert(node->flags & HEAP_FLAG_ALLOCATED); 151 | 152 | void *heap_base = offset_to_pointer(heap, heap->buffer_offset); 153 | uint32_t capacity = heap->size - heap->buffer_offset; 154 | 155 | uint32_t node_real_next = node->next_node_offset == NULL_OFFSET ? capacity : node->next_node_offset; 156 | uint32_t node_real_size = node_real_next - pointer_to_offset(heap_base, node); 157 | assert(node_real_size <= heap->buffer_usage); 158 | 159 | node->flags &= ~HEAP_FLAG_ALLOCATED; 160 | heap->buffer_usage -= node_real_size; 161 | 162 | // Forward scan. 163 | while (node->next_node_offset != NULL_OFFSET) { 164 | HeapNode *next = offset_to_pointer(heap_base, node->next_node_offset); 165 | assert(check_fourcc(next->magic, "memz")); 166 | 167 | if (next->flags & HEAP_FLAG_ALLOCATED) 168 | break; 169 | 170 | node->next_node_offset = next->next_node_offset; 171 | std::memset(next->magic, 0, sizeof(next->magic)); 172 | } 173 | 174 | // Reverse scan. 175 | while (node->prev_node_offset != NULL_OFFSET) { 176 | HeapNode *prev = offset_to_pointer(heap_base, node->prev_node_offset); 177 | assert(check_fourcc(prev->magic, "memz")); 178 | 179 | if (prev->flags & HEAP_FLAG_ALLOCATED) 180 | break; 181 | 182 | prev->next_node_offset = node->next_node_offset; 183 | std::memset(node->magic, 0, sizeof(node->magic)); 184 | node = prev; 185 | } 186 | 187 | heap->last_free_offset = pointer_to_offset(heap_base, node); 188 | } 189 | 190 | } // namespace ipc 191 | -------------------------------------------------------------------------------- /msvc/ipc64/ipc64.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {65E94A7A-365E-4CCE-8999-B1E475C02A3F} 24 | ipc64 25 | 10.0 26 | 27 | 28 | 29 | StaticLibrary 30 | true 31 | v143 32 | Unicode 33 | 34 | 35 | StaticLibrary 36 | false 37 | v143 38 | true 39 | Unicode 40 | 41 | 42 | StaticLibrary 43 | true 44 | v143 45 | Unicode 46 | 47 | 48 | StaticLibrary 49 | false 50 | v143 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 80 | 81 | 82 | 83 | 84 | Level3 85 | Disabled 86 | true 87 | true 88 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 89 | 90 | 91 | 92 | 93 | Level3 94 | MaxSpeed 95 | true 96 | true 97 | true 98 | true 99 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 100 | 101 | 102 | true 103 | true 104 | 105 | 106 | 107 | 108 | Level3 109 | MaxSpeed 110 | true 111 | true 112 | true 113 | true 114 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 115 | 116 | 117 | true 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /ipc/ipc_commands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IPC_COMMANDS_H_ 4 | #define IPC_COMMANDS_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "video_types.h" 11 | 12 | namespace ipc { 13 | struct Command; 14 | } // namespace ipc 15 | 16 | 17 | namespace ipc_client { 18 | 19 | constexpr uint32_t INVALID_TRANSACTION = ~static_cast(0); 20 | 21 | class IPCClient; 22 | class IPCError; 23 | 24 | enum class CommandType : int32_t { 25 | ACK, 26 | ERR, 27 | SET_LOG_FILE, 28 | LOAD_AVISYNTH, 29 | NEW_SCRIPT_ENV, 30 | GET_SCRIPT_VAR, 31 | SET_SCRIPT_VAR, 32 | EVAL_SCRIPT, 33 | GET_FRAME, 34 | SET_FRAME, 35 | }; 36 | 37 | class Command { 38 | protected: 39 | uint32_t m_transaction_id; 40 | uint32_t m_response_id; 41 | CommandType m_type; 42 | 43 | explicit Command(CommandType type) : 44 | m_transaction_id{ INVALID_TRANSACTION }, 45 | m_response_id{ INVALID_TRANSACTION }, 46 | m_type{ type } 47 | {} 48 | 49 | void deserialize_common(const ipc::Command *command); 50 | 51 | virtual size_t size_internal() const noexcept { return 0; } 52 | virtual void serialize_internal(void *buf) const noexcept {} 53 | public: 54 | virtual ~Command() = default; 55 | 56 | // Deallocate resources held on the IPC heap. Any heap resources must be deallocated 57 | // or relinquished before the destructor is called. 58 | virtual void deallocate_heap_resources(IPCClient *client) {} 59 | 60 | // Relinquish heap resources after command is successfully written to IPC queue. 61 | virtual void relinquish_heap_resources() noexcept {} 62 | 63 | void set_transaction_id(uint32_t transaction_id) { m_transaction_id = transaction_id; } 64 | void set_response_id(uint32_t response_id) { m_response_id = response_id; } 65 | 66 | size_t serialized_size() const noexcept; 67 | void serialize(void *buf) const noexcept; 68 | 69 | uint32_t transaction_id() const { return m_transaction_id; } 70 | uint32_t response_id() const { return m_response_id; } 71 | CommandType type() const { return m_type; } 72 | 73 | friend std::unique_ptr deserialize_command(const ipc::Command *command); 74 | }; 75 | 76 | std::unique_ptr deserialize_command(const ipc::Command *command); 77 | 78 | 79 | namespace detail { 80 | 81 | [[noreturn]] void throw_deserialization_error(const char *msg); 82 | 83 | template 84 | class Command_Args0 : public Command { 85 | public: 86 | Command_Args0() : Command{ Type } {} 87 | }; 88 | 89 | template 90 | class Command_Args1_str : public Command { 91 | protected: 92 | std::string m_arg; 93 | 94 | template 95 | static std::unique_ptr deserialize_internal(const void *buf, size_t size); 96 | 97 | size_t size_internal() const noexcept override { return ipc::serialize_str(nullptr, m_arg.c_str(), m_arg.size()); } 98 | void serialize_internal(void *buf) const noexcept override { ipc::serialize_str(buf, m_arg.c_str(), m_arg.size()); } 99 | public: 100 | explicit Command_Args1_str(const std::string &arg) : Command{ Type }, m_arg{ arg } {} 101 | 102 | const std::string &arg() const { return m_arg; } 103 | 104 | friend std::unique_ptr (::ipc_client::deserialize_command)(const ipc::Command *command); 105 | }; 106 | 107 | template 108 | class Command_Args1_wstr : public Command { 109 | protected: 110 | std::wstring m_arg; 111 | 112 | template 113 | static std::unique_ptr deserialize_internal(const void *buf, size_t size); 114 | 115 | size_t size_internal() const noexcept override { return ipc::serialize_wstr(nullptr, m_arg.c_str(), m_arg.size()); } 116 | void serialize_internal(void *buf) const noexcept override { ipc::serialize_wstr(buf, m_arg.c_str(), m_arg.size()); } 117 | public: 118 | explicit Command_Args1_wstr(const std::wstring &arg) : Command{ Type }, m_arg{ arg } {} 119 | 120 | const std::wstring &arg() const { return m_arg; } 121 | 122 | friend std::unique_ptr (::ipc_client::deserialize_command)(const ipc::Command *command); 123 | }; 124 | 125 | template 126 | class Command_Args1_pod : public Command { 127 | protected: 128 | T m_arg; 129 | 130 | template 131 | static std::unique_ptr deserialize_internal(const void *buf, size_t size); 132 | 133 | size_t size_internal() const noexcept override { return sizeof(T); } 134 | void serialize_internal(void *buf) const noexcept override { *static_cast(buf) = m_arg; } 135 | public: 136 | explicit Command_Args1_pod(const T &arg) : Command{ Type }, m_arg(arg) {} 137 | 138 | const T &arg() const { return m_arg; } 139 | 140 | friend std::unique_ptr (::ipc_client::deserialize_command)(const ipc::Command *command); 141 | }; 142 | 143 | class CommandSetScriptVar : public Command { 144 | std::string m_name; 145 | ipc::Value m_value; 146 | protected: 147 | static std::unique_ptr deserialize_internal(const void *buf, size_t size); 148 | 149 | size_t size_internal() const noexcept override; 150 | void serialize_internal(void *buf) const noexcept override; 151 | public: 152 | CommandSetScriptVar(const std::string &name, const ipc::Value &value) : 153 | Command{ CommandType::SET_SCRIPT_VAR }, 154 | m_name{ name }, 155 | m_value{ value } 156 | {} 157 | 158 | ~CommandSetScriptVar() override; 159 | 160 | void deallocate_heap_resources(IPCClient *client) override; 161 | void relinquish_heap_resources() noexcept override; 162 | 163 | const std::string &name() const { return m_name; } 164 | const ipc::Value &value() const { return m_value; } 165 | 166 | friend std::unique_ptr (::ipc_client::deserialize_command)(const ipc::Command *command); 167 | }; 168 | 169 | class CommandEvalScript : public Command_Args1_pod { 170 | protected: 171 | static std::unique_ptr deserialize_internal(const void *buf, size_t size) 172 | { 173 | return Command_Args1_pod::deserialize_internal(buf, size); 174 | } 175 | public: 176 | using Command_Args1_pod::Command_Args1_pod; 177 | 178 | ~CommandEvalScript() override; 179 | 180 | void deallocate_heap_resources(IPCClient *client) override; 181 | void relinquish_heap_resources() noexcept override; 182 | 183 | friend std::unique_ptr(::ipc_client::deserialize_command)(const ipc::Command *command); 184 | }; 185 | 186 | class CommandSetFrame : public Command_Args1_pod { 187 | protected: 188 | static std::unique_ptr deserialize_internal(const void *buf, size_t size) 189 | { 190 | return Command_Args1_pod::deserialize_internal(buf, size); 191 | } 192 | public: 193 | using Command_Args1_pod::Command_Args1_pod; 194 | 195 | ~CommandSetFrame() override; 196 | 197 | void deallocate_heap_resources(IPCClient *client) override; 198 | void relinquish_heap_resources() noexcept override; 199 | 200 | friend std::unique_ptr(::ipc_client::deserialize_command)(const ipc::Command *command); 201 | }; 202 | 203 | } // namespace detail 204 | 205 | 206 | typedef detail::Command_Args0 CommandAck; 207 | typedef detail::Command_Args0 CommandErr; 208 | typedef detail::Command_Args1_wstr CommandSetLogFile; 209 | typedef detail::Command_Args1_wstr CommandLoadAvisynth; 210 | typedef detail::Command_Args0 CommandNewScriptEnv; 211 | typedef detail::Command_Args1_str CommandGetScriptVar; 212 | typedef detail::CommandSetScriptVar CommandSetScriptVar; 213 | typedef detail::CommandEvalScript CommandEvalScript; 214 | typedef detail::Command_Args1_pod CommandGetFrame; 215 | typedef detail::CommandSetFrame CommandSetFrame; 216 | 217 | class CommandObserver { 218 | protected: 219 | virtual int observe(std::unique_ptr c) { return 0; } 220 | virtual int observe(std::unique_ptr c) { return 0; } 221 | virtual int observe(std::unique_ptr c) { return 0; } 222 | virtual int observe(std::unique_ptr c) { return 0; } 223 | virtual int observe(std::unique_ptr c) { return 0; } 224 | virtual int observe(std::unique_ptr c) { return 0; } 225 | virtual int observe(std::unique_ptr c) { return 0; } 226 | virtual int observe(std::unique_ptr c) { return 0; } 227 | virtual int observe(std::unique_ptr c) { return 0; } 228 | virtual int observe(std::unique_ptr c) { return 0; } 229 | public: 230 | int dispatch(std::unique_ptr c); 231 | }; 232 | 233 | } // namespace ipc_client 234 | 235 | #endif // IPC_COMMANDS_H_ 236 | -------------------------------------------------------------------------------- /msvc/avsproxy/avsproxy.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {552A627B-59C7-44FA-96AA-110766783B83} 24 | avsproxy 25 | 10.0 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v143 32 | Unicode 33 | 34 | 35 | DynamicLibrary 36 | false 37 | v143 38 | true 39 | Unicode 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v143 45 | Unicode 46 | 47 | 48 | DynamicLibrary 49 | false 50 | v143 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | $(ProjectDir)..\..\libp2p;$(ProjectDir)..\..\vsxx;$(ProjectDir)..\..\vsxx\vapoursynth;$(ProjectDir)..\.. 80 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 81 | 82 | 83 | p2p_simd.lib;ipc64.lib;%(AdditionalDependencies) 84 | $(OutDir) 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | true 92 | true 93 | $(ProjectDir)..\..\libp2p;$(ProjectDir)..\..\vsxx;$(ProjectDir)..\..\vsxx\vapoursynth;$(ProjectDir)..\.. 94 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 95 | 96 | 97 | p2p_simd.lib;ipc64.lib;%(AdditionalDependencies) 98 | $(OutDir) 99 | 100 | 101 | 102 | 103 | Level3 104 | MaxSpeed 105 | true 106 | true 107 | true 108 | true 109 | $(ProjectDir)..\..\libp2p;$(ProjectDir)..\..\vsxx;$(ProjectDir)..\..\vsxx\vapoursynth;$(ProjectDir)..\.. 110 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 111 | 112 | 113 | true 114 | true 115 | p2p_simd.lib;ipc64.lib;%(AdditionalDependencies) 116 | $(OutDir) 117 | 118 | 119 | 120 | 121 | Level3 122 | MaxSpeed 123 | true 124 | true 125 | true 126 | true 127 | $(ProjectDir)..\..\libp2p;$(ProjectDir)..\..\vsxx;$(ProjectDir)..\..\vsxx\vapoursynth;$(ProjectDir)..\.. 128 | _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) 129 | 130 | 131 | true 132 | true 133 | p2p_simd.lib;ipc64.lib;%(AdditionalDependencies) 134 | $(OutDir) 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /ipc/ipc_commands.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ipc_client.h" 4 | #include "ipc_commands.h" 5 | #include "ipc_types.h" 6 | #include "logging.h" 7 | 8 | namespace ipc_client { 9 | 10 | namespace { 11 | 12 | template 13 | std::unique_ptr unique_ptr_cast(std::unique_ptr &&ptr) noexcept 14 | { 15 | return std::unique_ptr(static_cast(ptr.release())); 16 | } 17 | 18 | } // namespace 19 | 20 | 21 | namespace detail { 22 | 23 | void throw_deserialization_error(const char *msg) 24 | { 25 | throw IPCError{ msg }; 26 | } 27 | 28 | 29 | template 30 | template 31 | std::unique_ptr Command_Args1_str::deserialize_internal(const void *buf, size_t size) 32 | { 33 | size_t len = ipc::deserialize_str(nullptr, buf, size); 34 | if (len == ~static_cast(0)) 35 | throw_deserialization_error("buffer overrun"); 36 | 37 | std::string str(len, '\0'); 38 | ipc::deserialize_str(&str[0], buf, size); 39 | return std::make_unique(str); 40 | } 41 | 42 | 43 | template 44 | template 45 | std::unique_ptr Command_Args1_wstr::deserialize_internal(const void *buf, size_t size) 46 | { 47 | size_t len = ipc::deserialize_wstr(nullptr, buf, size); 48 | if (len == ~static_cast(0)) 49 | throw_deserialization_error("buffer overrun"); 50 | 51 | std::wstring str(len, '\0'); 52 | ipc::deserialize_wstr(&str[0], buf, size); 53 | return std::make_unique(str); 54 | } 55 | 56 | 57 | template 58 | template 59 | std::unique_ptr Command_Args1_pod::deserialize_internal(const void *buf, size_t size) 60 | { 61 | if (size < sizeof(T)) 62 | throw_deserialization_error("buffer overrun"); 63 | return std::make_unique(*static_cast(buf)); 64 | } 65 | 66 | 67 | std::unique_ptr CommandSetScriptVar::deserialize_internal(const void *buf, size_t size) 68 | { 69 | size_t len = ipc::deserialize_str(nullptr, buf, size); 70 | if (len == ~static_cast(0)) 71 | detail::throw_deserialization_error("buffer overrun"); 72 | 73 | std::string name(len, '\0'); 74 | ipc::deserialize_str(&name[0], buf, size); 75 | 76 | size_t consumed = ipc::serialize_str(nullptr, name.data(), name.size()); 77 | if (consumed % alignof(ipc::Value)) { 78 | size_t pad = alignof(ipc::Value) - consumed % alignof(ipc::Value); 79 | if (pad > size - consumed) 80 | detail::throw_deserialization_error("buffer overrun"); 81 | consumed += pad; 82 | } 83 | 84 | buf = static_cast(buf) + consumed; 85 | size -= consumed; 86 | 87 | ipc::Value value = *static_cast(buf); 88 | return std::make_unique(name, value); 89 | } 90 | 91 | CommandSetScriptVar::~CommandSetScriptVar() 92 | { 93 | if (m_value.type == ipc::Value::STRING && m_value.s != ipc::NULL_OFFSET) 94 | ipc_log("leaking heap allocation at %u", m_value.s); 95 | } 96 | 97 | size_t CommandSetScriptVar::size_internal() const noexcept 98 | { 99 | size_t size = ipc::serialize_str(nullptr, m_name.data(), m_name.size()); 100 | assert(size <= UINT32_MAX - sizeof(ipc::Value)); 101 | if (size % alignof(ipc::Value)) 102 | size += alignof(ipc::Value) - size % alignof(ipc::Value); 103 | return size + sizeof(ipc::Value); 104 | } 105 | 106 | void CommandSetScriptVar::serialize_internal(void *buf) const noexcept 107 | { 108 | size_t size = ipc::serialize_str(buf, m_name.data(), m_name.size()); 109 | if (size % alignof(ipc::Value)) 110 | size += alignof(ipc::Value) - size % alignof(ipc::Value); 111 | *reinterpret_cast(static_cast(buf) + size) = m_value; 112 | } 113 | 114 | void CommandSetScriptVar::deallocate_heap_resources(IPCClient *client) 115 | { 116 | if (m_value.type == ipc::Value::STRING) { 117 | client->deallocate(client->offset_to_pointer(m_value.s)); 118 | m_value.s = ipc::NULL_OFFSET; 119 | } 120 | } 121 | 122 | void CommandSetScriptVar::relinquish_heap_resources() noexcept 123 | { 124 | if (m_value.type == ipc::Value::STRING) 125 | m_value.s = ipc::NULL_OFFSET; 126 | } 127 | 128 | 129 | CommandEvalScript::~CommandEvalScript() 130 | { 131 | if (m_arg != ipc::NULL_OFFSET) 132 | ipc_log("leaking heap allocation at %u", m_arg); 133 | } 134 | 135 | void CommandEvalScript::deallocate_heap_resources(IPCClient *client) 136 | { 137 | client->deallocate(client->offset_to_pointer(m_arg)); 138 | m_arg = ipc::NULL_OFFSET; 139 | } 140 | 141 | void CommandEvalScript::relinquish_heap_resources() noexcept 142 | { 143 | m_arg = ipc::NULL_OFFSET; 144 | } 145 | 146 | 147 | CommandSetFrame::~CommandSetFrame() 148 | { 149 | if (m_arg.heap_offset != ipc::NULL_OFFSET) 150 | ipc_log("leaking heap allocation at %u", m_arg.heap_offset); 151 | } 152 | 153 | void CommandSetFrame::deallocate_heap_resources(IPCClient *client) 154 | { 155 | client->deallocate(client->offset_to_pointer(m_arg.heap_offset)); 156 | m_arg.heap_offset = ipc::NULL_OFFSET; 157 | } 158 | 159 | void CommandSetFrame::relinquish_heap_resources() noexcept 160 | { 161 | m_arg.heap_offset = ipc::NULL_OFFSET; 162 | } 163 | 164 | } // namespace detail 165 | 166 | 167 | void Command::deserialize_common(const ipc::Command *command) 168 | { 169 | m_transaction_id = command->transaction_id; 170 | m_response_id = command->response_id; 171 | } 172 | 173 | size_t Command::serialized_size() const noexcept 174 | { 175 | size_t internal_size = size_internal(); 176 | assert(internal_size <= UINT32_MAX); 177 | size_t size = sizeof(ipc::Command) + internal_size; 178 | assert(size <= UINT32_MAX); 179 | return size; 180 | } 181 | 182 | void Command::serialize(void *buf) const noexcept 183 | { 184 | ipc::Command *command = new (buf) ipc::Command{}; 185 | command->size = static_cast(serialized_size()); 186 | command->transaction_id = transaction_id(); 187 | command->response_id = response_id(); 188 | command->type = static_cast(type()); 189 | 190 | void *payload = ipc::offset_to_pointer(command, sizeof(ipc::Command)); 191 | serialize_internal(payload); 192 | } 193 | 194 | 195 | std::unique_ptr deserialize_command(const ipc::Command *command) 196 | { 197 | assert(ipc::check_fourcc(command->magic, "cmdx")); 198 | 199 | if (command->size < sizeof(ipc::Command)) 200 | detail::throw_deserialization_error("buffer overrun"); 201 | 202 | void *payload = ipc::offset_to_pointer(command, sizeof(ipc::Command)); 203 | size_t payload_size = command->size - sizeof(ipc::Command); 204 | 205 | std::unique_ptr deserialized; 206 | 207 | switch (static_cast(command->type)) { 208 | case CommandType::ACK: 209 | deserialized = std::make_unique(); 210 | break; 211 | case CommandType::ERR: 212 | deserialized = std::make_unique(); 213 | break; 214 | case CommandType::SET_LOG_FILE: 215 | deserialized = CommandSetLogFile::deserialize_internal(payload, payload_size); 216 | break; 217 | case CommandType::LOAD_AVISYNTH: 218 | deserialized = CommandLoadAvisynth::deserialize_internal(payload, payload_size); 219 | break; 220 | case CommandType::NEW_SCRIPT_ENV: 221 | deserialized = std::make_unique(); 222 | break; 223 | case CommandType::GET_SCRIPT_VAR: 224 | deserialized = CommandGetScriptVar::deserialize_internal(payload, payload_size); 225 | break; 226 | case CommandType::SET_SCRIPT_VAR: 227 | deserialized = CommandSetScriptVar::deserialize_internal(payload, payload_size); 228 | break; 229 | case CommandType::EVAL_SCRIPT: 230 | deserialized = CommandEvalScript::deserialize_internal(payload, payload_size); 231 | break; 232 | case CommandType::GET_FRAME: 233 | deserialized = CommandGetFrame::deserialize_internal(payload, payload_size); 234 | break; 235 | case CommandType::SET_FRAME: 236 | deserialized = CommandSetFrame::deserialize_internal(payload, payload_size); 237 | break; 238 | default: 239 | break; 240 | } 241 | 242 | if (deserialized) 243 | deserialized->deserialize_common(command); 244 | 245 | return deserialized; 246 | } 247 | 248 | 249 | int CommandObserver::dispatch(std::unique_ptr c) 250 | { 251 | switch (c->type()) { 252 | case CommandType::ACK: 253 | return observe(unique_ptr_cast(std::move(c))); 254 | case CommandType::ERR: 255 | return observe(unique_ptr_cast(std::move(c))); 256 | case CommandType::SET_LOG_FILE: 257 | return observe(unique_ptr_cast(std::move(c))); 258 | case CommandType::LOAD_AVISYNTH: 259 | return observe(unique_ptr_cast(std::move(c))); 260 | case CommandType::NEW_SCRIPT_ENV: 261 | return observe(unique_ptr_cast(std::move(c))); 262 | case CommandType::GET_SCRIPT_VAR: 263 | return observe(unique_ptr_cast(std::move(c))); 264 | case CommandType::SET_SCRIPT_VAR: 265 | return observe(unique_ptr_cast(std::move(c))); 266 | case CommandType::EVAL_SCRIPT: 267 | return observe(unique_ptr_cast(std::move(c))); 268 | case CommandType::GET_FRAME: 269 | return observe(unique_ptr_cast(std::move(c))); 270 | case CommandType::SET_FRAME: 271 | return observe(unique_ptr_cast(std::move(c))); 272 | default: 273 | return 0; 274 | } 275 | } 276 | 277 | } // namespace ipc_client 278 | -------------------------------------------------------------------------------- /ipc/ipc_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ipc_client.h" 11 | #include "ipc_commands.h" 12 | #include "ipc_types.h" 13 | #include "logging.h" 14 | 15 | namespace ipc_client { 16 | 17 | namespace { 18 | 19 | constexpr uint32_t QUEUE_SIZE = 4096; 20 | constexpr uint32_t SHMEM_SIZE = 256 * (1UL << 20); 21 | 22 | std::wstring create_slave_command(const std::wstring &slave_path, ::HANDLE shmem_handle, uint32_t shmem_size) 23 | { 24 | #define FORMAT L"\"%s\" %u %u %u", slave_path.c_str(), ::GetCurrentProcessId(), HandleToULong(shmem_handle), shmem_size 25 | if (slave_path.empty() || slave_path.find(L'"') != std::wstring::npos || slave_path.back() == L'/' || slave_path.back() == L'\\') 26 | throw IPCError{ "invalid characters in path" }; 27 | 28 | std::wstring cmd(MAX_PATH, L'\0'); 29 | 30 | while (std::swprintf(&cmd[0], cmd.size(), FORMAT) < 0) { 31 | cmd.resize(cmd.size() * 2); 32 | } 33 | 34 | return cmd; 35 | #undef FORMAT 36 | } 37 | 38 | void wait_remote_process_write(::HANDLE event, ::HANDLE process) 39 | { 40 | ::HANDLE handles[2] = { event, process }; 41 | ::DWORD result = ::WaitForMultipleObjects(sizeof(handles) / sizeof(::HANDLE), handles, FALSE, INFINITE); 42 | 43 | switch (result) { 44 | case WAIT_OBJECT_0: 45 | return; 46 | case WAIT_OBJECT_0 + 1: 47 | throw IPCError{ "remote process terminated unexpectedly" }; 48 | case WAIT_ABANDONED_0: 49 | case WAIT_ABANDONED_0 + 1: 50 | ::SetLastError(ERROR_ABANDONED_WAIT_0); 51 | win32::trap_error("remote process abandoned event"); 52 | break; 53 | case WAIT_TIMEOUT: 54 | ::SetLastError(ERROR_TIMEOUT); 55 | win32::trap_error(); 56 | break; 57 | case WAIT_FAILED: 58 | win32::trap_error("failed to wait for event"); 59 | break; 60 | default: 61 | ::SetLastError(ERROR_UNIDENTIFIED_ERROR); 62 | win32::trap_error("unknown error while waiting on event"); 63 | break; 64 | } 65 | } 66 | 67 | void print_heap(const ipc::Heap *heap) 68 | { 69 | const ipc::HeapNode *base = ipc::offset_to_pointer(heap, heap->buffer_offset); 70 | const ipc::HeapNode *node = base; 71 | 72 | while (true) { 73 | ipc_log("0x%08x - 0x%08x (%u): %s\n", 74 | ipc::pointer_to_offset(base, node), 75 | node->next_node_offset, 76 | node->next_node_offset - ipc::pointer_to_offset(base, node), 77 | (node->flags & ipc::HEAP_FLAG_ALLOCATED) ? "allocated" : "free"); 78 | if (node->next_node_offset == ipc::NULL_OFFSET) 79 | break; 80 | node = ipc::offset_to_pointer(base, node->next_node_offset); 81 | }; 82 | } 83 | 84 | } // namespace 85 | 86 | 87 | IPCClient::IPCClient(bool master) : 88 | m_master_queue{}, 89 | m_slave_queue{}, 90 | m_heap{}, 91 | m_remote_process{}, 92 | m_master{ master }, 93 | m_transaction_id{}, 94 | m_kill_flag{} 95 | {} 96 | 97 | IPCClient::IPCClient(master_tag, const wchar_t *slave_path) : IPCClient{ true } 98 | { 99 | ::SECURITY_ATTRIBUTES inheritable_attributes{ sizeof(::SECURITY_ATTRIBUTES), nullptr, TRUE }; 100 | 101 | // Allocate and map shared memory. 102 | ipc_log0("allocate shared memory\n"); 103 | 104 | m_shmem_handle.reset(::CreateFileMappingW(INVALID_HANDLE_VALUE, &inheritable_attributes, PAGE_READWRITE, 0, SHMEM_SIZE, nullptr)); 105 | if (!m_shmem_handle) 106 | win32::trap_error("error allocating IPC shared memory"); 107 | 108 | m_shmem.reset(::MapViewOfFile(m_shmem_handle.get().h, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, SHMEM_SIZE)); 109 | if (!m_shmem) 110 | win32::trap_error("error mapping shared memory"); 111 | 112 | // Create synchronization events. 113 | ipc_log0("initialize Win32 objects\n"); 114 | 115 | m_master_event.reset(::CreateEventW(&inheritable_attributes, FALSE, FALSE, nullptr)); 116 | if (!m_master_event) 117 | win32::trap_error("error creating synchronization object"); 118 | m_master_mutex.reset(::CreateMutexW(&inheritable_attributes, FALSE, nullptr)); 119 | if (!m_master_mutex) 120 | win32::trap_error("error creating synchronization object"); 121 | 122 | m_slave_event.reset(::CreateEventW(&inheritable_attributes, FALSE, FALSE, nullptr)); 123 | if (!m_slave_event) 124 | win32::trap_error("error creating synchronization object"); 125 | m_slave_mutex.reset(::CreateMutexW(&inheritable_attributes, FALSE, nullptr)); 126 | if (!m_slave_mutex) 127 | win32::trap_error("error creating synchronization object"); 128 | 129 | m_heap_mutex.reset(::CreateMutexW(&inheritable_attributes, FALSE, nullptr)); 130 | if (!m_heap_mutex) 131 | win32::trap_error("error creating synchronization object"); 132 | 133 | // Initialize IPC structures. 134 | ipc::SharedMemoryHeader *header = new (m_shmem.get()) ipc::SharedMemoryHeader{}; 135 | header->size = SHMEM_SIZE; 136 | 137 | m_master_queue = new (ipc::offset_to_pointer(header, sizeof(ipc::SharedMemoryHeader))) ipc::Queue{}; 138 | m_master_queue->size = QUEUE_SIZE; 139 | m_master_queue->event_handle = HandleToULong(m_master_event.get().h); 140 | m_master_queue->mutex_handle = HandleToULong(m_master_mutex.get().h); 141 | 142 | m_slave_queue = new (ipc::offset_to_pointer(m_master_queue, QUEUE_SIZE)) ipc::Queue{}; 143 | m_slave_queue->size = QUEUE_SIZE; 144 | m_slave_queue->event_handle = HandleToULong(m_slave_event.get().h); 145 | m_slave_queue->mutex_handle = HandleToULong(m_slave_mutex.get().h); 146 | 147 | m_heap = new (ipc::offset_to_pointer(m_slave_queue, QUEUE_SIZE)) ipc::Heap{}; 148 | m_heap->size = SHMEM_SIZE - ipc::pointer_to_offset(header, m_heap); 149 | m_heap->mutex_handle = HandleToULong(m_heap_mutex.get().h); 150 | 151 | new (ipc::offset_to_pointer(m_heap, m_heap->buffer_offset)) ipc::HeapNode{}; 152 | 153 | header->master_queue_offset = ipc::pointer_to_offset(header, m_master_queue); 154 | header->slave_queue_offset = ipc::pointer_to_offset(header, m_slave_queue); 155 | header->heap_offset = ipc::pointer_to_offset(header, m_heap); 156 | 157 | // Start slave process. 158 | std::wstring slave_command = create_slave_command(slave_path, m_shmem_handle.get().h, SHMEM_SIZE); 159 | ipc_wlog(L"start slave process: %s\n", slave_command.c_str()); 160 | 161 | ::STARTUPINFO startup_info{ sizeof(::STARTUPINFO) }; 162 | ::PROCESS_INFORMATION process_info{}; 163 | 164 | #ifdef _DEBUG 165 | #define FLAGS CREATE_NEW_CONSOLE 166 | #else 167 | #define FLAGS CREATE_NO_WINDOW 168 | #endif 169 | if (!::CreateProcessW(nullptr, &slave_command[0], nullptr, nullptr, TRUE, FLAGS, nullptr, nullptr, &startup_info, &process_info)) 170 | win32::trap_error("error starting slave process"); 171 | #undef FLAGS 172 | 173 | ipc_log("slave process pid: %u\n", process_info.dwProcessId); 174 | ::CloseHandle(process_info.hThread); 175 | m_remote_process = process_info.hProcess; 176 | } 177 | 178 | IPCClient::IPCClient(slave_tag, ::HANDLE master_process, ::HANDLE shmem_handle, size_t shmem_size) : IPCClient{ false } 179 | { 180 | ipc_log("open shared memory\n"); 181 | 182 | if (shmem_size < sizeof(ipc::SharedMemoryHeader)) 183 | throw IPCError{ "wrong shared memory size" }; 184 | 185 | m_shmem_handle.reset(shmem_handle); 186 | m_shmem.reset(::MapViewOfFile(m_shmem_handle.get().h, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, shmem_size)); 187 | if (!m_shmem) 188 | win32::trap_error("error mapping shared memory"); 189 | 190 | ipc::SharedMemoryHeader *header = static_cast(m_shmem.get()); 191 | if (!ipc::check_fourcc(header->magic, "avsw")) 192 | throw IPCError{ "bad header in shared memory" }; 193 | if (header->size != shmem_size) 194 | throw IPCError{ "wrong shared memory size" }; 195 | if (header->version != ipc::VERSION) 196 | throw IPCError{ "IPC version mismatch" }; 197 | if (header->slave_queue_offset > header->size - sizeof(ipc::Queue) || 198 | header->master_queue_offset > header->size - sizeof(ipc::Queue) || 199 | header->heap_offset > header->size - sizeof(ipc::Heap)) 200 | { 201 | throw IPCError{ "pointer out of bounds" }; 202 | } 203 | 204 | m_master_queue = ipc::offset_to_pointer(header, header->master_queue_offset); 205 | if (!ipc::check_fourcc(m_master_queue->magic, "cmdq")) 206 | throw IPCError{ "bad queue header" }; 207 | if (m_master_queue->size > header->size - header->master_queue_offset || 208 | m_master_queue->buffer_offset > m_master_queue->size - sizeof(ipc::Command)) 209 | { 210 | throw IPCError{ "pointer out of bounds" }; 211 | } 212 | 213 | m_slave_queue = ipc::offset_to_pointer(header, header->slave_queue_offset); 214 | if (!ipc::check_fourcc(m_slave_queue->magic, "cmdq")) 215 | throw IPCError{ "bad queue header" }; 216 | if (m_slave_queue->size > header->size - header->slave_queue_offset || 217 | m_slave_queue->buffer_offset > m_slave_queue->size - sizeof(ipc::Command)) 218 | { 219 | throw IPCError{ "pointer out of bounds" }; 220 | } 221 | 222 | m_heap = ipc::offset_to_pointer(header, header->heap_offset); 223 | if (!ipc::check_fourcc(m_heap->magic, "heap")) 224 | throw IPCError{ "bad heap header" }; 225 | if (m_heap->size > header->size - header->heap_offset || 226 | m_heap->buffer_offset > m_heap->size - sizeof(ipc::HeapNode)) 227 | { 228 | throw IPCError{ "pointer out of bounds" }; 229 | } 230 | 231 | m_master_event.reset(ULongToHandle(m_master_queue->event_handle)); 232 | m_master_mutex.reset(ULongToHandle(m_master_queue->mutex_handle)); 233 | m_slave_event.reset(ULongToHandle(m_slave_queue->event_handle)); 234 | m_slave_mutex.reset(ULongToHandle(m_slave_queue->mutex_handle)); 235 | m_heap_mutex.reset(ULongToHandle(m_heap->mutex_handle)); 236 | 237 | m_remote_process = master_process; 238 | } 239 | 240 | IPCClient::~IPCClient() 241 | { 242 | try { 243 | stop(); 244 | } catch (...) { 245 | ipc_log_current_exception(); 246 | } 247 | 248 | if (m_master) { 249 | ipc_log("terminate slave process\n"); 250 | ::Sleep(100); 251 | ::TerminateProcess(m_remote_process, 0); 252 | ::CloseHandle(m_remote_process); 253 | } 254 | } 255 | 256 | uint32_t IPCClient::next_transaction_id() 257 | { 258 | uint32_t transaction_id; 259 | 260 | do { 261 | transaction_id = m_transaction_id++; 262 | } while (transaction_id == INVALID_TRANSACTION); 263 | 264 | return transaction_id; 265 | } 266 | 267 | void IPCClient::recv_thread_func() 268 | { 269 | std::unique_ptr command; 270 | std::exception_ptr eptr; 271 | 272 | // Exception safety: exceptions on the receiver thread are session-fatal. Heap cleanup is not required. 273 | try { 274 | std::vector command_buf; 275 | command_buf.reserve(QUEUE_SIZE); 276 | 277 | while (true) { 278 | if (m_kill_flag) { 279 | ipc_log0("exit receiver thread after kill flag\n"); 280 | break; 281 | } 282 | 283 | wait_remote_process_write(recv_event(), m_remote_process); 284 | 285 | { 286 | win32::MutexGuard lock{ recv_mutex() }; 287 | command_buf.resize(recv_queue()->buffer_usage); 288 | ipc::queue_read(recv_queue(), command_buf.data()); 289 | } 290 | 291 | size_t pos = 0; 292 | while (pos < command_buf.size()) { 293 | if (command_buf.size() - pos < sizeof(ipc::Command)) 294 | throw IPCError{ "pointer out of bounds" }; 295 | 296 | const ipc::Command *raw_command = reinterpret_cast(command_buf.data() + pos); 297 | if (!ipc::check_fourcc(raw_command->magic, "cmdx")) 298 | throw IPCError{ "bad command header" }; 299 | if (raw_command->size > command_buf.size() - pos) 300 | throw IPCError{ "pointer out of bounds" }; 301 | 302 | ipc_log("received command type %d: %u => %u\n", raw_command->type, raw_command->response_id, raw_command->transaction_id); 303 | 304 | command = deserialize_command(raw_command); 305 | pos += raw_command->size; 306 | 307 | if (!command) { 308 | ipc_log0("failed to deserialize command\n"); 309 | 310 | if (raw_command->response_id != INVALID_TRANSACTION) { 311 | std::lock_guard lock{ m_worker_mutex }; 312 | auto it = m_callbacks.find(command->response_id()); 313 | 314 | if (it != m_callbacks.end()) { 315 | it->second(nullptr); 316 | m_callbacks.erase(it); 317 | } 318 | } 319 | 320 | continue; 321 | } 322 | 323 | callback_type callback; 324 | 325 | if (command->response_id() != INVALID_TRANSACTION) { 326 | std::lock_guard lock{ m_worker_mutex }; 327 | auto it = m_callbacks.find(command->response_id()); 328 | 329 | if (it != m_callbacks.end()) { 330 | callback = std::move(it->second); 331 | m_callbacks.erase(it); 332 | } 333 | } 334 | 335 | if (callback) { 336 | ipc_log("invoke callback for original transaction %u\n", command->response_id()); 337 | callback(std::move(command)); 338 | } else if (m_default_cb) { 339 | m_default_cb(std::move(command)); 340 | } else { 341 | // Cleanup orphaned command. 342 | command->deallocate_heap_resources(this); 343 | command = nullptr; 344 | } 345 | } 346 | } 347 | } catch (...) { 348 | ipc_log0("exit receiver thread after exception\n"); 349 | eptr = std::current_exception(); 350 | 351 | if (command) 352 | command->relinquish_heap_resources(); 353 | } 354 | 355 | // Record exception information and wake all waiters. 356 | std::lock_guard lock{ m_worker_mutex }; 357 | m_recv_exception = eptr; 358 | 359 | for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ) { 360 | it->second(nullptr); 361 | it = m_callbacks.erase(it); 362 | } 363 | if (m_default_cb) 364 | m_default_cb(nullptr); 365 | 366 | m_kill_flag = true; 367 | } 368 | 369 | void IPCClient::start(callback_type default_cb) 370 | { 371 | assert(!m_recv_thread); 372 | assert(!m_kill_flag); 373 | 374 | ::DWORD exit_code = STILL_ACTIVE; 375 | if (!::GetExitCodeProcess(m_remote_process, &exit_code)) 376 | win32::trap_error("error polling remote process"); 377 | if (exit_code != STILL_ACTIVE) 378 | throw IPCError{ "remote process exited" }; 379 | 380 | m_default_cb = std::move(default_cb); 381 | 382 | ipc_log0("start IPC receiver thread\n"); 383 | m_recv_thread = std::make_unique(&IPCClient::recv_thread_func, this); 384 | } 385 | 386 | void IPCClient::stop() 387 | { 388 | if (!m_recv_thread) 389 | return; 390 | 391 | ipc_log0("stop IPC receiver thread\n"); 392 | m_kill_flag = true; 393 | 394 | if (!::SetEvent(recv_event())) { 395 | try { 396 | win32::trap_error("error interrupting IPC receiver thread"); 397 | } catch (...) { 398 | ipc_log_current_exception(); 399 | std::terminate(); 400 | } 401 | } 402 | 403 | m_recv_thread->join(); 404 | m_recv_thread.reset(); 405 | m_callbacks.clear(); 406 | 407 | if (m_recv_exception) { 408 | ipc_log("rethrow exception from receiver thread\n"); 409 | std::exception_ptr eptr = m_recv_exception; 410 | m_recv_exception = nullptr; 411 | std::rethrow_exception(eptr); 412 | } 413 | } 414 | 415 | uint32_t IPCClient::pointer_to_offset(void *ptr) const 416 | { 417 | if (!ptr) 418 | return ipc::NULL_OFFSET; 419 | 420 | return ipc::pointer_to_offset(ipc::offset_to_pointer(m_heap, m_heap->buffer_offset), ptr); 421 | } 422 | 423 | void *IPCClient::offset_to_pointer(uint32_t off) const 424 | { 425 | if (off == ipc::NULL_OFFSET) 426 | return nullptr; 427 | 428 | if (off > m_heap->size - m_heap->buffer_offset) 429 | throw IPCError{ "pointer out of bounds" }; 430 | 431 | return ipc::offset_to_pointer(ipc::offset_to_pointer(m_heap, m_heap->buffer_offset), off); 432 | } 433 | 434 | void *IPCClient::allocate(size_t size) 435 | { 436 | if (size > static_cast(INT32_MAX)) 437 | throw IPCError{ "cannot allocate more than 2 GB" }; 438 | 439 | win32::MutexGuard lock{ m_heap_mutex.get().h }; 440 | ipc::HeapNode *node = ipc::heap_alloc(m_heap, static_cast(size)); 441 | if (!node) { 442 | ipc_log("heap full, could not allocate %zu bytes\n", size); 443 | print_heap(m_heap); 444 | throw IPCHeapFull{ (m_heap->size - m_heap->buffer_offset) - m_heap->buffer_usage, size }; 445 | } 446 | 447 | return ipc::offset_to_pointer(node, sizeof(ipc::HeapNode)); 448 | } 449 | 450 | void IPCClient::deallocate(void *ptr) 451 | { 452 | if (!ptr) 453 | return; 454 | 455 | ipc::HeapNode *node = reinterpret_cast(static_cast(ptr) - sizeof(ipc::HeapNode)); 456 | if (!ipc::check_fourcc(node->magic, "memz")) 457 | throw IPCError{ "pointer not a heap block" }; 458 | 459 | win32::MutexGuard lock{ m_heap_mutex.get().h }; 460 | ipc::heap_free(m_heap, node); 461 | } 462 | 463 | void IPCClient::send_async(std::unique_ptr command, callback_type cb) 464 | { 465 | uint32_t transaction_id = INVALID_TRANSACTION; 466 | 467 | if (m_kill_flag) { 468 | stop(); 469 | return; 470 | } 471 | 472 | // Exception safety: strong guarantee for exceptions prior to queue write. Callback is never invoked on exception. 473 | try { 474 | if (cb) { 475 | transaction_id = next_transaction_id(); 476 | command->set_transaction_id(transaction_id); 477 | 478 | std::lock_guard lock{ m_worker_mutex }; 479 | m_callbacks[transaction_id] = std::move(cb); 480 | } 481 | 482 | std::vector data(command->serialized_size()); 483 | command->serialize(data.data()); 484 | 485 | ipc_log("async send command type %d: %u\n", command->type(), transaction_id); 486 | { 487 | win32::MutexGuard lock_guard{ send_mutex() }; 488 | ipc::queue_write(send_queue(), data.data(), static_cast(data.size())); 489 | } 490 | } catch (...) { 491 | command->deallocate_heap_resources(this); 492 | 493 | if (transaction_id != INVALID_TRANSACTION) { 494 | try { 495 | std::lock_guard lock{ m_worker_mutex }; 496 | m_callbacks.erase(transaction_id); 497 | } catch (...) { 498 | std::terminate(); 499 | } 500 | } 501 | 502 | throw IPCError{ std::current_exception(), "error sending command" }; 503 | } 504 | 505 | // Command is visible to remote process. Heap can no longer be deallocated by client. 506 | command->relinquish_heap_resources(); 507 | 508 | // Exception safety: command already in-flight. Communication errors are session-fatal. 509 | try { 510 | if (!::SetEvent(send_event())) 511 | win32::trap_error("error setting event"); 512 | } catch (...) { 513 | ipc_log_current_exception(); 514 | stop(); 515 | throw; 516 | } 517 | } 518 | 519 | std::unique_ptr IPCClient::send_sync(std::unique_ptr command) 520 | { 521 | std::condition_variable cond; 522 | std::mutex mutex; 523 | 524 | assert(std::this_thread::get_id() != m_recv_thread->get_id()); 525 | ipc_log("sync send command type: %d\n", command->type()); 526 | 527 | std::unique_ptr result; 528 | bool called = false; 529 | 530 | callback_type func = [&](std::unique_ptr c) 531 | { 532 | { 533 | std::lock_guard lock{ mutex }; 534 | result = std::move(c); 535 | } 536 | called = true; 537 | cond.notify_all(); 538 | }; 539 | 540 | std::unique_lock lock{ mutex }; 541 | send_async(std::move(command), std::move(func)); 542 | cond.wait(lock, [&]() { return called; }); 543 | 544 | return result; 545 | } 546 | 547 | } // namespace ipc_client 548 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /avshost_native/avshost.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ipc/ipc_client.h" 10 | #include "ipc/ipc_types.h" 11 | #include "ipc/logging.h" 12 | #include "ipc/video_types.h" 13 | 14 | #ifdef AVISYNTH_PLUS 15 | #include 16 | #else 17 | #include "avisynth_2.6.h" 18 | #endif 19 | 20 | #include "avshost.h" 21 | 22 | const AVS_Linkage *AVS_linkage; 23 | bool g_avisynth_plus; 24 | 25 | namespace avs { 26 | 27 | namespace { 28 | 29 | constexpr size_t MAX_STR_LEN = 1UL << 20; 30 | 31 | 32 | bool is_avisynth_plus() 33 | { 34 | ::VideoInfo vi{}; 35 | vi.pixel_type = -536805376; // CS_Y16 36 | return vi.BitsPerPixel() == 16; 37 | } 38 | 39 | char *save_string(::IScriptEnvironment *env, const std::string &s) 40 | { 41 | return env->SaveString(s.c_str(), static_cast(s.size())); 42 | } 43 | 44 | std::string heap_to_local_str(ipc_client::IPCClient *client, uint32_t offset) 45 | { 46 | void *ptr = client->offset_to_pointer(offset); 47 | if (!ptr) 48 | return ""; 49 | 50 | size_t len = ipc::deserialize_str(nullptr, ptr, -1); 51 | if (len > MAX_STR_LEN) 52 | throw AvisynthError_{ "string too long" }; 53 | 54 | std::string ret(len, '\0'); 55 | ipc::deserialize_str(&ret[0], ptr, -1); 56 | return ret; 57 | } 58 | 59 | uint32_t local_to_heap_str(ipc_client::IPCClient *client, const char *str, size_t len) 60 | { 61 | if (len > MAX_STR_LEN) 62 | throw AvisynthError_{ "string too long" }; 63 | 64 | size_t size = ipc::serialize_str(nullptr, str, len); 65 | void *ptr = client->allocate(size); 66 | ipc::serialize_str(ptr, str, len); 67 | return client->pointer_to_offset(ptr); 68 | } 69 | 70 | ::VideoInfo deserialize_video_info(const ipc::VideoInfo &ipc_vi) 71 | { 72 | ::VideoInfo vi{}; 73 | 74 | vi.width = ipc_vi.width; 75 | vi.height = ipc_vi.height; 76 | vi.fps_numerator = ipc_vi.fps_num; 77 | vi.fps_denominator = ipc_vi.fps_den; 78 | 79 | switch (ipc_vi.color_family) { 80 | case ipc::VideoInfo::YUV: 81 | if (ipc_vi.subsample_w == 0 && ipc_vi.subsample_h == 0) 82 | vi.pixel_type = ::VideoInfo::CS_YV24; 83 | else if (ipc_vi.subsample_w == 1 && ipc_vi.subsample_h == 0) 84 | vi.pixel_type = ::VideoInfo::CS_YV16; 85 | else if (ipc_vi.subsample_w == 1 && ipc_vi.subsample_h == 1) 86 | vi.pixel_type = ::VideoInfo::CS_YV12; 87 | else if (ipc_vi.subsample_w == 2 && ipc_vi.subsample_h == 0) 88 | vi.pixel_type = ::VideoInfo::CS_YV411; 89 | else 90 | throw AvisynthError_{ "color format not supported" }; 91 | break; 92 | case ipc::VideoInfo::GRAY: 93 | vi.pixel_type = ::VideoInfo::CS_Y8; 94 | break; 95 | case ipc::VideoInfo::RGB24: 96 | vi.pixel_type = ::VideoInfo::CS_BGR24; 97 | break; 98 | case ipc::VideoInfo::RGB32: 99 | vi.pixel_type = ::VideoInfo::CS_BGR32; 100 | break; 101 | case ipc::VideoInfo::YUY2: 102 | vi.pixel_type = ::VideoInfo::CS_YUY2; 103 | break; 104 | case ipc::VideoInfo::RGB: 105 | default: 106 | throw AvisynthError_{ "color format not supported" }; 107 | } 108 | 109 | vi.num_frames = ipc_vi.num_frames; 110 | return vi; 111 | } 112 | 113 | ipc::VideoInfo serialize_video_info(const ::VideoInfo &vi) 114 | { 115 | ipc::VideoInfo ipc_vi{}; 116 | 117 | ipc_vi.width = vi.width; 118 | ipc_vi.height = vi.height; 119 | ipc_vi.fps_num = vi.fps_numerator; 120 | ipc_vi.fps_den = vi.fps_denominator; 121 | ipc_vi.num_frames = vi.num_frames; 122 | 123 | if (vi.IsRGB24()) { 124 | ipc_vi.color_family = ipc::VideoInfo::RGB24; 125 | } else if (vi.IsRGB32()) { 126 | ipc_vi.color_family = ipc::VideoInfo::RGB32; 127 | } else if (vi.IsYUY2()) { 128 | ipc_vi.color_family = ipc::VideoInfo::YUY2; 129 | } else if (vi.IsY8()) { 130 | ipc_vi.color_family = ipc::VideoInfo::GRAY; 131 | } else if (vi.IsPlanar()) { 132 | ipc_vi.color_family = ipc::VideoInfo::YUV; 133 | 134 | if (vi.IsYV24()) { 135 | ipc_vi.subsample_w = 0; 136 | ipc_vi.subsample_h = 0; 137 | } else if (vi.IsYV16()) { 138 | ipc_vi.subsample_w = 1; 139 | ipc_vi.subsample_h = 0; 140 | } else if (vi.IsYV12()) { 141 | ipc_vi.subsample_w = 1; 142 | ipc_vi.subsample_h = 1; 143 | } else if (vi.IsYV411()) { 144 | ipc_vi.subsample_w = 2; 145 | ipc_vi.subsample_h = 0; 146 | } else { 147 | throw AvisynthError_{ "color format not supported" }; 148 | } 149 | } else { 150 | throw AvisynthError_{ "color format not supported" }; 151 | } 152 | 153 | return ipc_vi; 154 | } 155 | 156 | ::PVideoFrame heap_to_local_frame(ipc_client::IPCClient *client, const ::VideoInfo &vi, const ipc::VideoFrame &ipc_frame, ::IScriptEnvironment *env) 157 | { 158 | constexpr int plane_order[3] = { PLANAR_Y, PLANAR_U, PLANAR_V }; 159 | 160 | void *heap_ptr = client->offset_to_pointer(ipc_frame.heap_offset); 161 | if (!heap_ptr) 162 | throw AvisynthError_{ "missing frame data" }; 163 | 164 | int num_planes = vi.IsPlanar() && !vi.IsY8() ? 3 : 1; 165 | 166 | for (int p = 0; p < num_planes; ++p) { 167 | if (vi.RowSize(plane_order[p]) > ipc_frame.stride[p]) 168 | throw AvisynthError_{ "wrong width" }; 169 | if (ipc_frame.height[p] != vi.height >> (p ? vi.GetPlaneHeightSubsampling(plane_order[p]) : 0)) 170 | throw AvisynthError_{ "wrong height" }; 171 | } 172 | 173 | const unsigned char *src_ptr = static_cast(heap_ptr); 174 | ::PVideoFrame frame = env->NewVideoFrame(vi); 175 | 176 | for (int p = 0; p < num_planes; ++p) { 177 | int avs_plane = plane_order[p]; 178 | 179 | env->BitBlt(frame->GetWritePtr(avs_plane), frame->GetPitch(avs_plane), src_ptr, ipc_frame.stride[p], frame->GetRowSize(avs_plane), frame->GetHeight(avs_plane)); 180 | src_ptr += ipc_frame.stride[p] * ipc_frame.height[p]; 181 | } 182 | 183 | return frame; 184 | } 185 | 186 | ipc::VideoFrame local_to_heap_frame(ipc_client::IPCClient *client, uint32_t clip_id, int32_t n, const ::VideoInfo &vi, const ::PVideoFrame &frame, ::IScriptEnvironment *env) 187 | { 188 | constexpr int plane_order[3] = { PLANAR_Y, PLANAR_U, PLANAR_V }; 189 | 190 | ipc::VideoFrame ipc_frame{ { clip_id, n } }; 191 | size_t size = 0; 192 | 193 | int num_planes = vi.IsPlanar() && !vi.IsY8() ? 3 : 1; 194 | 195 | for (int p = 0; p < num_planes; ++p) { 196 | int rowsize = vi.RowSize(plane_order[p]); 197 | ipc_frame.stride[p] = rowsize % 64 ? rowsize + 64 - rowsize % 64 : rowsize; 198 | ipc_frame.height[p] = frame->GetHeight(plane_order[p]); 199 | 200 | size += ipc_frame.stride[p] * ipc_frame.height[p]; 201 | } 202 | 203 | unsigned char *dst_ptr = static_cast(client->allocate(size)); 204 | ipc_frame.heap_offset = client->pointer_to_offset(dst_ptr); 205 | 206 | for (int p = 0; p < num_planes; ++p) { 207 | int avs_plane = plane_order[p]; 208 | env->BitBlt(dst_ptr, ipc_frame.stride[p], frame->GetReadPtr(avs_plane), frame->GetPitch(avs_plane), frame->GetRowSize(avs_plane), frame->GetHeight(avs_plane)); 209 | dst_ptr += ipc_frame.stride[p] * ipc_frame.height[p]; 210 | } 211 | 212 | return ipc_frame; 213 | } 214 | 215 | } // namespace 216 | 217 | 218 | class Cache { 219 | static constexpr size_t MEMORY_MAX = 8 * (1 << 20UL); 220 | 221 | std::deque> m_cache; 222 | size_t m_memory_usage; 223 | public: 224 | Cache() : m_memory_usage{} {} 225 | 226 | void insert(uint32_t clip_id, int n, ::PVideoFrame frame) 227 | { 228 | size_t size = frame->GetFrameBuffer()->GetDataSize(); 229 | 230 | if (size > MEMORY_MAX) 231 | return; 232 | 233 | while (MEMORY_MAX - m_memory_usage < size) { 234 | ::PVideoFrame frame = std::get<2>(m_cache.back()); 235 | m_cache.pop_back(); 236 | m_memory_usage -= frame->GetFrameBuffer()->GetDataSize(); 237 | } 238 | 239 | m_cache.emplace_back(clip_id, n, frame); 240 | m_memory_usage += size; 241 | } 242 | 243 | ::PVideoFrame find(uint32_t clip_id, int n) 244 | { 245 | auto it = std::find_if(m_cache.begin(), m_cache.end(), [=](const std::tuple &x) 246 | { 247 | return std::get<0>(x) == clip_id && std::get<1>(x) == n; 248 | }); 249 | 250 | if (it == m_cache.end()) 251 | return nullptr; 252 | 253 | auto val = *it; 254 | m_cache.erase(it); 255 | m_cache.emplace_front(val); 256 | return std::get<2>(val); 257 | } 258 | }; 259 | 260 | 261 | class VirtualClip : public ::IClip { 262 | ipc_client::IPCClient *m_client; 263 | Cache *m_cache; 264 | uint32_t m_clip_id; 265 | ::VideoInfo m_vi; 266 | public: 267 | VirtualClip(ipc_client::IPCClient *client, Cache *cache, uint32_t clip_id, const ::VideoInfo &vi) : 268 | m_client{ client }, 269 | m_cache{ cache }, 270 | m_clip_id{ clip_id }, 271 | m_vi(vi) 272 | {} 273 | 274 | ::PVideoFrame __stdcall GetFrame(int n, ::IScriptEnvironment *env) override 275 | { 276 | ::PVideoFrame frame = m_cache->find(m_clip_id, n); 277 | 278 | if (!frame) { 279 | ipc_log("clip %u frame %d not prefetched\n", m_clip_id, n); 280 | 281 | auto response = m_client->send_sync(std::make_unique(ipc::VideoFrameRequest{ m_clip_id, n })); 282 | try { 283 | if (!response || response->type() != ipc_client::CommandType::SET_FRAME) 284 | env->ThrowError("remote get frame failed"); 285 | 286 | ipc_client::CommandSetFrame *set_frame = static_cast(response.get()); 287 | if (set_frame->arg().request.clip_id != m_clip_id || set_frame->arg().request.frame_number != n) 288 | env->ThrowError("remote get frame returned wrong frame"); 289 | 290 | frame = heap_to_local_frame(m_client, m_vi, set_frame->arg(), env); 291 | m_cache->insert(m_clip_id, n, frame); 292 | } catch (...) { 293 | response->deallocate_heap_resources(m_client); 294 | throw; 295 | } 296 | 297 | response->deallocate_heap_resources(m_client); 298 | } 299 | 300 | return frame; 301 | } 302 | 303 | bool __stdcall GetParity(int n) override { return false; } 304 | void __stdcall GetAudio(void *, __int64, __int64, ::IScriptEnvironment *) override {} 305 | int __stdcall SetCacheHints(int, int) override { return 0; } 306 | const ::VideoInfo & __stdcall GetVideoInfo() override { return m_vi; } 307 | }; 308 | 309 | 310 | void AvisynthHost::IScriptEnvironmentDeleter::operator()(::IScriptEnvironment *env) 311 | { 312 | env->DeleteScriptEnvironment(); 313 | } 314 | 315 | 316 | AvisynthHost::PClip_::PClip_() : m_val{} 317 | { 318 | static_assert(sizeof(PClip_) == sizeof(PClip), "wrong size"); 319 | static_assert(alignof(PClip_) == alignof(PClip), "wrong alignment"); 320 | new (m_val.x) ::PClip{}; 321 | } 322 | 323 | AvisynthHost::PClip_::PClip_(const ::PClip &p) : m_val{} 324 | { 325 | new (m_val.x) ::PClip{ p }; 326 | } 327 | 328 | AvisynthHost::PClip_::PClip_(const PClip_ &other) : m_val{} 329 | { 330 | new (m_val.x) ::PClip{ other.get() }; 331 | } 332 | 333 | AvisynthHost::PClip_::~PClip_() { get().~PClip(); } 334 | 335 | AvisynthHost::PClip_ &AvisynthHost::PClip_::operator=(const AvisynthHost::PClip_ &other) 336 | { 337 | get().~PClip(); 338 | new (m_val.x) ::PClip{ other.get() }; 339 | return *this; 340 | } 341 | 342 | 343 | AvisynthHost::AvisynthHost(ipc_client::IPCClient *client) : 344 | m_client{ client }, 345 | m_create_script_env{}, 346 | m_local_clip_id{} 347 | {} 348 | 349 | AvisynthHost::~AvisynthHost() = default; 350 | 351 | #define CHECK_AVS_LOADED(c) \ 352 | do { \ 353 | if (!m_library) { \ 354 | ipc_log("received command type %d before Avisynth loaded\n", c->type()); \ 355 | if (c->transaction_id()) \ 356 | send_err(c->transaction_id()); \ 357 | c->deallocate_heap_resources(m_client); \ 358 | return 1; \ 359 | } \ 360 | } while (0) 361 | 362 | #define COMMAND_EX_BEGIN try { 363 | 364 | #define AVS_EX_BEGIN try { 365 | 366 | #define AVS_EX_END \ 367 | } catch (const ::AvisynthError &e) { \ 368 | throw AvisynthError_{ e.msg }; \ 369 | } catch (const ::IScriptEnvironment::NotFound &) { \ 370 | throw AvisynthError_{ "function or variable not defined"}; \ 371 | } 372 | 373 | #define COMMAND_EX_END \ 374 | } catch (...) { \ 375 | c->deallocate_heap_resources(m_client); \ 376 | throw; \ 377 | } \ 378 | c->deallocate_heap_resources(m_client); \ 379 | c.reset(); 380 | 381 | int AvisynthHost::observe(std::unique_ptr c) 382 | { 383 | if (m_library) { 384 | ipc_log0("Avisynth already loaded\n"); 385 | 386 | if (c->transaction_id()) 387 | send_err(c->transaction_id()); 388 | 389 | c->deallocate_heap_resources(m_client); 390 | return 1; 391 | } 392 | 393 | ipc_wlog("load avisynth DLL from '%s'\n", c->arg().c_str()); 394 | 395 | COMMAND_EX_BEGIN 396 | m_library.reset(::LoadLibraryW(c->arg().empty() ? L"avisynth" : c->arg().c_str())); 397 | COMMAND_EX_END 398 | 399 | if (!m_library) 400 | win32::trap_error("error loading avisynth library"); 401 | 402 | FARPROC proc = ::GetProcAddress(m_library.get(), "CreateScriptEnvironment"); 403 | if (!proc) { 404 | m_library.reset(); 405 | win32::trap_error("entry point not found"); 406 | } 407 | 408 | m_create_script_env = reinterpret_cast(proc); 409 | 410 | try { 411 | AVS_EX_BEGIN 412 | m_env.reset(m_create_script_env(AVISYNTH_INTERFACE_VERSION)); 413 | AVS_linkage = m_env->GetAVSLinkage(); 414 | g_avisynth_plus = is_avisynth_plus(); 415 | AVS_EX_END 416 | 417 | if (!m_env) 418 | throw AvisynthError_{ "avisynth library has incompatible interface version" }; 419 | 420 | m_cache = std::make_unique(); 421 | } catch (...) { 422 | m_library.reset(); 423 | m_create_script_env = nullptr; 424 | throw; 425 | } 426 | 427 | return 0; 428 | } 429 | 430 | int AvisynthHost::observe(std::unique_ptr c) 431 | { 432 | CHECK_AVS_LOADED(c); 433 | c->deallocate_heap_resources(m_client); 434 | c.reset(); 435 | 436 | ipc_log0("new script env\n"); 437 | 438 | AVS_EX_BEGIN 439 | std::unique_ptr<::IScriptEnvironment, IScriptEnvironmentDeleter> env{ m_create_script_env(6) }; 440 | if (!env) 441 | throw AvisynthError_{ "avisynth library has incompatible interface version" }; 442 | 443 | m_local_clips.clear(); 444 | m_remote_clips.clear(); 445 | m_cache.reset(); 446 | 447 | m_env = std::move(env); 448 | AVS_linkage = m_env->GetAVSLinkage(); 449 | g_avisynth_plus = is_avisynth_plus(); 450 | m_cache = std::make_unique(); 451 | AVS_EX_END 452 | 453 | return 0; 454 | } 455 | 456 | int AvisynthHost::observe(std::unique_ptr c) 457 | { 458 | CHECK_AVS_LOADED(c); 459 | 460 | ipc_log("get script var '%s'\n", c->arg()); 461 | 462 | COMMAND_EX_BEGIN 463 | AVS_EX_BEGIN 464 | ::AVSValue result = m_env->GetVar(c->arg().c_str()); 465 | if (!result.Defined()) 466 | throw IScriptEnvironment::NotFound{}; 467 | send_avsvalue(c->transaction_id(), result); 468 | AVS_EX_END 469 | COMMAND_EX_END 470 | 471 | return 1; 472 | } 473 | 474 | int AvisynthHost::observe(std::unique_ptr c) 475 | { 476 | CHECK_AVS_LOADED(c); 477 | 478 | ipc_log("set script var '%s'\n", c->name().c_str()); 479 | 480 | COMMAND_EX_BEGIN 481 | AVS_EX_BEGIN 482 | switch (c->value().type) { 483 | case ipc::Value::CLIP: 484 | { 485 | const ipc::VideoInfo &vi = c->value().c.vi; 486 | 487 | ipc_log("remote clip %u: %dx%d %d/%d/%d\n", 488 | c->value().c.clip_id, vi.width, vi.height, vi.color_family, vi.subsample_w, vi.subsample_h); 489 | 490 | PClip clip = new VirtualClip{ m_client, m_cache.get(), c->value().c.clip_id, deserialize_video_info(vi) }; 491 | m_env->SetVar(save_string(m_env.get(), c->name()), clip); 492 | m_remote_clips[c->value().c.clip_id] = clip; 493 | break; 494 | } 495 | case ipc::Value::BOOL_: 496 | m_env->SetVar(save_string(m_env.get(), c->name()), c->value().b); 497 | break; 498 | case ipc::Value::INT: 499 | m_env->SetVar(save_string(m_env.get(), c->name()), static_cast(c->value().i)); 500 | break; 501 | case ipc::Value::FLOAT: 502 | m_env->SetVar(save_string(m_env.get(), c->name()), static_cast(c->value().f)); 503 | break; 504 | case ipc::Value::STRING: 505 | m_env->SetVar(save_string(m_env.get(), c->name()), save_string(m_env.get(), heap_to_local_str(m_client, c->value().s))); 506 | break; 507 | default: 508 | throw AvisynthError_{ "unsupported variable type" }; 509 | } 510 | AVS_EX_END 511 | COMMAND_EX_END 512 | 513 | return 0; 514 | } 515 | 516 | int AvisynthHost::observe(std::unique_ptr c) 517 | { 518 | CHECK_AVS_LOADED(c); 519 | 520 | COMMAND_EX_BEGIN 521 | AVS_EX_BEGIN 522 | std::string script = heap_to_local_str(m_client, c->arg()); 523 | 524 | ipc_log0("begin eval script\n"); 525 | (ipc_log)("%s", script.c_str()); 526 | ipc_log0("end eval script\n"); 527 | 528 | ::AVSValue result = m_env->Invoke("Eval", save_string(m_env.get(), script)); 529 | send_avsvalue(c->transaction_id(), result); 530 | AVS_EX_END 531 | COMMAND_EX_END 532 | 533 | return 1; 534 | } 535 | 536 | int AvisynthHost::observe(std::unique_ptr c) 537 | { 538 | CHECK_AVS_LOADED(c); 539 | 540 | ipc_log("GetFrame clip %u frame %u\n", c->arg().clip_id, c->arg().frame_number); 541 | 542 | COMMAND_EX_BEGIN 543 | auto it = m_local_clips.find(c->arg().clip_id); 544 | if (it == m_local_clips.end()) { 545 | ipc_log0("invalid local clip id\n"); 546 | send_err(c->transaction_id()); 547 | return 1; 548 | } 549 | 550 | AVS_EX_BEGIN 551 | const ::PClip &clip = it->second.get(); 552 | ::PVideoFrame frame = clip->GetFrame(c->arg().frame_number, m_env.get()); 553 | ipc::VideoFrame ipc_frame = local_to_heap_frame(m_client, c->arg().clip_id, c->arg().frame_number, clip->GetVideoInfo(), frame, m_env.get()); 554 | std::unique_ptr result; 555 | 556 | try { 557 | result = std::make_unique(ipc_frame); 558 | ipc_frame.heap_offset = ipc::NULL_OFFSET; 559 | } catch (...) { 560 | result->deallocate_heap_resources(m_client); 561 | m_client->deallocate(m_client->offset_to_pointer(ipc_frame.heap_offset)); 562 | throw; 563 | } 564 | 565 | if (c->transaction_id() != ipc_client::INVALID_TRANSACTION) 566 | result->set_response_id(c->transaction_id()); 567 | 568 | m_client->send_async(std::move(result)); 569 | AVS_EX_END 570 | COMMAND_EX_END 571 | 572 | return 1; 573 | } 574 | 575 | int AvisynthHost::observe(std::unique_ptr c) 576 | { 577 | CHECK_AVS_LOADED(c); 578 | 579 | ipc_log("SetFrame clip %u frame %u\n", c->arg().request.clip_id, c->arg().request.frame_number); 580 | 581 | auto it = m_remote_clips.find(c->arg().request.clip_id); 582 | if (it == m_remote_clips.end()) { 583 | ipc_log0("invalid remote clip id\n"); 584 | send_err(c->transaction_id()); 585 | c->deallocate_heap_resources(m_client); 586 | return 1; 587 | } 588 | 589 | COMMAND_EX_BEGIN 590 | AVS_EX_BEGIN 591 | VirtualClip *clip = static_cast(it->second.get().operator void *()); 592 | ::PVideoFrame frame = heap_to_local_frame(m_client, clip->GetVideoInfo(), c->arg(), m_env.get()); 593 | m_cache->insert(c->arg().request.clip_id, c->arg().request.frame_number, frame); 594 | AVS_EX_END 595 | COMMAND_EX_END 596 | 597 | return 0; 598 | } 599 | 600 | void AvisynthHost::send_avsvalue(uint32_t response_id, const ::AVSValue &avs_value) 601 | { 602 | if (response_id == ipc_client::INVALID_TRANSACTION) 603 | return; 604 | 605 | ipc::Value value{}; 606 | 607 | if (avs_value.IsClip()) { 608 | ::PClip clip = avs_value.AsClip(); 609 | const ::VideoInfo &vi = clip->GetVideoInfo(); 610 | uint32_t clip_id = m_local_clip_id++; 611 | ipc_log("local clip %u: %dx%d %d\n", clip_id, vi.width, vi.height, vi.pixel_type); 612 | 613 | value.type = ipc::Value::CLIP; 614 | value.c.clip_id = clip_id; 615 | value.c.vi = serialize_video_info(vi); 616 | m_local_clips[clip_id] = clip; 617 | } else if (avs_value.IsBool()) { 618 | value.type = ipc::Value::BOOL_; 619 | value.b = avs_value.AsBool(); 620 | } else if (avs_value.IsInt()) { 621 | value.type = ipc::Value::INT; 622 | value.i = avs_value.AsInt(); 623 | } else if (avs_value.IsFloat()) { 624 | value.type = ipc::Value::FLOAT; 625 | value.f = avs_value.AsFloat(); 626 | } else if (avs_value.IsString()) { 627 | const char *str = avs_value.AsString(); 628 | value.type = ipc::Value::STRING; 629 | value.s = local_to_heap_str(m_client, str, std::strlen(str)); 630 | } 631 | 632 | std::unique_ptr response; 633 | 634 | try { 635 | response = std::make_unique("", value); 636 | } catch (...) { 637 | if (value.type == ipc::Value::STRING) 638 | m_client->deallocate(m_client->offset_to_pointer(value.s)); 639 | throw; 640 | } 641 | 642 | response->set_response_id(response_id); 643 | m_client->send_async(std::move(response)); 644 | } 645 | 646 | void AvisynthHost::send_err(uint32_t response_id) 647 | { 648 | if (response_id == ipc_client::INVALID_TRANSACTION) 649 | return; 650 | 651 | auto response = std::make_unique(); 652 | response->set_response_id(response_id); 653 | m_client->send_async(std::move(response)); 654 | } 655 | 656 | } // namespace avs 657 | -------------------------------------------------------------------------------- /avsproxy/avsproxy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ipc/ipc_client.h" 11 | #include "ipc/ipc_commands.h" 12 | #include "ipc/ipc_types.h" 13 | #include "ipc/video_types.h" 14 | #include "ipc/win32util.h" 15 | #include "p2p_api.h" 16 | #include "VSHelper4.h" 17 | #include "vsxx4_pluginmain.h" 18 | 19 | using namespace vsxx4; 20 | 21 | namespace { 22 | 23 | constexpr char PLUGIN_ID[] = "xxx.abc.avsproxy"; 24 | 25 | constexpr size_t MAX_STR_LEN = 1UL << 20; 26 | 27 | 28 | std::wstring utf8_to_utf16(const std::string &s) 29 | { 30 | if (s.empty()) 31 | return L""; 32 | 33 | int required = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast(s.size()), nullptr, 0); 34 | if (required <= 0) 35 | win32::trap_error("UTF-8 decoding error"); 36 | 37 | std::wstring ws(required, L'\0'); 38 | ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast(s.size()), &ws[0], required); 39 | return ws; 40 | } 41 | 42 | std::string heap_to_local_str(ipc_client::IPCClient *client, uint32_t offset) 43 | { 44 | void *ptr = client->offset_to_pointer(offset); 45 | if (!ptr) 46 | return ""; 47 | 48 | size_t len = ipc::deserialize_str(nullptr, ptr, -1); 49 | if (len > MAX_STR_LEN) 50 | throw std::runtime_error{ "string too long" }; 51 | 52 | std::string ret(len, '\0'); 53 | ipc::deserialize_str(&ret[0], ptr, -1); 54 | return ret; 55 | } 56 | 57 | uint32_t local_to_heap_str(ipc_client::IPCClient *client, const char *str, size_t len) 58 | { 59 | if (len > MAX_STR_LEN) 60 | throw std::runtime_error{ "string too long" }; 61 | 62 | size_t size = ipc::serialize_str(nullptr, str, len); 63 | void *ptr = client->allocate(size); 64 | ipc::serialize_str(ptr, str, len); 65 | return client->pointer_to_offset(ptr); 66 | } 67 | 68 | ::VSVideoInfo deserialize_video_info(const ipc::VideoInfo &ipc_vi, const Core &core) 69 | { 70 | ::VSVideoInfo vi{}; 71 | 72 | vi.fpsNum = ipc_vi.fps_num; 73 | vi.fpsDen = ipc_vi.fps_den; 74 | vi.width = ipc_vi.width; 75 | vi.height = ipc_vi.height; 76 | vi.numFrames = ipc_vi.num_frames; 77 | 78 | vsh::reduceRational(&vi.fpsNum, &vi.fpsDen); 79 | 80 | switch (ipc_vi.color_family) { 81 | case ipc::VideoInfo::RGB: 82 | vi.format = core.query_video_format(::cfRGB, ::stInteger, 8, ipc_vi.subsample_w, ipc_vi.subsample_h); 83 | break; 84 | case ipc::VideoInfo::YUV: 85 | vi.format = core.query_video_format(::cfYUV, ::stInteger, 8, ipc_vi.subsample_w, ipc_vi.subsample_h); 86 | break; 87 | case ipc::VideoInfo::GRAY: 88 | vi.format = core.get_video_format_by_id(::pfGray8); 89 | break; 90 | case ipc::VideoInfo::RGB24: 91 | case ipc::VideoInfo::RGB32: 92 | vi.format = core.get_video_format_by_id(::pfRGB24); 93 | break; 94 | case ipc::VideoInfo::YUY2: 95 | vi.format = core.get_video_format_by_id(::pfYUV422P8); 96 | break; 97 | default: 98 | break; 99 | } 100 | 101 | if (vi.format.colorFamily == ::cfUndefined) 102 | throw std::runtime_error{ "color format not supported" }; 103 | 104 | return vi; 105 | } 106 | 107 | ipc::VideoInfo serialize_video_info(const ::VSVideoInfo &vi) 108 | { 109 | if (!vsh::isConstantVideoFormat(&vi)) 110 | throw std::runtime_error{ "constant format required" }; 111 | if (vi.format.bitsPerSample != 8) 112 | throw std::runtime_error{ "high bit-depth not supported" }; 113 | 114 | ipc::VideoInfo ipc_vi{}; 115 | 116 | int64_t fps_num = vi.fpsNum; 117 | int64_t fps_den = vi.fpsDen; 118 | 119 | if (fps_num > INT32_MAX || fps_den > INT32_MAX) { 120 | int64_t max_val = std::max(fps_num, fps_den); 121 | int64_t divisor = max_val / INT32_MAX + (max_val % INT32_MAX ? 1 : 0); 122 | 123 | fps_num /= divisor; 124 | fps_den /= divisor; 125 | assert(fps_num <= INT32_MAX); 126 | assert(fps_den <= INT32_MAX); 127 | } 128 | 129 | ipc_vi.width = vi.width; 130 | ipc_vi.height = vi.height; 131 | ipc_vi.fps_num = static_cast(fps_num); 132 | ipc_vi.fps_den = static_cast(fps_den); 133 | ipc_vi.num_frames = vi.numFrames; 134 | 135 | switch (vi.format.colorFamily) { 136 | case ::cfRGB: 137 | ipc_vi.color_family = ipc::VideoInfo::RGB32; 138 | break; 139 | case ::cfYUV: 140 | ipc_vi.color_family = ipc::VideoInfo::YUV; 141 | break; 142 | case ::cfGray: 143 | ipc_vi.color_family = ipc::VideoInfo::GRAY; 144 | break; 145 | default: 146 | throw std::runtime_error{ "color format not supported" }; 147 | } 148 | 149 | ipc_vi.subsample_w = vi.format.subSamplingW; 150 | ipc_vi.subsample_h = vi.format.subSamplingH; 151 | 152 | return ipc_vi; 153 | } 154 | 155 | Frame heap_to_local_frame(ipc_client::IPCClient *client, const ::VSVideoInfo &vi, int32_t color_family, const ipc::VideoFrame &ipc_frame, const Core &core) 156 | { 157 | unsigned char *heap_ptr = static_cast(client->offset_to_pointer(ipc_frame.heap_offset)); 158 | if (!heap_ptr) 159 | throw std::runtime_error{ "missing frame data" }; 160 | 161 | int src_planes = vi.format.numPlanes; 162 | 163 | if (color_family == ipc::VideoInfo::RGB24 || color_family == ipc::VideoInfo::RGB32 || color_family == ipc::VideoInfo::YUY2) 164 | src_planes = 1; 165 | 166 | for (int p = 0; p < src_planes; ++p) { 167 | int row_size = (vi.width >> (p ? vi.format.subSamplingW : 0)) * vi.format.bytesPerSample; 168 | 169 | if (color_family == ipc::VideoInfo::RGB24) 170 | row_size *= 3; 171 | else if (color_family == ipc::VideoInfo::RGB32) 172 | row_size *= 4; 173 | else if (color_family == ipc::VideoInfo::YUY2) 174 | row_size *= 2; 175 | 176 | if (ipc_frame.stride[p] < row_size) 177 | throw std::runtime_error{ "wrong width" }; 178 | if (ipc_frame.height[p] != (vi.height >> (p ? vi.format.subSamplingH : 0))) 179 | throw std::runtime_error{ "wrong height" }; 180 | } 181 | 182 | Frame frame = core.new_video_frame(vi.format, vi.width, vi.height); 183 | Frame alpha; 184 | 185 | if (color_family == ipc::VideoInfo::RGB24 || color_family == ipc::VideoInfo::RGB32 || color_family == ipc::VideoInfo::YUY2) { 186 | p2p_buffer_param param{}; 187 | 188 | if (color_family == ipc::VideoInfo::RGB24 || color_family == ipc::VideoInfo::RGB32) { 189 | param.src[0] = heap_ptr + (ipc_frame.height[0] - 1) * ipc_frame.stride[0]; 190 | param.src_stride[0] = -ipc_frame.stride[0]; 191 | } else { 192 | param.src[0] = heap_ptr; 193 | param.src_stride[0] = ipc_frame.stride[0]; 194 | } 195 | 196 | for (int p = 0; p < vi.format.numPlanes; ++p) { 197 | param.dst[p] = frame.write_ptr(p); 198 | param.dst_stride[p] = frame.stride(p); 199 | } 200 | if (color_family == ipc::VideoInfo::RGB32) { 201 | alpha = core.new_video_frame(core.get_video_format_by_id(::pfGray8), vi.width, vi.height); 202 | param.dst[3] = alpha.write_ptr(0) + (vi.height - 1) * alpha.stride(0); 203 | param.dst_stride[3] = -alpha.stride(0); 204 | } 205 | 206 | param.width = vi.width; 207 | param.height = vi.height; 208 | 209 | if (color_family == ipc::VideoInfo::RGB24) 210 | param.packing = p2p_rgb24_le; 211 | else if (color_family == ipc::VideoInfo::RGB32) 212 | param.packing = p2p_argb32_le; 213 | else if (color_family == ipc::VideoInfo::YUY2) 214 | param.packing = p2p_yuy2; 215 | 216 | p2p_unpack_frame(¶m, 0); 217 | } else { 218 | const unsigned char *src_ptr = static_cast(heap_ptr); 219 | 220 | for (int p = 0; p < vi.format.numPlanes; ++p) { 221 | unsigned row_size = (vi.width >> (p ? vi.format.subSamplingW : 0)) * vi.format.bytesPerSample; 222 | vsh::bitblt(frame.write_ptr(p), frame.stride(p), src_ptr, ipc_frame.stride[p], row_size, ipc_frame.height[p]); 223 | src_ptr += ipc_frame.stride[p] * ipc_frame.height[p]; 224 | } 225 | } 226 | 227 | if (alpha) 228 | frame.frame_props_rw().set_prop("_Alpha", alpha); 229 | 230 | return frame; 231 | } 232 | 233 | ipc::VideoFrame local_to_heap_frame(ipc_client::IPCClient *client, uint32_t clip_id, int32_t n, const ::VSVideoInfo &vi, const ConstFrame &frame) 234 | { 235 | ipc::VideoFrame ipc_frame{ { clip_id, n } }; 236 | size_t size = 0; 237 | 238 | if (vi.format.colorFamily == ::cfRGB) { 239 | int rowsize = vi.width * 4; 240 | ipc_frame.stride[0] = rowsize % 64 ? rowsize + 64 - rowsize % 64 : rowsize; 241 | ipc_frame.height[0] = vi.height; 242 | size = ipc_frame.stride[0] * vi.height; 243 | } else { 244 | for (int p = 0; p < vi.format.numPlanes; ++p) { 245 | int rowsize = frame.width(p); 246 | ipc_frame.stride[p] = rowsize % 64 ? rowsize + 64 - rowsize % 64 : rowsize; 247 | ipc_frame.height[p] = frame.height(p); 248 | size += ipc_frame.stride[p] * ipc_frame.height[p]; 249 | } 250 | } 251 | 252 | unsigned char *dst_ptr = static_cast(client->allocate(size)); 253 | ipc_frame.heap_offset = client->pointer_to_offset(dst_ptr); 254 | 255 | if (vi.format.colorFamily == ::cfRGB) { 256 | ConstFrame alpha = frame.frame_props_ro().get_prop("_Alpha", map::Ignore{}); 257 | p2p_buffer_param param{}; 258 | 259 | param.src[0] = frame.read_ptr(0); 260 | param.src[1] = frame.read_ptr(1); 261 | param.src[2] = frame.read_ptr(2); 262 | param.src[3] = alpha ? alpha.read_ptr(0) : nullptr; 263 | 264 | param.src_stride[0] = frame.stride(0); 265 | param.src_stride[1] = frame.stride(1); 266 | param.src_stride[2] = frame.stride(2); 267 | param.src_stride[3] = alpha ? alpha.stride(0) : 0; 268 | 269 | param.dst[0] = dst_ptr + (ipc_frame.height[0] - 1) * ipc_frame.stride[0]; 270 | param.dst_stride[0] = -ipc_frame.stride[0]; 271 | 272 | param.width = vi.width; 273 | param.height = vi.height; 274 | param.packing = p2p_argb32_le; 275 | 276 | p2p_pack_frame(¶m, P2P_ALPHA_SET_ONE); 277 | } else { 278 | for (int p = 0; p < vi.format.numPlanes; ++p) { 279 | unsigned rowsize = frame.width(p); 280 | vsh::bitblt(dst_ptr, ipc_frame.stride[p], frame.read_ptr(p), frame.stride(p), rowsize, frame.height(p)); 281 | dst_ptr += ipc_frame.stride[p] * static_cast(frame.height(p)); 282 | } 283 | } 284 | 285 | return ipc_frame; 286 | } 287 | 288 | } // namespace 289 | 290 | 291 | class AVSProxy : public FilterBase { 292 | std::unique_ptr m_client; 293 | std::unordered_map m_clips; 294 | ipc::Value m_script_result; 295 | ::VSVideoInfo m_vi; 296 | 297 | std::deque> m_command_queue; 298 | std::unique_ptr m_runloop_response; 299 | std::mutex m_mutex; 300 | std::condition_variable m_cond; 301 | std::atomic_uint32_t m_active_request; 302 | std::atomic_bool m_runloop_response_received; 303 | std::atomic_bool m_remote_exit; 304 | 305 | void fatal() 306 | { 307 | m_client->stop(); 308 | m_remote_exit = true; 309 | } 310 | 311 | std::unique_ptr expect_response(std::unique_ptr c, ipc_client::CommandType expected_type) 312 | { 313 | auto throw_ = [&](const char *msg) 314 | { 315 | if (c) 316 | reject(std::move(c)); 317 | throw std::runtime_error{ msg }; 318 | }; 319 | 320 | if (!c) 321 | throw_("no response received for command"); 322 | if (c->type() == ipc_client::CommandType::ERR) 323 | throw_("command failed"); 324 | if (c->type() != expected_type) 325 | throw_("unexpected resposne received for command"); 326 | 327 | return c; 328 | } 329 | 330 | void recv_callback(std::unique_ptr c) 331 | { 332 | std::unique_lock lock{ m_mutex }; 333 | 334 | if (c) 335 | m_command_queue.emplace_back(std::move(c)); 336 | else 337 | m_remote_exit = true; 338 | 339 | lock.unlock(); 340 | m_cond.notify_all(); 341 | } 342 | 343 | void runloop_callback(uint32_t request, std::unique_ptr c) 344 | { 345 | if (request != m_active_request) { 346 | c->deallocate_heap_resources(m_client.get()); 347 | send_err(c->transaction_id()); 348 | return; 349 | } 350 | 351 | std::unique_lock lock{ m_mutex }; 352 | 353 | m_runloop_response = std::move(c); 354 | m_runloop_response_received = true; 355 | 356 | lock.unlock(); 357 | m_cond.notify_all(); 358 | } 359 | 360 | void send_ack(uint32_t response_id) 361 | { 362 | if (response_id == ipc_client::INVALID_TRANSACTION) 363 | return; 364 | 365 | auto response = std::make_unique(); 366 | response->set_response_id(response_id); 367 | m_client->send_async(std::move(response)); 368 | } 369 | 370 | void send_err(uint32_t response_id) 371 | { 372 | if (response_id == ipc_client::INVALID_TRANSACTION) 373 | return; 374 | 375 | auto response = std::make_unique(); 376 | response->set_response_id(response_id); 377 | m_client->send_async(std::move(response)); 378 | } 379 | 380 | void expect_ack(std::unique_ptr c) 381 | { 382 | c = expect_response(std::move(c), ipc_client::CommandType::ACK); 383 | c->deallocate_heap_resources(m_client.get()); 384 | } 385 | 386 | void reject(std::unique_ptr c) 387 | { 388 | c->deallocate_heap_resources(m_client.get()); 389 | send_err(c->transaction_id()); 390 | } 391 | 392 | void reject_commands() 393 | { 394 | // Caller must acquire mutex. 395 | assert(!m_mutex.try_lock()); 396 | 397 | // Reject any slave activity from a previous frame. 398 | while (!m_command_queue.empty()) { 399 | std::unique_ptr c{ std::move(m_command_queue.front()) }; 400 | m_command_queue.pop_front(); 401 | reject(std::move(c)); 402 | } 403 | } 404 | 405 | void service_remote_getframe(std::unique_ptr c) 406 | { 407 | auto it = m_clips.find(c->arg().clip_id); 408 | if (it == m_clips.end()) { 409 | c->deallocate_heap_resources(m_client.get()); 410 | send_err(c->transaction_id()); 411 | return; 412 | } 413 | 414 | ConstFrame frame; 415 | 416 | try { 417 | frame = it->second.get_frame(c->arg().frame_number); 418 | } catch (...) { 419 | c->deallocate_heap_resources(m_client.get()); 420 | send_err(c->transaction_id()); 421 | return; 422 | } 423 | 424 | ipc::VideoFrame ipc_frame = local_to_heap_frame(m_client.get(), c->arg().clip_id, c->arg().frame_number, it->second.video_info(), frame); 425 | std::unique_ptr response; 426 | 427 | try { 428 | response = std::make_unique(ipc_frame); 429 | } catch (...) { 430 | m_client->deallocate(m_client->offset_to_pointer(ipc_frame.heap_offset)); 431 | throw; 432 | } 433 | 434 | response->set_response_id(c->transaction_id()); 435 | m_client->send_async(std::move(response)); 436 | } 437 | 438 | std::unique_ptr runloop(std::unique_ptr c) 439 | { 440 | if (m_remote_exit) { 441 | c->deallocate_heap_resources(m_client.get()); 442 | throw std::runtime_error{ "remote process exited" }; 443 | } 444 | 445 | std::unique_lock lock{ m_mutex }; 446 | reject_commands(); 447 | 448 | m_runloop_response.reset(); 449 | m_runloop_response_received = false; 450 | m_client->send_async(std::move(c), std::bind(&AVSProxy::runloop_callback, this, ++m_active_request, std::placeholders::_1)); 451 | 452 | while (true) { 453 | m_cond.wait(lock, [&]() { return m_remote_exit || m_runloop_response_received || !m_command_queue.empty(); }); 454 | 455 | if (m_remote_exit) 456 | throw std::runtime_error{ "remote process exited" }; 457 | 458 | if (m_runloop_response_received) 459 | break; 460 | 461 | while (!m_command_queue.empty()) { 462 | std::unique_ptr c{ std::move(m_command_queue.front()) }; 463 | m_command_queue.pop_front(); 464 | lock.unlock(); 465 | 466 | if (c->type() != ipc_client::CommandType::GET_FRAME) { 467 | reject(std::move(c)); 468 | lock.lock(); 469 | continue; 470 | } 471 | 472 | std::unique_ptr get{ static_cast(c.release()) }; 473 | service_remote_getframe(std::move(get)); 474 | lock.lock(); 475 | } 476 | } 477 | 478 | reject_commands(); 479 | if (m_runloop_response) 480 | send_ack(m_runloop_response->transaction_id()); 481 | 482 | return std::move(m_runloop_response); 483 | } 484 | public: 485 | AVSProxy(void * = nullptr) : 486 | m_script_result{}, 487 | m_vi{}, 488 | m_active_request{}, 489 | m_runloop_response_received{}, 490 | m_remote_exit{} 491 | {} 492 | 493 | const char *get_name(void *) noexcept override { return "Avisynth 32-bit proxy"; } 494 | 495 | void init(const ConstMap &in, const Map &out, const Core &core) override 496 | { 497 | Plugin this_plugin = core.get_plugin_by_id(PLUGIN_ID); 498 | 499 | std::string script = in.get_prop("script"); 500 | std::wstring avisynth_path = utf8_to_utf16(in.get_prop("avisynth", map::Ignore{})); 501 | std::wstring slave_path = utf8_to_utf16(in.get_prop("slave", map::Ignore{})); 502 | 503 | if (slave_path.empty()) { 504 | std::string plugin_path = this_plugin.path(); 505 | slave_path = utf8_to_utf16(plugin_path.substr(0, plugin_path.find_last_of('/')) + "/avshost_native.exe"); 506 | } 507 | 508 | m_client = std::make_unique(ipc_client::IPCClient::master(), slave_path.c_str()); 509 | m_client->start(std::bind(&AVSProxy::recv_callback, this, std::placeholders::_1)); 510 | 511 | if (in.contains("slave_log")) { 512 | std::wstring log_path = utf8_to_utf16(in.get_prop("slave_log")); 513 | m_client->send_async(std::make_unique(log_path)); 514 | } 515 | 516 | std::unique_ptr response; 517 | 518 | response = m_client->send_sync(std::make_unique(avisynth_path.c_str())); 519 | expect_ack(std::move(response)); 520 | 521 | if (in.contains("clips")) { 522 | size_t num_clips = in.num_elements("clips"); 523 | 524 | if (!in.contains("clip_names") || in.num_elements("clip_names") != num_clips) 525 | throw std::runtime_error{ "clips and clip_names must have same number of elements" }; 526 | 527 | for (size_t i = 0; i < num_clips; ++i) { 528 | FilterNode node = in.get_prop("clips", static_cast(i)); 529 | std::string name = in.get_prop("clip_names", static_cast(i)); 530 | 531 | ipc::Value value{ ipc::Value::CLIP }; 532 | value.c.clip_id = static_cast(i); 533 | value.c.vi = serialize_video_info(node.video_info()); 534 | 535 | response = m_client->send_sync(std::make_unique(name, value)); 536 | expect_ack(std::move(response)); 537 | 538 | m_clips[static_cast(i)] = std::move(node); 539 | } 540 | } 541 | 542 | uint32_t heap_script = local_to_heap_str(m_client.get(), script.c_str(), script.size()); 543 | std::unique_ptr eval_command; 544 | 545 | try { 546 | eval_command = std::make_unique(heap_script); 547 | } catch (...) { 548 | m_client->deallocate(m_client->offset_to_pointer(heap_script)); 549 | throw; 550 | } 551 | 552 | response = runloop(std::move(eval_command)); 553 | response = expect_response(std::move(response), ipc_client::CommandType::SET_SCRIPT_VAR); 554 | 555 | m_script_result = static_cast(response.get())->value(); 556 | response->relinquish_heap_resources(); 557 | 558 | switch (m_script_result.type) { 559 | // Create a filter if the result was a clip. 560 | case ipc::Value::CLIP: { 561 | m_vi = deserialize_video_info(m_script_result.c.vi, core); 562 | 563 | FilterDependencyBuilder deps = make_deps(); 564 | for (const auto &entry : m_clips) { 565 | deps.add_dep(entry.second); 566 | } 567 | create_video_filter(out, m_vi, fmFrameState, deps, core); 568 | break; 569 | } 570 | // Otherwise return the result directly. 571 | case ipc::Value::BOOL_: 572 | out.set_prop("result", m_script_result.b); break; 573 | case ipc::Value::INT: 574 | out.set_prop("result", m_script_result.i); break; 575 | case ipc::Value::FLOAT: 576 | out.set_prop("result", m_script_result.f); break; 577 | case ipc::Value::STRING: 578 | // Strings need to be deallocated. 579 | try { 580 | out.set_prop("result", heap_to_local_str(m_client.get(), m_script_result.s)); 581 | } catch (...) { 582 | m_client->deallocate(m_client->offset_to_pointer(m_script_result.s)); 583 | throw; 584 | } 585 | m_client->deallocate(m_client->offset_to_pointer(m_script_result.s)); 586 | break; 587 | default: 588 | break; 589 | } 590 | } 591 | 592 | ConstFrame get_frame_initial(int n, const Core &core, const FrameContext &, void *) override 593 | { 594 | try { 595 | std::unique_ptr response = runloop( 596 | std::make_unique(ipc::VideoFrameRequest{ m_script_result.c.clip_id, n })); 597 | response = expect_response(std::move(response), ipc_client::CommandType::SET_FRAME); 598 | 599 | ipc_client::CommandSetFrame *set_frame = static_cast(response.get()); 600 | ConstFrame result; 601 | 602 | try { 603 | result = heap_to_local_frame(m_client.get(), m_vi, m_script_result.c.vi.color_family, set_frame->arg(), core); 604 | } catch (...) { 605 | response->deallocate_heap_resources(m_client.get()); 606 | throw; 607 | } 608 | 609 | response->deallocate_heap_resources(m_client.get()); 610 | return result; 611 | } catch (const ipc_client::IPCError &) { 612 | fatal(); 613 | throw; 614 | } 615 | } 616 | 617 | ConstFrame get_frame(int n, const Core &core, const FrameContext &, void *) override 618 | { 619 | return nullptr; 620 | } 621 | }; 622 | 623 | const PluginInfo4 g_plugin_info4{ 624 | PLUGIN_ID, "avsw", "avsproxy", 0, { 625 | { &FilterBase::filter_create, "Eval", "script:data;clips:vnode[]:opt;clip_names:data[]:opt;avisynth:data:opt;slave:data:opt;slave_log:data:opt;", "any" } 626 | } 627 | }; 628 | -------------------------------------------------------------------------------- /avshost_native/avisynth_2.6.h: -------------------------------------------------------------------------------- 1 | // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. 2 | // Avisynth v2.6. Copyright 2006 Klaus Post. 3 | // Avisynth v2.6. Copyright 2009 Ian Brabham. 4 | // http://www.avisynth.org 5 | 6 | // This program is free software; you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation; either version 2 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program; if not, write to the Free Software 18 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 19 | // http://www.gnu.org/copyleft/gpl.html . 20 | // 21 | // Linking Avisynth statically or dynamically with other modules is making 22 | // a combined work based on Avisynth. Thus, the terms and conditions of 23 | // the GNU General Public License cover the whole combination. 24 | // 25 | // As a special exception, the copyright holders of Avisynth give you 26 | // permission to link Avisynth with independent modules that communicate 27 | // with Avisynth solely through the interfaces defined in avisynth.h, 28 | // regardless of the license terms of these independent modules, and to 29 | // copy and distribute the resulting combined work under terms of your 30 | // choice, provided that every copy of the combined work is accompanied 31 | // by a complete copy of the source code of Avisynth (the version of 32 | // Avisynth used to produce the combined work), being distributed under 33 | // the terms of the GNU General Public License plus this exception. An 34 | // independent module is a module which is not derived from or based on 35 | // Avisynth, such as 3rd-party filters, import and export plugins, or 36 | // graphical user interfaces. 37 | 38 | 39 | 40 | 41 | #ifndef __AVISYNTH_6_H__ 42 | #define __AVISYNTH_6_H__ 43 | 44 | enum { AVISYNTH_INTERFACE_VERSION = 6 }; 45 | 46 | 47 | /* Define all types necessary for interfacing with avisynth.dll 48 | Moved from internal.h */ 49 | 50 | // Win32 API macros, notably the types BYTE, DWORD, ULONG, etc. 51 | #include 52 | 53 | 54 | // Raster types used by VirtualDub & Avisynth 55 | #define in64 (__int64)(unsigned short) 56 | typedef unsigned long Pixel; // this will break on 64-bit machines! 57 | typedef unsigned long Pixel32; 58 | typedef unsigned char Pixel8; 59 | typedef long PixCoord; 60 | typedef long PixDim; 61 | typedef long PixOffset; 62 | 63 | 64 | /* Compiler-specific crap */ 65 | 66 | // Tell MSVC to stop precompiling here 67 | #ifdef _MSC_VER 68 | #pragma hdrstop 69 | #endif 70 | 71 | // Set up debugging macros for MS compilers; for others, step down to the 72 | // standard interface 73 | #ifdef _MSC_VER 74 | #include 75 | #else 76 | #define _RPT0(a,b) ((void)0) 77 | #define _RPT1(a,b,c) ((void)0) 78 | #define _RPT2(a,b,c,d) ((void)0) 79 | #define _RPT3(a,b,c,d,e) ((void)0) 80 | #define _RPT4(a,b,c,d,e,f) ((void)0) 81 | 82 | #define _ASSERTE(x) assert(x) 83 | #include 84 | #endif 85 | 86 | 87 | 88 | // I had problems with Premiere wanting 1-byte alignment for its structures, 89 | // so I now set the Avisynth struct alignment explicitly here. 90 | #pragma pack(push,8) 91 | 92 | #define FRAME_ALIGN 16 93 | // Default frame alignment is 16 bytes, to help P4, when using SSE2 94 | 95 | // The VideoInfo struct holds global information about a clip (i.e. 96 | // information that does not depend on the frame number). The GetVideoInfo 97 | // method in IClip returns this struct. 98 | 99 | // Audio Sample information 100 | typedef float SFLOAT; 101 | 102 | enum {SAMPLE_INT8 = 1<<0, 103 | SAMPLE_INT16 = 1<<1, 104 | SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware. 105 | SAMPLE_INT32 = 1<<3, 106 | SAMPLE_FLOAT = 1<<4}; 107 | 108 | enum { 109 | PLANAR_Y=1<<0, 110 | PLANAR_U=1<<1, 111 | PLANAR_V=1<<2, 112 | PLANAR_ALIGNED=1<<3, 113 | PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED, 114 | PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED, 115 | PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED, 116 | PLANAR_A=1<<4, 117 | PLANAR_R=1<<5, 118 | PLANAR_G=1<<6, 119 | PLANAR_B=1<<7, 120 | PLANAR_A_ALIGNED=PLANAR_A|PLANAR_ALIGNED, 121 | PLANAR_R_ALIGNED=PLANAR_R|PLANAR_ALIGNED, 122 | PLANAR_G_ALIGNED=PLANAR_G|PLANAR_ALIGNED, 123 | PLANAR_B_ALIGNED=PLANAR_B|PLANAR_ALIGNED, 124 | }; 125 | 126 | class AvisynthError /* exception */ { 127 | public: 128 | const char* const msg; 129 | AvisynthError(const char* _msg) : msg(_msg) {} 130 | }; // end class AvisynthError 131 | 132 | 133 | /* Forward references */ 134 | struct __single_inheritance VideoInfo; 135 | class __single_inheritance VideoFrameBuffer; 136 | class __single_inheritance VideoFrame; 137 | class IClip; 138 | class __single_inheritance PClip; 139 | class __single_inheritance PVideoFrame; 140 | class IScriptEnvironment; 141 | class __single_inheritance AVSValue; 142 | 143 | 144 | /* 145 | * Avisynth C++ plugin API code function pointers. 146 | * 147 | * In order to maintain binary compatibility with 148 | * future version do not change the order of the 149 | * existing function pointers. It will be baked 150 | * into all existing plugins. 151 | * 152 | * Add new function pointers to the end of the 153 | * structure. The linkage macros generate some 154 | * protection code to ensure newer plugin do not 155 | * call non-existing functions in an older host. 156 | */ 157 | 158 | struct AVS_Linkage { 159 | 160 | size_t Size; 161 | 162 | /**********************************************************************/ 163 | 164 | // struct VideoInfo 165 | bool (VideoInfo::*HasVideo)() const; 166 | bool (VideoInfo::*HasAudio)() const; 167 | bool (VideoInfo::*IsRGB)() const; 168 | bool (VideoInfo::*IsRGB24)() const; 169 | bool (VideoInfo::*IsRGB32)() const; 170 | bool (VideoInfo::*IsYUV)() const; 171 | bool (VideoInfo::*IsYUY2)() const; 172 | bool (VideoInfo::*IsYV24)() const; 173 | bool (VideoInfo::*IsYV16)() const; 174 | bool (VideoInfo::*IsYV12)() const; 175 | bool (VideoInfo::*IsYV411)() const; 176 | bool (VideoInfo::*IsY8)() const; 177 | bool (VideoInfo::*IsColorSpace)(int c_space) const; 178 | bool (VideoInfo::*Is)(int property) const; 179 | bool (VideoInfo::*IsPlanar)() const; 180 | bool (VideoInfo::*IsFieldBased)() const; 181 | bool (VideoInfo::*IsParityKnown)() const; 182 | bool (VideoInfo::*IsBFF)() const; 183 | bool (VideoInfo::*IsTFF)() const; 184 | bool (VideoInfo::*IsVPlaneFirst)() const; 185 | int (VideoInfo::*BytesFromPixels)(int pixels) const; 186 | int (VideoInfo::*RowSize)(int plane) const; 187 | int (VideoInfo::*BMPSize)() const; 188 | __int64 (VideoInfo::*AudioSamplesFromFrames)(int frames) const; 189 | int (VideoInfo::*FramesFromAudioSamples)(__int64 samples) const; 190 | __int64 (VideoInfo::*AudioSamplesFromBytes)(__int64 bytes) const; 191 | __int64 (VideoInfo::*BytesFromAudioSamples)(__int64 samples) const; 192 | int (VideoInfo::*AudioChannels)() const; 193 | int (VideoInfo::*SampleType)() const; 194 | bool (VideoInfo::*IsSampleType)(int testtype) const; 195 | int (VideoInfo::*SamplesPerSecond)() const; 196 | int (VideoInfo::*BytesPerAudioSample)() const; 197 | void (VideoInfo::*SetFieldBased)(bool isfieldbased); 198 | void (VideoInfo::*Set)(int property); 199 | void (VideoInfo::*Clear)(int property); 200 | int (VideoInfo::*GetPlaneWidthSubsampling)(int plane) const; 201 | int (VideoInfo::*GetPlaneHeightSubsampling)(int plane) const; 202 | int (VideoInfo::*BitsPerPixel)() const; 203 | int (VideoInfo::*BytesPerChannelSample)() const; 204 | void (VideoInfo::*SetFPS)(unsigned numerator, unsigned denominator); 205 | void (VideoInfo::*MulDivFPS)(unsigned multiplier, unsigned divisor); 206 | bool (VideoInfo::*IsSameColorspace)(const VideoInfo& vi) const; 207 | // end struct VideoInfo 208 | 209 | /**********************************************************************/ 210 | 211 | // class VideoFrameBuffer 212 | const BYTE* (VideoFrameBuffer::*VFBGetReadPtr)() const; 213 | BYTE* (VideoFrameBuffer::*VFBGetWritePtr)(); 214 | size_t (VideoFrameBuffer::*GetDataSize)() const; 215 | int (VideoFrameBuffer::*GetSequenceNumber)() const; 216 | int (VideoFrameBuffer::*GetRefcount)() const; 217 | // end class VideoFrameBuffer 218 | 219 | /**********************************************************************/ 220 | 221 | // class VideoFrame 222 | int (VideoFrame::*GetPitch)(int plane) const; 223 | int (VideoFrame::*GetRowSize)(int plane) const; 224 | int (VideoFrame::*GetHeight)(int plane) const; 225 | VideoFrameBuffer* (VideoFrame::*GetFrameBuffer)() const; 226 | size_t (VideoFrame::*GetOffset)(int plane) const; 227 | const BYTE* (VideoFrame::*VFGetReadPtr)(int plane) const; 228 | bool (VideoFrame::*IsWritable)() const; 229 | BYTE* (VideoFrame::*VFGetWritePtr)(int plane) const; 230 | void (VideoFrame::*VideoFrame_DESTRUCTOR)(); 231 | // end class VideoFrame 232 | 233 | /**********************************************************************/ 234 | 235 | // class IClip 236 | /* nothing */ 237 | // end class IClip 238 | 239 | /**********************************************************************/ 240 | 241 | // class PClip 242 | void (PClip::*PClip_CONSTRUCTOR0)(); 243 | void (PClip::*PClip_CONSTRUCTOR1)(const PClip& x); 244 | void (PClip::*PClip_CONSTRUCTOR2)(IClip* x); 245 | void (PClip::*PClip_OPERATOR_ASSIGN0)(IClip* x); 246 | void (PClip::*PClip_OPERATOR_ASSIGN1)(const PClip& x); 247 | void (PClip::*PClip_DESTRUCTOR)(); 248 | // end class PClip 249 | 250 | /**********************************************************************/ 251 | 252 | // class PVideoFrame 253 | void (PVideoFrame::*PVideoFrame_CONSTRUCTOR0)(); 254 | void (PVideoFrame::*PVideoFrame_CONSTRUCTOR1)(const PVideoFrame& x); 255 | void (PVideoFrame::*PVideoFrame_CONSTRUCTOR2)(VideoFrame* x); 256 | void (PVideoFrame::*PVideoFrame_OPERATOR_ASSIGN0)(VideoFrame* x); 257 | void (PVideoFrame::*PVideoFrame_OPERATOR_ASSIGN1)(const PVideoFrame& x); 258 | void (PVideoFrame::*PVideoFrame_DESTRUCTOR)(); 259 | // end class PVideoFrame 260 | 261 | /**********************************************************************/ 262 | 263 | // class AVSValue 264 | void (AVSValue::*AVSValue_CONSTRUCTOR0)(); 265 | void (AVSValue::*AVSValue_CONSTRUCTOR1)(IClip* c); 266 | void (AVSValue::*AVSValue_CONSTRUCTOR2)(const PClip& c); 267 | void (AVSValue::*AVSValue_CONSTRUCTOR3)(bool b); 268 | void (AVSValue::*AVSValue_CONSTRUCTOR4)(int i); 269 | void (AVSValue::*AVSValue_CONSTRUCTOR5)(float f); 270 | void (AVSValue::*AVSValue_CONSTRUCTOR6)(double f); 271 | void (AVSValue::*AVSValue_CONSTRUCTOR7)(const char* s); 272 | void (AVSValue::*AVSValue_CONSTRUCTOR8)(const AVSValue* a, int size); 273 | void (AVSValue::*AVSValue_CONSTRUCTOR9)(const AVSValue& v); 274 | void (AVSValue::*AVSValue_DESTRUCTOR)(); 275 | AVSValue& (AVSValue::*AVSValue_OPERATOR_ASSIGN)(const AVSValue& v); 276 | const AVSValue& (AVSValue::*AVSValue_OPERATOR_INDEX)(int index) const; 277 | bool (AVSValue::*Defined)() const; 278 | bool (AVSValue::*IsClip)() const; 279 | bool (AVSValue::*IsBool)() const; 280 | bool (AVSValue::*IsInt)() const; 281 | bool (AVSValue::*IsFloat)() const; 282 | bool (AVSValue::*IsString)() const; 283 | bool (AVSValue::*IsArray)() const; 284 | PClip (AVSValue::*AsClip)() const; 285 | bool (AVSValue::*AsBool1)() const; 286 | int (AVSValue::*AsInt1)() const; 287 | const char* (AVSValue::*AsString1)() const; 288 | double (AVSValue::*AsFloat1)() const; 289 | bool (AVSValue::*AsBool2)(bool def) const; 290 | int (AVSValue::*AsInt2)(int def) const; 291 | double (AVSValue::*AsDblDef)(double def) const; 292 | double (AVSValue::*AsFloat2)(float def) const; 293 | const char* (AVSValue::*AsString2)(const char* def) const; 294 | int (AVSValue::*ArraySize)() const; 295 | // end class AVSValue 296 | 297 | /**********************************************************************/ 298 | }; 299 | 300 | #ifdef AVISYNTH_CORE 301 | /* Macro resolution for code inside Avisynth.dll */ 302 | # define AVS_BakedCode(arg) ; 303 | # define AVS_LinkCall(arg) 304 | # define AVS_LinkCallV(arg) 305 | 306 | #else 307 | /* Macro resolution for code inside user plugin */ 308 | # ifdef AVS_LINKAGE_DLLIMPORT 309 | extern __declspec(dllimport) const AVS_Linkage* const AVS_linkage; 310 | # else 311 | extern const AVS_Linkage* AVS_linkage; 312 | # endif 313 | 314 | # ifndef offsetof 315 | # include 316 | # endif 317 | 318 | # define AVS_BakedCode(arg) { arg ; } 319 | # define AVS_LinkCall(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? 0 : (this->*(AVS_linkage->arg)) 320 | # define AVS_LinkCallV(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? *this : (this->*(AVS_linkage->arg)) 321 | 322 | #endif 323 | 324 | struct VideoInfo { 325 | int width, height; // width=0 means no video 326 | unsigned fps_numerator, fps_denominator; 327 | int num_frames; 328 | // This is more extensible than previous versions. 329 | // More properties can be added seeminglesly. 330 | 331 | // Colorspace properties. 332 | /* 333 | 7<<0 Planar Width Subsampling bits 334 | Use (X+1) & 3 for GetPlaneWidthSubsampling 335 | 000 => 1 YV12, YV16 336 | 001 => 2 YV411, YUV9 337 | 010 => reserved 338 | 011 => 0 YV24 339 | 1xx => reserved 340 | 341 | 1<<3 VPlaneFirst YV12, YV16, YV24, YV411, YUV9 342 | 1<<4 UPlaneFirst I420 343 | 344 | 7<<8 Planar Height Subsampling bits 345 | Use ((X>>8)+1) & 3 for GetPlaneHeightSubsampling 346 | 000 => 1 YV12 347 | 001 => 2 YUV9 348 | 010 => reserved 349 | 011 => 0 YV16, YV24, YV411 350 | 1xx => reserved 351 | 352 | 7<<16 Sample resolution bits 353 | 000 => 8 354 | 001 => 16 355 | 010 => 32 356 | 011 => reserved 357 | 1xx => reserved 358 | 359 | Planar match mask 1111.0000.0000.0111.0000.0111.0000.0111 360 | Planar signature 10xx.0000.0000.00xx.0000.00xx.00xx.00xx 361 | Planar filter mask 1111.1111.1111.1111.1111.1111.1100.1111 362 | */ 363 | enum { 364 | CS_BGR = 1<<28, 365 | CS_YUV = 1<<29, 366 | CS_INTERLEAVED = 1<<30, 367 | CS_PLANAR = 1<<31, 368 | 369 | CS_Shift_Sub_Width = 0, 370 | CS_Shift_Sub_Height = 8, 371 | CS_Shift_Sample_Bits = 16, 372 | 373 | CS_Sub_Width_Mask = 7 << CS_Shift_Sub_Width, 374 | CS_Sub_Width_1 = 3 << CS_Shift_Sub_Width, // YV24 375 | CS_Sub_Width_2 = 0 << CS_Shift_Sub_Width, // YV12, I420, YV16 376 | CS_Sub_Width_4 = 1 << CS_Shift_Sub_Width, // YUV9, YV411 377 | 378 | CS_VPlaneFirst = 1 << 3, // YV12, YV16, YV24, YV411, YUV9 379 | CS_UPlaneFirst = 1 << 4, // I420 380 | 381 | CS_Sub_Height_Mask = 7 << CS_Shift_Sub_Height, 382 | CS_Sub_Height_1 = 3 << CS_Shift_Sub_Height, // YV16, YV24, YV411 383 | CS_Sub_Height_2 = 0 << CS_Shift_Sub_Height, // YV12, I420 384 | CS_Sub_Height_4 = 1 << CS_Shift_Sub_Height, // YUV9 385 | 386 | CS_Sample_Bits_Mask = 7 << CS_Shift_Sample_Bits, 387 | CS_Sample_Bits_8 = 0 << CS_Shift_Sample_Bits, 388 | CS_Sample_Bits_16 = 1 << CS_Shift_Sample_Bits, 389 | CS_Sample_Bits_32 = 2 << CS_Shift_Sample_Bits, 390 | 391 | CS_PLANAR_MASK = CS_PLANAR | CS_INTERLEAVED | CS_YUV | CS_BGR | CS_Sample_Bits_Mask 392 | | CS_Sub_Height_Mask | CS_Sub_Width_Mask, 393 | CS_PLANAR_FILTER = ~( CS_VPlaneFirst | CS_UPlaneFirst ), 394 | 395 | // Specific colorformats 396 | CS_UNKNOWN = 0, 397 | CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED, 398 | CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED, 399 | CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED, 400 | // CS_YV12 = 1<<3 Reserved 401 | // CS_I420 = 1<<4 Reserved 402 | CS_RAW32 = 1<<5 | CS_INTERLEAVED, 403 | 404 | // YV12 must be 0xA000008 2.5 Baked API will see all new planar as YV12 405 | // I420 must be 0xA000010 406 | 407 | CS_YV24 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_1, // YVU 4:4:4 planar 408 | CS_YV16 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_2, // YVU 4:2:2 planar 409 | CS_YV12 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_2 | CS_Sub_Width_2, // YVU 4:2:0 planar 410 | CS_I420 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_UPlaneFirst | CS_Sub_Height_2 | CS_Sub_Width_2, // YUV 4:2:0 planar 411 | CS_IYUV = CS_I420, 412 | CS_YUV9 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_4 | CS_Sub_Width_4, // YUV 4:1:0 planar 413 | CS_YV411 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_4, // YUV 4:1:1 planar 414 | 415 | CS_Y8 = CS_PLANAR | CS_INTERLEAVED | CS_YUV | CS_Sample_Bits_8, // Y 4:0:0 planar 416 | /* 417 | CS_YV48 = CS_PLANAR | CS_YUV | CS_Sample_Bits_16 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_1, // YUV 4:4:4 16bit samples 418 | CS_Y16 = CS_PLANAR | CS_INTERLEAVED | CS_YUV | CS_Sample_Bits_16, // Y 4:0:0 16bit samples 419 | 420 | CS_YV96 = CS_PLANAR | CS_YUV | CS_Sample_Bits_32 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_1, // YUV 4:4:4 32bit samples 421 | CS_Y32 = CS_PLANAR | CS_INTERLEAVED | CS_YUV | CS_Sample_Bits_32, // Y 4:0:0 32bit samples 422 | 423 | CS_PRGB = CS_PLANAR | CS_RGB | CS_Sample_Bits_8, // Planar RGB 424 | CS_RGB48 = CS_PLANAR | CS_RGB | CS_Sample_Bits_16, // Planar RGB 16bit samples 425 | CS_RGB96 = CS_PLANAR | CS_RGB | CS_Sample_Bits_32, // Planar RGB 32bit samples 426 | */ 427 | }; 428 | 429 | int pixel_type; // changed to int as of 2.5 430 | 431 | 432 | int audio_samples_per_second; // 0 means no audio 433 | int sample_type; // as of 2.5 434 | __int64 num_audio_samples; // changed as of 2.5 435 | int nchannels; // as of 2.5 436 | 437 | // Imagetype properties 438 | 439 | int image_type; 440 | 441 | enum { 442 | IT_BFF = 1<<0, 443 | IT_TFF = 1<<1, 444 | IT_FIELDBASED = 1<<2 445 | }; 446 | 447 | // Chroma placement bits 20 -> 23 ::FIXME:: Really want a Class to support this 448 | enum { 449 | CS_UNKNOWN_CHROMA_PLACEMENT = 0 << 20, 450 | CS_MPEG1_CHROMA_PLACEMENT = 1 << 20, 451 | CS_MPEG2_CHROMA_PLACEMENT = 2 << 20, 452 | CS_YUY2_CHROMA_PLACEMENT = 3 << 20, 453 | CS_TOPLEFT_CHROMA_PLACEMENT = 4 << 20 454 | }; 455 | 456 | // useful functions of the above 457 | bool HasVideo() const AVS_BakedCode( return AVS_LinkCall(HasVideo)() ) 458 | bool HasAudio() const AVS_BakedCode( return AVS_LinkCall(HasAudio)() ) 459 | bool IsRGB() const AVS_BakedCode( return AVS_LinkCall(IsRGB)() ) 460 | bool IsRGB24() const AVS_BakedCode( return AVS_LinkCall(IsRGB24)() ) 461 | bool IsRGB32() const AVS_BakedCode( return AVS_LinkCall(IsRGB32)() ) 462 | bool IsYUV() const AVS_BakedCode( return AVS_LinkCall(IsYUV)() ) 463 | bool IsYUY2() const AVS_BakedCode( return AVS_LinkCall(IsYUY2)() ) 464 | 465 | bool IsYV24() const AVS_BakedCode( return AVS_LinkCall(IsYV24)() ) 466 | bool IsYV16() const AVS_BakedCode( return AVS_LinkCall(IsYV16)() ) 467 | bool IsYV12() const AVS_BakedCode( return AVS_LinkCall(IsYV12)() ) 468 | bool IsYV411() const AVS_BakedCode( return AVS_LinkCall(IsYV411)() ) 469 | //bool IsYUV9() const; 470 | bool IsY8() const AVS_BakedCode( return AVS_LinkCall(IsY8)() ) 471 | 472 | bool IsColorSpace(int c_space) const AVS_BakedCode( return AVS_LinkCall(IsColorSpace)(c_space) ) 473 | 474 | bool Is(int property) const AVS_BakedCode( return AVS_LinkCall(Is)(property) ) 475 | bool IsPlanar() const AVS_BakedCode( return AVS_LinkCall(IsPlanar)() ) 476 | bool IsFieldBased() const AVS_BakedCode( return AVS_LinkCall(IsFieldBased)() ) 477 | bool IsParityKnown() const AVS_BakedCode( return AVS_LinkCall(IsParityKnown)() ) 478 | bool IsBFF() const AVS_BakedCode( return AVS_LinkCall(IsBFF)() ) 479 | bool IsTFF() const AVS_BakedCode( return AVS_LinkCall(IsTFF)() ) 480 | 481 | bool IsVPlaneFirst() const AVS_BakedCode( return AVS_LinkCall(IsVPlaneFirst)() ) // Don't use this 482 | // Will not work on planar images, but will return only luma planes 483 | int BytesFromPixels(int pixels) const AVS_BakedCode( return AVS_LinkCall(BytesFromPixels)(pixels) ) 484 | int RowSize(int plane=0) const AVS_BakedCode( return AVS_LinkCall(RowSize)(plane) ) 485 | int BMPSize() const AVS_BakedCode( return AVS_LinkCall(BMPSize)() ) 486 | 487 | __int64 AudioSamplesFromFrames(int frames) const AVS_BakedCode( return AVS_LinkCall(AudioSamplesFromFrames)(frames) ) 488 | int FramesFromAudioSamples(__int64 samples) const AVS_BakedCode( return AVS_LinkCall(FramesFromAudioSamples)(samples) ) 489 | __int64 AudioSamplesFromBytes(__int64 bytes) const AVS_BakedCode( return AVS_LinkCall(AudioSamplesFromBytes)(bytes) ) 490 | __int64 BytesFromAudioSamples(__int64 samples) const AVS_BakedCode( return AVS_LinkCall(BytesFromAudioSamples)(samples) ) 491 | int AudioChannels() const AVS_BakedCode( return AVS_LinkCall(AudioChannels)() ) 492 | int SampleType() const AVS_BakedCode( return AVS_LinkCall(SampleType)() ) 493 | bool IsSampleType(int testtype) const AVS_BakedCode( return AVS_LinkCall(IsSampleType)(testtype) ) 494 | int SamplesPerSecond() const AVS_BakedCode( return AVS_LinkCall(SamplesPerSecond)() ) 495 | int BytesPerAudioSample() const AVS_BakedCode( return AVS_LinkCall(BytesPerAudioSample)() ) 496 | void SetFieldBased(bool isfieldbased) AVS_BakedCode( AVS_LinkCall(SetFieldBased)(isfieldbased) ) 497 | void Set(int property) AVS_BakedCode( AVS_LinkCall(Set)(property) ) 498 | void Clear(int property) AVS_BakedCode( AVS_LinkCall(Clear)(property) ) 499 | // Subsampling in bitshifts! 500 | int GetPlaneWidthSubsampling(int plane) const AVS_BakedCode( return AVS_LinkCall(GetPlaneWidthSubsampling)(plane) ) 501 | int GetPlaneHeightSubsampling(int plane) const AVS_BakedCode( return AVS_LinkCall(GetPlaneHeightSubsampling)(plane) ) 502 | int BitsPerPixel() const AVS_BakedCode( return AVS_LinkCall(BitsPerPixel)() ) 503 | 504 | int BytesPerChannelSample() const AVS_BakedCode( return AVS_LinkCall(BytesPerChannelSample)() ) 505 | 506 | // useful mutator 507 | void SetFPS(unsigned numerator, unsigned denominator) AVS_BakedCode( AVS_LinkCall(SetFPS)(numerator, denominator) ) 508 | 509 | // Range protected multiply-divide of FPS 510 | void MulDivFPS(unsigned multiplier, unsigned divisor) AVS_BakedCode( AVS_LinkCall(MulDivFPS)(multiplier, divisor) ) 511 | 512 | // Test for same colorspace 513 | bool IsSameColorspace(const VideoInfo& vi) const AVS_BakedCode( return AVS_LinkCall(IsSameColorspace)(vi) ) 514 | 515 | }; // end struct VideoInfo 516 | 517 | 518 | 519 | 520 | // VideoFrameBuffer holds information about a memory block which is used 521 | // for video data. For efficiency, instances of this class are not deleted 522 | // when the refcount reaches zero; instead they're stored in a linked list 523 | // to be reused. The instances are deleted when the corresponding AVS 524 | // file is closed. 525 | 526 | class VideoFrameBuffer { 527 | BYTE* const data; 528 | const size_t data_size; 529 | // sequence_number is incremented every time the buffer is changed, so 530 | // that stale views can tell they're no longer valid. 531 | volatile long sequence_number; 532 | 533 | friend class VideoFrame; 534 | friend class Cache; 535 | friend class ScriptEnvironment; 536 | volatile long refcount; 537 | 538 | protected: 539 | VideoFrameBuffer(size_t size); 540 | VideoFrameBuffer(); 541 | ~VideoFrameBuffer(); 542 | 543 | public: 544 | const BYTE* GetReadPtr() const AVS_BakedCode( return AVS_LinkCall(VFBGetReadPtr)() ) 545 | BYTE* GetWritePtr() AVS_BakedCode( return AVS_LinkCall(VFBGetWritePtr)() ) 546 | size_t GetDataSize() const AVS_BakedCode( return AVS_LinkCall(GetDataSize)() ) 547 | int GetSequenceNumber() const AVS_BakedCode( return AVS_LinkCall(GetSequenceNumber)() ) 548 | int GetRefcount() const AVS_BakedCode( return AVS_LinkCall(GetRefcount)() ) 549 | }; // end class VideoFrameBuffer 550 | 551 | 552 | // VideoFrame holds a "window" into a VideoFrameBuffer. Operator new 553 | // is overloaded to recycle class instances. 554 | 555 | class VideoFrame { 556 | volatile long refcount; 557 | VideoFrameBuffer* const vfb; 558 | const size_t offset; 559 | const int pitch, row_size, height; 560 | const size_t offsetU, offsetV; // U&V offsets are from top of picture. 561 | const int pitchUV, row_sizeUV, heightUV; 562 | 563 | friend class PVideoFrame; 564 | void AddRef(); 565 | void Release(); 566 | 567 | friend class ScriptEnvironment; 568 | friend class Cache; 569 | 570 | VideoFrame(VideoFrameBuffer* _vfb, size_t _offset, int _pitch, int _row_size, int _height); 571 | VideoFrame(VideoFrameBuffer* _vfb, size_t _offset, int _pitch, int _row_size, int _height, 572 | size_t _offsetU, size_t _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV); 573 | 574 | void* operator new(unsigned size); 575 | // TESTME: OFFSET U/V may be switched to what could be expected from AVI standard! 576 | public: 577 | int GetPitch(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetPitch)(plane) ) 578 | int GetRowSize(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetRowSize)(plane) ) 579 | int GetHeight(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetHeight)(plane) ) 580 | 581 | // generally you shouldn't use these three 582 | VideoFrameBuffer* GetFrameBuffer() const AVS_BakedCode( return AVS_LinkCall(GetFrameBuffer)() ) 583 | size_t GetOffset(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetOffset)(plane) ) 584 | 585 | // in plugins use env->SubFrame() -- because implementation code is only available inside avisynth.dll. Doh! 586 | VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const; 587 | VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, 588 | int rel_offsetU, int rel_offsetV, int pitchUV) const; 589 | 590 | const BYTE* GetReadPtr(int plane=0) const AVS_BakedCode( return AVS_LinkCall(VFGetReadPtr)(plane) ) 591 | bool IsWritable() const AVS_BakedCode( return AVS_LinkCall(IsWritable)() ) 592 | BYTE* GetWritePtr(int plane=0) const AVS_BakedCode( return AVS_LinkCall(VFGetWritePtr)(plane) ) 593 | 594 | ~VideoFrame() AVS_BakedCode( AVS_LinkCall(VideoFrame_DESTRUCTOR)() ) 595 | #ifdef AVISYNTH_CORE 596 | public: 597 | void DESTRUCTOR(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ 598 | #endif 599 | }; // end class VideoFrame 600 | 601 | enum { 602 | // Values 0 to 5 are reserved for old 2.5 plugins 603 | // do not use them in new plugins 604 | 605 | // New 2.6 explicitly defined cache hints. 606 | CACHE_NOTHING=10, // Do not cache video. 607 | CACHE_WINDOW=11, // Hard protect upto X frames within a range of X from the current frame N. 608 | CACHE_GENERIC=12, // LRU cache upto X frames. 609 | CACHE_FORCE_GENERIC=13, // LRU cache upto X frames, override any previous CACHE_WINDOW. 610 | 611 | CACHE_GET_POLICY=30, // Get the current policy. 612 | CACHE_GET_WINDOW=31, // Get the current window h_span. 613 | CACHE_GET_RANGE=32, // Get the current generic frame range. 614 | 615 | CACHE_AUDIO=50, // Explicitly cache audio, X byte cache. 616 | CACHE_AUDIO_NOTHING=51, // Explicitly do not cache audio. 617 | CACHE_AUDIO_NONE=52, // Audio cache off (auto mode), X byte intial cache. 618 | CACHE_AUDIO_AUTO=53, // Audio cache on (auto mode), X byte intial cache. 619 | 620 | CACHE_GET_AUDIO_POLICY=70, // Get the current audio policy. 621 | CACHE_GET_AUDIO_SIZE=71, // Get the current audio cache size. 622 | 623 | CACHE_PREFETCH_FRAME=100, // Queue request to prefetch frame N. 624 | CACHE_PREFETCH_GO=101, // Action video prefetches. 625 | 626 | CACHE_PREFETCH_AUDIO_BEGIN=120, // Begin queue request transaction to prefetch audio (take critical section). 627 | CACHE_PREFETCH_AUDIO_STARTLO=121, // Set low 32 bits of start. 628 | CACHE_PREFETCH_AUDIO_STARTHI=122, // Set high 32 bits of start. 629 | CACHE_PREFETCH_AUDIO_COUNT=123, // Set low 32 bits of length. 630 | CACHE_PREFETCH_AUDIO_COMMIT=124, // Enqueue request transaction to prefetch audio (release critical section). 631 | CACHE_PREFETCH_AUDIO_GO=125, // Action audio prefetches. 632 | 633 | CACHE_GETCHILD_CACHE_MODE=200, // Cache ask Child for desired video cache mode. 634 | CACHE_GETCHILD_CACHE_SIZE=201, // Cache ask Child for desired video cache size. 635 | CACHE_GETCHILD_AUDIO_MODE=202, // Cache ask Child for desired audio cache mode. 636 | CACHE_GETCHILD_AUDIO_SIZE=203, // Cache ask Child for desired audio cache size. 637 | 638 | CACHE_GETCHILD_COST=220, // Cache ask Child for estimated processing cost. 639 | CACHE_COST_ZERO=221, // Child response of zero cost (ptr arithmetic only). 640 | CACHE_COST_UNIT=222, // Child response of unit cost (less than or equal 1 full frame blit). 641 | CACHE_COST_LOW=223, // Child response of light cost. (Fast) 642 | CACHE_COST_MED=224, // Child response of medium cost. (Real time) 643 | CACHE_COST_HI=225, // Child response of heavy cost. (Slow) 644 | 645 | CACHE_GETCHILD_THREAD_MODE=240, // Cache ask Child for thread safetyness. 646 | CACHE_THREAD_UNSAFE=241, // Only 1 thread allowed for all instances. 2.5 filters default! 647 | CACHE_THREAD_CLASS=242, // Only 1 thread allowed for each instance. 2.6 filters default! 648 | CACHE_THREAD_SAFE=243, // Allow all threads in any instance. 649 | CACHE_THREAD_OWN=244, // Safe but limit to 1 thread, internally threaded. 650 | 651 | CACHE_GETCHILD_ACCESS_COST=260, // Cache ask Child for preferred access pattern. 652 | CACHE_ACCESS_RAND=261, // Filter is access order agnostic. 653 | CACHE_ACCESS_SEQ0=262, // Filter prefers sequential access (low cost) 654 | CACHE_ACCESS_SEQ1=263, // Filter needs sequential access (high cost) 655 | 656 | }; 657 | 658 | // Base class for all filters. 659 | class IClip { 660 | friend class PClip; 661 | friend class AVSValue; 662 | volatile long refcnt; 663 | void AddRef(); 664 | void Release(); 665 | public: 666 | IClip() : refcnt(0) {} 667 | virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; } 668 | virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0; 669 | virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame 670 | virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0; // start and count are in samples 671 | /* Need to check GetVersion first, pre v5 will return random crap from EAX reg. */ 672 | virtual int __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter. 673 | virtual const VideoInfo& __stdcall GetVideoInfo() = 0; 674 | virtual __stdcall ~IClip() {} 675 | }; // end class IClip 676 | 677 | 678 | // smart pointer to IClip 679 | class PClip { 680 | 681 | IClip* p; 682 | 683 | IClip* GetPointerWithAddRef() const; 684 | friend class AVSValue; 685 | friend class VideoFrame; 686 | 687 | void Init(IClip* x); 688 | void Set(IClip* x); 689 | 690 | public: 691 | PClip() AVS_BakedCode( AVS_LinkCall(PClip_CONSTRUCTOR0)() ) 692 | PClip(const PClip& x) AVS_BakedCode( AVS_LinkCall(PClip_CONSTRUCTOR1)(x) ) 693 | PClip(IClip* x) AVS_BakedCode( AVS_LinkCall(PClip_CONSTRUCTOR2)(x) ) 694 | void operator=(IClip* x) AVS_BakedCode( AVS_LinkCall(PClip_OPERATOR_ASSIGN0)(x) ) 695 | void operator=(const PClip& x) AVS_BakedCode( AVS_LinkCall(PClip_OPERATOR_ASSIGN1)(x) ) 696 | 697 | IClip* operator->() const { return p; } 698 | 699 | // useful in conditional expressions 700 | operator void*() const { return p; } 701 | bool operator!() const { return !p; } 702 | 703 | ~PClip() AVS_BakedCode( AVS_LinkCall(PClip_DESTRUCTOR)() ) 704 | #ifdef AVISYNTH_CORE 705 | public: 706 | void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ 707 | void CONSTRUCTOR1(const PClip& x); 708 | void CONSTRUCTOR2(IClip* x); 709 | void OPERATOR_ASSIGN0(IClip* x); 710 | void OPERATOR_ASSIGN1(const PClip& x); 711 | void DESTRUCTOR(); 712 | #endif 713 | }; // end class PClip 714 | 715 | 716 | // smart pointer to VideoFrame 717 | class PVideoFrame { 718 | 719 | VideoFrame* p; 720 | 721 | void Init(VideoFrame* x); 722 | void Set(VideoFrame* x); 723 | 724 | public: 725 | PVideoFrame() AVS_BakedCode( AVS_LinkCall(PVideoFrame_CONSTRUCTOR0)() ) 726 | PVideoFrame(const PVideoFrame& x) AVS_BakedCode( AVS_LinkCall(PVideoFrame_CONSTRUCTOR1)(x) ) 727 | PVideoFrame(VideoFrame* x) AVS_BakedCode( AVS_LinkCall(PVideoFrame_CONSTRUCTOR2)(x) ) 728 | void operator=(VideoFrame* x) AVS_BakedCode( AVS_LinkCall(PVideoFrame_OPERATOR_ASSIGN0)(x) ) 729 | void operator=(const PVideoFrame& x) AVS_BakedCode( AVS_LinkCall(PVideoFrame_OPERATOR_ASSIGN1)(x) ) 730 | 731 | VideoFrame* operator->() const { return p; } 732 | 733 | // for conditional expressions 734 | operator void*() const { return p; } 735 | bool operator!() const { return !p; } 736 | 737 | ~PVideoFrame() AVS_BakedCode( AVS_LinkCall(PVideoFrame_DESTRUCTOR)() ) 738 | #ifdef AVISYNTH_CORE 739 | public: 740 | void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ 741 | void CONSTRUCTOR1(const PVideoFrame& x); 742 | void CONSTRUCTOR2(VideoFrame* x); 743 | void OPERATOR_ASSIGN0(VideoFrame* x); 744 | void OPERATOR_ASSIGN1(const PVideoFrame& x); 745 | void DESTRUCTOR(); 746 | #endif 747 | }; // end class PVideoFrame 748 | 749 | 750 | class AVSValue { 751 | public: 752 | 753 | AVSValue() AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR0)() ) 754 | AVSValue(IClip* c) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR1)(c) ) 755 | AVSValue(const PClip& c) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR2)(c) ) 756 | AVSValue(bool b) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR3)(b) ) 757 | AVSValue(int i) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR4)(i) ) 758 | // AVSValue(__int64 l); 759 | AVSValue(float f) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR5)(f) ) 760 | AVSValue(double f) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR6)(f) ) 761 | AVSValue(const char* s) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR7)(s) ) 762 | AVSValue(const AVSValue* a, int size) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR8)(a, size) ) 763 | AVSValue(const AVSValue& v) AVS_BakedCode( AVS_LinkCall(AVSValue_CONSTRUCTOR9)(v) ) 764 | 765 | ~AVSValue() AVS_BakedCode( AVS_LinkCall(AVSValue_DESTRUCTOR)() ) 766 | AVSValue& operator=(const AVSValue& v) AVS_BakedCode( return AVS_LinkCallV(AVSValue_OPERATOR_ASSIGN)(v) ) 767 | 768 | // Note that we transparently allow 'int' to be treated as 'float'. 769 | // There are no int<->bool conversions, though. 770 | 771 | bool Defined() const AVS_BakedCode( return AVS_LinkCall(Defined)() ) 772 | bool IsClip() const AVS_BakedCode( return AVS_LinkCall(IsClip)() ) 773 | bool IsBool() const AVS_BakedCode( return AVS_LinkCall(IsBool)() ) 774 | bool IsInt() const AVS_BakedCode( return AVS_LinkCall(IsInt)() ) 775 | // bool IsLong() const; 776 | bool IsFloat() const AVS_BakedCode( return AVS_LinkCall(IsFloat)() ) 777 | bool IsString() const AVS_BakedCode( return AVS_LinkCall(IsString)() ) 778 | bool IsArray() const AVS_BakedCode( return AVS_LinkCall(IsArray)() ) 779 | 780 | PClip AsClip() const AVS_BakedCode( return AVS_LinkCall(AsClip)() ) 781 | bool AsBool() const AVS_BakedCode( return AVS_LinkCall(AsBool1)() ) 782 | int AsInt() const AVS_BakedCode( return AVS_LinkCall(AsInt1)() ) 783 | // int AsLong() const; 784 | const char* AsString() const AVS_BakedCode( return AVS_LinkCall(AsString1)() ) 785 | double AsFloat() const AVS_BakedCode( return AVS_LinkCall(AsFloat1)() ) 786 | 787 | bool AsBool(bool def) const AVS_BakedCode( return AVS_LinkCall(AsBool2)(def) ) 788 | int AsInt(int def) const AVS_BakedCode( return AVS_LinkCall(AsInt2)(def) ) 789 | double AsDblDef(double def) const AVS_BakedCode( return AVS_LinkCall(AsDblDef)(def) ) // Value is still a float 790 | //float AsFloat(double def) const; // def demoted to a float 791 | double AsFloat(float def) const AVS_BakedCode( return AVS_LinkCall(AsFloat2)(def) ) 792 | const char* AsString(const char* def) const AVS_BakedCode( return AVS_LinkCall(AsString2)(def) ) 793 | 794 | int ArraySize() const AVS_BakedCode( return AVS_LinkCall(ArraySize)() ) 795 | 796 | const AVSValue& operator[](int index) const AVS_BakedCode( return AVS_LinkCallV(AVSValue_OPERATOR_INDEX)(index) ) 797 | 798 | private: 799 | #ifdef AVISYNTH64 800 | union { 801 | IClip* clip; 802 | bool boolean; 803 | __int64 integer; 804 | double floating_pt; 805 | const char* string; 806 | const AVSValue* array; 807 | }; 808 | #endif 809 | short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong 810 | short array_size; 811 | #ifndef AVISYNTH64 812 | union { 813 | IClip* clip; 814 | bool boolean; 815 | int integer; 816 | float floating_pt; 817 | const char* string; 818 | const AVSValue* array; 819 | // __int64 longlong; 820 | }; 821 | #endif 822 | 823 | void Assign(const AVSValue* src, bool init); 824 | #ifdef AVISYNTH_CORE 825 | public: 826 | void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ 827 | void CONSTRUCTOR1(IClip* c); 828 | void CONSTRUCTOR2(const PClip& c); 829 | void CONSTRUCTOR3(bool b); 830 | void CONSTRUCTOR4(int i); 831 | void CONSTRUCTOR5(float f); 832 | void CONSTRUCTOR6(double f); 833 | void CONSTRUCTOR7(const char* s); 834 | void CONSTRUCTOR8(const AVSValue* a, int size); 835 | void CONSTRUCTOR9(const AVSValue& v); 836 | void DESTRUCTOR(); 837 | AVSValue& OPERATOR_ASSIGN(const AVSValue& v); 838 | const AVSValue& OPERATOR_INDEX(int index) const; 839 | 840 | bool AsBool1() const; 841 | int AsInt1() const; 842 | const char* AsString1() const; 843 | double AsFloat1() const; 844 | 845 | bool AsBool2(bool def) const; 846 | int AsInt2(int def) const; 847 | double AsFloat2(float def) const; 848 | const char* AsString2(const char* def) const; 849 | #endif 850 | }; // end class AVSValue 851 | 852 | #undef AVS_LinkCallV 853 | #undef AVS_LinkCall 854 | #undef AVS_BakedCode 855 | 856 | // instantiable null filter 857 | class GenericVideoFilter : public IClip { 858 | protected: 859 | PClip child; 860 | VideoInfo vi; 861 | public: 862 | GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); } 863 | PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); } 864 | void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); } 865 | const VideoInfo& __stdcall GetVideoInfo() { return vi; } 866 | bool __stdcall GetParity(int n) { return child->GetParity(n); } 867 | int __stdcall SetCacheHints(int cachehints,int frame_range) { return 0; } ; // We do not pass cache requests upwards, only to the next filter. 868 | }; 869 | 870 | 871 | 872 | 873 | // For GetCPUFlags. These are backwards-compatible with those in VirtualDub. 874 | enum { 875 | /* oldest CPU to support extension */ 876 | CPUF_FORCE = 0x01, // N/A 877 | CPUF_FPU = 0x02, // 386/486DX 878 | CPUF_MMX = 0x04, // P55C, K6, PII 879 | CPUF_INTEGER_SSE = 0x08, // PIII, Athlon 880 | CPUF_SSE = 0x10, // PIII, Athlon XP/MP 881 | CPUF_SSE2 = 0x20, // PIV, K8 882 | CPUF_3DNOW = 0x40, // K6-2 883 | CPUF_3DNOW_EXT = 0x80, // Athlon 884 | CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which 885 | // only Hammer will have anyway) 886 | CPUF_SSE3 = 0x100, // PIV+, K8 Venice 887 | CPUF_SSSE3 = 0x200, // Core 2 888 | CPUF_SSE4 = 0x400, // Penryn, Wolfdale, Yorkfield 889 | CPUF_SSE4_1 = 0x400, 890 | //CPUF_AVX = 0x800, // Sandy Bridge, Bulldozer 891 | CPUF_SSE4_2 = 0x1000, // Nehalem 892 | //CPUF_AVX2 = 0x2000, // Haswell 893 | //CPUF_AVX512 = 0x4000, // Knights Landing 894 | }; 895 | 896 | 897 | 898 | class IScriptEnvironment { 899 | public: 900 | virtual __stdcall ~IScriptEnvironment() {} 901 | 902 | virtual /*static*/ long __stdcall GetCPUFlags() = 0; 903 | 904 | virtual char* __stdcall SaveString(const char* s, int length = -1) = 0; 905 | virtual char* __stdcall Sprintf(const char* fmt, ...) = 0; 906 | // note: val is really a va_list; I hope everyone typedefs va_list to a pointer 907 | virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0; 908 | 909 | __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0; 910 | 911 | class NotFound /*exception*/ {}; // thrown by Invoke and GetVar 912 | 913 | typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env); 914 | 915 | virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0; 916 | virtual bool __stdcall FunctionExists(const char* name) = 0; 917 | virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char* const* arg_names=0) = 0; 918 | 919 | virtual AVSValue __stdcall GetVar(const char* name) = 0; 920 | virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0; 921 | virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0; 922 | 923 | virtual void __stdcall PushContext(int level=0) = 0; 924 | virtual void __stdcall PopContext() = 0; 925 | 926 | virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0; 927 | 928 | virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0; 929 | 930 | virtual void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0; 931 | 932 | typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env); 933 | virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0; 934 | 935 | virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0; 936 | 937 | virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0; 938 | 939 | virtual int __stdcall SetMemoryMax(int mem) = 0; 940 | 941 | virtual int __stdcall SetWorkingDir(const char * newdir) = 0; 942 | 943 | virtual void* __stdcall ManageCache(int key, void* data) = 0; 944 | 945 | enum PlanarChromaAlignmentMode { 946 | PlanarChromaAlignmentOff, 947 | PlanarChromaAlignmentOn, 948 | PlanarChromaAlignmentTest }; 949 | 950 | virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0; 951 | 952 | virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, 953 | int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0; 954 | 955 | virtual void __stdcall DeleteScriptEnvironment() = 0; 956 | 957 | virtual void _stdcall ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, 958 | int textcolor, int halocolor, int bgcolor) = 0; 959 | 960 | virtual const AVS_Linkage* const __stdcall GetAVSLinkage() = 0; 961 | 962 | // noThrow version of GetVar 963 | virtual AVSValue __stdcall GetVarDef(const char* name, const AVSValue& def=AVSValue()) = 0; 964 | 965 | }; // end class IScriptEnvironment 966 | 967 | 968 | // avisynth.dll exports this; it's a way to use it as a library, without 969 | // writing an AVS script or without going through AVIFile. 970 | IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION); 971 | 972 | 973 | #pragma pack(pop) 974 | 975 | #endif //__AVISYNTH_6_H__ 976 | --------------------------------------------------------------------------------