OSDN Git Service

Various improvements to Avisynth and VapourSynth detection code. Refactored common...
[x264-launcher/x264-launcher.git] / src / thread_avisynth.cpp
index 5240b02..129fb3d 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // Simple x264 Launcher
-// Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2019 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_avisynth.h"
 
+//Qt
 #include <QLibrary>
 #include <QEventLoop>
 #include <QTimer>
-#include <QMutexLocker>
 #include <QApplication>
+#include <QDir>
 
+//Internal
 #include "global.h"
-#include "avisynth_c.h"
+#include "model_sysinfo.h"
 
+//MUtils
+#include <MUtils/Global.h>
+#include <MUtils/OSSupport.h>
+
+//Const
+static const bool ENABLE_PORTABLE_AVS = true;
+
+//Static
 QMutex AvisynthCheckThread::m_avsLock;
-QLibrary *AvisynthCheckThread::m_avsLib = NULL;
+QScopedPointer<QFile> AvisynthCheckThread::m_avsDllPath[2];
+
+//Helper
+#define VALID_DIR(STR) ((!(STR).isEmpty()) && QDir((STR)).exists())
+#define BOOLIFY(X) ((X) ? '1' : '0')
 
-AvisynthCheckThread::AvisynthCheckThread(void)
+//Utility function
+QString AVS_CHECK_BINARY(const SysinfoModel *sysinfo, const bool& x64)
 {
-       m_success = false;
-       m_exception = false;
-       m_version = 0.0;
+       return QString("%1/toolset/%2/avs_check_%2.exe").arg(sysinfo->getAppPath(), (x64 ? "x64": "x86"));
 }
 
-AvisynthCheckThread::~AvisynthCheckThread(void)
+class Wow64RedirectionDisabler
 {
-}
+public:
+       Wow64RedirectionDisabler(void)
+       {
+               m_oldValue = NULL;
+               m_disabled = MUtils::OS::wow64fsredir_disable(m_oldValue);
+       }
+       ~Wow64RedirectionDisabler(void)
+       {
+               if(m_disabled)
+               {
+                       if(!MUtils::OS::wow64fsredir_revert(m_oldValue))
+                       {
+                               qWarning("Failed to renable WOW64 filesystem redirection!");
+                       }
+               }
+       }
+private:
+       bool  m_disabled;
+       void* m_oldValue;
+};
 
-int AvisynthCheckThread::detect(volatile double *version)
+//-------------------------------------
+// External API
+//-------------------------------------
+
+bool AvisynthCheckThread::detect(SysinfoModel *sysinfo)
 {
-       *version = 0.0;
+       sysinfo->clearAvisynth();
+       double version = 0.0;
        QMutexLocker lock(&m_avsLock);
 
        QEventLoop loop;
-       AvisynthCheckThread thread;
-       QTimer timer;
+       AvisynthCheckThread thread(sysinfo);
 
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
 
        connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
        connect(&thread, SIGNAL(terminated()), &loop, SLOT(quit()));
-       connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
        
        thread.start();
-       timer.start(8000);
+       QTimer::singleShot(30000, &loop, SLOT(quit()));
        
        qDebug("Avisynth thread has been created, please wait...");
        loop.exec(QEventLoop::ExcludeUserInputEvents);
@@ -73,145 +108,199 @@ int AvisynthCheckThread::detect(volatile double *version)
                qWarning("Avisynth thread encountered timeout -> probably deadlock!");
                thread.terminate();
                thread.wait();
-               return -1;
+               return false;
        }
 
        if(thread.getException())
        {
                qWarning("Avisynth thread encountered an exception !!!");
-               return -1;
+               return false;
        }
        
        if(thread.getSuccess())
        {
-               *version = thread.getVersion();
-               qDebug("Version check completed: %.2f", *version);
-               return 1;
+               sysinfo->setAvisynth(SysinfoModel::Avisynth_X86, thread.getSuccess() & AVISYNTH_X86);
+               sysinfo->setAvisynth(SysinfoModel::Avisynth_X64, thread.getSuccess() & AVISYNTH_X64);
+               sysinfo->setAVSPath(thread.getPath());
+               qDebug("Avisynth support is officially enabled now! [x86=%c, x64=%c]", BOOLIFY(sysinfo->getAvisynth(SysinfoModel::Avisynth_X86)), BOOLIFY(sysinfo->getAvisynth(SysinfoModel::Avisynth_X64)));
+       }
+       else
+       {
+               qWarning("Avisynth could not be found -> Avisynth support disabled!");
        }
 
-       qWarning("Avisynth thread failed to determine the version!");
-       return 0;
+       return true;
 }
 
-void AvisynthCheckThread::unload(void)
+//-------------------------------------
+// Thread class
+//-------------------------------------
+
+AvisynthCheckThread::AvisynthCheckThread(const SysinfoModel *const sysinfo)
+:
+       m_sysinfo(sysinfo)
 {
-       QMutexLocker lock(&m_avsLock);
+       m_success = false;
+       m_exception = false;
+}
 
