/* * (C) 2003-2006 Gabest * (C) 2006-2014, 2016 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include #include #include "resource.h" #include "../../../Subtitles/VobSubFile.h" #include "../../../Subtitles/RTS.h" #include "../../../SubPic/MemSubPic.h" #include "../../../SubPic/SubPicQueueImpl.h" #include "vfr.h" #ifndef _WIN64 #include "vd2/extras/FilterSDK/VirtualDub.h" #else #include "vd2/plugin/vdplugin.h" #include "vd2/plugin/vdvideofilt.h" #endif // Size of the char buffer according to VirtualDub Filters SDK doc #define STRING_PROC_BUFFER_SIZE 128 // // Generic interface // namespace Plugin { class CFilter : public CAMThread, public CCritSec { private: CString m_fn; protected: float m_fps; CCritSec m_csSubLock; CComPtr m_pSubPicQueue; CComPtr m_pSubPicProvider; DWORD_PTR m_SubPicProviderId; public: CFilter() : m_fps(-1) , m_SubPicProviderId(0) { CAMThread::Create(); } virtual ~CFilter() { CAMThread::CallWorker(0); } CString GetFileName() { CAutoLock cAutoLock(this); return m_fn; } void SetFileName(CString fn) { CAutoLock cAutoLock(this); m_fn = fn; } bool Render(SubPicDesc& dst, REFERENCE_TIME rt, float fps) { if (!m_pSubPicProvider) { return false; } CSize size(dst.w, dst.h); if (!m_pSubPicQueue) { CComPtr pAllocator = DEBUG_NEW CMemSubPicAllocator(dst.type, size); HRESULT hr = E_FAIL; if (!(m_pSubPicQueue = DEBUG_NEW CSubPicQueueNoThread(SubPicQueueSettings(0, 0, false, 50, 100, false), pAllocator, &hr)) || FAILED(hr)) { m_pSubPicQueue = nullptr; return false; } } if (m_SubPicProviderId != (DWORD_PTR)(ISubPicProvider*)m_pSubPicProvider) { m_pSubPicQueue->SetSubPicProvider(m_pSubPicProvider); m_SubPicProviderId = (DWORD_PTR)(ISubPicProvider*)m_pSubPicProvider; } CComPtr pSubPic; if (!m_pSubPicQueue->LookupSubPic(rt, pSubPic)) { return false; } CRect r; pSubPic->GetDirtyRect(r); if (dst.type == MSP_RGB32 || dst.type == MSP_RGB24 || dst.type == MSP_RGB16 || dst.type == MSP_RGB15) { dst.h = -dst.h; } pSubPic->AlphaBlt(r, r, &dst); return true; } DWORD ThreadProc() { SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST); CAtlArray handles; handles.Add(GetRequestHandle()); CString fn = GetFileName(); CFileStatus fs; fs.m_mtime = 0; CFileGetStatus(fn, fs); for (;;) { DWORD i = WaitForMultipleObjects((DWORD)handles.GetCount(), handles.GetData(), FALSE, 1000); if (WAIT_OBJECT_0 == i) { Reply(S_OK); break; } else if (WAIT_OBJECT_0 + 1 >= i && i <= WAIT_OBJECT_0 + handles.GetCount()) { if (FindNextChangeNotification(handles[i - WAIT_OBJECT_0])) { CFileStatus fs2; fs2.m_mtime = 0; CFileGetStatus(fn, fs2); if (fs.m_mtime < fs2.m_mtime) { fs.m_mtime = fs2.m_mtime; if (CComQIPtr pSubStream = m_pSubPicProvider) { CAutoLock cAutoLock(&m_csSubLock); pSubStream->Reload(); } } } } else if (WAIT_TIMEOUT == i) { CString fn2 = GetFileName(); if (fn != fn2) { CPath p(fn2); p.RemoveFileSpec(); HANDLE h = FindFirstChangeNotification(p, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); if (h != INVALID_HANDLE_VALUE) { fn = fn2; handles.SetCount(1); handles.Add(h); } } } else { // if (WAIT_ABANDONED_0 == i || WAIT_FAILED == i) break; } } m_hThread = 0; for (size_t i = 1; i < handles.GetCount(); i++) { FindCloseChangeNotification(handles[i]); } return 0; } }; class CVobSubFilter : virtual public CFilter { public: CVobSubFilter(CString fn = _T("")) { if (!fn.IsEmpty()) { Open(fn); } } bool Open(CString fn) { SetFileName(_T("")); m_pSubPicProvider = nullptr; if (CVobSubFile* vsf = DEBUG_NEW CVobSubFile(&m_csSubLock)) { m_pSubPicProvider = (ISubPicProvider*)vsf; if (vsf->Open(CString(fn))) { SetFileName(fn); } else { m_pSubPicProvider = nullptr; } } return !!m_pSubPicProvider; } }; class CTextSubFilter : virtual public CFilter { int m_CharSet; public: CTextSubFilter(CString fn = _T(""), int CharSet = DEFAULT_CHARSET, float fps = -1) : m_CharSet(CharSet) { m_fps = fps; if (!fn.IsEmpty()) { Open(fn, CharSet); } } int GetCharSet() { return m_CharSet; } bool Open(CString fn, int CharSet = DEFAULT_CHARSET) { SetFileName(_T("")); m_pSubPicProvider = nullptr; if (!m_pSubPicProvider) { if (CRenderedTextSubtitle* rts = DEBUG_NEW CRenderedTextSubtitle(&m_csSubLock)) { m_pSubPicProvider = (ISubPicProvider*)rts; if (rts->Open(CString(fn), CharSet)) { SetFileName(fn); } else { m_pSubPicProvider = nullptr; } } } return !!m_pSubPicProvider; } }; #ifndef _WIN64 // // old VirtualDub interface // namespace VirtualDub { class CVirtualDubFilter : virtual public CFilter { public: CVirtualDubFilter() {} virtual ~CVirtualDubFilter() {} virtual int RunProc(const FilterActivation* fa, const FilterFunctions* ff) { SubPicDesc dst; dst.type = MSP_RGB32; dst.w = fa->src.w; dst.h = fa->src.h; dst.bpp = 32; dst.pitch = fa->src.pitch; dst.bits = (BYTE*)fa->src.data; Render(dst, 10000i64 * fa->pfsi->lSourceFrameMS, 1000.0f / fa->pfsi->lMicrosecsPerFrame); return 0; } virtual long ParamProc(FilterActivation* fa, const FilterFunctions* ff) { fa->dst.offset = fa->src.offset; fa->dst.modulo = fa->src.modulo; fa->dst.pitch = fa->src.pitch; return 0; } virtual int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd) = 0; virtual void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str) = 0; virtual bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen) = 0; }; class CVobSubVirtualDubFilter : public CVobSubFilter, public CVirtualDubFilter { public: CVobSubVirtualDubFilter(CString fn = _T("")) : CVobSubFilter(fn) {} int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CFileDialog fd(TRUE, nullptr, GetFileName(), OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY, _T("VobSub files (*.idx;*.sub)|*.idx;*.sub||"), CWnd::FromHandle(hwnd), 0); if (fd.DoModal() != IDOK) { return 1; } return Open(fd.GetPathName()) ? 0 : 1; } void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str) { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (%s)", GetFileName().IsEmpty() ? " (empty)" : CStringA(GetFileName())); } bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen) { CStringA fn(GetFileName()); fn.Replace("\\", "\\\\"); _snprintf_s(buf, buflen, buflen, "Config(\"%s\")", fn); return true; } }; class CTextSubVirtualDubFilter : public CTextSubFilter, public CVirtualDubFilter { public: CTextSubVirtualDubFilter(CString fn = _T(""), int CharSet = DEFAULT_CHARSET) : CTextSubFilter(fn, CharSet) {} int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); const TCHAR formats[] = _T("TextSub files (*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt)|*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt||"); CFileDialog fd(TRUE, nullptr, GetFileName(), OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK, formats, CWnd::FromHandle(hwnd), sizeof(OPENFILENAME)); UINT_PTR CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam); fd.m_pOFN->hInstance = AfxGetResourceHandle(); fd.m_pOFN->lpTemplateName = MAKEINTRESOURCE(IDD_TEXTSUBOPENTEMPLATE); fd.m_pOFN->lpfnHook = (LPOFNHOOKPROC)OpenHookProc; fd.m_pOFN->lCustData = (LPARAM)DEFAULT_CHARSET; if (fd.DoModal() != IDOK) { return 1; } return Open(fd.GetPathName(), fd.m_pOFN->lCustData) ? 0 : 1; } void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str) { if (!GetFileName().IsEmpty()) { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (%s, %d)", CStringA(GetFileName()), GetCharSet()); } else { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (empty)"); } } bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen) { CStringA fn(GetFileName()); fn.Replace("\\", "\\\\"); _snprintf_s(buf, buflen, buflen, "Config(\"%s\", %d)", fn, GetCharSet()); return true; } }; int vobsubInitProc(FilterActivation* fa, const FilterFunctions* ff) { *(CVirtualDubFilter**)fa->filter_data = DEBUG_NEW CVobSubVirtualDubFilter(); return !(*(CVirtualDubFilter**)fa->filter_data); } int textsubInitProc(FilterActivation* fa, const FilterFunctions* ff) { *(CVirtualDubFilter**)fa->filter_data = DEBUG_NEW CTextSubVirtualDubFilter(); return !(*(CVirtualDubFilter**)fa->filter_data); } void baseDeinitProc(FilterActivation* fa, const FilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; SAFE_DELETE(f); } int baseRunProc(const FilterActivation* fa, const FilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->RunProc(fa, ff) : 1; } long baseParamProc(FilterActivation* fa, const FilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->ParamProc(fa, ff) : 1; } int baseConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->ConfigProc(fa, ff, hwnd) : 1; } void baseStringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { f->StringProc(fa, ff, str); } } bool baseFssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->FssProc(fa, ff, buf, buflen) : false; } void vobsubScriptConfig(IScriptInterpreter* isi, void* lpVoid, CScriptValue* argv, int argc) { FilterActivation* fa = (FilterActivation*)lpVoid; CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { delete f; } f = DEBUG_NEW CVobSubVirtualDubFilter(CString(*argv[0].asString())); *(CVirtualDubFilter**)fa->filter_data = f; } void textsubScriptConfig(IScriptInterpreter* isi, void* lpVoid, CScriptValue* argv, int argc) { FilterActivation* fa = (FilterActivation*)lpVoid; CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { delete f; } f = DEBUG_NEW CTextSubVirtualDubFilter(CString(*argv[0].asString()), argv[1].asInt()); *(CVirtualDubFilter**)fa->filter_data = f; } ScriptFunctionDef vobsub_func_defs[] = { { (ScriptFunctionPtr)vobsubScriptConfig, const_cast("Config"), const_cast("0s") }, { nullptr }, }; CScriptObject vobsub_obj = { nullptr, vobsub_func_defs }; struct FilterDefinition filterDef_vobsub = { nullptr, nullptr, nullptr, // next, prev, module "VobSub", // name "Adds subtitles from a vob sequence.", // desc "MPC-HC", // maker nullptr, // private_data sizeof(CVirtualDubFilter**), // inst_data_size vobsubInitProc, // initProc baseDeinitProc, // deinitProc baseRunProc, // runProc baseParamProc, // paramProc baseConfigProc, // configProc baseStringProc, // stringProc nullptr, // startProc nullptr, // endProc &vobsub_obj, // script_obj baseFssProc, // fssProc }; ScriptFunctionDef textsub_func_defs[] = { { (ScriptFunctionPtr)textsubScriptConfig, const_cast("Config"), const_cast("0si") }, { nullptr }, }; CScriptObject textsub_obj = { nullptr, textsub_func_defs }; struct FilterDefinition filterDef_textsub = { nullptr, nullptr, nullptr, // next, prev, module "TextSub", // name "Adds subtitles from srt, sub, psb, smi, ssa, ass file formats.", // desc "MPC-HC", // maker nullptr, // private_data sizeof(CVirtualDubFilter**), // inst_data_size textsubInitProc, // initProc baseDeinitProc, // deinitProc baseRunProc, // runProc baseParamProc, // paramProc baseConfigProc, // configProc baseStringProc, // stringProc nullptr, // startProc nullptr, // endProc &textsub_obj, // script_obj baseFssProc, // fssProc }; static FilterDefinition* fd_vobsub; static FilterDefinition* fd_textsub; extern "C" __declspec(dllexport) int __cdecl VirtualdubFilterModuleInit2(FilterModule* fm, const FilterFunctions* ff, int& vdfd_ver, int& vdfd_compat) { fd_vobsub = ff->addFilter(fm, &filterDef_vobsub, sizeof(FilterDefinition)); if (!fd_vobsub) { return 1; } fd_textsub = ff->addFilter(fm, &filterDef_textsub, sizeof(FilterDefinition)); if (!fd_textsub) { return 1; } vdfd_ver = VIRTUALDUB_FILTERDEF_VERSION; vdfd_compat = VIRTUALDUB_FILTERDEF_COMPATIBLE; return 0; } extern "C" __declspec(dllexport) void __cdecl VirtualdubFilterModuleDeinit(FilterModule* fm, const FilterFunctions* ff) { ff->removeFilter(fd_textsub); ff->removeFilter(fd_vobsub); } }/**/ #else // // VirtualDub new plugin interface sdk 1.1 // namespace VirtualDubNew { class CVirtualDubFilter : virtual public CFilter { public: CVirtualDubFilter() {} virtual ~CVirtualDubFilter() {} virtual int RunProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff) { SubPicDesc dst; dst.type = MSP_RGB32; dst.w = fa->src.w; dst.h = fa->src.h; dst.bpp = 32; dst.pitch = fa->src.pitch; dst.bits = (BYTE*)fa->src.data; Render(dst, 10000i64 * fa->pfsi->lSourceFrameMS, 1000.0f / fa->pfsi->lMicrosecsPerFrame); return 0; } virtual long ParamProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff) { fa->dst.offset = fa->src.offset; fa->dst.modulo = fa->src.modulo; fa->dst.pitch = fa->src.pitch; return 0; } virtual int ConfigProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, VDXHWND hwnd) = 0; virtual void StringProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* str) = 0; virtual bool FssProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* buf, int buflen) = 0; }; class CVobSubVirtualDubFilter : public CVobSubFilter, public CVirtualDubFilter { public: CVobSubVirtualDubFilter(CString fn = _T("")) : CVobSubFilter(fn) {} int ConfigProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, VDXHWND hwnd) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CFileDialog fd(TRUE, nullptr, GetFileName(), OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY, _T("VobSub files (*.idx;*.sub)|*.idx;*.sub||"), CWnd::FromHandle((HWND)hwnd), 0); if (fd.DoModal() != IDOK) { return 1; } return Open(fd.GetPathName()) ? 0 : 1; } void StringProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* str) { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (%s)", GetFileName().IsEmpty() ? " (empty)" : CStringA(GetFileName())); } bool FssProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* buf, int buflen) { CStringA fn(GetFileName()); fn.Replace("\\", "\\\\"); _snprintf_s(buf, buflen, buflen, "Config(\"%s\")", fn.GetString()); return true; } }; class CTextSubVirtualDubFilter : public CTextSubFilter, public CVirtualDubFilter { public: CTextSubVirtualDubFilter(CString fn = _T(""), int CharSet = DEFAULT_CHARSET) : CTextSubFilter(fn, CharSet) {} int ConfigProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, VDXHWND hwnd) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); /* off encoding changing */ #ifndef _DEBUG const TCHAR formats[] = _T("TextSub files (*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt)|*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt||"); CFileDialog fd(TRUE, nullptr, GetFileName(), OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK, formats, CWnd::FromHandle((HWND)hwnd), sizeof(OPENFILENAME)); UINT_PTR CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam); fd.m_pOFN->hInstance = AfxGetResourceHandle(); fd.m_pOFN->lpTemplateName = MAKEINTRESOURCE(IDD_TEXTSUBOPENTEMPLATE); fd.m_pOFN->lpfnHook = (LPOFNHOOKPROC)OpenHookProc; fd.m_pOFN->lCustData = (LPARAM)DEFAULT_CHARSET; #else const TCHAR formats[] = _T("TextSub files (*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt)|*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt||"); CFileDialog fd(TRUE, nullptr, GetFileName(), OFN_ENABLESIZING | OFN_HIDEREADONLY, formats, CWnd::FromHandle((HWND)hwnd), sizeof(OPENFILENAME)); #endif if (fd.DoModal() != IDOK) { return 1; } return Open(fd.GetPathName(), (int)fd.m_pOFN->lCustData) ? 0 : 1; } void StringProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* str) { if (!GetFileName().IsEmpty()) { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (%s, %d)", CStringA(GetFileName()).GetString(), GetCharSet()); } else { sprintf_s(str, STRING_PROC_BUFFER_SIZE, " (empty)"); } } bool FssProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* buf, int buflen) { CStringA fn(GetFileName()); fn.Replace("\\", "\\\\"); _snprintf_s(buf, buflen, buflen, "Config(\"%s\", %d)", fn.GetString(), GetCharSet()); return true; } }; int vobsubInitProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff) { return ((*(CVirtualDubFilter**)fa->filter_data = DEBUG_NEW CVobSubVirtualDubFilter()) == nullptr); } int textsubInitProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff) { return ((*(CVirtualDubFilter**)fa->filter_data = DEBUG_NEW CTextSubVirtualDubFilter()) == nullptr); } void baseDeinitProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; SAFE_DELETE(f); } int baseRunProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->RunProc(fa, ff) : 1; } long baseParamProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->ParamProc(fa, ff) : 1; } int baseConfigProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, VDXHWND hwnd) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->ConfigProc(fa, ff, hwnd) : 1; } void baseStringProc(const VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* str) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { f->StringProc(fa, ff, str); } } bool baseFssProc(VDXFilterActivation* fa, const VDXFilterFunctions* ff, char* buf, int buflen) { CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; return f ? f->FssProc(fa, ff, buf, buflen) : false; } void vobsubScriptConfig(IVDXScriptInterpreter* isi, void* lpVoid, VDXScriptValue* argv, int argc) { VDXFilterActivation* fa = (VDXFilterActivation*)lpVoid; CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { delete f; } f = DEBUG_NEW CVobSubVirtualDubFilter(CString(*argv[0].asString())); *(CVirtualDubFilter**)fa->filter_data = f; } void textsubScriptConfig(IVDXScriptInterpreter* isi, void* lpVoid, VDXScriptValue* argv, int argc) { VDXFilterActivation* fa = (VDXFilterActivation*)lpVoid; CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data; if (f) { delete f; } f = DEBUG_NEW CTextSubVirtualDubFilter(CString(*argv[0].asString()), argv[1].asInt()); *(CVirtualDubFilter**)fa->filter_data = f; } VDXScriptFunctionDef vobsub_func_defs[] = { { (VDXScriptFunctionPtr)vobsubScriptConfig, "Config", "0s" }, { nullptr }, }; VDXScriptObject vobsub_obj = { nullptr, vobsub_func_defs }; struct VDXFilterDefinition filterDef_vobsub = { nullptr, nullptr, nullptr, // next, prev, module "VobSub", // name "Adds subtitles from a vob sequence.", // desc "MPC-HC", // maker nullptr, // private_data sizeof(CVirtualDubFilter**), // inst_data_size vobsubInitProc, // initProc baseDeinitProc, // deinitProc baseRunProc, // runProc baseParamProc, // paramProc baseConfigProc, // configProc baseStringProc, // stringProc nullptr, // startProc nullptr, // endProc &vobsub_obj, // script_obj baseFssProc, // fssProc }; VDXScriptFunctionDef textsub_func_defs[] = { { (VDXScriptFunctionPtr)textsubScriptConfig, "Config", "0si" }, { nullptr }, }; VDXScriptObject textsub_obj = { nullptr, textsub_func_defs }; struct VDXFilterDefinition filterDef_textsub = { nullptr, nullptr, nullptr, // next, prev, module "TextSub", // name "Adds subtitles from srt, sub, psb, smi, ssa, ass file formats.", // desc "MPC-HC", // maker nullptr, // private_data sizeof(CVirtualDubFilter**), // inst_data_size textsubInitProc, // initProc baseDeinitProc, // deinitProc baseRunProc, // runProc baseParamProc, // paramProc baseConfigProc, // configProc baseStringProc, // stringProc nullptr, // startProc nullptr, // endProc &textsub_obj, // script_obj baseFssProc, // fssProc }; static VDXFilterDefinition* fd_vobsub; static VDXFilterDefinition* fd_textsub; extern "C" __declspec(dllexport) int __cdecl VirtualdubFilterModuleInit2(VDXFilterModule* fm, const VDXFilterFunctions* ff, int& vdfd_ver, int& vdfd_compat) { if (((fd_vobsub = ff->addFilter(fm, &filterDef_vobsub, sizeof(VDXFilterDefinition))) == nullptr) || ((fd_textsub = ff->addFilter(fm, &filterDef_textsub, sizeof(VDXFilterDefinition))) == nullptr)) { return 1; } vdfd_ver = VIRTUALDUB_FILTERDEF_VERSION; vdfd_compat = VIRTUALDUB_FILTERDEF_COMPATIBLE; return 0; } extern "C" __declspec(dllexport) void __cdecl VirtualdubFilterModuleDeinit(VDXFilterModule* fm, const VDXFilterFunctions* ff) { ff->removeFilter(fd_textsub); ff->removeFilter(fd_vobsub); } } #endif // // Avisynth interface // namespace AviSynth1 { #include "avisynth/avisynth1.h" class CAvisynthFilter : public GenericVideoFilter, virtual public CFilter { public: CAvisynthFilter(PClip c, IScriptEnvironment* env) : GenericVideoFilter(c) {} PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { PVideoFrame frame = child->GetFrame(n, env); env->MakeWritable(&frame); SubPicDesc dst; dst.w = vi.width; dst.h = vi.height; dst.pitch = frame->GetPitch(); dst.bits = frame->GetWritePtr(); dst.bpp = vi.BitsPerPixel(); dst.type = vi.IsRGB32() ? (env->GetVar("RGBA").AsBool() ? MSP_RGBA : MSP_RGB32) : vi.IsRGB24() ? MSP_RGB24 : vi.IsYUY2() ? MSP_YUY2 : -1; float fps = m_fps > 0 ? m_fps : (float)vi.fps_numerator / vi.fps_denominator; Render(dst, (REFERENCE_TIME)(10000000i64 * n / fps), fps); return frame; } }; class CVobSubAvisynthFilter : public CVobSubFilter, public CAvisynthFilter { public: CVobSubAvisynthFilter(PClip c, const char* fn, IScriptEnvironment* env) : CVobSubFilter(CString(fn)) , CAvisynthFilter(c, env) { if (!m_pSubPicProvider) { env->ThrowError("VobSub: Can't open \"%s\"", fn); } } }; AVSValue __cdecl VobSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env) { return (DEBUG_NEW CVobSubAvisynthFilter(args[0].AsClip(), args[1].AsString(), env)); } class CTextSubAvisynthFilter : public CTextSubFilter, public CAvisynthFilter { public: CTextSubAvisynthFilter(PClip c, IScriptEnvironment* env, const char* fn, int CharSet = DEFAULT_CHARSET, float fps = -1) : CTextSubFilter(CString(fn), CharSet, fps) , CAvisynthFilter(c, env) { if (!m_pSubPicProvider) { env->ThrowError("TextSub: Can't open \"%s\"", fn); } } }; AVSValue __cdecl TextSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env) { return (DEBUG_NEW CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString())); } AVSValue __cdecl TextSubCreateSI(AVSValue args, void* user_data, IScriptEnvironment* env) { return (DEBUG_NEW CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt())); } AVSValue __cdecl TextSubCreateSIF(AVSValue args, void* user_data, IScriptEnvironment* env) { return (DEBUG_NEW CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt(), (float)args[3].AsFloat())); } AVSValue __cdecl MaskSubCreateSIIFI(AVSValue args, void* user_data, IScriptEnvironment* env) { AVSValue rgb32("RGB32"); AVSValue tab[5] = { args[1], args[2], args[3], args[4], rgb32 }; AVSValue value(tab, 5); const char* nom[5] = { "width", "height", "fps", "length", "pixel_type" }; AVSValue clip(env->Invoke("Blackness", value, nom)); env->SetVar(env->SaveString("RGBA"), true); return (DEBUG_NEW CTextSubAvisynthFilter(clip.AsClip(), env, args[0].AsString())); } extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit(IScriptEnvironment* env) { env->AddFunction("VobSub", "cs", VobSubCreateS, 0); env->AddFunction("TextSub", "cs", TextSubCreateS, 0); env->AddFunction("TextSub", "csi", TextSubCreateSI, 0); env->AddFunction("TextSub", "csif", TextSubCreateSIF, 0); env->AddFunction("MaskSub", "siifi", MaskSubCreateSIIFI, 0); env->SetVar(env->SaveString("RGBA"), false); return nullptr; } } namespace AviSynth25 { #include "avisynth/avisynth25.h" static bool s_fSwapUV = false; class CAvisynthFilter : public GenericVideoFilter, virtual public CFilter { public: VFRTranslator* vfr; CAvisynthFilter(PClip c, IScriptEnvironment* env, VFRTranslator* _vfr = 0) : GenericVideoFilter(c), vfr(_vfr) {} PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { PVideoFrame frame = child->GetFrame(n, env); env->MakeWritable(&frame); SubPicDesc dst; dst.w = vi.width; dst.h = vi.height; dst.pitch = frame->GetPitch(); dst.pitchUV = frame->GetPitch(PLANAR_U); dst.bits = frame->GetWritePtr(); dst.bitsU = frame->GetWritePtr(PLANAR_U); dst.bitsV = frame->GetWritePtr(PLANAR_V); dst.bpp = dst.pitch / dst.w * 8; //vi.BitsPerPixel(); dst.type = vi.IsRGB32() ? (env->GetVar("RGBA").AsBool() ? MSP_RGBA : MSP_RGB32) : vi.IsRGB24() ? MSP_RGB24 : vi.IsYUY2() ? MSP_YUY2 : /*vi.IsYV12()*/ vi.pixel_type == VideoInfo::CS_YV12 ? (s_fSwapUV ? MSP_IYUV : MSP_YV12) : /*vi.IsIYUV()*/ vi.pixel_type == VideoInfo::CS_IYUV ? (s_fSwapUV ? MSP_YV12 : MSP_IYUV) : -1; float fps = m_fps > 0 ? m_fps : (float)vi.fps_numerator / vi.fps_denominator; REFERENCE_TIME timestamp; if (!vfr) { timestamp = (REFERENCE_TIME)(10000000i64 * n / fps); } else { timestamp = (REFERENCE_TIME)(10000000 * vfr->TimeStampFromFrameNumber(n)); } Render(dst, timestamp, fps); return frame; } }; class CVobSubAvisynthFilter : public CVobSubFilter, public CAvisynthFilter { public: CVobSubAvisynthFilter(PClip c, const char* fn, IScriptEnvironment* env) : CVobSubFilter(CString(fn)) , CAvisynthFilter(c, env) { if (!m_pSubPicProvider) { env->ThrowError("VobSub: Can't open \"%s\"", fn); } } }; AVSValue __cdecl VobSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env) { return (DEBUG_NEW CVobSubAvisynthFilter(args[0].AsClip(), args[1].AsString(), env)); } class CTextSubAvisynthFilter : public CTextSubFilter, public CAvisynthFilter { public: CTextSubAvisynthFilter(PClip c, IScriptEnvironment* env, const char* fn, int CharSet = DEFAULT_CHARSET, float fps = -1, VFRTranslator* vfr = 0) //vfr patch : CTextSubFilter(CString(fn), CharSet, fps) , CAvisynthFilter(c, env, vfr) { if (!m_pSubPicProvider) { env->ThrowError("TextSub: Can't open \"%s\"", fn); } } }; AVSValue __cdecl TextSubCreateGeneral(AVSValue args, void* user_data, IScriptEnvironment* env) { if (!args[1].Defined()) { env->ThrowError("TextSub: You must specify a subtitle file to use"); } VFRTranslator* vfr = 0; if (args[4].Defined()) { vfr = GetVFRTranslator(args[4].AsString()); } return (DEBUG_NEW CTextSubAvisynthFilter( args[0].AsClip(), env, args[1].AsString(), args[2].AsInt(DEFAULT_CHARSET), (float)args[3].AsFloat(-1), vfr)); } AVSValue __cdecl TextSubSwapUV(AVSValue args, void* user_data, IScriptEnvironment* env) { s_fSwapUV = args[0].AsBool(false); return AVSValue(); } AVSValue __cdecl MaskSubCreate(AVSValue args, void* user_data, IScriptEnvironment* env)/*SIIFI*/ { if (!args[0].Defined()) { env->ThrowError("MaskSub: You must specify a subtitle file to use"); } if (!args[3].Defined() && !args[6].Defined()) { env->ThrowError("MaskSub: You must specify either FPS or a VFR timecodes file"); } VFRTranslator* vfr = 0; if (args[6].Defined()) { vfr = GetVFRTranslator(args[6].AsString()); } AVSValue rgb32("RGB32"); AVSValue fps(args[3].AsFloat(25)); AVSValue tab[6] = { args[1], args[2], args[3], args[4], rgb32 }; AVSValue value(tab, 5); const char* nom[5] = { "width", "height", "fps", "length", "pixel_type" }; AVSValue clip(env->Invoke("Blackness", value, nom)); env->SetVar(env->SaveString("RGBA"), true); //return (DEBUG_NEW CTextSubAvisynthFilter(clip.AsClip(), env, args[0].AsString())); return (DEBUG_NEW CTextSubAvisynthFilter( clip.AsClip(), env, args[0].AsString(), args[5].AsInt(DEFAULT_CHARSET), (float)args[3].AsFloat(-1), vfr)); } extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env) { env->AddFunction("VobSub", "cs", VobSubCreateS, 0); env->AddFunction("TextSub", "c[file]s[charset]i[fps]f[vfr]s", TextSubCreateGeneral, 0); env->AddFunction("TextSubSwapUV", "b", TextSubSwapUV, 0); env->AddFunction("MaskSub", "[file]s[width]i[height]i[fps]f[length]i[charset]i[vfr]s", MaskSubCreate, 0); env->SetVar(env->SaveString("RGBA"), false); return nullptr; } } } UINT_PTR CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) { switch (uiMsg) { case WM_NOTIFY: { OPENFILENAME* ofn = ((OFNOTIFY*)lParam)->lpOFN; if (((NMHDR*)lParam)->code == CDN_FILEOK) { ofn->lCustData = (LPARAM)CharSetList[SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_GETCURSEL, 0, 0)]; } break; } case WM_INITDIALOG: { SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); for (ptrdiff_t i = 0; i < CharSetLen; i++) { CString s; s.Format(_T("%s (%u)"), CharSetNames[i], CharSetList[i]); SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)s); if (CharSetList[i] == (int)((OPENFILENAME*)lParam)->lCustData) { SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_SETCURSEL, i, 0); } } break; } default: break; } return FALSE; }