OSDN Git Service

586df3a9c79e8c7f4e6fee253f7b4c686fb61c4d
[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
35 //Internal
36 #include "global.h"
37 #include "model_sysinfo.h"
38
39 //CRT
40 #include <cassert>
41
42 //Static
43 QMutex VapourSynthCheckThread::m_vpsLock;
44 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
45 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
46
47 //Const
48 static const char* const VPS_REG_KEYS = "SOFTWARE\\VapourSynth";
49 static const char* const VPS_REG_NAME = "VapourSynthDLL";
50
51 //Default VapurSynth architecture
52 #if _WIN64 || __x86_64__
53 #define VAPOURSYNTH_DEF VAPOURSYNTH_X64
54 #else
55 #define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
56 #endif
57
58 //Enable detection of "portabel" edition?
59 #define ENABLE_PORTABLE_VPS true
60
61 //-------------------------------------
62 // Auxilary functions
63 //-------------------------------------
64
65 #define BOOLIFY(X) ((X) ? '1' : '0')
66 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
67
68 static inline bool VALID_DIR(const QString &path)
69 {
70         return (!path.isEmpty()) && QDir(path).exists();
71 }
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_scope_t REG_SCOPE[3] =
169         {
170                 MUtils::Registry::scope_wow_x32,
171                 MUtils::Registry::scope_wow_x64,
172                 MUtils::Registry::scope_default
173         };
174
175         QHash<int, QString> vapoursynthPath;
176         int flags = 0;
177
178         //Look for "portable" VapourSynth version
179         for (size_t i = 0; i < 2U; i++)
180         {
181                 const QString vpsPortableDir = QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i)));
182                 if (VALID_DIR(vpsPortableDir))
183                 {
184                         const QFileInfo vpsPortableFile = QFileInfo(QString("%1/vspipe.exe").arg(vpsPortableDir));
185                         if (vpsPortableFile.exists() && vpsPortableFile.isFile())
186                         {
187                                 vapoursynthPath.insert(VPS_BIT_FLAG[i], vpsPortableDir);
188                         }
189                 }
190         }
191
192         //Read VapourSynth path from the registry
193         if (vapoursynthPath.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_KEYS), REG_SCOPE[i]))
198                         {
199                                 QString vpsDllPath;
200                                 if (MUtils::Registry::reg_value_read(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS), QString::fromLatin1(VPS_REG_NAME), vpsDllPath, REG_SCOPE[i]))
201                                 {
202                                         const QFileInfo vpsDllInfo(QDir::fromNativeSeparators(vpsDllPath));
203                                         if (vpsDllInfo.exists() && vpsDllInfo.isFile())
204                                         {
205                                                 const QString vpsCorePath = vpsDllInfo.canonicalPath();
206                                                 if (!vpsCorePath.isEmpty())
207                                                 {
208                                                         const int flag = getVapourSynthType(REG_SCOPE[i]);
209                                                         if (!vapoursynthPath.contains(flag))
210                                                         {
211                                                                 vapoursynthPath.insert(flag, vpsCorePath);
212                                                         }
213                                                 }
214                                         }
215                                 }
216                         }
217                 }
218         }
219
220         //Make sure VapourSynth directory does exist
221         if(vapoursynthPath.isEmpty())
222         {
223                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
224                 return 0;
225         }
226
227         //Validate the VapourSynth installation now!
228         for (size_t i = 0; i < 2U; i++)
229         {
230                 if (vapoursynthPath.contains(VPS_BIT_FLAG[i]))
231                 {
232                         const QString path = vapoursynthPath[VPS_BIT_FLAG[i]];
233                         qDebug("VapourSynth %u-Bit \"core\" path: %s", VPS_BITNESS(i), MUTILS_UTF8(path));
234                         QFile *vpsExeFile, *vpsDllFile;
235                         if (isVapourSynthComplete(path, vpsExeFile, vpsDllFile))
236                         {
237                                 if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
238                                 {
239                                         qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
240                                         m_vpsExePath[i].reset(vpsExeFile);
241                                         m_vpsDllPath[i].reset(vpsDllFile);
242                                         m_vpsPath[i] = path;
243                                         flags |= VPS_BIT_FLAG[i];
244                                 }
245                                 else
246                                 {
247                                         qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
248                                 }
249                         }
250                         else
251                         {
252                                 qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
253                         }
254                 }
255                 else
256                 {
257                         qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
258                 }
259         }
260
261         return flags;
262 }
263
264 //-------------------------------------
265 // Internal functions
266 //-------------------------------------
267
268 VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
269 {
270         switch (scope)
271         {
272                 case MUtils::Registry::scope_wow_x32:
273                         return VAPOURSYNTH_X86;
274                 case MUtils::Registry::scope_wow_x64:
275                         return VAPOURSYNTH_X64;
276                 default:
277                         return VAPOURSYNTH_DEF;
278         }
279 }
280
281 bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
282 {
283         bool complete = false;
284         vpsExeFile = vpsDllFile = NULL;
285
286         QFileInfo vpsExeInfo(QString("%1/vspipe.exe"     ).arg(vsCorePath));
287         QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
288         
289         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
290         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
291
292         if(vpsExeInfo.exists() && vpsDllInfo.exists())
293         {
294                 vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
295                 vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
296                 if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
297                 {
298                         complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
299                 }
300         }
301
302         if(!complete)
303         {
304                 MUTILS_DELETE(vpsExeFile);
305                 MUTILS_DELETE(vpsDllFile);
306         }
307
308         return complete;
309 }
310
311 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
312 {
313         //Try to run VSPIPE.EXE
314         const QStringList output = runProcess(vspipePath, QStringList() << "--version");
315
316         //Init regular expressions
317         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
318
319         //Check for version info
320         bool vapoursynthLogo = false;
321         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
322         {
323                 if(vpsLogo.lastIndexIn(*iter) >= 0)
324                 {
325                         vapoursynthLogo = true;
326                         break;
327                 }
328         }
329
330         //Minimum required version found?
331         if(vapoursynthLogo)
332         {
333                 qDebug("VapourSynth version was detected successfully.");
334                 return true;
335         }
336
337         //Failed to determine version
338         qWarning("Failed to determine VapourSynth version!");
339         return false;
340 }