Happy new year 2017!
[simple-x264-launcher.git] / src / thread_vapoursynth.cpp
blob276c13d8d67cc0577cdf19b75f93c0e8063f5ae1
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2017 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 <QMutexLocker>
33 #include <QApplication>
34 #include <QDir>
35 #include <QProcess>
37 //Internal
38 #include "global.h"
39 #include "model_sysinfo.h"
41 //CRT
42 #include <cassert>
44 //Const
45 static const bool ENABLE_PORTABLE_VPS = true;
47 //Static
48 QMutex VapourSynthCheckThread::m_vpsLock;
49 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
50 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
52 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
53 #define BOOLIFY(X) ((X) ? '1' : '0')
54 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
56 static inline QString &cleanDir(QString &path)
58 if(!path.isEmpty())
60 path = QDir::fromNativeSeparators(path);
61 while(path.endsWith('/'))
63 path.chop(1);
66 return path;
69 //-------------------------------------
70 // External API
71 //-------------------------------------
73 bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
75 sysinfo->clearVapourSynth();
76 sysinfo->clearVPSPath();
78 QMutexLocker lock(&m_vpsLock);
80 QEventLoop loop;
81 VapourSynthCheckThread thread;
83 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
85 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
86 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
88 thread.start();
89 QTimer::singleShot(15000, &loop, SLOT(quit()));
91 qDebug("VapourSynth thread has been created, please wait...");
92 loop.exec(QEventLoop::ExcludeUserInputEvents);
93 qDebug("VapourSynth thread finished.");
95 QApplication::restoreOverrideCursor();
97 if(!thread.wait(1000))
99 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
100 thread.terminate();
101 thread.wait();
102 return false;
105 if(thread.getException())
107 qWarning("VapourSynth thread encountered an exception !!!");
108 return false;
111 if(thread.getSuccess())
113 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, thread.getSuccess() & VAPOURSYNTH_X86);
114 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, thread.getSuccess() & VAPOURSYNTH_X64);
115 sysinfo->setVPSPath(thread.getPath());
116 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
118 else
120 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
123 return true;
126 //-------------------------------------
127 // Thread class
128 //-------------------------------------
130 VapourSynthCheckThread::VapourSynthCheckThread(void)
132 m_success &= 0;
133 m_exception = false;
134 m_vpsPath.clear();
137 VapourSynthCheckThread::~VapourSynthCheckThread(void)
141 void VapourSynthCheckThread::run(void)
143 m_success &= 0;
144 m_exception = false;
145 m_vpsPath.clear();
147 detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
150 void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
152 __try
154 return detectVapourSynthPath2(success, path, exception);
156 __except(1)
158 *exception = true;
159 qWarning("Unhandled exception error in VapourSynth thread !!!");
163 void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
167 return detectVapourSynthPath3(success, path);
169 catch(...)
171 *exception = true;
172 qWarning("VapourSynth initializdation raised an C++ exception!");
176 void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
178 success &= 0;
179 path.clear();
181 static const char *VPS_CORE_DIR[] =
183 "core32",
184 "core64",
185 NULL
187 static const int VPS_BIT_FLAG[] =
189 VAPOURSYNTH_X86,
190 VAPOURSYNTH_X64,
191 NULL
193 static const char *VPS_REG_KEYS[] =
195 "SOFTWARE\\VapourSynth",
196 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
197 NULL
199 static const char *VPS_REG_NAME[] =
201 "Path",
202 "InstallLocation",
203 "Inno Setup: App Path",
204 NULL
206 static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
208 MUtils::Registry::scope_default,
209 MUtils::Registry::scope_wow_x32,
210 MUtils::Registry::scope_wow_x64
213 QString vapoursynthPath;
215 //Look for "portable" VapourSynth version
216 if (ENABLE_PORTABLE_VPS)
218 const QString vpsPortableDir = QString("%1/extra/VapourSynth").arg(QCoreApplication::applicationDirPath());
219 if (VALID_DIR(vpsPortableDir))
221 for (size_t i = 0; VPS_CORE_DIR[i]; i++)
223 const QFileInfo vpsPortableDll = QFileInfo(QString("%1/%2/VapourSynth.dll").arg(vpsPortableDir, QString::fromLatin1(VPS_CORE_DIR[i])));
224 if (vpsPortableDll.exists() && vpsPortableDll.isFile())
226 vapoursynthPath = vpsPortableDir;
227 break;
233 //Read VapourSynth path from registry
234 if (vapoursynthPath.isEmpty())
236 for (size_t i = 0; VPS_REG_KEYS[i]; i++)
238 for (size_t j = 0; VPS_REG_NAME[j]; j++)
240 for (size_t k = 0; k < 3; k++)
242 if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), REG_SCOPE[k]))
244 QString temp;
245 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]))
247 temp = cleanDir(temp);
248 if (VALID_DIR(temp))
250 vapoursynthPath = temp;
251 break;
256 if (!vapoursynthPath.isEmpty())
258 break;
261 if (!vapoursynthPath.isEmpty())
263 break;
268 //Make sure VapourSynth directory does exist
269 if(vapoursynthPath.isEmpty())
271 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
272 return;
275 //Validate the VapourSynth installation now!
276 qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
277 for (size_t i = 0; VPS_CORE_DIR[i]; i++)
279 QFile *vpsExeFile, *vpsDllFile;
280 if (isVapourSynthComplete(QString("%1/%2").arg(vapoursynthPath, QString::fromLatin1(VPS_CORE_DIR[i])), vpsExeFile, vpsDllFile))
282 if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
284 success |= VPS_BIT_FLAG[i];
285 qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
286 m_vpsExePath[i].reset(vpsExeFile);
287 m_vpsDllPath[i].reset(vpsDllFile);
289 else
291 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
294 else
296 qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
300 //Return VapourSynth path
301 if(success)
303 path = vapoursynthPath;
307 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
309 bool complete = false;
310 vpsExeFile = vpsDllFile = NULL;
312 QFileInfo vpsExeInfo(QString("%1/vspipe.exe" ).arg(vsCorePath));
313 QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
315 qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
316 qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
318 if(vpsExeInfo.exists() && vpsDllInfo.exists())
320 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
321 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
322 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
324 complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
328 if(!complete)
330 MUTILS_DELETE(vpsExeFile);
331 MUTILS_DELETE(vpsDllFile);
334 return complete;
337 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
339 QProcess process;
340 QStringList output;
342 //Setup process object
343 process.setWorkingDirectory(QDir::tempPath());
344 process.setProcessChannelMode(QProcess::MergedChannels);
345 process.setReadChannel(QProcess::StandardOutput);
347 //Try to start VSPIPE.EXE
348 process.start(vspipePath, QStringList() << "--version");
349 if(!process.waitForStarted())
351 qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
352 return false;
355 //Wait for process to finish
356 while(process.state() != QProcess::NotRunning)
358 if(process.waitForReadyRead(12000))
360 while(process.canReadLine())
362 output << QString::fromUtf8(process.readLine()).simplified();
364 continue;
366 if(process.state() != QProcess::NotRunning)
368 qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
369 break;
373 //Make sure VSPIPE.EXE has terminated!
374 process.waitForFinished(2500);
375 if(process.state() != QProcess::NotRunning)
377 qWarning("VSPIPE.EXE process still running, going to kill it!");
378 process.kill();
379 process.waitForFinished(-1);
382 //Read pending lines
383 while(process.canReadLine())
385 output << QString::fromUtf8(process.readLine()).simplified();
388 //Check exit code
389 if(process.exitCode() != 0)
391 qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
392 return false;
395 //Init regular expressions
396 QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
398 //Check for version info
399 bool vapoursynthLogo = false;
400 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
402 if(vpsLogo.lastIndexIn(*iter) >= 0)
404 vapoursynthLogo = true;
405 break;
409 //Minimum required version found?
410 if(vapoursynthLogo)
412 qDebug("VapourSynth version was detected successfully.");
413 return true;
416 //Failed to determine version
417 qWarning("Failed to determine VapourSynth version!");
418 return false;