OSDN Git Service

19dd0782e3efdfc5e84a2248e65b37ef0d93932e
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2015 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 #include <QLibrary>
25 #include <QEventLoop>
26 #include <QTimer>
27 #include <QMutexLocker>
28 #include <QApplication>
29 #include <QDir>
30 #include <QProcess>
31
32 #include "global.h"
33
34 QMutex VapourSynthCheckThread::m_vpsLock;
35 QFile *VapourSynthCheckThread::m_vpsExePath = NULL;
36 QFile *VapourSynthCheckThread::m_vpsDllPath = NULL;
37 QLibrary *VapourSynthCheckThread::m_vpsLib = NULL;
38
39 #define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
40
41 static inline QString &cleanDir(QString &path)
42 {
43         if(!path.isEmpty())
44         {
45                 path = QDir::fromNativeSeparators(path);
46                 while(path.endsWith('/'))
47                 {
48                         path.chop(1);
49                 }
50         }
51         return path;
52 }
53
54 //-------------------------------------
55 // External API
56 //-------------------------------------
57
58 int VapourSynthCheckThread::detect(QString &path)
59 {
60         path.clear();
61         QMutexLocker lock(&m_vpsLock);
62
63         QEventLoop loop;
64         VapourSynthCheckThread thread;
65
66         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
67
68         connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
69         connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
70         
71         thread.start();
72         QTimer::singleShot(15000, &loop, SLOT(quit()));
73         
74         qDebug("VapourSynth thread has been created, please wait...");
75         loop.exec(QEventLoop::ExcludeUserInputEvents);
76         qDebug("VapourSynth thread finished.");
77
78         QApplication::restoreOverrideCursor();
79
80         if(!thread.wait(1000))
81         {
82                 qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
83                 thread.terminate();
84                 thread.wait();
85                 return -1;
86         }
87
88         if(thread.getException())
89         {
90                 qWarning("VapourSynth thread encountered an exception !!!");
91                 return -1;
92         }
93         
94         if(thread.getSuccess())
95         {
96                 path = thread.getPath();
97                 qDebug("VapourSynth check completed successfully.");
98                 return 1;
99         }
100
101         qWarning("VapourSynth thread failed to detect installation!");
102         return 0;
103 }
104
105 void VapourSynthCheckThread::unload(void)
106 {
107         QMutexLocker lock(&m_vpsLock);
108
109         if(m_vpsLib)
110         {
111                 if(m_vpsLib->isLoaded())
112                 {
113                         m_vpsLib->unload();
114                 }
115         }
116
117         if(m_vpsExePath)
118         {
119                 if (m_vpsExePath->isOpen())
120                 {
121                         m_vpsExePath->close();
122                 }
123         }
124
125         if(m_vpsDllPath)
126         {
127                 if(m_vpsDllPath->isOpen())
128                 {
129                         m_vpsDllPath->close();
130                 }
131         }
132
133         X264_DELETE(m_vpsExePath);
134         X264_DELETE(m_vpsDllPath);
135         X264_DELETE(m_vpsLib);
136 }
137
138 //-------------------------------------
139 // Thread class
140 //-------------------------------------
141
142 VapourSynthCheckThread::VapourSynthCheckThread(void)
143 {
144         m_success = false;
145         m_exception = false;
146         m_vpsPath.clear();
147 }
148
149 VapourSynthCheckThread::~VapourSynthCheckThread(void)
150 {
151 }
152
153 void VapourSynthCheckThread::run(void)
154 {
155         m_exception = m_success = false;
156         m_success = detectVapourSynthPath1(m_vpsPath, &m_exception);
157 }
158
159 bool VapourSynthCheckThread::detectVapourSynthPath1(QString &path, volatile bool *exception)
160 {
161         __try
162         {
163                 return detectVapourSynthPath2(path, exception);
164         }
165         __except(1)
166         {
167                 *exception = true;
168                 qWarning("Unhandled exception error in VapourSynth thread !!!");
169                 return false;
170         }
171 }
172
173 bool VapourSynthCheckThread::detectVapourSynthPath2(QString &path, volatile bool *exception)
174 {
175         try
176         {
177                 return detectVapourSynthPath3(path);
178         }
179         catch(...)
180         {
181                 *exception = true;
182                 qWarning("VapourSynth initializdation raised an C++ exception!");
183                 return false;
184         }
185 }
186
187 bool VapourSynthCheckThread::detectVapourSynthPath3(QString &path)
188 {
189         bool success = false;
190         X264_DELETE(m_vpsExePath);
191         X264_DELETE(m_vpsDllPath);
192         path.clear();
193
194         static const char *VPS_REG_KEYS[] = 
195         {
196                 "SOFTWARE\\VapourSynth",
197                 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
198                 NULL
199         };
200         static const char *VPS_REG_NAME[] =
201         {
202                 "Path",
203                 "InstallLocation",
204                 "Inno Setup: App Path",
205                 NULL
206         };
207
208         //Read VapourSynth path from registry
209         QString vapoursynthPath;
210         for(size_t i = 0; VPS_REG_KEYS[i]; i++)
211         {
212                 for(size_t j = 0; VPS_REG_NAME[j]; j++)
213                 {
214                         vapoursynthPath = cleanDir(x264_query_reg_string(false, VPS_REG_KEYS[i], VPS_REG_NAME[j]));
215                         if(VALID_DIR(vapoursynthPath)) break;
216                 }
217                 if(VALID_DIR(vapoursynthPath)) break;
218         }
219
220         //Make sure VapourSynth does exist
221         if(!VALID_DIR(vapoursynthPath))
222         {
223                 qWarning("VapourSynth install path not found -> disable VapouSynth support!");
224                 vapoursynthPath.clear();
225         }
226
227         //Make sure that 'vapoursynth.dll' and 'vspipe.exe' are available
228         bool vapoursynthComplete = false;
229         if(!vapoursynthPath.isEmpty())
230         {
231                 static const char *CORE_PATH[3] = { "core32", "core64", "core" };
232                 qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
233                 for(int i = 0; (i < 3) && (!vapoursynthComplete); i++)
234                 {
235                         QFileInfo vpsExeInfo(QString("%1/%2/vspipe.exe"     ).arg(vapoursynthPath, CORE_PATH[i]));
236                         QFileInfo vpsDllInfo(QString("%1/%2/vapoursynth.dll").arg(vapoursynthPath, CORE_PATH[i]));
237                         qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
238                         qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
239                         if(vpsExeInfo.exists() && vpsDllInfo.exists())
240                         {
241                                 m_vpsExePath = new QFile(vpsExeInfo.canonicalFilePath());
242                                 m_vpsDllPath = new QFile(vpsDllInfo.canonicalFilePath());
243                                 if(m_vpsExePath->open(QIODevice::ReadOnly) && m_vpsDllPath->open(QIODevice::ReadOnly))
244                                 {
245                                         if(vapoursynthComplete = x264_is_executable(m_vpsExePath->fileName()))
246                                         {
247                                                 vapoursynthPath.append("/").append(CORE_PATH[i]);
248                                         }
249                                         break;
250                                 }
251                                 X264_DELETE(m_vpsExePath);
252                                 X264_DELETE(m_vpsDllPath);
253                         }
254                 }
255                 if(!vapoursynthComplete)
256                 {
257                         qWarning("VapourSynth installation incomplete -> disable VapouSynth support!");
258                 }
259         }
260
261         //Make sure 'vsscript.dll' can be loaded successfully
262         if(vapoursynthComplete && m_vpsExePath)
263         {
264                 qDebug("VapourSynth detection is running, please stand by...");
265                 success = checkVapourSynth(m_vpsExePath->fileName());
266         }
267
268         //Return VapourSynth path
269         if(success)
270         {
271                 path = vapoursynthPath;
272         }
273
274         return success;
275 }
276
277 bool VapourSynthCheckThread::checkVapourSynth(const QString vspipePath)
278 {
279         QProcess process;
280         QStringList output;
281
282         //Setup process object
283         process.setWorkingDirectory(QDir::tempPath());
284         process.setProcessChannelMode(QProcess::MergedChannels);
285         process.setReadChannel(QProcess::StandardOutput);
286
287         //Try to start VSPIPE.EXE
288         process.start(vspipePath, QStringList() << "--version");
289         if(!process.waitForStarted())
290         {
291                 qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
292                 return false;
293         }
294
295         //Wait for process to finish
296         while(process.state() != QProcess::NotRunning)
297         {
298                 if(process.waitForReadyRead(12000))
299                 {
300                         while(process.canReadLine())
301                         {
302                                 output << QString::fromUtf8(process.readLine()).simplified();
303                         }
304                         continue;
305                 }
306                 if(process.state() != QProcess::NotRunning)
307                 {
308                         qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
309                         break;
310                 }
311         }
312
313         //Make sure VSPIPE.EXE has terminated!
314         process.waitForFinished(2500);
315         if(process.state() != QProcess::NotRunning)
316         {
317                 qWarning("VSPIPE.EXE process still running, going to kill it!");
318                 process.kill();
319                 process.waitForFinished(-1);
320         }
321
322         //Read pending lines
323         while(process.canReadLine())
324         {
325                 output << QString::fromUtf8(process.readLine()).simplified();
326         }
327
328         //Check exit code
329         if(process.exitCode() != 0)
330         {
331                 qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
332                 return false;
333         }
334
335         //Init regular expressions
336         QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
337
338         //Check for version info
339         bool vapoursynthLogo = false;
340         for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
341         {
342                 if(vpsLogo.lastIndexIn(*iter) >= 0)
343                 {
344                         vapoursynthLogo = true;
345                         break;
346                 }
347         }
348
349         //Minimum required version found?
350         if(vapoursynthLogo)
351         {
352                 qDebug("VapourSynth was detected successfully.");
353                 return true;
354         }
355
356         //Failed to determine version
357         qWarning("Failed to determine VapourSynth version -> disable Vapousynth support!");
358         qWarning("VapourSynth version is unsupported or VapourSynth installation is corrupted.");
359         return false;
360 }