OSDN Git Service

Updated copyright year.
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2022 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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "thread_vapoursynth.h"
23
24 //Mutils
25 #include <MUtils/OSSupport.h>
26 #include <MUtils/Registry.h>
27
28 //Qt
29 #include <QEventLoop>
30 #include <QTimer>
31 #include <QApplication>
32 #include <QDir>
33 #include <QHash>
34 #include <QAbstractFileEngine.h>
35
36 //Internal
37 #include "global.h"
38 #include "model_sysinfo.h"
39
40 //CRT
41 #include <cassert>
42
43 //Static
44 QMutex VapourSynthCheckThread::m_vpsLock;
45 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
46 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
47
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";
54
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
61
62 //Enable detection of "portabel" edition?
63 #define ENABLE_PORTABLE_VPS true
64
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))
68
69 //Auxilary functions
70 #define BOOLIFY(X) ((X) ? '1' : '0')
71 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
72
73 //-------------------------------------
74 // External API
75 //-------------------------------------
76
77 bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
78 {
79         QMutexLocker lock(&m_vpsLock);
80
81         sysinfo->clearVapourSynth();
82         sysinfo->clearVPS32Path();
83         sysinfo->clearVPS64Path();
84
85         QEventLoop loop;
86         VapourSynthCheckThread thread;
87
88         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
89
90         connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
91         connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
92
93         thread.start();
94         QTimer::singleShot(30000, &loop, SLOT(quit()));
95
96         qDebug("VapourSynth thread has been created, please wait...");
97         loop.exec(QEventLoop::ExcludeUserInputEvents);
98         qDebug("VapourSynth thread finished.");
99
100         QApplication::restoreOverrideCursor();
101
102         if (!thread.wait(1000))
103         {
104                 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
105                 thread.terminate();
106                 thread.wait();
107                 return false;
108         }
109
110         if (thread.getException())
111         {
112                 qWarning("VapourSynth thread encountered an exception !!!");
113                 return false;
114         }
115
116         const int success = thread.getSuccess();
117         if (!success)
118         {
119                 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
120                 return true;
121         }
122
123         if (success & VAPOURSYNTH_X86)
124         {
125                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
126                 sysinfo->setVPS32Path(thread.getPath32());
127         }
128
129         if (success & VAPOURSYNTH_X64)
130         {
131                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
132                 sysinfo->setVPS64Path(thread.getPath64());
133         }
134
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;
137 }
138
139 //-------------------------------------
140 // Thread functions
141 //-------------------------------------
142
143 VapourSynthCheckThread::VapourSynthCheckThread(void)
144 {
145         m_vpsPath[0U].clear();
146         m_vpsPath[1U].clear();
147 }
148
149 VapourSynthCheckThread::~VapourSynthCheckThread(void)
150 {
151 }
152
153 void VapourSynthCheckThread::run(void)
154 {
155         m_vpsPath[0U].clear();
156         m_vpsPath[1U].clear();
157         StarupThread::run();
158 }
159
160 int VapourSynthCheckThread::threadMain(void)
161 {
162         static const int VPS_BIT_FLAG[] =
163         {
164                 VAPOURSYNTH_X86,
165                 VAPOURSYNTH_X64,
166                 NULL
167         };
168         static const MUtils::Registry::reg_root_t REG_ROOTS[] =
169         {
170                 MUtils::Registry::root_machine,
171                 MUtils::Registry::root_user,
172                 REG_ROOT_EOL
173         };
174         static const char* const REG_PATHS_HKLM[] =
175         {
176                 VPS_REG_KEY1,
177                 NULL
178         };
179         static const char* const REG_PATHS_HKCU[] =
180         {
181                 VPS_REG_KEY1,
182                 VPS_REG_KEY2,
183                 NULL
184         };
185         static const MUtils::Registry::reg_scope_t REG_SCOPE_X86[] =
186         {
187                 MUtils::Registry::scope_default,
188                 REG_SCOPE_EOL
189         };
190         static const MUtils::Registry::reg_scope_t REG_SCOPE_X64[] =
191         {
192                 MUtils::Registry::scope_wow_x32,
193                 MUtils::Registry::scope_wow_x64,
194                 REG_SCOPE_EOL
195         };
196
197         QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
198         int flags = 0;
199
200         //Look for "portable" VapourSynth version
201         for (size_t i = 0; i < 2U; i++)
202         {
203                 const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
204                 if (vpsPortableDir.exists())
205                 {
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()))
209                         {
210                                 vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
211                                 vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
212                         }
213                 }
214         }
215
216         //Read VapourSynth path from registry
217         if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
218         {
219                 for (size_t i = 0; REG_ROOTS[i] != REG_ROOT_EOL; i++)
220                 {
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++)
224                         {
225                                 for (size_t k = 0; scopes[k] != REG_SCOPE_EOL; k++)
226                                 {
227                                         if (MUtils::Registry::reg_key_exists(REG_ROOTS[i], QString::fromLatin1(paths[j]), scopes[k]))
228                                         {
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]))
231                                                 {
232                                                         QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
233                                                         vpsRegDllInfo.makeAbsolute();
234                                                         if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
235                                                         {
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)))
238                                                                 {
239                                                                         vpsDllInfo.insert(vpsArch, vpsRegDllInfo);
240                                                                         vpsExeInfo.insert(vpsArch, vpsRegDllInfo.absoluteDir().absoluteFilePath(VPS_EXE_NAME)); /*derive VSPipe.EXE path from VapourSynth.DLL path!*/
241                                                                 }
242                                                         }
243                                                 }
244                                         }
245                                 }
246                         }
247                 }
248         }
249
250         //Abort, if VapourSynth was *not* found
251         if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
252         {
253                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
254                 return 0;
255         }
256
257         //Validate the VapourSynth installation now!
258         for (size_t i = 0; i < 2U; i++)
259         {
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]))
262                 {
263                         QFile *vpsExeFile, *vpsDllFile;
264                         if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
265                         {
266                                 m_vpsExePath[i].reset(vpsExeFile);
267                                 m_vpsDllPath[i].reset(vpsDllFile);
268                                 if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
269                                 {
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];
273                                 }
274                                 else
275                                 {
276                                         qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
277                                 }
278                         }
279                         else
280                         {
281                                 qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
282                         }
283                 }
284                 else
285                 {
286                         qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
287                 }
288         }
289
290         return flags;
291 }
292
293 //-------------------------------------
294 // Internal functions
295 //-------------------------------------
296
297 VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
298 {
299         if (MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64)
300         {
301                 switch (scope)
302                 {
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;
309                 }
310         }
311         else
312         {
313                 return VAPOURSYNTH_X86; /*ignore scope on 32-Bit OS*/
314         }
315 }
316
317 bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
318 {
319         bool complete = false;
320         vpsExeFile = vpsDllFile = NULL;
321         
322         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
323         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
324
325         if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
326         {
327                 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
328                 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
329                 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
330                 {
331                         complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
332                 }
333         }
334
335         if(!complete)
336         {
337                 MUTILS_DELETE(vpsExeFile);
338                 MUTILS_DELETE(vpsDllFile);
339         }
340
341         return complete;
342 }
343
344 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
345 {
346         //Try to run VSPIPE.EXE
347         const QStringList output = runProcess(vspipePath, QStringList() << "--version");
348
349         //Init regular expressions
350         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
351
352         //Check for version info
353         bool vapoursynthLogo = false;
354         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
355         {
356                 if(vpsLogo.lastIndexIn(*iter) >= 0)
357                 {
358                         vapoursynthLogo = true;
359                         break;
360                 }
361         }
362
363         //Minimum required version found?
364         if(vapoursynthLogo)
365         {
366                 qDebug("VapourSynth version was detected successfully.");
367                 return true;
368         }
369
370         //Failed to determine version
371         qWarning("Failed to determine VapourSynth version!");
372         return false;
373 }