OSDN Git Service

Happy new year 2020!
[x264-launcher/x264-launcher.git] / src / thread_vapoursynth.cpp
index dea90df..a2bdd35 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // Simple x264 Launcher
-// Copyright (C) 2004-2019 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2020 LoRd_MuldeR <MuldeR2@GMX.de>
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
 #include <MUtils/Registry.h>
 
 //Qt
-#include <QLibrary>
 #include <QEventLoop>
 #include <QTimer>
-#include <QMutexLocker>
 #include <QApplication>
 #include <QDir>
-#include <QProcess>
+#include <QHash>
+#include <QAbstractFileEngine.h>
 
 //Internal
 #include "global.h"
 //CRT
 #include <cassert>
 
-//Const
-static const bool ENABLE_PORTABLE_VPS = true;
-
 //Static
 QMutex VapourSynthCheckThread::m_vpsLock;
 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
 QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
 
-#define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
+//Const
+static const char* const VPS_DLL_NAME = "vapoursynth.dll";
+static const char* const VPS_EXE_NAME = "vspipe.exe";
+static const char* const VPS_REG_KEY1 = "SOFTWARE\\VapourSynth";
+static const char* const VPS_REG_KEY2 = "SOFTWARE\\VapourSynth-32";
+static const char* const VPS_REG_NAME = "VapourSynthDLL";
+
+//Default VapurSynth architecture
+#if _WIN64 || __x86_64__
+#define VAPOURSYNTH_DEF VAPOURSYNTH_X64
+#else
+#define VAPOURSYNTH_DEF VAPOURSYNTH_X86;
+#endif
+
+//Enable detection of "portabel" edition?
+#define ENABLE_PORTABLE_VPS true
+
+//EOL flags
+#define REG_ROOT_EOL  (MUtils::Registry::reg_root_t (-1))
+#define REG_SCOPE_EOL (MUtils::Registry::reg_scope_t(-1))
+
+//Auxilary functions
 #define BOOLIFY(X) ((X) ? '1' : '0')
 #define VPS_BITNESS(X) (((X) + 1U) * 32U)
 
-static inline QString &cleanDir(QString &path)
-{
-       if(!path.isEmpty())
-       {
-               path = QDir::fromNativeSeparators(path);
-               while(path.endsWith('/'))
-               {
-                       path.chop(1);
-               }
-       }
-       return path;
-}
-
 //-------------------------------------
 // External API
 //-------------------------------------
 
-bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
+bool VapourSynthCheckThread::detect(SysinfoModelsysinfo)
 {
-       sysinfo->clearVapourSynth();
-       sysinfo->clearVPSPath();
-       
        QMutexLocker lock(&m_vpsLock);
 
+       sysinfo->clearVapourSynth();
+       sysinfo->clearVPS32Path();
+       sysinfo->clearVPS64Path();
+
        QEventLoop loop;
        VapourSynthCheckThread thread;
 
@@ -84,17 +89,17 @@ bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
 
        connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
        connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
-       
+
        thread.start();
-       QTimer::singleShot(15000, &loop, SLOT(quit()));
-       
+       QTimer::singleShot(30000, &loop, SLOT(quit()));
+
        qDebug("VapourSynth thread has been created, please wait...");
        loop.exec(QEventLoop::ExcludeUserInputEvents);
        qDebug("VapourSynth thread finished.");
 
        QApplication::restoreOverrideCursor();
 
-       if(!thread.wait(1000))
+       if (!thread.wait(1000))
        {
                qWarning("VapourSynth thread encountered timeout -> probably deadlock!");
                thread.terminate();
@@ -102,36 +107,43 @@ bool VapourSynthCheckThread::detect(SysinfoModel *sysinfo)
                return false;
        }
 
-       if(thread.getException())
+       if (thread.getException())
        {
                qWarning("VapourSynth thread encountered an exception !!!");
                return false;
        }
 
-       if(thread.getSuccess())
+       const int success = thread.getSuccess();
+       if (!success)
        {
-               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, thread.getSuccess() & VAPOURSYNTH_X86);
-               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, thread.getSuccess() & VAPOURSYNTH_X64);
-               sysinfo->setVPSPath(thread.getPath());
-               qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
+               qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
+               return true;
        }
