OSDN Git Service

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