OSDN Git Service

26e63618bb4bb9c6569fc277e86d871bd9900e6b
[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 <QLibrary>
30 #include <QEventLoop>
31 #include <QTimer>
32 #include <QApplication>
33 #include <QDir>
34
35 //Internal
36 #include "global.h"
37 #include "model_sysinfo.h"
38
39 //CRT
40 #include <cassert>
41
42 //Const
43 static const bool ENABLE_PORTABLE_VPS = true;
44
45 //Static
46 QMutex VapourSynthCheckThread::m_vpsLock;
47 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
48 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
49
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)
53
54 static inline QString &cleanDir(QString &path)
55 {
56         if(!path.isEmpty())
57         {
58                 path = QDir::fromNativeSeparators(path);
59                 while(path.endsWith('/'))
60                 {
61                         path.chop(1);
62                 }
63         }
64         return path;
65 }
66
67 //-------------------------------------
68 // External API
69 //-------------------------------------
70
71 bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
72 {
73         sysinfo->clearVapourSynth();
74         sysinfo->clearVPSPath();
75         
76         QMutexLocker lock(&m_vpsLock);
77
78         QEventLoop loop;
79         VapourSynthCheckThread thread;
80
81         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
82
83         connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
84         connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
85         
86         thread.start();
87         QTimer::singleShot(30000, &loop, SLOT(quit()));
88         
89         qDebug("VapourSynth thread has been created, please wait...");
90         loop.exec(QEventLoop::ExcludeUserInputEvents);
91         qDebug("VapourSynth thread finished.");
92
93         QApplication::restoreOverrideCursor();
94
95         if(!thread.wait(1000))
96         {
97                 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
98                 thread.terminate();
99                 thread.wait();
100                 return false;
101         }
102
103         if(thread.getException())
104         {
105                 qWarning("VapourSynth thread encountered an exception !!!");
106                 return false;
107         }
108
109         if(thread.getSuccess())
110         {
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)));
115         }
116         else
117         {
118                 qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
119         }
120
121         return true;
122 }
123
124 //-------------------------------------
125 // Thread class
126 //-------------------------------------
127
128 VapourSynthCheckThread::VapourSynthCheckThread(void)
129 {
130         m_success &= 0;
131         m_exception = false;
132         m_vpsPath.clear();
133 }
134
135 VapourSynthCheckThread::~VapourSynthCheckThread(void)
136 {
137 }
138
139 void VapourSynthCheckThread::run(void)
140 {
141         m_success &= 0;
142         m_exception = false;
143         m_vpsPath.clear();
144
145         detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
146 }
147
148 void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
149 {
150         __try
151         {
152                 return detectVapourSynthPath2(success, path, exception);
153         }
154         __except(1)
155         {
156                 *exception = true;
157                 qWarning("Unhandled exception error in VapourSynth thread !!!");
158         }
159 }
160
161 void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
162 {
163         try
164         {
165                 return detectVapourSynthPath3(success, path);
166         }
167         catch(...)
168         {
169                 *exception = true;
170                 qWarning("VapourSynth initializdation raised an C++ exception!");
171         }
172 }
173
174 void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
175 {
176         success &= 0;
177         path.clear();
178
179         static const char *VPS_CORE_DIR[] =
180         {
181                 "core32",
182                 "core64",
183                 NULL
184         };
185         static const int VPS_BIT_FLAG[] =
186         {
187                 VAPOURSYNTH_X86,
188                 VAPOURSYNTH_X64,
189                 NULL
190         };
191         static const char *VPS_REG_KEYS[] = 
192         {
193                 "SOFTWARE\\VapourSynth",
194                 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
195                 NULL
196         };
197         static const char *VPS_REG_NAME[] =
198         {
199                 "Path",
200                 "InstallLocation",
201                 "Inno Setup: App Path",
202                 NULL
203         };
204         static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
205         {
206                 MUtils::Registry::scope_default,
207                 MUtils::Registry::scope_wow_x32,
208                 MUtils::Registry::scope_wow_x64
209         };
210
211         QString vapoursynthPath;
212
213         //Look for "portable" VapourSynth version
214         if (ENABLE_PORTABLE_VPS)
215         {
216                 const QString vpsPortableDir = QString("%1/extra/VapourSynth").arg(QCoreApplication::applicationDirPath());
217                 if (VALID_DIR(vpsPortableDir))
218                 {
219                         for (size_t i = 0; VPS_CORE_DIR[i]; i++)
220                         {
221                                 const QFileInfo vpsPortableDll = QFileInfo(QString("%1/%2/VapourSynth.dll").arg(vpsPortableDir, QString::fromLatin1(VPS_CORE_DIR[i])));
222                                 if (vpsPortableDll.exists() && vpsPortableDll.isFile())
223                                 {
224                                         vapoursynthPath = vpsPortableDir;
225                                         break;
226                                 }
227                         }
228                 }
229         }
230
231         //Read VapourSynth path from registry
232         if (vapoursynthPath.isEmpty())
233         {
234                 for (size_t i = 0; VPS_REG_KEYS[i]; i++)
235                 {
236                         for (size_t j = 0; VPS_REG_NAME[j]; j++)
237                         {
238                                 for (size_t k = 0; k < 3; k++)
239                                 {
240                                         if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), REG_SCOPE[k]))
241                                         {
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]))
244                                                 {
245                                                         temp = cleanDir(temp);
246                                                         if (VALID_DIR(temp))
247                                                         {
248                                                                 vapoursynthPath = temp;
249                                                                 break;
250                                                         }
251                                                 }
252                                         }
253                                 }
254                                 if (!vapoursynthPath.isEmpty())
255                                 {
256                                         break;
257                                 }
258                         }
259                         if (!vapoursynthPath.isEmpty())
260                         {
261                                 break;
262                         }
263                 }
264         }
265
266         //Make sure VapourSynth directory does exist
267         if(vapoursynthPath.isEmpty())
268         {
269                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
270                 return;
271         }
272
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++)
276         {
277                 QFile *vpsExeFile, *vpsDllFile;
278                 if (isVapourSynthComplete(QString("%1/%2").arg(vapoursynthPath, QString::fromLatin1(VPS_CORE_DIR[i])), vpsExeFile, vpsDllFile))
279                 {
280                         if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
281                         {
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);
286                         }
287                         else
288                         {
289                                 qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
290                         }
291                 }
292                 else
293                 {
294                         qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
295                 }
296         }
297
298         //Return VapourSynth path
299         if(success)
300         {
301                 path = vapoursynthPath;
302         }
303 }
304
305 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
306 {
307         bool complete = false;
308         vpsExeFile = vpsDllFile = NULL;
309
310         QFileInfo vpsExeInfo(QString("%1/vspipe.exe"     ).arg(vsCorePath));
311         QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
312         
313         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
314         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
315
316         if(vpsExeInfo.exists() && vpsDllInfo.exists())
317         {
318                 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
319                 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
320                 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
321                 {
322                         complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
323                 }
324         }
325
326         if(!complete)
327         {
328                 MUTILS_DELETE(vpsExeFile);
329                 MUTILS_DELETE(vpsDllFile);
330         }
331
332         return complete;
333 }
334
335 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
336 {
337         //Try to run VSPIPE.EXE
338         const QStringList output = runProcess(vspipePath, QStringList() << "--version");
339
340         //Init regular expressions
341         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
342
343         //Check for version info
344         bool vapoursynthLogo = false;
345         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
346         {
347                 if(vpsLogo.lastIndexIn(*iter) >= 0)
348                 {
349                         vapoursynthLogo = true;
350                         break;
351                 }
352         }
353
354         //Minimum required version found?
355         if(vapoursynthLogo)
356         {
357                 qDebug("VapourSynth version was detected successfully.");
358                 return true;
359         }
360
361         //Failed to determine version
362         qWarning("Failed to determine VapourSynth version!");
363         return false;
364 }