-       else
+
+       if (success & VAPOURSYNTH_X86)
        {
-               qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
+               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
+               sysinfo->setVPS32Path(thread.getPath32());
+       }
+
+       if (success & VAPOURSYNTH_X64)
+       {
+               sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
+               sysinfo->setVPS64Path(thread.getPath64());
        }
 
+       qDebug("VapourSynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X86)), BOOLIFY(sysinfo->getVapourSynth(SysinfoModel::VapourSynth_X64)));
        return true;
 }
 
 //-------------------------------------
-// Thread class
+// Thread functions
 //-------------------------------------
 
 VapourSynthCheckThread::VapourSynthCheckThread(void)
 {
-       m_success &= 0;
-       m_exception = false;
-       m_vpsPath.clear();
+       m_vpsPath[0U].clear();
+       m_vpsPath[1U].clear();
 }
 
 VapourSynthCheckThread::~VapourSynthCheckThread(void)
@@ -140,155 +152,133 @@ VapourSynthCheckThread::~VapourSynthCheckThread(void)
 
 void VapourSynthCheckThread::run(void)
 {
-       m_success &= 0;
-       m_exception = false;
-       m_vpsPath.clear();
-
-       detectVapourSynthPath1(m_success, m_vpsPath, &m_exception);
-}
-
-void VapourSynthCheckThread::detectVapourSynthPath1(int &success, QString &path, volatile bool *exception)
-{
-       __try
-       {
-               return detectVapourSynthPath2(success, path, exception);
-       }
-       __except(1)
-       {
-               *exception = true;
-               qWarning("Unhandled exception error in VapourSynth thread !!!");
-       }
-}
-
-void VapourSynthCheckThread::detectVapourSynthPath2(int &success, QString &path, volatile bool *exception)
-{
-       try
-       {
-               return detectVapourSynthPath3(success, path);
-       }
-       catch(...)
-       {
-               *exception = true;
-               qWarning("VapourSynth initializdation raised an C++ exception!");
-       }
+       m_vpsPath[0U].clear();
+       m_vpsPath[1U].clear();
+       StarupThread::run();
 }
 