-       if(m_avsLib)
-       {
-               if(m_avsLib->isLoaded())
-               {
-                       m_avsLib->unload();
-               }
-               X264_DELETE(m_avsLib);
-       }
+AvisynthCheckThread::~AvisynthCheckThread(void)
+{
 }
 
 void AvisynthCheckThread::run(void)
 {
-       m_success = detectAvisynthVersion1(&m_version, &m_exception);
+       m_exception = false;
+       m_success &= 0;
+       m_basePath.clear();
+
+       detectAvisynthVersion1(m_success, m_basePath, m_sysinfo, &m_exception);
 }
 
-bool AvisynthCheckThread::detectAvisynthVersion1(volatile double *version_number, volatile bool *exception)
+void AvisynthCheckThread::detectAvisynthVersion1(int &success, QString &basePath, const SysinfoModel *const sysinfo, volatile bool *exception)
 {
        __try
        {
-               return detectAvisynthVersion2(version_number, exception);
+               detectAvisynthVersion2(success, basePath, sysinfo, exception);
        }
        __except(1)
        {
                *exception = true;
                qWarning("Unhandled exception error in Avisynth thread !!!");
-               return false;
        }
 }
 
-bool AvisynthCheckThread::detectAvisynthVersion2(volatile double *version_number, volatile bool *exception)
+void AvisynthCheckThread::detectAvisynthVersion2(int &success, QString &basePath, const SysinfoModel *const sysinfo, volatile bool *exception)
 {
        try
        {
-               return detectAvisynthVersion3(version_number);
+               return detectAvisynthVersion3(success, basePath, sysinfo);
        }
        catch(...)
        {
                *exception = true;
                qWarning("Avisynth initializdation raised an C++ exception!");
-               return false;
        }
 }
 
