OSDN Git Service

Massive clean up: Removed Win32 API dependency from "global.h". All Win32 API calls...
[x264-launcher/x264-launcher.git] / src / thread_encode.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2013 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_encode.h"
23
24 #include "global.h"
25 #include "model_options.h"
26 #include "model_preferences.h"
27 #include "job_object.h"
28 #include "version.h"
29
30 #include <QDate>
31 #include <QTime>
32 #include <QDateTime>
33 #include <QFileInfo>
34 #include <QDir>
35 #include <QProcess>
36 #include <QMutex>
37 #include <QTextCodec>
38 #include <QLocale>
39 #include <QCryptographicHash>
40
41 /*
42  * Static vars
43  */
44 QMutex EncodeThread::m_mutex_startProcess;
45
46 /*
47  * Macros
48  */
49 #define CHECK_STATUS(ABORT_FLAG, OK_FLAG) do \
50 { \
51         if(ABORT_FLAG) \
52         { \
53                 log("\nPROCESS ABORTED BY USER !!!"); \
54                 setStatus(JobStatus_Aborted); \
55                 if(QFileInfo(indexFile).exists()) QFile::remove(indexFile); \
56                 if(QFileInfo(m_outputFileName).exists() && (QFileInfo(m_outputFileName).size() == 0)) QFile::remove(m_outputFileName); \
57                 return; \
58         } \
59         else if(!(OK_FLAG)) \
60         { \
61                 setStatus(JobStatus_Failed); \
62                 if(QFileInfo(indexFile).exists()) QFile::remove(indexFile); \
63                 if(QFileInfo(m_outputFileName).exists() && (QFileInfo(m_outputFileName).size() == 0)) QFile::remove(m_outputFileName); \
64                 return; \
65         } \
66 } \
67 while(0)
68
69 #define APPEND_AND_CLEAR(LIST, STR) do \
70 { \
71         if(!((STR).isEmpty())) \
72         { \
73                 (LIST) << (STR); \
74                 (STR).clear(); \
75         } \
76 } \
77 while(0)
78
79 #define REMOVE_CUSTOM_ARG(LIST, ITER, FLAG, PARAM) do \
80 { \
81         if(ITER != LIST.end()) \
82         { \
83                 if((*ITER).compare(PARAM, Qt::CaseInsensitive) == 0) \
84                 { \
85                         log(tr("WARNING: Custom parameter \"" PARAM "\" will be ignored in Pipe'd mode!\n")); \
86                         ITER = LIST.erase(ITER); \
87                         if(ITER != LIST.end()) \
88                         { \
89                                 if(!((*ITER).startsWith("--", Qt::CaseInsensitive))) ITER = LIST.erase(ITER); \
90                         } \
91                         FLAG = true; \
92                 } \
93         } \
94 } \
95 while(0)
96
97 #define AVS2_BINARY(BIN_DIR, IS_X64) (QString("%1/%2/avs2yuv_%2.exe").arg((BIN_DIR), ((IS_X64) ? "x64" : "x86")))
98 #define X264_BINARY(BIN_DIR, IS_10BIT, IS_X64) (QString("%1/%2/x264_%3_%2.exe").arg((BIN_DIR), ((IS_X64) ? "x64" : "x86"), ((IS_10BIT) ? "10bit" : "8bit")))
99 #define VPSP_BINARY(VPS_DIR) (QString("%1/core/vspipe.exe").arg((VPS_DIR)))
100
101 /*
102  * Static vars
103  */
104 static const unsigned int REV_MULT = 10000;
105 static const char *VPS_TEST_FILE = "import vapoursynth as vs\ncore = vs.get_core()\nv = core.std.BlankClip()\nv.set_output()\n";
106
107 ///////////////////////////////////////////////////////////////////////////////
108 // Constructor & Destructor
109 ///////////////////////////////////////////////////////////////////////////////
110
111 EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, const QString &vpsDir, const bool &x264_x64, const bool &x264_10bit, const bool &avs2yuv_x64, const bool &skipVersionTest, const int &processPriroity, const bool &abortOnTimeout)
112 :
113         m_jobId(QUuid::createUuid()),
114         m_sourceFileName(sourceFileName),
115         m_outputFileName(outputFileName),
116         m_options(new OptionsModel(*options)),
117         m_binDir(binDir),
118         m_vpsDir(vpsDir),
119         m_x264_x64(x264_x64),
120         m_x264_10bit(x264_10bit),
121         m_avs2yuv_x64(avs2yuv_x64),
122         m_skipVersionTest(skipVersionTest),
123         m_processPriority(processPriroity),
124         m_abortOnTimeout(abortOnTimeout),
125         m_jobObject(new JobObject),
126         m_semaphorePaused(0)
127 {
128         m_abort = false;
129         m_pause = false;
130 }
131
132 EncodeThread::~EncodeThread(void)
133 {
134         X264_DELETE(m_options);
135         X264_DELETE(m_jobObject);
136 }
137
138 ///////////////////////////////////////////////////////////////////////////////
139 // Thread entry point
140 ///////////////////////////////////////////////////////////////////////////////
141
142 void EncodeThread::run(void)
143 {
144 #if !defined(_DEBUG)
145         __try
146         {
147                 checkedRun();
148         }
149         __except(1)
150         {
151                 qWarning("STRUCTURED EXCEPTION ERROR IN ENCODE THREAD !!!");
152         }
153 #else
154         checkedRun();
155 #endif
156
157         if(m_jobObject)
158         {
159                 m_jobObject->terminateJob(42);
160                 X264_DELETE(m_jobObject);
161         }
162 }
163
164 void EncodeThread::checkedRun(void)
165 {
166         m_progress = 0;
167         m_status = JobStatus_Starting;
168
169         try
170         {
171                 try
172                 {
173                         encode();
174                 }
175                 catch(char *msg)
176                 {
177                         log(tr("EXCEPTION ERROR IN THREAD: ").append(QString::fromLatin1(msg)));
178                         setStatus(JobStatus_Failed);
179                 }
180                 catch(...)
181                 {
182                         log(tr("UNHANDLED EXCEPTION ERROR IN THREAD !!!"));
183                         setStatus(JobStatus_Failed);
184                 }
185         }
186         catch(...)
187         {
188                 x264_fatal_exit(L"Unhandeled exception error in encode thread!");
189         }
190 }
191
192 void EncodeThread::start(Priority priority)
193 {
194         qDebug("Thread starting...");
195
196         m_abort = false;
197         m_pause = false;
198
199         while(m_semaphorePaused.tryAcquire(1, 0));
200         QThread::start(priority);
201 }
202
203 ///////////////////////////////////////////////////////////////////////////////
204 // Encode functions
205 ///////////////////////////////////////////////////////////////////////////////
206
207 void EncodeThread::encode(void)
208 {
209         QDateTime startTime = QDateTime::currentDateTime();
210
211         //Print some basic info
212         log(tr("Simple x264 Launcher (Build #%1), built %2\n").arg(QString::number(x264_version_build()), x264_version_date().toString(Qt::ISODate)));
213         log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
214         log(tr("Source file: %1").arg(QDir::toNativeSeparators(m_sourceFileName)));
215         log(tr("Output file: %1").arg(QDir::toNativeSeparators(m_outputFileName)));
216         
217         if(!m_vpsDir.isEmpty())
218         {
219                 log(tr("\nVapourSynth: %1").arg(m_vpsDir));
220         }
221
222         //Print encoder settings
223         log(tr("\n--- SETTINGS ---\n"));
224         log(tr("RC Mode: %1").arg(OptionsModel::rcMode2String(m_options->rcMode())));
225         log(tr("Preset:  %1").arg(m_options->preset()));
226         log(tr("Tuning:  %1").arg(m_options->tune()));
227         log(tr("Profile: %1").arg(m_options->profile()));
228         log(tr("Custom:  %1").arg(m_options->customX264().isEmpty() ? tr("(None)") : m_options->customX264()));
229         
230         log(m_binDir);
231
232         bool ok = false;
233         unsigned int frames = 0;
234
235         //Seletct type of input
236         const int inputType = getInputType(QFileInfo(m_sourceFileName).suffix());
237         const QString indexFile = QString("%1/x264_%2.ffindex").arg(QDir::tempPath(), stringToHash(m_sourceFileName));
238
239         //Checking x264 version
240         log(tr("\n--- CHECK VERSION ---\n"));
241         unsigned int revision_x264 = UINT_MAX;
242         bool x264_modified = false;
243         ok = ((revision_x264 = checkVersionX264(m_x264_x64, m_x264_10bit, x264_modified)) != UINT_MAX);
244         CHECK_STATUS(m_abort, ok);
245         
246         //Checking avs2yuv version
247         unsigned int revision_avs2yuv = UINT_MAX;
248         switch(inputType)
249         {
250         case INPUT_AVISYN:
251                 ok = ((revision_avs2yuv = checkVersionAvs2yuv(m_avs2yuv_x64)) != UINT_MAX);
252                 CHECK_STATUS(m_abort, ok);
253                 break;
254         case INPUT_VAPOUR:
255                 ok = checkVersionVapoursynth();
256                 CHECK_STATUS(m_abort, ok);
257                 break;
258         }
259
260         //Print versions
261         log(tr("\nx264 revision: %1 (core #%2)").arg(QString::number(revision_x264 % REV_MULT), QString::number(revision_x264 / REV_MULT)).append(x264_modified ? tr(" - with custom patches!") : QString()));
262         if(revision_avs2yuv != UINT_MAX) log(tr("Avs2YUV version: %1.%2.%3").arg(QString::number(revision_avs2yuv / REV_MULT), QString::number((revision_avs2yuv % REV_MULT) / 10),QString::number((revision_avs2yuv % REV_MULT) % 10)));
263
264         //Is x264 revision supported?
265         if((revision_x264 % REV_MULT) < (VER_X264_MINIMUM_REV))
266         {
267                 log(tr("\nERROR: Your revision of x264 is too old! (Minimum required revision is %2)").arg(QString::number(VER_X264_MINIMUM_REV)));
268                 setStatus(JobStatus_Failed);
269                 return;
270         }
271         if((revision_x264 / REV_MULT) != (VER_X264_CURRENT_API))
272         {
273                 log(tr("\nWARNING: Your revision of x264 uses an unsupported core (API) version, take care!"));
274                 log(tr("This application works best with x264 core (API) version %2.").arg(QString::number(VER_X264_CURRENT_API)));
275         }
276         if((revision_avs2yuv != UINT_MAX) && ((revision_avs2yuv % REV_MULT) != (VER_X264_AVS2YUV_VER)))
277         {
278                 log(tr("\nERROR: Your version of avs2yuv is unsupported (Required version: v0.24 BugMaster's mod 2)"));
279                 log(tr("You can find the required version at: http://komisar.gin.by/tools/avs2yuv/"));
280                 setStatus(JobStatus_Failed);
281                 return;
282         }
283
284         //Detect source info
285         if(inputType != INPUT_NATIVE)
286         {
287                 log(tr("\n--- SOURCE INFO ---\n"));
288                 switch(inputType)
289                 {
290                 case INPUT_AVISYN:
291                         ok = checkPropertiesAvisynth(m_avs2yuv_x64, frames);
292                         CHECK_STATUS(m_abort, ok);
293                         break;
294                 case INPUT_VAPOUR:
295                         ok = checkPropertiesVapoursynth(frames);
296                         CHECK_STATUS(m_abort, ok);
297                         break;
298                 }
299         }
300
301         //Run encoding passes
302         if(m_options->rcMode() == OptionsModel::RCMode_2Pass)
303         {
304                 QFileInfo info(m_outputFileName);
305                 QString passLogFile = QString("%1/%2.stats").arg(info.path(), info.completeBaseName());
306
307                 if(QFileInfo(passLogFile).exists())
308                 {
309                         int n = 2;
310                         while(QFileInfo(passLogFile).exists())
311                         {
312                                 passLogFile = QString("%1/%2.%3.stats").arg(info.path(), info.completeBaseName(), QString::number(n++));
313                         }
314                 }
315                 
316                 log(tr("\n--- PASS 1 ---\n"));
317                 ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile, 1, passLogFile);
318                 CHECK_STATUS(m_abort, ok);
319
320                 log(tr("\n--- PASS 2 ---\n"));
321                 ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile, 2, passLogFile);
322                 CHECK_STATUS(m_abort, ok);
323         }
324         else
325         {
326                 log(tr("\n--- ENCODING ---\n"));
327                 ok = runEncodingPass(m_x264_x64, m_x264_10bit, m_avs2yuv_x64, inputType, frames, indexFile);
328                 CHECK_STATUS(m_abort, ok);
329         }
330
331         log(tr("\n--- DONE ---\n"));
332         int timePassed = startTime.secsTo(QDateTime::currentDateTime());
333         log(tr("Job finished at %1, %2. Process took %3 minutes, %4 seconds.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString(Qt::ISODate), QString::number(timePassed / 60), QString::number(timePassed % 60)));
334         setStatus(JobStatus_Completed);
335 }
336
337 #define X264_UPDATE_PROGRESS(X) do \
338 { \
339         bool ok = false; \
340         unsigned int progress = (X).cap(1).toUInt(&ok); \
341         setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running)); \
342         if(ok && ((progress > last_progress) || (last_progress == UINT_MAX))) \
343         { \
344                 setProgress(progress); \
345                 size_estimate = estimateSize(progress); \
346                 last_progress = progress; \
347         } \
348         setDetails(tr("%1, est. file size %2").arg(text.mid(offset).trimmed(), sizeToString(size_estimate))); \
349         last_indexing = UINT_MAX; \
350 } \
351 while(0)
352
353 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)
354 {
355         QProcess processEncode, processInput;
356         
357         if(inputType != INPUT_NATIVE)
358         {
359                 QStringList cmdLine_Input;
360                 processInput.setStandardOutputProcess(&processEncode);
361                 switch(inputType)
362                 {
363                 case INPUT_AVISYN:
364                         if(!m_options->customAvs2YUV().isEmpty())
365                         {
366                                 cmdLine_Input.append(splitParams(m_options->customAvs2YUV()));
367                         }
368                         cmdLine_Input << x264_path2ansi(QDir::toNativeSeparators(m_sourceFileName));
369                         cmdLine_Input << "-";
370                         log("Creating Avisynth process:");
371                         if(!startProcess(processInput, AVS2_BINARY(m_binDir, avs2yuv_x64), cmdLine_Input, false))
372                         {
373                                 return false;
374                         }
375                         break;
376                 case INPUT_VAPOUR:
377                         cmdLine_Input << x264_path2ansi(QDir::toNativeSeparators(m_sourceFileName));
378                         cmdLine_Input << "-" << "-y4m";
379                         log("Creating Vapoursynth process:");
380                         if(!startProcess(processInput, VPSP_BINARY(m_vpsDir), cmdLine_Input, false))
381                         {
382                                 return false;
383                         }
384                         break;
385                 default:
386                         throw "Bad input type encontered!";
387                 }
388         }
389
390         QStringList cmdLine_Encode = buildCommandLine((inputType != INPUT_NATIVE), x264_10bit, frames, indexFile, pass, passLogFile);
391
392         log("Creating x264 process:");
393         if(!startProcess(processEncode, X264_BINARY(m_binDir, x264_10bit, x264_x64), cmdLine_Encode))
394         {
395                 return false;
396         }
397
398         QRegExp regExpIndexing("indexing.+\\[(\\d+)\\.(\\d+)%\\]");
399         QRegExp regExpProgress("\\[(\\d+)\\.(\\d+)%\\].+frames");
400         QRegExp regExpModified("\\[\\s*(\\d+)\\.(\\d+)%\\]\\s+(\\d+)/(\\d+)\\s(\\d+).(\\d+)\\s(\\d+).(\\d+)\\s+(\\d+):(\\d+):(\\d+)\\s+(\\d+):(\\d+):(\\d+)");
401         QRegExp regExpFrameCnt("^(\\d+) frames:");
402         
403         QTextCodec *localCodec = QTextCodec::codecForName("System");
404
405         bool bTimeout = false;
406         bool bAborted = false;
407
408         unsigned int last_progress = UINT_MAX;
409         unsigned int last_indexing = UINT_MAX;
410         qint64 size_estimate = 0I64;
411
412         //Main processing loop
413         while(processEncode.state() != QProcess::NotRunning)
414         {
415                 unsigned int waitCounter = 0;
416
417                 //Wait until new output is available
418                 forever
419                 {
420                         if(m_abort)
421                         {
422                                 processEncode.kill();
423                                 processInput.kill();
424                                 bAborted = true;
425                                 break;
426                         }
427                         if(m_pause && (processEncode.state() == QProcess::Running))
428                         {
429                                 JobStatus previousStatus = m_status;
430                                 setStatus(JobStatus_Paused);
431                                 log(tr("Job paused by user at %1, %2.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
432                                 bool ok[2] = {false, false};
433                                 QProcess *proc[2] = { &processEncode, &processInput };
434                                 ok[0] = x264_suspendProcess(proc[0], true);
435                                 ok[1] = x264_suspendProcess(proc[1], true);
436                                 while(m_pause) m_semaphorePaused.tryAcquire(1, 5000);
437                                 while(m_semaphorePaused.tryAcquire(1, 0));
438                                 ok[0] = x264_suspendProcess(proc[0], false);
439                                 ok[1] = x264_suspendProcess(proc[1], false);
440                                 if(!m_abort) setStatus(previousStatus);
441                                 log(tr("Job resumed by user at %1, %2.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
442                                 waitCounter = 0;
443                                 continue;
444                         }
445                         if(!processEncode.waitForReadyRead(m_processTimeoutInterval))
446                         {
447                                 if(processEncode.state() == QProcess::Running)
448                                 {
449                                         if(++waitCounter > m_processTimeoutMaxCounter)
450                                         {
451                                                 if(m_abortOnTimeout)
452                                                 {
453                                                         processEncode.kill();
454                                                         qWarning("x264 process timed out <-- killing!");
455                                                         log("\nPROCESS TIMEOUT !!!");
456                                                         bTimeout = true;
457                                                         break;
458                                                 }
459                                         }
460                                         else if(waitCounter == m_processTimeoutWarning)
461                                         {
462                                                 unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
463                                                 log(tr("Warning: x264 did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
464                                         }
465                                         continue;
466                                 }
467                         }
468                         if(m_abort || (m_pause && (processEncode.state() == QProcess::Running)))
469                         {
470                                 continue;
471                         }
472                         break;
473                 }
474                 
475                 //Exit main processing loop now?
476                 if(bAborted || bTimeout)
477                 {
478                         break;
479                 }
480
481                 //Process all output
482                 while(processEncode.bytesAvailable() > 0)
483                 {
484                         QList<QByteArray> lines = processEncode.readLine().split('\r');
485                         while(!lines.isEmpty())
486                         {
487                                 QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
488                                 int offset = -1;
489                                 if((offset = regExpProgress.lastIndexIn(text)) >= 0)
490                                 {
491                                         X264_UPDATE_PROGRESS(regExpProgress);
492                                 }
493                                 else if((offset = regExpIndexing.lastIndexIn(text)) >= 0)
494                                 {
495                                         bool ok = false;
496                                         unsigned int progress = regExpIndexing.cap(1).toUInt(&ok);
497                                         setStatus(JobStatus_Indexing);
498                                         if(ok && ((progress > last_indexing) || (last_indexing == UINT_MAX)))
499                                         {
500                                                 setProgress(progress);
501                                                 last_indexing = progress;
502                                         }
503                                         setDetails(text.mid(offset).trimmed());
504                                         last_progress = UINT_MAX;
505                                 }
506                                 else if((offset = regExpFrameCnt.lastIndexIn(text)) >= 0)
507                                 {
508                                         last_progress = last_indexing = UINT_MAX;
509                                         setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running));
510                                         setDetails(text.mid(offset).trimmed());
511                                 }
512                                 else if((offset = regExpModified.lastIndexIn(text)) >= 0)
513                                 {
514                                         X264_UPDATE_PROGRESS(regExpModified);
515                                 }
516                                 else if(!text.isEmpty())
517                                 {
518                                         last_progress = last_indexing = UINT_MAX;
519                                         log(text);
520                                 }
521                         }
522                 }
523         }
524
525         processEncode.waitForFinished(5000);
526         if(processEncode.state() != QProcess::NotRunning)
527         {
528                 qWarning("x264 process still running, going to kill it!");
529                 processEncode.kill();
530                 processEncode.waitForFinished(-1);
531         }
532         
533         processInput.waitForFinished(5000);
534         if(processInput.state() != QProcess::NotRunning)
535         {
536                 qWarning("Input process still running, going to kill it!");
537                 processInput.kill();
538                 processInput.waitForFinished(-1);
539         }
540
541         if(!(bTimeout || bAborted))
542         {
543                 while(processInput.bytesAvailable() > 0)
544                 {
545                         switch(inputType)
546                         {
547                         case INPUT_AVISYN:
548                                 log(tr("av2y [info]: %1").arg(QString::fromUtf8(processInput.readLine()).simplified()));
549                                 break;
550                         case INPUT_VAPOUR:
551                                 log(tr("vpyp [info]: %1").arg(QString::fromUtf8(processInput.readLine()).simplified()));
552                                 break;
553                         }
554                 }
555         }
556
557         if((inputType != INPUT_NATIVE) && (processInput.exitCode() != EXIT_SUCCESS))
558         {
559                 if(!(bTimeout || bAborted))
560                 {
561                         log(tr("\nWARNING: Input process exited with error code: %1").arg(QString::number(processInput.exitCode())));
562                 }
563         }
564
565         if(bTimeout || bAborted || processEncode.exitCode() != EXIT_SUCCESS)
566         {
567                 if(!(bTimeout || bAborted))
568                 {
569                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(processEncode.exitCode())));
570                 }
571                 processEncode.close();
572                 processInput.close();
573                 return false;
574         }
575
576         QThread::yieldCurrentThread();
577
578         const qint64 finalSize = QFileInfo(m_outputFileName).size();
579         QLocale locale(QLocale::English);
580         log(tr("Final file size is %1 bytes.").arg(locale.toString(finalSize)));
581
582         switch(pass)
583         {
584         case 1:
585                 setStatus(JobStatus_Running_Pass1);
586                 setDetails(tr("First pass completed. Preparing for second pass..."));
587                 break;
588         case 2:
589                 setStatus(JobStatus_Running_Pass2);
590                 setDetails(tr("Second pass completed successfully. Final size is %1.").arg(sizeToString(finalSize)));
591                 break;
592         default:
593                 setStatus(JobStatus_Running);
594                 setDetails(tr("Encode completed successfully. Final size is %1.").arg(sizeToString(finalSize)));
595                 break;
596         }
597
598         setProgress(100);
599         processEncode.close();
600         processInput.close();
601         return true;
602 }
603
604 QStringList EncodeThread::buildCommandLine(bool usePipe, bool use10Bit, unsigned int frames, const QString &indexFile, int pass, const QString &passLogFile)
605 {
606         QStringList cmdLine;
607         double crf_int = 0.0, crf_frc = 0.0;
608
609         switch(m_options->rcMode())
610         {
611         case OptionsModel::RCMode_CQ:
612                 cmdLine << "--qp" << QString::number(qRound(m_options->quantizer()));
613                 break;
614         case OptionsModel::RCMode_CRF:
615                 crf_frc = modf(m_options->quantizer(), &crf_int);
616                 cmdLine << "--crf" << QString("%1.%2").arg(QString::number(qRound(crf_int)), QString::number(qRound(crf_frc * 10.0)));
617                 break;
618         case OptionsModel::RCMode_2Pass:
619         case OptionsModel::RCMode_ABR:
620                 cmdLine << "--bitrate" << QString::number(m_options->bitrate());
621                 break;
622         default:
623                 throw "Bad rate-control mode !!!";
624                 break;
625         }
626         
627         if((pass == 1) || (pass == 2))
628         {
629                 cmdLine << "--pass" << QString::number(pass);
630                 cmdLine << "--stats" << QDir::toNativeSeparators(passLogFile);
631         }
632
633         cmdLine << "--preset" << m_options->preset().toLower();
634
635         if(m_options->tune().compare("none", Qt::CaseInsensitive))
636         {
637                 cmdLine << "--tune" << m_options->tune().toLower();
638         }
639
640         if(m_options->profile().compare("auto", Qt::CaseInsensitive))
641         {
642                 if(use10Bit)
643                 {
644                         if(m_options->profile().compare("baseline", Qt::CaseInsensitive) || m_options->profile().compare("main", Qt::CaseInsensitive) || m_options->profile().compare("high", Qt::CaseInsensitive))
645                         {
646                                 log(tr("WARNING: Selected H.264 Profile not compatible with 10-Bit encoding. Ignoring!\n"));
647                         }
648                         else
649                         {
650                                 cmdLine << "--profile" << m_options->profile().toLower();
651                         }
652                 }
653                 else
654                 {
655                         cmdLine << "--profile" << m_options->profile().toLower();
656                 }
657         }
658
659         if(!m_options->customX264().isEmpty())
660         {
661                 QStringList customArgs = splitParams(m_options->customX264());
662                 if(usePipe)
663                 {
664                         QStringList::iterator i = customArgs.begin();
665                         while(i != customArgs.end())
666                         {
667                                 bool bModified = false;
668                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--fps");
669                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--frames");
670                                 if(!bModified) i++;
671                         }
672                 }
673                 cmdLine.append(customArgs);
674         }
675
676         cmdLine << "--output" << QDir::toNativeSeparators(m_outputFileName);
677         
678         if(usePipe)
679         {
680                 if(frames < 1) throw "Frames not set!";
681                 cmdLine << "--frames" << QString::number(frames);
682                 cmdLine << "--demuxer" << "y4m";
683                 cmdLine << "--stdin" << "y4m" << "-";
684         }
685         else
686         {
687                 cmdLine << "--index" << QDir::toNativeSeparators(indexFile);
688                 cmdLine << QDir::toNativeSeparators(m_sourceFileName);
689         }
690
691         return cmdLine;
692 }
693
694 unsigned int EncodeThread::checkVersionX264(bool use_x64, bool use_10bit, bool &modified)
695 {
696         if(m_skipVersionTest)
697         {
698                 log("Warning: Skipping x264 version check this time!");
699                 return (999 * REV_MULT) + (9999 % REV_MULT);
700         }
701
702         QProcess process;
703         QStringList cmdLine = QStringList() << "--version";
704
705         log("Creating process:");
706         if(!startProcess(process, X264_BINARY(m_binDir, use_10bit, use_x64), cmdLine))
707         {
708                 return false;;
709         }
710
711         QRegExp regExpVersion("\\bx264\\s(\\d)\\.(\\d+)\\.(\\d+)\\s([a-f0-9]{7})", Qt::CaseInsensitive);
712         QRegExp regExpVersionMod("\\bx264 (\\d)\\.(\\d+)\\.(\\d+)", Qt::CaseInsensitive);
713         
714         bool bTimeout = false;
715         bool bAborted = false;
716
717         unsigned int revision = UINT_MAX;
718         unsigned int coreVers = UINT_MAX;
719         modified = false;
720
721         while(process.state() != QProcess::NotRunning)
722         {
723                 if(m_abort)
724                 {
725                         process.kill();
726                         bAborted = true;
727                         break;
728                 }
729                 if(!process.waitForReadyRead())
730                 {
731                         if(process.state() == QProcess::Running)
732                         {
733                                 process.kill();
734                                 qWarning("x264 process timed out <-- killing!");
735                                 log("\nPROCESS TIMEOUT !!!");
736                                 bTimeout = true;
737                                 break;
738                         }
739                 }
740                 while(process.bytesAvailable() > 0)
741                 {
742                         QList<QByteArray> lines = process.readLine().split('\r');
743                         while(!lines.isEmpty())
744                         {
745                                 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
746                                 int offset = -1;
747                                 if((offset = regExpVersion.lastIndexIn(text)) >= 0)
748                                 {
749                                         bool ok1 = false, ok2 = false;
750                                         unsigned int temp1 = regExpVersion.cap(2).toUInt(&ok1);
751                                         unsigned int temp2 = regExpVersion.cap(3).toUInt(&ok2);
752                                         if(ok1) coreVers = temp1;
753                                         if(ok2) revision = temp2;
754                                 }
755                                 else if((offset = regExpVersionMod.lastIndexIn(text)) >= 0)
756                                 {
757                                         bool ok1 = false, ok2 = false;
758                                         unsigned int temp1 = regExpVersionMod.cap(2).toUInt(&ok1);
759                                         unsigned int temp2 = regExpVersionMod.cap(3).toUInt(&ok2);
760                                         if(ok1) coreVers = temp1;
761                                         if(ok2) revision = temp2;
762                                         modified = true;
763                                 }
764                                 if(!text.isEmpty())
765                                 {
766                                         log(text);
767                                 }
768                         }
769                 }
770         }
771
772         process.waitForFinished();
773         if(process.state() != QProcess::NotRunning)
774         {
775                 process.kill();
776                 process.waitForFinished(-1);
777         }
778
779         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
780         {
781                 if(!(bTimeout || bAborted))
782                 {
783                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
784                 }
785                 return UINT_MAX;
786         }
787
788         if((revision == UINT_MAX) || (coreVers == UINT_MAX))
789         {
790                 log(tr("\nFAILED TO DETERMINE X264 VERSION !!!"));
791                 return UINT_MAX;
792         }
793         
794         return (coreVers * REV_MULT) + (revision % REV_MULT);
795 }
796
797 unsigned int EncodeThread::checkVersionAvs2yuv(bool x64)
798 {
799         QProcess process;
800
801         log("\nCreating process:");
802         if(!startProcess(process, AVS2_BINARY(m_binDir, x64), QStringList()))
803         {
804                 return false;;
805         }
806
807         QRegExp regExpVersionMod("\\bAvs2YUV (\\d+).(\\d+)bm(\\d)\\b", Qt::CaseInsensitive);
808         QRegExp regExpVersionOld("\\bAvs2YUV (\\d+).(\\d+)\\b", Qt::CaseInsensitive);
809         
810         bool bTimeout = false;
811         bool bAborted = false;
812
813         unsigned int ver_maj = UINT_MAX;
814         unsigned int ver_min = UINT_MAX;
815         unsigned int ver_mod = 0;
816
817         while(process.state() != QProcess::NotRunning)
818         {
819                 if(m_abort)
820                 {
821                         process.kill();
822                         bAborted = true;
823                         break;
824                 }
825                 if(!process.waitForReadyRead())
826                 {
827                         if(process.state() == QProcess::Running)
828                         {
829                                 process.kill();
830                                 qWarning("Avs2YUV process timed out <-- killing!");
831                                 log("\nPROCESS TIMEOUT !!!");
832                                 bTimeout = true;
833                                 break;
834                         }
835                 }
836                 while(process.bytesAvailable() > 0)
837                 {
838                         QList<QByteArray> lines = process.readLine().split('\r');
839                         while(!lines.isEmpty())
840                         {
841                                 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
842                                 int offset = -1;
843                                 if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX) || (ver_mod == UINT_MAX))
844                                 {
845                                         if(!text.isEmpty())
846                                         {
847                                                 log(text);
848                                         }
849                                 }
850                                 if((offset = regExpVersionMod.lastIndexIn(text)) >= 0)
851                                 {
852                                         bool ok1 = false, ok2 = false, ok3 = false;
853                                         unsigned int temp1 = regExpVersionMod.cap(1).toUInt(&ok1);
854                                         unsigned int temp2 = regExpVersionMod.cap(2).toUInt(&ok2);
855                                         unsigned int temp3 = regExpVersionMod.cap(3).toUInt(&ok3);
856                                         if(ok1) ver_maj = temp1;
857                                         if(ok2) ver_min = temp2;
858                                         if(ok3) ver_mod = temp3;
859                                 }
860                                 else if((offset = regExpVersionOld.lastIndexIn(text)) >= 0)
861                                 {
862                                         bool ok1 = false, ok2 = false;
863                                         unsigned int temp1 = regExpVersionOld.cap(1).toUInt(&ok1);
864                                         unsigned int temp2 = regExpVersionOld.cap(2).toUInt(&ok2);
865                                         if(ok1) ver_maj = temp1;
866                                         if(ok2) ver_min = temp2;
867                                 }
868                         }
869                 }
870         }
871
872         process.waitForFinished();
873         if(process.state() != QProcess::NotRunning)
874         {
875                 process.kill();
876                 process.waitForFinished(-1);
877         }
878
879         if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 2)))
880         {
881                 if(!(bTimeout || bAborted))
882                 {
883                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
884                 }
885                 return UINT_MAX;
886         }
887
888         if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX))
889         {
890                 log(tr("\nFAILED TO DETERMINE AVS2YUV VERSION !!!"));
891                 return UINT_MAX;
892         }
893         
894         return (ver_maj * REV_MULT) + ((ver_min % REV_MULT) * 10) + (ver_mod % 10);
895 }
896
897 bool EncodeThread::checkVersionVapoursynth(/*const QString &vspipePath*/)
898 {
899         //Is VapourSynth available at all?
900         if(m_vpsDir.isEmpty() || (!QFileInfo(VPSP_BINARY(m_vpsDir)).isFile()))
901         {
902                 log(tr("\nVPY INPUT REQUIRES VAPOURSYNTH, BUT IT IS *NOT* AVAILABLE !!!"));
903                 return false;
904         }
905
906         QProcess process;
907
908         log("\nCreating process:");
909         if(!startProcess(process, VPSP_BINARY(m_vpsDir), QStringList()))
910         {
911                 return false;;
912         }
913
914         QRegExp regExpSignature("\\bVSPipe\\s+usage\\b", Qt::CaseInsensitive);
915         
916         bool bTimeout = false;
917         bool bAborted = false;
918
919         bool vspipeSignature = false;
920
921         while(process.state() != QProcess::NotRunning)
922         {
923                 if(m_abort)
924                 {
925                         process.kill();
926                         bAborted = true;
927                         break;
928                 }
929                 if(!process.waitForReadyRead())
930                 {
931                         if(process.state() == QProcess::Running)
932                         {
933                                 process.kill();
934                                 qWarning("VSPipe process timed out <-- killing!");
935                                 log("\nPROCESS TIMEOUT !!!");
936                                 bTimeout = true;
937                                 break;
938                         }
939                 }
940                 while(process.bytesAvailable() > 0)
941                 {
942                         QList<QByteArray> lines = process.readLine().split('\r');
943                         while(!lines.isEmpty())
944                         {
945                                 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
946                                 if(regExpSignature.lastIndexIn(text) >= 0)
947                                 {
948                                         vspipeSignature = true;
949                                 }
950                                 if(!text.isEmpty())
951                                 {
952                                         log(text);
953                                 }
954                         }
955                 }
956         }
957
958         process.waitForFinished();
959         if(process.state() != QProcess::NotRunning)
960         {
961                 process.kill();
962                 process.waitForFinished(-1);
963         }
964
965         if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 1)))
966         {
967                 if(!(bTimeout || bAborted))
968                 {
969                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
970                 }
971                 return false;
972         }
973
974         if(!vspipeSignature)
975         {
976                 log(tr("\nFAILED TO DETECT VSPIPE SIGNATURE !!!"));
977                 return false;
978         }
979         
980         return vspipeSignature;
981 }
982
983 bool EncodeThread::checkPropertiesAvisynth(bool x64, unsigned int &frames)
984 {
985         QProcess process;
986         QStringList cmdLine;
987
988         if(!m_options->customAvs2YUV().isEmpty())
989         {
990                 cmdLine.append(splitParams(m_options->customAvs2YUV()));
991         }
992
993         cmdLine << "-frames" << "1";
994         cmdLine << x264_path2ansi(QDir::toNativeSeparators(m_sourceFileName)) << "NUL";
995
996         log("Creating process:");
997         if(!startProcess(process, AVS2_BINARY(m_binDir, x64), cmdLine))
998         {
999                 return false;;
1000         }
1001
1002         QRegExp regExpInt(": (\\d+)x(\\d+), (\\d+) fps, (\\d+) frames");
1003         QRegExp regExpFrc(": (\\d+)x(\\d+), (\\d+)/(\\d+) fps, (\\d+) frames");
1004         
1005         QTextCodec *localCodec = QTextCodec::codecForName("System");
1006
1007         bool bTimeout = false;
1008         bool bAborted = false;
1009
1010         frames = 0;
1011         
1012         unsigned int fpsNom = 0;
1013         unsigned int fpsDen = 0;
1014         unsigned int fSizeW = 0;
1015         unsigned int fSizeH = 0;
1016         
1017         unsigned int waitCounter = 0;
1018
1019         while(process.state() != QProcess::NotRunning)
1020         {
1021                 if(m_abort)
1022                 {
1023                         process.kill();
1024                         bAborted = true;
1025                         break;
1026                 }
1027                 if(!process.waitForReadyRead(m_processTimeoutInterval))
1028                 {
1029                         if(process.state() == QProcess::Running)
1030                         {
1031                                 if(++waitCounter > m_processTimeoutMaxCounter)
1032                                 {
1033                                         if(m_abortOnTimeout)
1034                                         {
1035                                                 process.kill();
1036                                                 qWarning("Avs2YUV process timed out <-- killing!");
1037                                                 log("\nPROCESS TIMEOUT !!!");
1038                                                 log("\nAvisynth has encountered a deadlock or your script takes EXTREMELY long to initialize!");
1039                                                 bTimeout = true;
1040                                                 break;
1041                                         }
1042                                 }
1043                                 else if(waitCounter == m_processTimeoutWarning)
1044                                 {
1045                                         unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
1046                                         log(tr("Warning: Avisynth did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
1047                                 }
1048                         }
1049                         continue;
1050                 }
1051                 
1052                 waitCounter = 0;
1053                 
1054                 while(process.bytesAvailable() > 0)
1055                 {
1056                         QList<QByteArray> lines = process.readLine().split('\r');
1057                         while(!lines.isEmpty())
1058                         {
1059                                 QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
1060                                 int offset = -1;
1061                                 if((offset = regExpInt.lastIndexIn(text)) >= 0)
1062                                 {
1063                                         bool ok1 = false, ok2 = false;
1064                                         bool ok3 = false, ok4 = false;
1065                                         unsigned int temp1 = regExpInt.cap(1).toUInt(&ok1);
1066                                         unsigned int temp2 = regExpInt.cap(2).toUInt(&ok2);
1067                                         unsigned int temp3 = regExpInt.cap(3).toUInt(&ok3);
1068                                         unsigned int temp4 = regExpInt.cap(4).toUInt(&ok4);
1069                                         if(ok1) fSizeW = temp1;
1070                                         if(ok2) fSizeH = temp2;
1071                                         if(ok3) fpsNom = temp3;
1072                                         if(ok4) frames = temp4;
1073                                 }
1074                                 else if((offset = regExpFrc.lastIndexIn(text)) >= 0)
1075                                 {
1076                                         bool ok1 = false, ok2 = false;
1077                                         bool ok3 = false, ok4 = false, ok5 = false;
1078                                         unsigned int temp1 = regExpFrc.cap(1).toUInt(&ok1);
1079                                         unsigned int temp2 = regExpFrc.cap(2).toUInt(&ok2);
1080                                         unsigned int temp3 = regExpFrc.cap(3).toUInt(&ok3);
1081                                         unsigned int temp4 = regExpFrc.cap(4).toUInt(&ok4);
1082                                         unsigned int temp5 = regExpFrc.cap(5).toUInt(&ok5);
1083                                         if(ok1) fSizeW = temp1;
1084                                         if(ok2) fSizeH = temp2;
1085                                         if(ok3) fpsNom = temp3;
1086                                         if(ok4) fpsDen = temp4;
1087                                         if(ok5) frames = temp5;
1088                                 }
1089                                 if(!text.isEmpty())
1090                                 {
1091                                         log(text);
1092                                 }
1093                                 if(text.contains("failed to load avisynth.dll", Qt::CaseInsensitive))
1094                                 {
1095                                         log(tr("\nWarning: It seems that %1-Bit Avisynth is not currently installed !!!").arg(x64 ? "64" : "32"));
1096                                 }
1097                                 if(text.contains(QRegExp("couldn't convert input clip to (YV16|YV24)", Qt::CaseInsensitive)))
1098                                 {
1099                                         log(tr("\nWarning: YV16 (4:2:2) and YV24 (4:4:4) color-spaces only supported in Avisynth 2.6 !!!"));
1100                                 }
1101                         }
1102                 }
1103         }
1104
1105         process.waitForFinished();
1106         if(process.state() != QProcess::NotRunning)
1107         {
1108                 process.kill();
1109                 process.waitForFinished(-1);
1110         }
1111
1112         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
1113         {
1114                 if(!(bTimeout || bAborted))
1115                 {
1116                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
1117                 }
1118                 return false;
1119         }
1120
1121         if(frames == 0)
1122         {
1123                 log(tr("\nFAILED TO DETERMINE AVS PROPERTIES !!!"));
1124                 return false;
1125         }
1126         
1127         log("");
1128
1129         if((fSizeW > 0) && (fSizeH > 0))
1130         {
1131                 log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
1132         }
1133         if((fpsNom > 0) && (fpsDen > 0))
1134         {
1135                 log(tr("Frame Rate: %1/%2").arg(QString::number(fpsNom), QString::number(fpsDen)));
1136         }
1137         if((fpsNom > 0) && (fpsDen == 0))
1138         {
1139                 log(tr("Frame Rate: %1").arg(QString::number(fpsNom)));
1140         }
1141         if(frames > 0)
1142         {
1143                 log(tr("No. Frames: %1").arg(QString::number(frames)));
1144         }
1145
1146         return true;
1147 }
1148
1149 bool EncodeThread::checkPropertiesVapoursynth(/*const QString &vspipePath,*/ unsigned int &frames)
1150 {
1151         QProcess process;
1152         QStringList cmdLine;
1153
1154         cmdLine << x264_path2ansi(QDir::toNativeSeparators(m_sourceFileName));
1155         cmdLine << "-" << "-info";
1156
1157         log("Creating process:");
1158         if(!startProcess(process, VPSP_BINARY(m_vpsDir), cmdLine))
1159         {
1160                 return false;;
1161         }
1162
1163         QRegExp regExpFrm("\\bFrames:\\s+(\\d+)\\b");
1164         QRegExp regExpSzW("\\bWidth:\\s+(\\d+)\\b");
1165         QRegExp regExpSzH("\\bHeight:\\s+(\\d+)\\b");
1166         
1167         QTextCodec *localCodec = QTextCodec::codecForName("System");
1168
1169         bool bTimeout = false;
1170         bool bAborted = false;
1171
1172         frames = 0;
1173         
1174         unsigned int fSizeW = 0;
1175         unsigned int fSizeH = 0;
1176         
1177         unsigned int waitCounter = 0;
1178
1179         while(process.state() != QProcess::NotRunning)
1180         {
1181                 if(m_abort)
1182                 {
1183                         process.kill();
1184                         bAborted = true;
1185                         break;
1186                 }
1187                 if(!process.waitForReadyRead(m_processTimeoutInterval))
1188                 {
1189                         if(process.state() == QProcess::Running)
1190                         {
1191                                 if(++waitCounter > m_processTimeoutMaxCounter)
1192                                 {
1193                                         if(m_abortOnTimeout)
1194                                         {
1195                                                 process.kill();
1196                                                 qWarning("VSPipe process timed out <-- killing!");
1197                                                 log("\nPROCESS TIMEOUT !!!");
1198                                                 log("\nVapoursynth has encountered a deadlock or your script takes EXTREMELY long to initialize!");
1199                                                 bTimeout = true;
1200                                                 break;
1201                                         }
1202                                 }
1203                                 else if(waitCounter == m_processTimeoutWarning)
1204                                 {
1205                                         unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
1206                                         log(tr("Warning: nVapoursynth did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
1207                                 }
1208                         }
1209                         continue;
1210                 }
1211                 
1212                 waitCounter = 0;
1213                 
1214                 while(process.bytesAvailable() > 0)
1215                 {
1216                         QList<QByteArray> lines = process.readLine().split('\r');
1217                         while(!lines.isEmpty())
1218                         {
1219                                 QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
1220                                 int offset = -1;
1221                                 if((offset = regExpFrm.lastIndexIn(text)) >= 0)
1222                                 {
1223                                         bool ok = false;
1224                                         unsigned int temp = regExpFrm.cap(1).toUInt(&ok);
1225                                         if(ok) frames = temp;
1226                                 }
1227                                 if((offset = regExpSzW.lastIndexIn(text)) >= 0)
1228                                 {
1229                                         bool ok = false;
1230                                         unsigned int temp = regExpSzW.cap(1).toUInt(&ok);
1231                                         if(ok) fSizeW = temp;
1232                                 }
1233                                 if((offset = regExpSzH.lastIndexIn(text)) >= 0)
1234                                 {
1235                                         bool ok = false;
1236                                         unsigned int temp = regExpSzH.cap(1).toUInt(&ok);
1237                                         if(ok) fSizeH = temp;
1238                                 }
1239                                 if(!text.isEmpty())
1240                                 {
1241                                         log(text);
1242                                 }
1243                         }
1244                 }
1245         }
1246
1247         process.waitForFinished();
1248         if(process.state() != QProcess::NotRunning)
1249         {
1250                 process.kill();
1251                 process.waitForFinished(-1);
1252         }
1253
1254         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
1255         {
1256                 if(!(bTimeout || bAborted))
1257                 {
1258                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
1259                 }
1260                 return false;
1261         }
1262
1263         if(frames == 0)
1264         {
1265                 log(tr("\nFAILED TO DETERMINE VPY PROPERTIES !!!"));
1266                 return false;
1267         }
1268         
1269         log("");
1270
1271         if((fSizeW > 0) && (fSizeH > 0))
1272         {
1273                 log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
1274         }
1275         if(frames > 0)
1276         {
1277                 log(tr("No. Frames: %1").arg(QString::number(frames)));
1278         }
1279
1280         return true;
1281 }
1282
1283 ///////////////////////////////////////////////////////////////////////////////
1284 // Misc functions
1285 ///////////////////////////////////////////////////////////////////////////////
1286
1287 void EncodeThread::setStatus(JobStatus newStatus)
1288 {
1289         if(m_status != newStatus)
1290         {
1291                 if((newStatus != JobStatus_Completed) && (newStatus != JobStatus_Failed) && (newStatus != JobStatus_Aborted) && (newStatus != JobStatus_Paused))
1292                 {
1293                         if(m_status != JobStatus_Paused) setProgress(0);
1294                 }
1295                 if(newStatus == JobStatus_Failed)
1296                 {
1297                         setDetails("The job has failed. See log for details!");
1298                 }
1299                 if(newStatus == JobStatus_Aborted)
1300                 {
1301                         setDetails("The job was aborted by the user!");
1302                 }
1303                 m_status = newStatus;
1304                 emit statusChanged(m_jobId, newStatus);
1305         }
1306 }
1307
1308 void EncodeThread::setProgress(unsigned int newProgress)
1309 {
1310         if(m_progress != newProgress)
1311         {
1312                 m_progress = newProgress;
1313                 emit progressChanged(m_jobId, m_progress);
1314         }
1315 }
1316
1317 void EncodeThread::setDetails(const QString &text)
1318 {
1319         emit detailsChanged(m_jobId, text);
1320 }
1321
1322 QString EncodeThread::stringToHash(const QString &string)
1323 {
1324         QByteArray result(10, char(0));
1325         const QByteArray hash = QCryptographicHash::hash(string.toUtf8(), QCryptographicHash::Sha1);
1326
1327         if((hash.size() == 20) && (result.size() == 10))
1328         {
1329                 unsigned char *out = reinterpret_cast<unsigned char*>(result.data());
1330                 const unsigned char *in = reinterpret_cast<const unsigned char*>(hash.constData());
1331                 for(int i = 0; i < 10; i++)
1332                 {
1333                         out[i] = (in[i] ^ in[10+i]);
1334                 }
1335         }
1336
1337         return QString::fromLatin1(result.toHex().constData());
1338 }
1339
1340 bool EncodeThread::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels)
1341 {
1342         QMutexLocker lock(&m_mutex_startProcess);
1343         log(commandline2string(program, args) + "\n");
1344
1345         process.setWorkingDirectory(QDir::tempPath());
1346
1347         if(mergeChannels)
1348         {
1349                 process.setProcessChannelMode(QProcess::MergedChannels);
1350                 process.setReadChannel(QProcess::StandardOutput);
1351         }
1352         else
1353         {
1354                 process.setProcessChannelMode(QProcess::SeparateChannels);
1355                 process.setReadChannel(QProcess::StandardError);
1356         }
1357
1358         process.start(program, args);
1359         
1360         if(process.waitForStarted())
1361         {
1362                 Q_PID pid = process.pid();
1363                 if((pid != NULL) && (m_jobObject != NULL))
1364                 {
1365                         m_jobObject->addProcessToJob(&process);
1366                         x264_change_process_priority(&process, m_processPriority);
1367                 }
1368                 
1369                 lock.unlock();
1370                 return true;
1371         }
1372
1373         log("Process creation has failed :-(");
1374         QString errorMsg= process.errorString().trimmed();
1375         if(!errorMsg.isEmpty()) log(errorMsg);
1376
1377         process.kill();
1378         process.waitForFinished(-1);
1379         return false;
1380 }
1381
1382 QString EncodeThread::commandline2string(const QString &program, const QStringList &arguments)
1383 {
1384         QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
1385         
1386         for(int i = 0; i < arguments.count(); i++)
1387         {
1388                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
1389         }
1390
1391         return commandline;
1392 }
1393
1394 QStringList EncodeThread::splitParams(const QString &params)
1395 {
1396         QStringList list; 
1397         bool ignoreWhitespaces = false;
1398         QString temp;
1399
1400         for(int i = 0; i < params.length(); i++)
1401         {
1402                 const QChar c = params.at(i);
1403
1404                 if(c == QChar::fromLatin1('"'))
1405                 {
1406                         ignoreWhitespaces = (!ignoreWhitespaces);
1407                         continue;
1408                 }
1409                 else if((!ignoreWhitespaces) && (c == QChar::fromLatin1(' ')))
1410                 {
1411                         APPEND_AND_CLEAR(list, temp);
1412                         continue;
1413                 }
1414                 
1415                 temp.append(c);
1416         }
1417         
1418         APPEND_AND_CLEAR(list, temp);
1419
1420         list.replaceInStrings("$(INPUT)", QDir::toNativeSeparators(m_sourceFileName), Qt::CaseInsensitive);
1421         list.replaceInStrings("$(OUTPUT)", QDir::toNativeSeparators(m_outputFileName), Qt::CaseInsensitive);
1422
1423         return list;
1424 }
1425
1426 qint64 EncodeThread::estimateSize(int progress)
1427 {
1428         if(progress >= 3)
1429         {
1430                 qint64 currentSize = QFileInfo(m_outputFileName).size();
1431                 qint64 estimatedSize = (currentSize * 100I64) / static_cast<qint64>(progress);
1432                 return estimatedSize;
1433         }
1434
1435         return 0I64;
1436 }
1437
1438 QString EncodeThread::sizeToString(qint64 size)
1439 {
1440         static char *prefix[5] = {"Byte", "KB", "MB", "GB", "TB"};
1441
1442         if(size > 1024I64)
1443         {
1444                 qint64 estimatedSize = size;
1445                 qint64 remainderSize = 0I64;
1446
1447                 int prefixIdx = 0;
1448                 while((estimatedSize > 1024I64) && (prefixIdx < 4))
1449                 {
1450                         remainderSize = estimatedSize % 1024I64;
1451                         estimatedSize = estimatedSize / 1024I64;
1452                         prefixIdx++;
1453                 }
1454                         
1455                 double value = static_cast<double>(estimatedSize) + (static_cast<double>(remainderSize) / 1024.0);
1456                 return QString().sprintf((value < 10.0) ? "%.2f %s" : "%.1f %s", value, prefix[prefixIdx]);
1457         }
1458
1459         return tr("N/A");
1460 }
1461
1462 int EncodeThread::getInputType(const QString &fileExt)
1463 {
1464         int type = INPUT_NATIVE;
1465         if(fileExt.compare("avs", Qt::CaseInsensitive) == 0)       type = INPUT_AVISYN;
1466         else if(fileExt.compare("avsi", Qt::CaseInsensitive) == 0) type = INPUT_AVISYN;
1467         else if(fileExt.compare("vpy", Qt::CaseInsensitive) == 0)  type = INPUT_VAPOUR;
1468         else if(fileExt.compare("py", Qt::CaseInsensitive) == 0)   type = INPUT_VAPOUR;
1469         return type;
1470 }