///////////////////////////////////////////////////////////////////////////////
// Simple x264 Launcher
-// Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2022 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 "thread_vapoursynth.h"
-#include <QLibrary>
+//Mutils
+#include <MUtils/OSSupport.h>
+#include <MUtils/Registry.h>
+
+//Qt
#include <QEventLoop>
#include <QTimer>
-#include <QMutexLocker>
#include <QApplication>
#include <QDir>
-#include <QProcess>
+#include <QHash>
+#include <QAbstractFileEngine.h>
+//Internal
#include "global.h"
+#include "model_sysinfo.h"
-static const unsigned int VAPOURSYNTH_VERSION_MIN = 20;
+//CRT
+#include <cassert>
+//Static
QMutex VapourSynthCheckThread::m_vpsLock;
-QFile *VapourSynthCheckThread::m_vpsExePath = NULL;
-QFile *VapourSynthCheckThread::m_vpsDllPath = NULL;
-QLibrary *VapourSynthCheckThread::m_vpsLib = NULL;
-
-#define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
-
-static inline QString &cleanDir(QString &path)
-{
- if(!path.isEmpty())
- {
- path = QDir::fromNativeSeparators(path);
- while(path.endsWith('/'))
- {
- path.chop(1);
- }
- }
- return path;
-}
+QScopedPointer<QFile> VapourSynthCheckThread::m_vpsExePath[2];
+QScopedPointer<QFile> VapourSynthCheckThread::m_vpsDllPath[2];
+
+//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)
//-------------------------------------
// External API
//-------------------------------------
-int VapourSynthCheckThread::detect(QString &path)
+bool VapourSynthCheckThread::detect(SysinfoModel* sysinfo)
{
- path.clear();
QMutexLocker lock(&m_vpsLock);
+ sysinfo->clearVapourSynth();
+ sysinfo->clearVPS32Path();
+ sysinfo->clearVPS64Path();
+
QEventLoop loop;
VapourSynthCheckThread thread;
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();
thread.wait();
- return -1;
+ return false;
}
- if(thread.getException())
+ if (thread.getException())
{
qWarning("VapourSynth thread encountered an exception !!!");
- return -1;
- }
-
- if(thread.getSuccess())
- {
- path = thread.getPath();
- qDebug("VapourSynth check completed successfully.");
- return 1;
+ return false;
}
- qWarning("VapourSynth thread failed to detect installation!");
- return 0;
-}
-
-void VapourSynthCheckThread::unload(void)
-{
- QMutexLocker lock(&m_vpsLock);
-
- if(m_vpsLib)
+ const int success = thread.getSuccess();
+ if (!success)
{
- if(m_vpsLib->isLoaded())
- {
- m_vpsLib->unload();
- }
+ qWarning("VapourSynth could not be found -> VapourSynth support disabled!");
+ return true;
}
- if(m_vpsExePath)
+ if (success & VAPOURSYNTH_X86)
{
- if (m_vpsExePath->isOpen())
- {
- m_vpsExePath->close();
- }
+ sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X86, true);
+ sysinfo->setVPS32Path(thread.getPath32());
}
- if(m_vpsDllPath)
+ if (success & VAPOURSYNTH_X64)
{
- if(m_vpsDllPath->isOpen())
- {
- m_vpsDllPath->close();
- }
+ sysinfo->setVapourSynth(SysinfoModel::VapourSynth_X64, true);
+ sysinfo->setVPS64Path(thread.getPath64());
}
- X264_DELETE(m_vpsExePath);
- X264_DELETE(m_vpsDllPath);
- X264_DELETE(m_vpsLib);
+ 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 = false;
- m_exception = false;
- m_vpsPath.clear();
+ m_vpsPath[0U].clear();
+ m_vpsPath[1U].clear();
}
VapourSynthCheckThread::~VapourSynthCheckThread(void)
void VapourSynthCheckThread::run(void)
{
- m_exception = m_success = false;
- m_success = detectVapourSynthPath1(m_vpsPath, &m_exception);
-}
-
-bool VapourSynthCheckThread::detectVapourSynthPath1(QString &path, volatile bool *exception)
-{
- __try
- {
- return detectVapourSynthPath2(path, exception);
- }
- __except(1)
- {
- *exception = true;
- qWarning("Unhandled exception error in VapourSynth thread !!!");
- return false;
- }
+ m_vpsPath[0U].clear();
+ m_vpsPath[1U].clear();
+ StarupThread::run();
}
-bool VapourSynthCheckThread::detectVapourSynthPath2(QString &path, volatile bool *exception)
+int VapourSynthCheckThread::threadMain(void)
{
- try
+ static const int VPS_BIT_FLAG[] =
{
- return detectVapourSynthPath3(path);
- }
- catch(...)
+ VAPOURSYNTH_X86,
+ VAPOURSYNTH_X64,
+ NULL
+ };
+ static const MUtils::Registry::reg_root_t REG_ROOTS[] =
{
- *exception = true;
- qWarning("VapourSynth initializdation raised an C++ exception!");
- return false;
- }
-}
-
-bool VapourSynthCheckThread::detectVapourSynthPath3(QString &path)
-{
- bool success = false;
- X264_DELETE(m_vpsExePath);
- X264_DELETE(m_vpsDllPath);
- path.clear();
-
- static const char *VPS_REG_KEYS[] =
+ MUtils::Registry::root_machine,
+ MUtils::Registry::root_user,
+ REG_ROOT_EOL
+ };
+ static const char* const REG_PATHS_HKLM[] =
{
- "SOFTWARE\\VapourSynth",
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\VapourSynth_is1",
+ 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_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,
+ REG_SCOPE_EOL
+ };
+
+ QHash<int, QFileInfo> vpsDllInfo, vpsExeInfo;
+ int flags = 0;
+
+ //Look for "portable" VapourSynth version
+ for (size_t i = 0; i < 2U; i++)
+ {
+ const QDir vpsPortableDir(QString("%1/extra/VapourSynth-%2").arg(QCoreApplication::applicationDirPath(), QString::number(VPS_BITNESS(i))));
+ if (vpsPortableDir.exists())
+ {
+ 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()))
+ {
+ vpsDllInfo.insert(VPS_BIT_FLAG[i], vpsPortableDll);
+ vpsExeInfo.insert(VPS_BIT_FLAG[i], vpsPortableExe);
+ }
+ }
+ }
//Read VapourSynth path from registry
- QString vapoursynthPath;
- for(size_t i = 0; VPS_REG_KEYS[i]; i++)
+ if (vpsDllInfo.isEmpty() && vpsExeInfo.isEmpty())
{
- for(size_t j = 0; VPS_REG_NAME[j]; j++)
+ for (size_t i = 0; REG_ROOTS[i] != REG_ROOT_EOL; i++)
{
- vapoursynthPath = cleanDir(x264_query_reg_string(false, VPS_REG_KEYS[i], VPS_REG_NAME[j]));
- if(VALID_DIR(vapoursynthPath)) break;
+ 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; scopes[k] != REG_SCOPE_EOL; k++)
+ {
+ if (MUtils::Registry::reg_key_exists(REG_ROOTS[i], QString::fromLatin1(paths[j]), scopes[k]))
+ {
+ QString vpsRegDllPath;
+ if (MUtils::Registry::reg_value_read(REG_ROOTS[i], QString::fromLatin1(paths[j]), QString::fromLatin1(VPS_REG_NAME), vpsRegDllPath, scopes[k]))
+ {
+ QFileInfo vpsRegDllInfo(QDir::fromNativeSeparators(vpsRegDllPath));
+ vpsRegDllInfo.makeAbsolute();
+ if (vpsRegDllInfo.exists() && vpsRegDllInfo.isFile())
+ {
+ 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(VALID_DIR(vapoursynthPath)) break;
}
- //Make sure VapourSynth does exist
- if(!VALID_DIR(vapoursynthPath))
+ //Abort, if VapourSynth was *not* found
+ if (vpsDllInfo.isEmpty() || vpsExeInfo.isEmpty())
{
qWarning("VapourSynth install path not found -> disable VapouSynth support!");
- vapoursynthPath.clear();
+ return 0;
}
- //Make sure that 'vapoursynth.dll' and 'vspipe.exe' are available
- bool vapoursynthComplete = false;
- if(!vapoursynthPath.isEmpty())
+ //Validate the VapourSynth installation now!
+ for (size_t i = 0; i < 2U; i++)
{
- static const char *CORE_PATH[3] = { "core32", "core64", "core" };
- qDebug("VapourSynth Dir: %s", vapoursynthPath.toUtf8().constData());
- for(int i = 0; (i < 3) && (!vapoursynthComplete); i++)
+ qDebug("VapourSynth %u-Bit support is being tested.", VPS_BITNESS(i));
+ if (vpsDllInfo.contains(VPS_BIT_FLAG[i]) && vpsExeInfo.contains(VPS_BIT_FLAG[i]))
{
- QFileInfo vpsExeInfo(QString("%1/%2/vspipe.exe" ).arg(vapoursynthPath, CORE_PATH[i]));
- QFileInfo vpsDllInfo(QString("%1/%2/vapoursynth.dll").arg(vapoursynthPath, CORE_PATH[i]));
- qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
- qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
- if(vpsExeInfo.exists() && vpsDllInfo.exists())
+ QFile *vpsExeFile, *vpsDllFile;
+ if (isVapourSynthComplete(vpsDllInfo[VPS_BIT_FLAG[i]], vpsExeInfo[VPS_BIT_FLAG[i]], vpsExeFile, vpsDllFile))
{
- m_vpsExePath = new QFile(vpsExeInfo.canonicalFilePath());
- m_vpsDllPath = new QFile(vpsDllInfo.canonicalFilePath());
- if(m_vpsExePath->open(QIODevice::ReadOnly) && m_vpsDllPath->open(QIODevice::ReadOnly))
+ m_vpsExePath[i].reset(vpsExeFile);
+ m_vpsDllPath[i].reset(vpsDllFile);
+ if (checkVapourSynth(m_vpsExePath[i]->fileEngine()->fileName(QAbstractFileEngine::CanonicalName)))
{
- if(vapoursynthComplete = x264_is_executable(m_vpsExePath->fileName()))
- {
- vapoursynthPath.append("/").append(CORE_PATH[i]);
- }
- break;
+ 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));
}
- X264_DELETE(m_vpsExePath);
- X264_DELETE(m_vpsDllPath);
+ }
+ else
+ {
+ qWarning("VapourSynth %u-Bit edition was found, but appears to be incomplete!", VPS_BITNESS(i));
}
}
- if(!vapoursynthComplete)
+ else
{
- qWarning("VapourSynth installation incomplete -> disable VapouSynth support!");
+ qDebug("VapourSynth %u-Bit edition *not* found!", VPS_BITNESS(i));
}
}
- //Make sure 'vsscript.dll' can be loaded successfully
- if(vapoursynthComplete && m_vpsExePath)
+ return flags;
+}
+
+//-------------------------------------
+// Internal functions
+//-------------------------------------
+
+VapourSynthCheckThread::VapourSynthFlags VapourSynthCheckThread::getVapourSynthType(const int scope)
+{
+ if (MUtils::OS::os_architecture() == MUtils::OS::ARCH_X64)
{
- qDebug("VapourSynth detection is running, please stand by...");
- success = checkVapourSynthVersion(m_vpsExePath->fileName());
+ switch (scope)
+ {
+ case MUtils::Registry::scope_wow_x32:
+ return VAPOURSYNTH_X86;
+ case MUtils::Registry::scope_wow_x64:
+ return VAPOURSYNTH_X64;
+ default:
+ return VAPOURSYNTH_DEF;
+ }
}
-
- //Return VapourSynth path
- if(success)
+ else
{
- path = vapoursynthPath;
+ return VAPOURSYNTH_X86; /*ignore scope on 32-Bit OS*/
}
-
- return success;
}
-bool VapourSynthCheckThread::checkVapourSynthVersion(const QString vspipePath)
+bool VapourSynthCheckThread::isVapourSynthComplete(const QFileInfo& vpsDllInfo, const QFileInfo& vpsExeInfo, QFile*& vpsExeFile, QFile*& vpsDllFile)
{
- 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;
- }
+ bool complete = false;
+ vpsExeFile = vpsDllFile = NULL;
+
+ qDebug("VapourSynth EXE: %s", vpsExeInfo.absoluteFilePath().toUtf8().constData());
+ qDebug("VapourSynth DLL: %s", vpsDllInfo.absoluteFilePath().toUtf8().constData());
- //Wait for process to finish
- while(process.state() != QProcess::NotRunning)
+ if (vpsDllInfo.exists() && vpsDllInfo.isFile() && vpsExeInfo.exists() && vpsExeInfo.isFile())
{
- if(process.waitForReadyRead(12000))
+ vpsExeFile = new QFile(vpsExeInfo.canonicalFilePath());
+ vpsDllFile = new QFile(vpsDllInfo.canonicalFilePath());
+ if(vpsExeFile->open(QIODevice::ReadOnly) && vpsDllFile->open(QIODevice::ReadOnly))
{
- 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;
+ complete = MUtils::OS::is_executable_file(vpsExeFile->fileEngine()->fileName(QAbstractFileEngine::CanonicalName));
}
}
- //Make sure VSPIPE.EXE has terminated!
- process.waitForFinished(2500);
- if(process.state() != QProcess::NotRunning)
+ if(!complete)
{
- qWarning("VSPIPE.EXE process still running, going to kill it!");
- process.kill();
- process.waitForFinished(-1);
+ MUTILS_DELETE(vpsExeFile);
+ MUTILS_DELETE(vpsDllFile);
}
- //Read pending lines
- while(process.canReadLine())
- {
- output << QString::fromUtf8(process.readLine()).simplified();
- }
+ return complete;
+}
- //Check exit code
- if(process.exitCode() != 0)
- {
- qWarning("VSPIPE.EXE failed with code 0x%08X -> disable Vapousynth support!", process.exitCode());
- return false;
- }
+bool VapourSynthCheckThread::checkVapourSynth(const QString &vspipePath)
+{
+ //Try to run VSPIPE.EXE
+ const QStringList output = runProcess(vspipePath, QStringList() << "--version");
//Init regular expressions
- unsigned int vapursynthVersion = 0;
- bool vapoursynthLogo = false;
QRegExp vpsLogo("VapourSynth\\s+Video\\s+Processing\\s+Library");
- QRegExp vpsCore("Core\\s+r(\\d+)");
//Check for version info
+ bool vapoursynthLogo = false;
for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
{
if(vpsLogo.lastIndexIn(*iter) >= 0)
{
vapoursynthLogo = true;
- continue;
- }
- if(vapoursynthLogo && (vpsCore.lastIndexIn(*iter) >= 0))
- {
- bool ok = false;
- const unsigned int temp = vpsCore.cap(1).toUInt(&ok);
- if(ok) vapursynthVersion = temp;
+ break;
}
}
//Minimum required version found?
- if(vapoursynthLogo && (vapursynthVersion > 0))
+ if(vapoursynthLogo)
{
- qDebug("VapourSynth version \"Core r%u\" detected.", vapursynthVersion);
- if(vapursynthVersion < VAPOURSYNTH_VERSION_MIN)
- {
- qWarning("VapourSynth version is too old -> disable Vapousynth support!");
- return false;
- }
+ qDebug("VapourSynth version was detected successfully.");
return true;
}
//Failed to determine version
- qWarning("Failed to determine VapourSynth version -> disable Vapousynth support!");
- qWarning("VapourSynth version is unsupported or VapourSynth installation is corrupted.");
+ qWarning("Failed to determine VapourSynth version!");
return false;
}