-bool AvisynthCheckThread::detectAvisynthVersion3(volatile double *version_number)
+void AvisynthCheckThread::detectAvisynthVersion3(int &success, QString &basePath, const SysinfoModel *const sysinfo)
+{
+       success &= 0;
+
+       QFile *avsPath32;
+       if(checkAvisynth(basePath, sysinfo, avsPath32, false))
+       {
+               m_avsDllPath[0].reset(avsPath32);
+               success |= AVISYNTH_X86;
+               qDebug("Avisynth 32-Bit edition found!");
+       }
+       else
+       {
+               qDebug("Avisynth 32-Bit edition *not* found!");
+       }
+
+       if(sysinfo->getCPUFeatures(SysinfoModel::CPUFeatures_X64))
+       {
+               QFile *avsPath64;
+               if(checkAvisynth(basePath, sysinfo, avsPath64, true))
+               {
+                       m_avsDllPath[1].reset(avsPath64);
+                       success |= AVISYNTH_X64;
+                       qDebug("Avisynth 64-Bit edition found!");
+               }
+               else
+               {
+                       qDebug("Avisynth 64-Bit edition *not* found!");
+               }
+       }
+       else
+       {
+               qWarning("Skipping 64-Bit Avisynth check on non-x64 system!");
+       }
+}
+
+bool AvisynthCheckThread::checkAvisynth(QString &basePath, const SysinfoModel *const sysinfo, QFile *&path, const bool &x64)
 {
-       bool success = false;
-       *version_number = 0.0;
+       qDebug("Avisynth %s-Bit support is being tested.", x64 ? "64" : "32");
 
-       if(!m_avsLib)
+       //Look for "portable" Avisynth version
+       static const char *const ARCH_DIR[] = { "x64", "x86" };
+       const QLatin1String archSuffix = QLatin1String(ARCH_DIR[x64 ? 1 : 0]);
+       if (ENABLE_PORTABLE_AVS)
        {
-               m_avsLib = new QLibrary("avisynth.dll");
+               const QString avsPortableDir = QString("%1/extra/Avisynth").arg(QCoreApplication::applicationDirPath());
+               if (VALID_DIR(avsPortableDir))
+               {
+                       QFileInfo avsDllFile(QString("%1/%2/avisynth.dll").arg(avsPortableDir, archSuffix)), devilDllFile(QString("%1/%2/devil.dll").arg(avsPortableDir, archSuffix));
+                       if (avsDllFile.exists() && devilDllFile.exists() && avsDllFile.isFile() && devilDllFile.isFile())
+                       {
+                               qWarning("Adding portable Avisynth to PATH environment variable: %s", MUTILS_UTF8(avsPortableDir));
+                               basePath = avsPortableDir;
+                       }
+               }
        }
 
-       if(m_avsLib->isLoaded() || m_avsLib->load())
+       //Get extra paths
+       QStringList avisynthExtraPaths;
+       if (!basePath.isEmpty())
        {
-               avs_create_script_environment_func avs_create_script_environment_ptr = (avs_create_script_environment_func) m_avsLib->resolve("avs_create_script_environment");
-               avs_invoke_func avs_invoke_ptr = (avs_invoke_func) m_avsLib->resolve("avs_invoke");
-               avs_function_exists_func avs_function_exists_ptr = (avs_function_exists_func) m_avsLib->resolve("avs_function_exists");
-               avs_delete_script_environment_func avs_delete_script_environment_ptr = (avs_delete_script_environment_func) m_avsLib->resolve("avs_delete_script_environment");
-               avs_release_value_func avs_release_value_ptr = (avs_release_value_func) m_avsLib->resolve("avs_release_value");
+               avisynthExtraPaths << QString("%1/%2").arg(basePath, archSuffix);
+       }
+
+       //Setup process object
+       const QStringList output = runProcess(AVS_CHECK_BINARY(sysinfo, x64), QStringList(), &avisynthExtraPaths);
+
+       //Init regular expressions
+       QRegExp avsLogo("Avisynth\\s+Checker\\s+(x86|x64)");
+       QRegExp avsPath("Avisynth_DLLPath=(.+)");
+       QRegExp avsVers("Avisynth_Version=(\\d+)\\.(\\d+)");
        
-               if((avs_create_script_environment_ptr != NULL) && (avs_invoke_ptr != NULL) && (avs_function_exists_ptr != NULL))
+       //Check for version info
+       bool avisynthLogo = false;
+       quint32 avisynthVersion[2] = { 0, 0 };
+       QString avisynthPath;
+       for(QStringList::ConstIterator iter = output.constBegin(); iter != output.constEnd(); iter++)
+       {
+               if(avisynthLogo)
                {
-                       qDebug("avs_create_script_environment_ptr(AVS_INTERFACE_25)");
-                       AVS_ScriptEnvironment* avs_env = avs_create_script_environment_ptr(AVS_INTERFACE_25);
-                       if(avs_env != NULL)
+                       if(avsPath.indexIn(*iter) >= 0)
                        {
-                               qDebug("avs_function_exists_ptr(avs_env, \"VersionNumber\")");
-                               if(avs_function_exists_ptr(avs_env, "VersionNumber"))
-                               {
-                                       qDebug("avs_invoke_ptr(avs_env, \"VersionNumber\", avs_new_value_array(NULL, 0), NULL)");
-                                       AVS_Value avs_version = avs_invoke_ptr(avs_env, "VersionNumber", avs_new_value_array(NULL, 0), NULL);
-                                       if(!avs_is_error(avs_version))
-                                       {
-                                               if(avs_is_float(avs_version))
-                                               {
-                                                       qDebug("Avisynth version: v%.2f", avs_as_float(avs_version));
-                                                       *version_number = avs_as_float(avs_version);
-                                                       if(avs_release_value_ptr) avs_release_value_ptr(avs_version);
-                                                       success = true;
-                                               }
-                                               else
-                                               {
-                                                       qWarning("Failed to determine version number, Avisynth didn't return a float!");
-                                               }
-                                       }
-                                       else
-                                       {
-                                               qWarning("Failed to determine version number, Avisynth returned an error!");
-                                       }
-                               }
-                               else
-                               {
-                                       qWarning("The 'VersionNumber' function does not exist in your Avisynth DLL, can't determine version!");
-                               }
-                               if(avs_delete_script_environment_ptr != NULL)
-                               {
-                                       avs_delete_script_environment_ptr(avs_env);
-                                       avs_env = NULL;
-                               }
+                               avisynthPath =  avsPath.cap(1).trimmed();
                        }
-                       else
+                       else if(avsVers.indexIn(*iter) >= 0)
                        {
-                               qWarning("The Avisynth DLL failed to create the script environment!");
+                               quint32 temp[2];
+                               if(MUtils::regexp_parse_uint32(avsVers, temp, 2))
+                               {
+                                       avisynthVersion[0] = temp[0];
+                                       avisynthVersion[1] = temp[1];
+                               }
                        }
                }
                else
                {
-                       qWarning("It seems the Avisynth DLL is missing required API functions!");
+                       if(avsLogo.lastIndexIn(*iter) >= 0)
+                       {
+                               avisynthLogo = true;
+                       }
                }
        }
-       else
+       
+       //Minimum required version found?
+       if((avisynthVersion[0] >= 2) && (avisynthVersion[1] >= 50) && (!avisynthPath.isEmpty()))
        {
-               qWarning("Failed to load Avisynth.dll library!");
+               Wow64RedirectionDisabler disableWow64Redir;
+               path = new QFile(avisynthPath);
+               if(!path->open(QIODevice::ReadOnly))
+               {
+                       MUTILS_DELETE(path);
+               }
+               qDebug("Avisynth was detected successfully (current version: %u.%02u).", avisynthVersion[0], avisynthVersion[1]);
+               qDebug("Avisynth DLL path: %s", MUTILS_UTF8(avisynthPath));
+               return true;
        }
-
-       return success;
+       
+       //Failed to determine version
+       qWarning("Failed to determine Avisynth version!");
+       return false;
 }