OSDN Git Service

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