-void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
+int VapourSynthCheckThread::threadMain(void)
 {
-       success &= 0;
-       path.clear();
-
-       static const char *VPS_CORE_DIR[] =
-       {
-               "core32",
-               "core64",
-               NULL
-       };
        static const int VPS_BIT_FLAG[] =
        {
                VAPOURSYNTH_X86,
                VAPOURSYNTH_X64,
                NULL
        };
-       static const char *VPS_REG_KEYS[] = 
+       static const MUtils::Registry::reg_root_t REG_ROOTS[] =
        {
-               "SOFTWARE\\VapourSynth",
-               "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
+               MUtils::Registry::root_machine,
+               MUtils::Registry::root_user,
+               REG_ROOT_EOL
+       };
+       static const char* const REG_PATHS_HKLM[] =
+       {
+               VPS_REG_KEY1,
                NULL
        };
-       static const char *VPS_REG_NAME[] =
+       static const char* const REG_PATHS_HKCU[] =
        {
-               "Path",
-               "InstallLocation",
-               "Inno Setup: App Path",
+               VPS_REG_KEY1,
+               VPS_REG_KEY2,
                NULL
        };
-       static const MUtils::Registry::reg_scope_t REG_SCOPE[3] =
+       static const MUtils::Registry::reg_scope_t REG_SCOPE_X86[] =
        {
                MUtils::Registry::scope_default,
+               REG_SCOPE_EOL
+       };
+       static const MUtils::Registry::reg_scope_t REG_SCOPE_X64[] =
+       {
                MUtils::Registry::scope_wow_x32,
-               MUtils::Registry::scope_wow_x64
+               MUtils::Registry::scope_wow_x64,
+               REG_SCOPE_EOL
        };
 
-       QString vapoursynthPath;
+       QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
+       int flags = 0;
 
        //Look for "portable" VapourSynth version
-       if (ENABLE_PORTABLE_VPS)
+       for (size_t i = 0; i < 2U; i++)
        {
-               const QString vpsPortableDir = QString("%1/extra/VapourSynth").arg(QCoreApplication::applicationDirPath());
-               if (VALID_DIR(vpsPortableDir))
+               const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
+               if (vpsPortableDir.exists())
                {
-                       for (size_t i = 0; VPS_CORE_DIR[i]; i++)
+                       const QFileInfo vpsPortableDll(vpsPortableDir.absoluteFilePath(VPS_DLL_NAME));
+                       const QFileInfo vpsPortableExe(vpsPortableDir.absoluteFilePath(VPS_EXE_NAME));
+                       if ((vpsPortableDll.exists() && vpsPortableDll.isFile()) || (vpsPortableExe.exists() && vpsPortableExe.isFile()))
                        {
-                               const QFileInfo vpsPortableDll = QFileInfo(QString("%1/%2/VapourSynth.dll").arg(vpsPortableDir, QString::fromLatin1(VPS_CORE_DIR[i])));
-                               if (vpsPortableDll.exists() && vpsPortableDll.isFile())
-                               {
-                                       vapoursynthPath = vpsPortableDir;
-                                       break;
-                               }
+                               vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
+                               vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
                        }
                }
        }
 
        //Read VapourSynth path from registry
-       if (vapoursynthPath.isEmpty())
+       if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
        {
-               for (size_t i = 0; VPS_REG_KEYS[i]; i++)
+               for (size_t i = 0; REG_ROOTS[i] != REG_ROOT_EOL; i++)
                {
-                       for (size_t j = 0; VPS_REG_NAME[j]; j++)
+                       const char *const *const paths = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? REG_PATHS_HKLM : REG_PATHS_HKCU;
+                       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;
+                       for (size_t j = 0; paths[j]; j++)
                        {
-                               for (size_t k = 0; k < 3; k++)
+                               for (size_t k = 0; scopes[k] != REG_SCOPE_EOL; k++)
                                {
-                                       if (MUtils::Registry::reg_key_exists(MUtils::Registry::root_machine, QString::fromLatin1(VPS_REG_KEYS[i]), REG_SCOPE[k]))
+                                       if (MUtils::Registry::reg_key_exists(REG_ROOTS[i], QString::fromLatin1(paths[j]), scopes[k]))
                                        {
-                                               QString temp;
-                                               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]))
+                                               QString vpsRegDllPath;
+                                               if (MUtils::Registry::reg_value_read(REG_ROOTS[i], QString::fromLatin1(paths[j]), QString::fromLatin1(VPS_REG_NAME), vpsRegDllPath, scopes[k]))
                                                {
-                                                       temp = cleanDir(temp);
-                                                       if (VALID_DIR(temp))
+                                                       QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
+                                                       vpsRegDllInfo.makeAbsolute();
+                                                       if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
                                                        {
-                                                               vapoursynthPath = temp;
-                                                               break;
+                                                               const int vpsArch = (REG_ROOTS[i] == MUtils::Registry::root_machine) ? getVapourSynthType(scopes[k]) : ((j > 0U) ? VAPOURSYNTH_X86 : VAPOURSYNTH_X64);
+                                                               if ((!vpsDllInfo.contains(vpsArch)) || (!vpsExeInfo.contains(vpsArch)))
+                                                               {
+                                                                       vpsDllInfo.insert(vpsArch, vpsRegDllInfo);
+                                                                       vpsExeInfo.insert(vpsArch, vpsRegDllInfo.absoluteDir().absoluteFilePath(VPS_EXE_NAME)); /*derive VSPipe.EXE path from VapourSynth.DLL path!*/
+                                                               }
                                                        }
                                                }
                                        }
                                }
-                               if (!vapoursynthPath.isEmpty())
-                               {
-                                       break;
-                               }
-                       }
-                       if (!vapoursynthPath.isEmpty())
-                       {
-                               break;
                        }
                }
        }
 
-       //Make sure VapourSynth directory does exist
-       if(vapoursynthPath.isEmpty())
+       //Abort, if VapourSynth was *not* found
+       if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
        {
                qWarning("VapourSynth install path not found -> disable VapouSynth support!");
-               return;
+               return 0;
        }
 
        //Validate the VapourSynth installation now!
