OSDN Git Service

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