Implemented VaporSynth input support.

This commit is contained in:
LoRd_MuldeR 2013-08-02 20:44:47 +02:00
parent 78f882f1ad
commit 2aa2c7385f
5 changed files with 327 additions and 47 deletions

View File

@ -99,18 +99,20 @@ while(0)
* Static vars
*/
static const unsigned int REV_MULT = 10000;
static const char *VPS_TEST_FILE = "import vapoursynth as vs\ncore = vs.get_core()\nv = core.std.BlankClip()\nv.set_output()\n";
///////////////////////////////////////////////////////////////////////////////
// Constructor & Destructor
///////////////////////////////////////////////////////////////////////////////
EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool const skipVersionTest, int processPriroity)
EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, const QString &vpsDir, bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool const skipVersionTest, int processPriroity)
:
m_jobId(QUuid::createUuid()),
m_sourceFileName(sourceFileName),
m_outputFileName(outputFileName),
m_options(new OptionsModel(*options)),
m_binDir(binDir),
m_vpsDir(vpsDir),
m_x264_x64(x264_x64),
m_x264_10bit(x264_10bit),
m_avs2yuv_x64(avs2yuv_x64),
@ -140,6 +142,7 @@ EncodeThread::~EncodeThread(void)
void EncodeThread::run(void)
{
#if !defined(_DEBUG)
__try
{
checkedRun();
@ -148,6 +151,9 @@ void EncodeThread::run(void)
{
qWarning("STRUCTURED EXCEPTION ERROR IN ENCODE THREAD !!!");
}
#else
checkedRun();
#endif
if(m_handle_jobObject)
{
@ -217,11 +223,13 @@ void EncodeThread::encode(void)
log(tr("Profile: %1").arg(m_options->profile()));
log(tr("Custom: %1").arg(m_options->customX264().isEmpty() ? tr("(None)") : m_options->customX264()));
log(m_binDir);
bool ok = false;
unsigned int frames = 0;
//Use Avisynth?
const bool usePipe = (QFileInfo(m_sourceFileName).suffix().compare("avs", Qt::CaseInsensitive) == 0);
//Seletct type of input
const int inputType = getInputType(QFileInfo(m_sourceFileName).suffix());
const QString indexFile = QString("%1/%2.ffindex").arg(QDir::tempPath(), m_jobId.toString());
//Checking x264 version
@ -233,10 +241,16 @@ void EncodeThread::encode(void)
//Checking avs2yuv version
unsigned int revision_avs2yuv = UINT_MAX;
if(usePipe)
switch(inputType)
{
case INPUT_AVISYN:
ok = ((revision_avs2yuv = checkVersionAvs2yuv(m_avs2yuv_x64)) != UINT_MAX);
CHECK_STATUS(m_abort, ok);
break;
case INPUT_VAPOUR:
ok = checkVersionVapoursynth(QString("%1/vspipe.exe").arg(m_vpsDir));
CHECK_STATUS(m_abort, ok);
break;
}
//Print versions
@ -264,11 +278,20 @@ void EncodeThread::encode(void)
}
//Detect source info
if(usePipe)
if(inputType != INPUT_NATIVE)
{
log(tr("\n--- AVS INFO ---\n"));
ok = checkProperties(m_avs2yuv_x64, frames);
log(tr("\n--- SOURCE INFO ---\n"));
switch(inputType)
{
case INPUT_AVISYN:
ok = checkPropertiesAvisynth(m_avs2yuv_x64, frames);
CHECK_STATUS(m_abort, ok);
break;
case INPUT_VAPOUR:
ok = checkPropertiesVapoursynth(QString("%1/vspipe.exe").arg(m_vpsDir), frames);
CHECK_STATUS(m_abort, ok);
break;
}
}
//Run encoding passes
@ -287,17 +310,17 @@ void EncodeThread::encode(void)
}
log(tr("\n--- PASS 1 ---\n"));
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, usePipe, frames, indexFile, 1, passLogFile);
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile, 1, passLogFile);
CHECK_STATUS(m_abort, ok);
log(tr("\n--- PASS 2 ---\n"));
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, usePipe, frames, indexFile, 2, passLogFile);
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile, 2, passLogFile);
CHECK_STATUS(m_abort, ok);
}
else
{
log(tr("\n--- ENCODING ---\n"));
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, usePipe, frames, indexFile);
ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile);
CHECK_STATUS(m_abort, ok);
}
@ -308,29 +331,44 @@ void EncodeThread::encode(void)
setStatus(JobStatus_Completed);
}
bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool usePipe, unsigned int frames, const QString &indexFile, int pass, const QString &passLogFile)
bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_x64, int inputType, unsigned int frames, const QString &indexFile, int pass, const QString &passLogFile)
{
QProcess processEncode, processAvisynth;
QProcess processEncode, processInput;
if(usePipe)
if(inputType != INPUT_NATIVE)
{
QStringList cmdLine_Avisynth;
QStringList cmdLine_Input;
processInput.setStandardOutputProcess(&processEncode);
switch(inputType)
{
case INPUT_AVISYN:
if(!m_options->customAvs2YUV().isEmpty())
{
cmdLine_Avisynth.append(splitParams(m_options->customAvs2YUV()));
cmdLine_Input.append(splitParams(m_options->customAvs2YUV()));
}
cmdLine_Avisynth << pathToLocal(QDir::toNativeSeparators(m_sourceFileName));
cmdLine_Avisynth << "-";
processAvisynth.setStandardOutputProcess(&processEncode);
cmdLine_Input << pathToLocal(QDir::toNativeSeparators(m_sourceFileName));
cmdLine_Input << "-";
log("Creating Avisynth process:");
if(!startProcess(processAvisynth, AVS2_BINARY(m_binDir, avs2yuv_x64), cmdLine_Avisynth, false))
if(!startProcess(processInput, AVS2_BINARY(m_binDir, avs2yuv_x64), cmdLine_Input, false))
{
return false;
}
break;
case INPUT_VAPOUR:
cmdLine_Input << pathToLocal(QDir::toNativeSeparators(m_sourceFileName));
cmdLine_Input << "-" << "-y4m";
log("Creating Vapoursynth process:");
if(!startProcess(processInput, QString("%1/vspipe.exe").arg(m_vpsDir), cmdLine_Input, false))
{
return false;
}
break;
default:
throw "Bad input type encontered!";
}
}
QStringList cmdLine_Encode = buildCommandLine(usePipe, x264_10bit, frames, indexFile, pass, passLogFile);
QStringList cmdLine_Encode = buildCommandLine((inputType != INPUT_NATIVE), x264_10bit, frames, indexFile, pass, passLogFile);
log("Creating x264 process:");
if(!startProcess(processEncode, X264_BINARY(m_binDir, x264_10bit, x264_x64), cmdLine_Encode))
@ -362,7 +400,7 @@ bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_
if(m_abort)
{
processEncode.kill();
processAvisynth.kill();
processInput.kill();
bAborted = true;
break;
}
@ -372,7 +410,7 @@ bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_
setStatus(JobStatus_Paused);
log(tr("Job paused by user at %1, %2.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
bool ok[2] = {false, false};
Q_PID pid[2] = {processEncode.pid(), processAvisynth.pid()};
Q_PID pid[2] = {processEncode.pid(), processInput.pid()};
if(pid[0]) { ok[0] = (SuspendThread(pid[0]->hThread) != (DWORD)(-1)); }
if(pid[1]) { ok[1] = (SuspendThread(pid[1]->hThread) != (DWORD)(-1)); }
while(m_pause) m_semaphorePaused.tryAcquire(1, 5000);
@ -475,27 +513,35 @@ bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_
processEncode.waitForFinished(-1);
}
processAvisynth.waitForFinished(5000);
if(processAvisynth.state() != QProcess::NotRunning)
processInput.waitForFinished(5000);
if(processInput.state() != QProcess::NotRunning)
{
qWarning("Avisynth process still running, going to kill it!");
processAvisynth.kill();
processAvisynth.waitForFinished(-1);
qWarning("Input process still running, going to kill it!");
processInput.kill();
processInput.waitForFinished(-1);
}
if(!(bTimeout || bAborted))
{
while(processAvisynth.bytesAvailable() > 0)
while(processInput.bytesAvailable() > 0)
{
log(tr("av2y [info]: %1").arg(QString::fromUtf8(processAvisynth.readLine()).simplified()));
switch(inputType)
{
case INPUT_AVISYN:
log(tr("av2y [info]: %1").arg(QString::fromUtf8(processInput.readLine()).simplified()));
break;
case INPUT_VAPOUR:
log(tr("vpyp [info]: %1").arg(QString::fromUtf8(processInput.readLine()).simplified()));
break;
}
}
}
if(usePipe && (processAvisynth.exitCode() != EXIT_SUCCESS))
if((inputType != INPUT_NATIVE) && (processInput.exitCode() != EXIT_SUCCESS))
{
if(!(bTimeout || bAborted))
{
log(tr("\nWARNING: Avisynth process exited with error code: %1").arg(QString::number(processAvisynth.exitCode())));
log(tr("\nWARNING: Input process exited with error code: %1").arg(QString::number(processInput.exitCode())));
}
}
@ -506,7 +552,7 @@ bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_
log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(processEncode.exitCode())));
}
processEncode.close();
processAvisynth.close();
processInput.close();
return false;
}
@ -534,7 +580,7 @@ bool EncodeThread::runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_
setProgress(100);
processEncode.close();
processAvisynth.close();
processInput.close();
return true;
}
@ -831,7 +877,86 @@ unsigned int EncodeThread::checkVersionAvs2yuv(bool x64)
return (ver_maj * REV_MULT) + ((ver_min % REV_MULT) * 10) + (ver_mod % 10);
}
bool EncodeThread::checkProperties(bool x64, unsigned int &frames)
bool EncodeThread::checkVersionVapoursynth(const QString &vspipePath)
{
QProcess process;
log("\nCreating process:");
if(!startProcess(process, vspipePath, QStringList()))
{
return false;;
}
QRegExp regExpSignature("\\bVSPipe\\s+usage\\b", Qt::CaseInsensitive);
bool bTimeout = false;
bool bAborted = false;
bool vspipeSignature = false;
while(process.state() != QProcess::NotRunning)
{
if(m_abort)
{
process.kill();
bAborted = true;
break;
}
if(!process.waitForReadyRead())
{
if(process.state() == QProcess::Running)
{
process.kill();
qWarning("VSPipe process timed out <-- killing!");
log("\nPROCESS TIMEOUT !!!");
bTimeout = true;
break;
}
}
while(process.bytesAvailable() > 0)
{
QList<QByteArray> lines = process.readLine().split('\r');
while(!lines.isEmpty())
{
QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
if(regExpSignature.lastIndexIn(text) >= 0)
{
vspipeSignature = true;
}
if(!text.isEmpty())
{
log(text);
}
}
}
}
process.waitForFinished();
if(process.state() != QProcess::NotRunning)
{
process.kill();
process.waitForFinished(-1);
}
if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 1)))
{
if(!(bTimeout || bAborted))
{
log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
}
return false;
}
if(!vspipeSignature)
{
log(tr("\nFAILED TO DETECT VSPIPE SIGNATURE !!!"));
return false;
}
return vspipeSignature;
}
bool EncodeThread::checkPropertiesAvisynth(bool x64, unsigned int &frames)
{
QProcess process;
QStringList cmdLine;
@ -994,6 +1119,137 @@ bool EncodeThread::checkProperties(bool x64, unsigned int &frames)
return true;
}
bool EncodeThread::checkPropertiesVapoursynth(const QString &vspipePath, unsigned int &frames)
{
QProcess process;
QStringList cmdLine;
cmdLine << pathToLocal(QDir::toNativeSeparators(m_sourceFileName));
cmdLine << "-" << "-info";
log("Creating process:");
if(!startProcess(process, vspipePath, cmdLine))
{
return false;;
}
QRegExp regExpFrm("\\bFrames:\\s+(\\d+)\\b");
QRegExp regExpSzW("\\bWidth:\\s+(\\d+)\\b");
QRegExp regExpSzH("\\bHeight:\\s+(\\d+)\\b");
QTextCodec *localCodec = QTextCodec::codecForName("System");
bool bTimeout = false;
bool bAborted = false;
frames = 0;
unsigned int fSizeW = 0;
unsigned int fSizeH = 0;
unsigned int waitCounter = 0;
while(process.state() != QProcess::NotRunning)
{
if(m_abort)
{
process.kill();
bAborted = true;
break;
}
if(!process.waitForReadyRead(m_processTimeoutInterval))
{
if(process.state() == QProcess::Running)
{
if(waitCounter++ > m_processTimeoutMaxCounter)
{
process.kill();
qWarning("VSPipe process timed out <-- killing!");
log("\nPROCESS TIMEOUT !!!");
log("\nVapoursynth has encountered a deadlock or your script takes EXTREMELY long to initialize!");
bTimeout = true;
break;
}
else if(waitCounter == m_processTimeoutWarning)
{
unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
log(tr("Warning: nVapoursynth did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
}
}
continue;
}
waitCounter = 0;
while(process.bytesAvailable() > 0)
{
QList<QByteArray> lines = process.readLine().split('\r');
while(!lines.isEmpty())
{
QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
int offset = -1;
if((offset = regExpFrm.lastIndexIn(text)) >= 0)
{
bool ok = false;
unsigned int temp = regExpFrm.cap(1).toUInt(&ok);
if(ok) frames = temp;
}
if((offset = regExpSzW.lastIndexIn(text)) >= 0)
{
bool ok = false;
unsigned int temp = regExpSzW.cap(1).toUInt(&ok);
if(ok) fSizeW = temp;
}
if((offset = regExpSzH.lastIndexIn(text)) >= 0)
{
bool ok = false;
unsigned int temp = regExpSzH.cap(1).toUInt(&ok);
if(ok) fSizeH = temp;
}
if(!text.isEmpty())
{
log(text);
}
}
}
}
process.waitForFinished();
if(process.state() != QProcess::NotRunning)
{
process.kill();
process.waitForFinished(-1);
}
if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
{
if(!(bTimeout || bAborted))
{
log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
}
return false;
}
if(frames == 0)
{
log(tr("\nFAILED TO DETERMINE VPY PROPERTIES !!!"));
return false;
}
log("");
if((fSizeW > 0) && (fSizeH > 0))
{
log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
}
if(frames > 0)
{
log(tr("No. Frames: %1").arg(QString::number(frames)));
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Misc functions
///////////////////////////////////////////////////////////////////////////////
@ -1220,6 +1476,16 @@ QString EncodeThread::sizeToString(qint64 size)
return tr("N/A");
}
int EncodeThread::getInputType(const QString &fileExt)
{
int type = INPUT_NATIVE;
if(fileExt.compare("avs", Qt::CaseInsensitive) == 0) type = INPUT_AVISYN;
else if(fileExt.compare("avsi", Qt::CaseInsensitive) == 0) type = INPUT_AVISYN;
else if(fileExt.compare("vpy", Qt::CaseInsensitive) == 0) type = INPUT_VAPOUR;
else if(fileExt.compare("py", Qt::CaseInsensitive) == 0) type = INPUT_VAPOUR;
return type;
}
void EncodeThread::setPorcessPriority(void *processId, int priroity)
{
switch(priroity)

View File

@ -37,7 +37,7 @@ class EncodeThread : public QThread
Q_OBJECT
public:
EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool const skipVersionTest, int processPriroity);
EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, const QString &vpsDir, bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool const skipVersionTest, int processPriroity);
~EncodeThread(void);
QUuid getId(void) { return this->m_jobId; };
@ -73,12 +73,21 @@ protected:
const QString m_outputFileName;
const OptionsModel *m_options;
const QString m_binDir;
const QString m_vpsDir;
const bool m_x264_x64;
const bool m_x264_10bit;
const bool m_avs2yuv_x64;
const bool m_skipVersionTest;
const int m_processPriority;
//Types
enum inputType_t
{
INPUT_NATIVE = 0,
INPUT_AVISYN = 1,
INPUT_VAPOUR = 2
};
//Flags
volatile bool m_abort;
volatile bool m_pause;
@ -99,11 +108,13 @@ protected:
//Encode functions
void encode(void);
bool runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_x64, bool usePipe, unsigned int frames, const QString &indexFile, int pass = 0, const QString &passLogFile = QString());
bool runEncodingPass(bool x264_x64, bool x264_10bit, bool avs2yuv_x64, int inputType, unsigned int frames, const QString &indexFile, int pass = 0, const QString &passLogFile = QString());
QStringList buildCommandLine(bool usePipe, bool use10Bit, unsigned int frames, const QString &indexFile, int pass = 0, const QString &passLogFile = QString());
unsigned int checkVersionX264(bool use_x64, bool use_10bit, bool &modified);
unsigned int checkVersionAvs2yuv(bool x64);
bool checkProperties(bool x64, unsigned int &frames);
bool checkVersionVapoursynth(const QString &vspipePath);
bool checkPropertiesAvisynth(bool x64, unsigned int &frames);
bool checkPropertiesVapoursynth(const QString &vspipePath, unsigned int &frames);
//Auxiallary Stuff
void log(const QString &text) { emit messageLogged(m_jobId, text); }
@ -119,6 +130,7 @@ protected:
static QString commandline2string(const QString &program, const QStringList &arguments);
static QString sizeToString(qint64 size);
static void setPorcessPriority(void *processId, int priroity);
static int getInputType(const QString &fileExt);
signals:
void statusChanged(const QUuid &jobId, JobStatus newStatus);

View File

@ -21,8 +21,8 @@
#define VER_X264_MAJOR 2
#define VER_X264_MINOR 1
#define VER_X264_PATCH 7
#define VER_X264_BUILD 519
#define VER_X264_PATCH 8
#define VER_X264_BUILD 530
#define VER_X264_MINIMUM_REV 2339
#define VER_X264_CURRENT_API 135

View File

@ -958,6 +958,7 @@ QString AddJobDialog::getInputFilterLst(void)
s_filters[] =
{
{"Avisynth Scripts", "avs"},
{"VapourSynth Scripts", "vpy"},
{"Matroska Files", "mkv"},
{"MPEG-4 Part 14 Container", "mp4"},
{"Audio Video Interleaved", "avi"},

View File

@ -820,7 +820,7 @@ void MainWindow::init(void)
if(okay)
{
qDebug("VapourSynth support enabled.");
m_vapoursynthPath = QFileInfo(vpsExePath->fileName()).canonicalFilePath();
m_vapoursynthPath = QFileInfo(vpsExePath->fileName()).canonicalPath();
m_toolsList << vpsExePath;
m_toolsList << vpsDllPath;
}
@ -1164,6 +1164,7 @@ bool MainWindow::appendJob(const QString &sourceFileName, const QString &outputF
outputFileName,
options,
QString("%1/toolset").arg(m_appDir),
m_vapoursynthPath,
m_cpuFeatures->x64,
m_preferences->use10BitEncoding(),
m_cpuFeatures->x64 && m_preferences->useAvisyth64Bit(),