1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2019 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "thread_vapoursynth.h"
25 #include <MUtils/OSSupport.h>
26 #include <MUtils/Registry.h>
31 #include <QApplication>
34 #include <QAbstractFileEngine.h>
38 #include "model_sysinfo.h"
44 QMutex VapourSynthCheckThread::m_vpsLock;
45 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
46 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
49 static const char* const VPS_DLL_NAME = "vapoursynth.dll";
50 static const char* const VPS_EXE_NAME = "vspipe.exe";
51 static const char* const VPS_REG_PATH = "SOFTWARE\\VapourSynth";
52 static const char* const VPS_REG_NAME = "VapourSynthDLL";
54 //Default VapurSynth architecture
55 #if _WIN64 || __x86_64__
56 #define VAPOURSYNTH_DEF VAPOURSYNTH_X64
58 #define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
61 //Enable detection of "portabel" edition?
62 #define ENABLE_PORTABLE_VPS true
64 //-------------------------------------
66 //-------------------------------------
68 #define BOOLIFY(X) ((X) ? '1' : '0')
69 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
71 //-------------------------------------
73 //-------------------------------------
75 bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
77 QMutexLocker lock(&m_vpsLock);
79 sysinfo->clearVapourSynth();
80 sysinfo->clearVPS32Path();
81 sysinfo->clearVPS64Path();
84 VapourSynthCheckThread thread;
86 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
88 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
89 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
92 QTimer::singleShot(30000, &loop, SLOT(quit()));
94 qDebug("VapourSynth thread has been created, please wait...");
95 loop.exec(QEventLoop::ExcludeUserInputEvents);
96 qDebug("VapourSynth thread finished.");
98 QApplication::restoreOverrideCursor();
100 if (!thread.wait(1000))
102 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
108 if (thread.getException())
110 qWarning("VapourSynth thread encountered an exception !!!");
114 const int success = thread.getSuccess();
117 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
121 if (success & VAPOURSYNTH_X86)
123 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
124 sysinfo->setVPS32Path(thread.getPath32());
127 if (success & VAPOURSYNTH_X64)
129 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
130 sysinfo->setVPS64Path(thread.getPath64());
133 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
137 //-------------------------------------
139 //-------------------------------------
141 VapourSynthCheckThread::VapourSynthCheckThread(void)
143 m_vpsPath[0U].clear();
144 m_vpsPath[1U].clear();
147 VapourSynthCheckThread::~VapourSynthCheckThread(void)
151 void VapourSynthCheckThread::run(void)
153 m_vpsPath[0U].clear();
154 m_vpsPath[1U].clear();
158 int VapourSynthCheckThread::threadMain(void)
160 static const int VPS_BIT_FLAG[] =
166 static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
168 MUtils::Registry::scope_wow_x32,
169 MUtils::Registry::scope_wow_x64,
170 MUtils::Registry::scope_default
173 QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
176 //Look for "portable" VapourSynth version
177 for (size_t i = 0; i < 2U; i++)
179 const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
180 if (vpsPortableDir.exists())
182 const QFileInfo vpsPortableDll(vpsPortableDir.absoluteFilePath(VPS_DLL_NAME));
183 const QFileInfo vpsPortableExe(vpsPortableDir.absoluteFilePath(VPS_EXE_NAME));
184 if ((vpsPortableDll.exists() && vpsPortableDll.isFile()) || (vpsPortableExe.exists() && vpsPortableExe.isFile()))
186 vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
187 vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
192 //Read VapourSynth path from registry
193 if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
195 for (size_t i = 0; i < 3U; i++)
197 if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_PATH), REG_SCOPE[i]))
199 QString vpsRegDllPath;
200 if (MUtils::Registry::reg_value_read(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_PATH), QString::fromLatin1(VPS_REG_NAME), vpsRegDllPath, REG_SCOPE[i]))
202 QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
203 vpsRegDllInfo.makeAbsolute();
204 if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
206 const int flag = getVapourSynthType(REG_SCOPE[i]);
207 vpsDllInfo.insert(flag, vpsRegDllInfo);
208 vpsExeInfo.insert(flag, vpsRegDllInfo.absoluteDir().absoluteFilePath(VPS_EXE_NAME)); /*derive VSPipe.EXE path from VapourSynth.DLL path for now!*/
215 //Abort, if VapourSynth was *not* found
216 if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
218 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
222 //Validate the VapourSynth installation now!
223 for (size_t i = 0; i < 2U; i++)
225 if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
227 QFile *vpsExeFile, *vpsDllFile;
228 if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
230 m_vpsExePath[i].reset(vpsExeFile);
231 m_vpsDllPath[i].reset(vpsDllFile);
232 if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
234 qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
235 m_vpsPath[i] = m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalPathName);
236 flags |= VPS_BIT_FLAG[i];
240 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
245 qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
250 qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
257 //-------------------------------------
258 // Internal functions
259 //-------------------------------------
261 VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
263 if (MUtils::OS::os_architecture() != MUtils::OS::ARCH_X64)
265 return VAPOURSYNTH_X86;
270 case MUtils::Registry::scope_wow_x32:
271 return VAPOURSYNTH_X86;
272 case MUtils::Registry::scope_wow_x64:
273 return VAPOURSYNTH_X64;
275 return VAPOURSYNTH_DEF;
279 bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
281 bool complete = false;
282 vpsExeFile = vpsDllFile = NULL;
284 qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
285 qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
287 if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
289 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
290 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
291 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
293 complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
299 MUTILS_DELETE(vpsExeFile);
300 MUTILS_DELETE(vpsDllFile);
306 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
308 //Try to run VSPIPE.EXE
309 const QStringList output = runProcess(vspipePath, QStringList() << "--version");
311 //Init regular expressions
312 QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
314 //Check for version info
315 bool vapoursynthLogo = false;
316 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
318 if(vpsLogo.lastIndexIn(*iter) >= 0)
320 vapoursynthLogo = true;
325 //Minimum required version found?
328 qDebug("VapourSynth version was detected successfully.");
332 //Failed to determine version
333 qWarning("Failed to determine VapourSynth version!");