Updated copyright year.
[simple-x264-launcher.git] / src / thread_vapoursynth.cpp
blob7dd3f2b5927c8ddc4864ec454a7e47efea6ba871
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2024 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 <QEventLoop>
30 #include <QTimer>
31 #include <QApplication>
32 #include <QDir>
33 #include <QHash>
34 #include <QAbstractFileEngine.h>
36 //Internal
37 #include "global.h"
38 #include "model_sysinfo.h"
40 //CRT
41 #include <cassert>
43 //Static
44 QMutex VapourSynthCheckThread::m_vpsLock;
45 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
46 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
48 //Const
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_KEY1 = "SOFTWARE\\VapourSynth";
52 static const char* const VPS_REG_KEY2 = "SOFTWARE\\VapourSynth-32";
53 static const char* const VPS_REG_NAME = "VapourSynthDLL";
55 //Default VapurSynth architecture
56 #if _WIN64 || __x86_64__
57 #define VAPOURSYNTH_DEF VAPOURSYNTH_X64
58 #else
59 #define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
60 #endif
62 //Enable detection of "portabel" edition?
63 #define ENABLE_PORTABLE_VPS true
65 //EOL flags
66 #define REG_ROOT_EOL (MUtils::Registry::reg_root_t (-1))
67 #define REG_SCOPE_EOL (MUtils::Registry::reg_scope_t(-1))
69 //Auxilary functions
70 #define BOOLIFY(X) ((X) ? '1' : '0')
71 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
73 //-------------------------------------
74 // External API
75 //-------------------------------------
77 bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
79 QMutexLocker lock(&m_vpsLock);
81 sysinfo->clearVapourSynth();
82 sysinfo->clearVPS32Path();
83 sysinfo->clearVPS64Path();
85 QEventLoop loop;
86 VapourSynthCheckThread thread;
88 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
90 connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
91 connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
93 thread.start();
94 QTimer::singleShot(30000, &loop, SLOT(quit()));
96 qDebug("VapourSynth thread has been created, please wait...");
97 loop.exec(QEventLoop::ExcludeUserInputEvents);
98 qDebug("VapourSynth thread finished.");
100 QApplication::restoreOverrideCursor();
102 if (!thread.wait(1000))
104 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
105 thread.terminate();
106 thread.wait();
107 return false;
110 if (thread.getException())
112 qWarning("VapourSynth thread encountered an exception !!!");
113 return false;
116 const int success = thread.getSuccess();
117 if (!success)
119 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
120 return true;
123 if (success & VAPOURSYNTH_X86)
125 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
126 sysinfo->setVPS32Path(thread.getPath32());
129 if (success & VAPOURSYNTH_X64)
131 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
132 sysinfo->setVPS64Path(thread.getPath64());
135 qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
136 return true;
139 //-------------------------------------
140 // Thread functions
141 //-------------------------------------
143 VapourSynthCheckThread::VapourSynthCheckThread(void)
145 m_vpsPath[0U].clear();
146 m_vpsPath[1U].clear();
149 VapourSynthCheckThread::~VapourSynthCheckThread(void)
153 void VapourSynthCheckThread::run(void)
155 m_vpsPath[0U].clear();
156 m_vpsPath[1U].clear();
157 StarupThread::run();
160 int VapourSynthCheckThread::threadMain(void)
162 static const int VPS_BIT_FLAG[] =
164 VAPOURSYNTH_X86,
165 VAPOURSYNTH_X64,
166 NULL
168 static const MUtils::Registry::reg_root_t REG_ROOTS[] =
170 MUtils::Registry::root_machine,
171 MUtils::Registry::root_user,
172 REG_ROOT_EOL
174 static const char* const REG_PATHS_HKLM[] =
176 VPS_REG_KEY1,
177 NULL
179 static const char* const REG_PATHS_HKCU[] =
181 VPS_REG_KEY1,
182 VPS_REG_KEY2,
183 NULL
185 static const MUtils::Registry::reg_scope_t REG_SCOPE_X86[] =
187 MUtils::Registry::scope_default,
188 REG_SCOPE_EOL
190 static const MUtils::Registry::reg_scope_t REG_SCOPE_X64[] =
192 MUtils::Registry::scope_wow_x32,
193 MUtils::Registry::scope_wow_x64,
194 REG_SCOPE_EOL
197 QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
198 int flags = 0;
200 //Look for "portable" VapourSynth version
201 for (size_t i = 0; i < 2U; i++)
203 const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
204 if (vpsPortableDir.exists())
206 const QFileInfo vpsPortableDll(vpsPortableDir.absoluteFilePath(VPS_DLL_NAME));
207 const QFileInfo vpsPortableExe(vpsPortableDir.absoluteFilePath(VPS_EXE_NAME));
208 if ((vpsPortableDll.exists() && vpsPortableDll.isFile()) || (vpsPortableExe.exists() && vpsPortableExe.isFile()))
210 vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
211 vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
216 //Read VapourSynth path from registry
217 if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
219 for (size_t i = 0; REG_ROOTS[i] != REG_ROOT_EOL; i++)
221 const char *const *const paths = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? REG_PATHS_HKLM : REG_PATHS_HKCU;
222 const MUtils::Registry::reg_scope_t* const scopes = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? ((MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64) ? REG_SCOPE_X64 : REG_SCOPE_X86) : REG_SCOPE_X86;
223 for (size_t j = 0; paths[j]; j++)
225 for (size_t k = 0; scopes[k] != REG_SCOPE_EOL; k++)
227 if (MUtils::Registry::reg_key_exists(REG_ROOTS[i], QString::fromLatin1(paths[j]), scopes[k]))
229 QString vpsRegDllPath;
230 if (MUtils::Registry::reg_value_read(REG_ROOTS[i], QString::fromLatin1(paths[j]), QString::fromLatin1(VPS_REG_NAME), vpsRegDllPath, scopes[k]))
232 QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
233 vpsRegDllInfo.makeAbsolute();
234 if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
236 const int vpsArch = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? getVapourSynthType(scopes[k]) : ((j > 0U) ? VAPOURSYNTH_X86 : VAPOURSYNTH_X64);
237 if ((!vpsDllInfo.contains(vpsArch)) || (!vpsExeInfo.contains(vpsArch)))
239 vpsDllInfo.insert(vpsArch, vpsRegDllInfo);
240 vpsExeInfo.insert(vpsArch, vpsRegDllInfo.absoluteDir().absoluteFilePath(VPS_EXE_NAME)); /*derive VSPipe.EXE path from VapourSynth.DLL path!*/
250 //Abort, if VapourSynth was *not* found
251 if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
253 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
254 return 0;
257 //Validate the VapourSynth installation now!
258 for (size_t i = 0; i < 2U; i++)
260 qDebug("VapourSynth %u-Bit support is being tested.", VPS_BITNESS(i));
261 if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
263 QFile *vpsExeFile, *vpsDllFile;
264 if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
266 m_vpsExePath[i].reset(vpsExeFile);
267 m_vpsDllPath[i].reset(vpsDllFile);
268 if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
270 qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
271 m_vpsPath[i] = m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalPathName);
272 flags |= VPS_BIT_FLAG[i];
274 else
276 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
279 else
281 qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
284 else
286 qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
290 return flags;
293 //-------------------------------------
294 // Internal functions
295 //-------------------------------------
297 VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
299 if (MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64)
301 switch (scope)
303 case MUtils::Registry::scope_wow_x32:
304 return VAPOURSYNTH_X86;
305 case MUtils::Registry::scope_wow_x64:
306 return VAPOURSYNTH_X64;
307 default:
308 return VAPOURSYNTH_DEF;
311 else
313 return VAPOURSYNTH_X86; /*ignore scope on 32-Bit OS*/
317 bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
319 bool complete = false;
320 vpsExeFile = vpsDllFile = NULL;
322 qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
323 qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
325 if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
327 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
328 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
329 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
331 complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
335 if(!complete)
337 MUTILS_DELETE(vpsExeFile);
338 MUTILS_DELETE(vpsDllFile);
341 return complete;
344 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
346 //Try to run VSPIPE.EXE
347 const QStringList output = runProcess(vspipePath, QStringList() << "--version");
349 //Init regular expressions
350 QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
352 //Check for version info
353 bool vapoursynthLogo = false;
354 for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
356 if(vpsLogo.lastIndexIn(*iter) >= 0)
358 vapoursynthLogo = true;
359 break;
363 //Minimum required version found?
364 if(vapoursynthLogo)
366 qDebug("VapourSynth version was detected successfully.");
367 return true;
370 //Failed to determine version
371 qWarning("Failed to determine VapourSynth version!");
372 return false;