OSDN Git Service

Don't mistakenly detect 64-Bit VapourSynth on 32-Bit-only OS.
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
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.
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_PATH = "SOFTWARE\\VapourSynth";
52 static const char* const VPS_REG_NAME = "VapourSynthDLL";
53
54 //Default VapurSynth architecture
55 #if _WIN64 || __x86_64__
56 #define VAPOURSYNTH_DEF VAPOURSYNTH_X64
57 #else
58 #define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
59 #endif
60
61 //Enable detection of "portabel" edition?
62 #define ENABLE_PORTABLE_VPS true
63
64 //-------------------------------------
65 // Auxilary functions
66 //-------------------------------------
67
68 #define BOOLIFY(X) ((X) ? '1' : '0')
69 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
70
71 //-------------------------------------
72 // External API
73 //-------------------------------------
74
75 bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
76 {
77         QMutexLocker lock(&m_vpsLock);
78
79         sysinfo->clearVapourSynth();
80         sysinfo->clearVPS32Path();
81         sysinfo->clearVPS64Path();
82
83         QEventLoop loop;
84         VapourSynthCheckThread thread;
85
86         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
87
88         connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
89         connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
90
91         thread.start();
92         QTimer::singleShot(30000, &loop, SLOT(quit()));
93
94         qDebug("VapourSynth thread has been created, please wait...");
95         loop.exec(QEventLoop::ExcludeUserInputEvents);
96         qDebug("VapourSynth thread finished.");
97
98         QApplication::restoreOverrideCursor();
99
100         if (!thread.wait(1000))
101         {
102                 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
103                 thread.terminate();
104                 thread.wait();
105                 return false;
106         }
107
108         if (thread.getException())
109         {
110                 qWarning("VapourSynth thread encountered an exception !!!");
111                 return false;
112         }
113
114         const int success = thread.getSuccess();
115         if (!success)
116         {
117                 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
118                 return true;
119         }
120
121         if (success & VAPOURSYNTH_X86)
122         {
123                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
124                 sysinfo->setVPS32Path(thread.getPath32());
125         }
126
127         if (success & VAPOURSYNTH_X64)
128         {
129                 sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
130                 sysinfo->setVPS64Path(thread.getPath64());
131         }
132
133         qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
134         return true;
135 }
136
137 //-------------------------------------
138 // Thread functions
139 //-------------------------------------
140
141 VapourSynthCheckThread::VapourSynthCheckThread(void)
142 {
143         m_vpsPath[0U].clear();
144         m_vpsPath[1U].clear();
145 }
146
147 VapourSynthCheckThread::~VapourSynthCheckThread(void)
148 {
149 }
150
151 void VapourSynthCheckThread::run(void)
152 {
153         m_vpsPath[0U].clear();
154         m_vpsPath[1U].clear();
155         StarupThread::run();
156 }
157
158 int VapourSynthCheckThread::threadMain(void)
159 {
160         static const int VPS_BIT_FLAG[] =
161         {
162                 VAPOURSYNTH_X86,
163                 VAPOURSYNTH_X64,
164                 NULL
165         };
166         static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
167         {
168                 MUtils::Registry::scope_wow_x32,
169                 MUtils::Registry::scope_wow_x64,
170                 MUtils::Registry::scope_default
171         };
172
173         QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
174         int flags = 0;
175
176         //Look for "portable" VapourSynth version
177         for (size_t i = 0; i < 2U; i++)
178         {
179                 const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
180                 if (vpsPortableDir.exists())
181                 {
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()))
185                         {
186                                 vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
187                                 vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
188                         }
189                 }
190         }
191
192         //Read VapourSynth path from registry
193         if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
194         {
195                 for (size_t i = 0; i < 3U; i++)
196                 {
197                         if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_PATH), REG_SCOPE[i]))
198                         {
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]))
201                                 {
202                                         QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
203                                         vpsRegDllInfo.makeAbsolute();
204                                         if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
205                                         {
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!*/
209                                         }
210                                 }
211                         }
212                 }
213         }
214
215         //Abort, if VapourSynth was *not* found
216         if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
217         {
218                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
219                 return 0;
220         }
221
222         //Validate the VapourSynth installation now!
223         for (size_t i = 0; i < 2U; i++)
224         {
225                 if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
226                 {
227                         QFile *vpsExeFile, *vpsDllFile;
228                         if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
229                         {
230                                 m_vpsExePath[i].reset(vpsExeFile);
231                                 m_vpsDllPath[i].reset(vpsDllFile);
232                                 if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
233                                 {
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];
237                                 }
238                                 else
239                                 {
240                                         qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
241                                 }
242                         }
243                         else
244                         {
245                                 qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
246                         }
247                 }
248                 else
249                 {
250                         qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
251                 }
252         }
253
254         return flags;
255 }
256
257 //-------------------------------------
258 // Internal functions
259 //-------------------------------------
260
261 VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
262 {
263         if (MUtils::OS::os_architecture() != MUtils::OS::ARCH_X64)
264         {
265                 return VAPOURSYNTH_X86;
266         }
267
268         switch (scope)
269         {
270                 case MUtils::Registry::scope_wow_x32:
271                         return VAPOURSYNTH_X86;
272                 case MUtils::Registry::scope_wow_x64:
273                         return VAPOURSYNTH_X64;
274                 default:
275                         return VAPOURSYNTH_DEF;
276         }
277 }
278
279 bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
280 {
281         bool complete = false;
282         vpsExeFile = vpsDllFile = NULL;
283         
284         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
285         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
286
287         if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
288         {
289                 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
290                 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
291                 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
292                 {
293                         complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
294                 }
295         }
296
297         if(!complete)
298         {
299                 MUTILS_DELETE(vpsExeFile);
300                 MUTILS_DELETE(vpsDllFile);
301         }
302
303         return complete;
304 }
305
306 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
307 {
308         //Try to run VSPIPE.EXE
309         const QStringList output = runProcess(vspipePath, QStringList() << "--version");
310
311         //Init regular expressions
312         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
313
314         //Check for version info
315         bool vapoursynthLogo = false;
316         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
317         {
318                 if(vpsLogo.lastIndexIn(*iter) >= 0)
319                 {
320                         vapoursynthLogo = true;
321                         break;
322                 }
323         }
324
325         //Minimum required version found?
326         if(vapoursynthLogo)
327         {
328                 qDebug("VapourSynth version was detected successfully.");
329                 return true;
330         }
331
332         //Failed to determine version
333         qWarning("Failed to determine VapourSynth version!");
334         return false;
335 }