-       qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
-       for (size_t i = 0; VPS_CORE_DIR[i]; i++)
+       for (size_t i = 0; i < 2U; i++)
        {
-               QFile *vpsExeFile, *vpsDllFile;
-               if (isVapourSynthComplete(QString("%1/%2").arg(vapoursynthPath, QString::fromLatin1(VPS_CORE_DIR[i])), vpsExeFile, vpsDllFile))
+               qDebug("VapourSynth %u-Bit support is being tested.", VPS_BITNESS(i));
+               if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
                {
-                       if (vpsExeFile && checkVapourSynth(vpsExeFile->fileName()))
+                       QFile *vpsExeFile, *vpsDllFile;
+                       if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
                        {
-                               success |= VPS_BIT_FLAG[i];
-                               qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
                                m_vpsExePath[i].reset(vpsExeFile);
                                m_vpsDllPath[i].reset(vpsDllFile);
+                               if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
+                               {
+                                       qDebug("VapourSynth %u-Bit edition found!", VPS_BITNESS(i));
+                                       m_vpsPath[i] = m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalPathName);
+                                       flags |= VPS_BIT_FLAG[i];
+                               }
+                               else
+                               {
+                                       qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
+                               }
                        }
                        else
                        {
-                               qWarning("VapourSynth %u-Bit edition was found, but version check has failed!", VPS_BITNESS(i));
+                               qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
                        }
                }
                else
@@ -297,31 +287,48 @@ void VapourSynthCheckThread::detectVapourSynthPath3(int &success, QString &path)
                }
        }
 
-       //Return VapourSynth path
-       if(success)
+       return flags;
+}
+
+//-------------------------------------
+// Internal functions
+//-------------------------------------
+
+VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
+{
+       if (MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64)
+       {
+               switch (scope)
+               {
+               case MUtils::Registry::scope_wow_x32:
+                       return VAPOURSYNTH_X86;
+               case MUtils::Registry::scope_wow_x64:
+                       return VAPOURSYNTH_X64;
+               default:
+                       return VAPOURSYNTH_DEF;
+               }
+       }
+       else
        {
-               path = vapoursynthPath;
+               return VAPOURSYNTH_X86; /*ignore scope on 32-Bit OS*/
        }
 }
 
-bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QFile *&vpsExeFile, QFile *&vpsDllFile)
+bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
 {
        bool complete = false;
        vpsExeFile = vpsDllFile = NULL;
-
-       QFileInfo vpsExeInfo(QString("%1/vspipe.exe"     ).arg(vsCorePath));
-       QFileInfo vpsDllInfo(QString("%1/vapoursynth.dll").arg(vsCorePath));
        
        qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
        qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
 
-       if(vpsExeInfo.exists() && vpsDllInfo.exists())
+       if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
        {
                vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
                vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
                if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
                {
-                       complete = MUtils::OS::is_executable_file(vpsExeFile->fileName());
+                       complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
                }
        }
 
@@ -336,61 +343,8 @@ bool VapourSynthCheckThread::isVapourSynthComplete(const QString &vsCorePath, QF
 
 bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
 {
-       QProcess process;
-       QStringList output;
-
-       //Setup process object
-       process.setWorkingDirectory(QDir::tempPath());
-       process.setProcessChannelMode(QProcess::MergedChannels);
-       process.setReadChannel(QProcess::StandardOutput);
-
-       //Try to start VSPIPE.EXE
-       process.start(vspipePath, QStringList() << "--version");
-       if(!process.waitForStarted())
-       {
-               qWarning("Failed to launch VSPIPE.EXE -> %s", process.errorString().toUtf8().constData());
-               return false;
-       }
-
-       //Wait for process to finish
-       while(process.state() != QProcess::NotRunning)
-       {
-               if(process.waitForReadyRead(12000))
-               {
-                       while(process.canReadLine())
-                       {
-                               output << QString::fromUtf8(process.readLine()).simplified();
-                       }
-                       continue;
-               }
-               if(process.state() != QProcess::NotRunning)
-               {
-                       qWarning("VSPIPE.EXE process encountered a deadlock -> aborting now!");
-                       break;
-               }
-       }
-
-       //Make sure VSPIPE.EXE has terminated!
-       process.waitForFinished(2500);
-       if(process.state() != QProcess::NotRunning)
-       {
-               qWarning("VSPIPE.EXE process still running, going to kill it!");
-               process.kill();
-               process.waitForFinished(-1);
-       }
-
-       //Read pending lines
-       while(process.canReadLine())
-       {
-               output << QString::fromUtf8(process.readLine()).simplified();
-       }
-
-       //Check exit code
-       if(process.exitCode() != 0)
-       {
-               qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
-               return false;
-       }
+       //Try to run VSPIPE.EXE
+       const QStringList output = runProcess(vspipePath, QStringList() << "--version");
 
        //Init regular expressions
        QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");