Various improvements to Avisynth and VapourSynth detection code. Refactored common...
[simple-x264-launcher.git] / src / thread_vapoursynth.cpp
blob26e63618bb4bb9c6569fc277e86d871bd9900e6b
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2019 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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"
24 //Mutils
25 #include <MUtils/OSSupport.h>
26 #include <MUtils/Registry.h>
28 //Qt
29 #include <QLibrary>
30 #include <QEventLoop>
31 #include <QTimer>
32 #include <QApplication>
33 #include <QDir>
35 //Internal
36 #include "global.h"
37 #include "model_sysinfo.h"
39 //CRT
40 #include <cassert>
42 //Const
43 static const bool ENABLE_PORTABLE_VPS = true;
45 //Static
46 QMutex VapourSynthCheckThread::m_vpsLock;
47 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
48 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
50 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
51 #define BOOLIFY(X) ((X) ? '1' : '0')
52 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
54 static inline QString &cleanDir(QString &path)
56 if(!path.isEmpty())
58 path = QDir::fromNativeSeparators(path);
59 while(path.endsWith('/'))
61 path.chop(1);
64 return path;
67 //-------------------------------------
68 // External API
69 //-------------------------------------
71 bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
73 sysinfo->clearVapourSynth();
74 sysinfo->clearVPSPath();
76 QMutexLocker lock(&m_vpsLock);
78 QEventLoop loop;
79 VapourSynthCheckThread thread;
81 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
83 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
84 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
86 thread.start();
87 QTimer::singleShot(30000, &loop, SLOT(quit()));
89 qDebug("VapourSynth thread has been created, please wait...");
90 loop.exec(QEventLoop::ExcludeUserInputEvents);
91 qDebug("VapourSynth thread finished.");
93 QApplication::restoreOverrideCursor();
95 if(!thread.wait(1000))
97 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
98 thread.terminate();
99 thread.wait();
100 return false;
103 if(thread.getException())
105 qWarning("VapourSynth thread encountered an exception !!!");
106 return false;
109 if(thread.getSuccess())
111 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, thread.getSuccess() & VAPOURSYNTH_X86);
112 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, thread.getSuccess() & VAPOURSYNTH_X64);
113 sysinfo->setVPSPath(thread.getPath());
114 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
116 else
118 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
121 return true;
124 //-------------------------------------
125 // Thread class
126 //-------------------------------------
128 VapourSynthCheckThread::VapourSynthCheckThread(void)
130 m_success &= 0;
131 m_exception = false;
132 m_vpsPath.clear();
135 VapourSynthCheckThread::~VapourSynthCheckThread(void)
139 void VapourSynthCheckThread::run(void)
141 m_success &= 0;
142 m_exception = false;
143 m_vpsPath.clear();
145 detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
148 void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
150 __try
152 return detectVapourSynthPath2(success, path, exception);
154 __except(1)
156 *exception = true;
157 qWarning("Unhandled exception error in VapourSynth thread !!!");
161 void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
165 return detectVapourSynthPath3(success, path);
167 catch(...)
169 *exception = true;
170 qWarning("VapourSynth initializdation raised an C++ exception!");
174 void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
176 success &= 0;
177 path.clear();
179 static const char *VPS_CORE_DIR[] =
181 "core32",
182 "core64",
183 NULL
185 static const int VPS_BIT_FLAG[] =
187 VAPOURSYNTH_X86,
188 VAPOURSYNTH_X64,
189 NULL
191 static const char *VPS_REG_KEYS[] =
193 "SOFTWARE\\VapourSynth",
194 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
195 NULL
197 static const char *VPS_REG_NAME[] =
199 "Path",
200 "InstallLocation",
201 "Inno Setup: App Path",
202 NULL
204 static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
206 MUtils::Registry::scope_default,
207 MUtils::Registry::scope_wow_x32,
208 MUtils::Registry::scope_wow_x64
211 QString vapoursynthPath;
213 //Look for "portable" VapourSynth version
214 if (ENABLE_PORTABLE_VPS)
216 const QString vpsPortableDir = QString("%1/extra/VapourSynth").arg(QCoreApplication::applicationDirPath());
217 if (VALID_DIR(vpsPortableDir))
219 for (size_t i = 0; VPS_CORE_DIR[i]; i++)
221 const QFileInfo vpsPortableDll = QFileInfo(QString("%1/%2/VapourSynth.dll").arg(vpsPortableDir, QString::fromLatin1(VPS_CORE_DIR[i])));
222 if (vpsPortableDll.exists() && vpsPortableDll.isFile())
224 vapoursynthPath = vpsPortableDir;
225 break;
231 //Read VapourSynth path from registry
232 if (vapoursynthPath.isEmpty())
234 for (size_t i = 0; VPS_REG_KEYS[i]; i++)
236 for (size_t j = 0; VPS_REG_NAME[j]; j++)
238 for (size_t k = 0; k < 3; k++)
240 if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), REG_SCOPE[k]))
242 QString temp;
243 if (MUtils::Registry::reg_value_read(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), QString::fromLatin1(VPS_REG_NAME[j]), temp, REG_SCOPE[k]))
245 temp = cleanDir(temp);
246 if (VALID_DIR(temp))
248 vapoursynthPath = temp;
249 break;
254 if (!vapoursynthPath.isEmpty())
256 break;
259 if (!vapoursynthPath.isEmpty())
261 break;
266 //Make sure VapourSynth directory does exist
267 if(vapoursynthPath.isEmpty())
269 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
270 return;
273 //Validate the VapourSynth installation now!
274 qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
275 for (size_t i = 0; VPS_CORE_DIR[i]; i++)
277 QFile *vpsExeFile, *vpsDllFile;
278 if (isVapourSynthComplete(QString("%1/%2").arg(vapoursynthPath, QString::fromLatin1(VPS_CORE_DIR[i])), vpsExeFile, vpsDllFile))
280 if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
282 success |= VPS_BIT_FLAG[i];
283 qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
284 m_vpsExePath[i].reset(vpsExeFile);
285 m_vpsDllPath[i].reset(vpsDllFile);
287 else
289 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
292 else
294 qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
298 //Return VapourSynth path
299 if(success)
301 path = vapoursynthPath;
305 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
307 bool complete = false;
308 vpsExeFile = vpsDllFile = NULL;
310 QFileInfo vpsExeInfo(QString("%1/vspipe.exe" ).arg(vsCorePath));
311 QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
313 qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
314 qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
316 if(vpsExeInfo.exists() && vpsDllInfo.exists())
318 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
319 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
320 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
322 complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
326 if(!complete)
328 MUTILS_DELETE(vpsExeFile);
329 MUTILS_DELETE(vpsDllFile);
332 return complete;
335 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
337 //Try to run VSPIPE.EXE
338 const QStringList output = runProcess(vspipePath, QStringList() << "--version");
340 //Init regular expressions
341 QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
343 //Check for version info
344 bool vapoursynthLogo = false;
345 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
347 if(vpsLogo.lastIndexIn(*iter) >= 0)
349 vapoursynthLogo = true;
350 break;
354 //Minimum required version found?
355 if(vapoursynthLogo)
357 qDebug("VapourSynth version was detected successfully.");
358 return true;
361 //Failed to determine version
362 qWarning("Failed to determine VapourSynth version!");
363 return false;