OSDN Git Service

Refactored runEncodingPass() into AbstractEncoder class and refactored encoder-specif...
[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 "encoder_x264.h"
29 #include "encoder_x265.h"
30 #include "job_object.h"
31 #include "binaries.h"
32
33 #include <QDate>
34 #include <QTime>
35 #include <QDateTime>
36 #include <QFileInfo>
37 #include <QDir>
38 #include <QProcess>
39 #include <QMutex>
40 #include <QTextCodec>
41 #include <QLocale>
42 #include <QCryptographicHash>
43
44 /*
45  * RAII execution state handler
46  */
47 class ExecutionStateHandler
48 {
49 public:
50         ExecutionStateHandler(void)
51         {
52                 x264_set_thread_execution_state(true);
53         }
54         ~ExecutionStateHandler(void)
55         {
56                 x264_set_thread_execution_state(false);
57         }
58 private:
59         //Disable copy constructor and assignment
60         ExecutionStateHandler(const ExecutionStateHandler &other) {}
61         ExecutionStateHandler &operator=(const ExecutionStateHandler &) {}
62
63         //Prevent object allocation on the heap
64         void *operator new(size_t);   void *operator new[](size_t);
65         void operator delete(void *); void operator delete[](void*);
66 };
67
68 /*
69  * Macros
70  */
71 #define CHECK_STATUS(ABORT_FLAG, OK_FLAG) do \
72 { \
73         if(ABORT_FLAG) \
74         { \
75                 log("\nPROCESS ABORTED BY USER !!!"); \
76                 setStatus(JobStatus_Aborted); \
77                 if(QFileInfo(m_outputFileName).exists() && (QFileInfo(m_outputFileName).size() == 0)) QFile::remove(m_outputFileName); \
78                 return; \
79         } \
80         else if(!(OK_FLAG)) \
81         { \
82                 setStatus(JobStatus_Failed); \
83                 if(QFileInfo(m_outputFileName).exists() && (QFileInfo(m_outputFileName).size() == 0)) QFile::remove(m_outputFileName); \
84                 return; \
85         } \
86 } \
87 while(0)
88
89 /*
90  * Static vars
91  */
92 static const char *VPS_TEST_FILE = "import vapoursynth as vs\ncore = vs.get_core()\nv = core.std.BlankClip()\nv.set_output()\n";
93
94 ///////////////////////////////////////////////////////////////////////////////
95 // Constructor & Destructor
96 ///////////////////////////////////////////////////////////////////////////////
97
98 EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const SysinfoModel *const sysinfo, const PreferencesModel *const preferences)
99 :
100         m_jobId(QUuid::createUuid()),
101         m_sourceFileName(sourceFileName),
102         m_outputFileName(outputFileName),
103         m_options(new OptionsModel(*options)),
104         m_sysinfo(sysinfo),
105         m_preferences(preferences),
106         m_jobObject(new JobObject),
107         m_semaphorePaused(0)
108 {
109         m_abort = false;
110         m_pause = false;
111
112         //Create encoder object
113         switch(options->encType())
114         {
115         case OptionsModel::EncType_X264:
116                 m_encoder = new X264Encoder(m_jobObject, m_options, m_sysinfo, m_preferences, m_status, &m_abort, &m_pause, &m_semaphorePaused, m_sourceFileName, m_outputFileName);
117                 break;
118         case OptionsModel::EncType_X265:
119                 m_encoder = new X265Encoder(m_jobObject, m_options, m_sysinfo, m_preferences, m_status, &m_abort, &m_pause, &m_semaphorePaused, m_sourceFileName, m_outputFileName);
120                 break;
121         default:
122                 throw "Unknown encoder type encountered!";
123         }
124
125         //Establish connections
126         connect(m_encoder, SIGNAL(statusChanged(JobStatus)), this, SIGNAL(setStatus(QString)), Qt::DirectConnection);
127         connect(m_encoder, SIGNAL(progressChanged(unsigned int)), this, SIGNAL(setProgress(QString)), Qt::DirectConnection);
128         connect(m_encoder, SIGNAL(messageLogged(QString)), this, SIGNAL(log(QString)), Qt::DirectConnection);
129         connect(m_encoder, SIGNAL(detailsChanged(QString)), this, SIGNAL(setDetails(QString)), Qt::DirectConnection);
130 }
131
132 EncodeThread::~EncodeThread(void)
133 {
134         X264_DELETE(m_encoder);
135         X264_DELETE(m_jobObject);
136         X264_DELETE(m_options);
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_jobObject)
159         {
160                 m_jobObject->terminateJob(42);
161                 X264_DELETE(m_jobObject);
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                         ExecutionStateHandler executionStateHandler;
175                         encode();
176                 }
177                 catch(char *msg)
178                 {
179                         log(tr("EXCEPTION ERROR IN THREAD: ").append(QString::fromLatin1(msg)));
180                         setStatus(JobStatus_Failed);
181                 }
182                 catch(...)
183                 {
184                         log(tr("UNHANDLED EXCEPTION ERROR IN THREAD !!!"));
185                         setStatus(JobStatus_Failed);
186                 }
187         }
188         catch(...)
189         {
190                 x264_fatal_exit(L"Unhandeled exception error in encode thread!");
191         }
192 }
193
194 void EncodeThread::start(Priority priority)
195 {
196         qDebug("Thread starting...");
197
198         m_abort = false;
199         m_pause = false;
200
201         while(m_semaphorePaused.tryAcquire(1, 0));
202         QThread::start(priority);
203 }
204
205 ///////////////////////////////////////////////////////////////////////////////
206 // Encode functions
207 ///////////////////////////////////////////////////////////////////////////////
208
209 void EncodeThread::encode(void)
210 {
211         QDateTime startTime = QDateTime::currentDateTime();
212
213         // -----------------------------------------------------------------------------------
214         // Print Information
215         // -----------------------------------------------------------------------------------
216
217         //Print some basic info
218         log(tr("Simple x264 Launcher (Build #%1), built %2\n").arg(QString::number(x264_version_build()), x264_version_date().toString(Qt::ISODate)));
219         log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
220         log(tr("Source file: %1").arg(QDir::toNativeSeparators(m_sourceFileName)));
221         log(tr("Output file: %1").arg(QDir::toNativeSeparators(m_outputFileName)));
222         
223         //Print system info
224         log(tr("\n--- SYSTEMINFO ---\n"));
225         log(tr("Binary Path: %1").arg(QDir::toNativeSeparators(m_sysinfo->getAppPath())));
226         log(tr("Avisynth OK: %1").arg(m_sysinfo->hasAVSSupport() ? tr("Yes") : tr("No")));
227         log(tr("VapourSynth: %1").arg(m_sysinfo->hasVPSSupport() ? QDir::toNativeSeparators(m_sysinfo->getVPSPath()) : tr("N/A")));
228
229         //Print encoder settings
230         log(tr("\n--- SETTINGS ---\n"));
231         log(tr("RC Mode: %1").arg(OptionsModel::rcMode2String(m_options->rcMode())));
232         log(tr("Preset:  %1").arg(m_options->preset()));
233         log(tr("Tuning:  %1").arg(m_options->tune()));
234         log(tr("Profile: %1").arg(m_options->profile()));
235         log(tr("Custom:  %1").arg(m_options->customEncParams().isEmpty() ? tr("(None)") : m_options->customEncParams()));
236         
237         bool ok = false;
238         unsigned int frames = 0;
239
240         //Seletct type of input
241         const int inputType = getInputType(QFileInfo(m_sourceFileName).suffix());
242         
243         // -----------------------------------------------------------------------------------
244         // Check Versions
245         // -----------------------------------------------------------------------------------
246         
247         log(tr("\n--- CHECK VERSION ---\n"));
248
249         //Check encoder version
250         bool encoderModified = false;
251         unsigned int encoderRevision = m_encoder->checkVersion(encoderModified);
252         CHECK_STATUS(m_abort, (ok = (encoderRevision != UINT_MAX)));
253         
254         //Checking avs2yuv version
255         unsigned int revision_avs2yuv = UINT_MAX;
256         switch(inputType)
257         {
258                 case INPUT_NATIVE: break;
259                 case INPUT_AVISYN: ok = ((revision_avs2yuv = checkVersionAvs2yuv()) != UINT_MAX); break;
260                 case INPUT_VAPOUR: ok = checkVersionVapoursynth();  break;
261                 default: throw "Invalid input type!";
262         }
263         CHECK_STATUS(m_abort, ok);
264
265         //Print versions
266         m_encoder->printVersion(encoderRevision, encoderModified);
267         if(revision_avs2yuv != UINT_MAX)
268         {
269                 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)));
270         }
271
272         //Is encoder version suppoprted?
273         if(!m_encoder->isVersionSupported(encoderRevision, encoderModified))
274         {
275                 setStatus(JobStatus_Failed);
276                 return;
277         }
278
279         //Is Avs2YUV version supported?
280         if((revision_avs2yuv != UINT_MAX) && ((revision_avs2yuv % REV_MULT) != x264_version_x264_avs2yuv_ver()))
281         {
282                 log(tr("\nERROR: Your version of avs2yuv is unsupported (Required version: v0.24 BugMaster's mod 2)"));
283                 log(tr("You can find the required version at: http://komisar.gin.by/tools/avs2yuv/"));
284                 setStatus(JobStatus_Failed);
285                 return;
286         }
287
288         // -----------------------------------------------------------------------------------
289         // Detect Source Info
290         // -----------------------------------------------------------------------------------
291
292         //Detect source info
293         if(inputType != INPUT_NATIVE)
294         {
295                 log(tr("\n--- SOURCE INFO ---\n"));
296                 switch(inputType)
297                 {
298                 case INPUT_AVISYN:
299                         ok = checkPropertiesAVS(frames);
300                         CHECK_STATUS(m_abort, ok);
301                         break;
302                 case INPUT_VAPOUR:
303                         ok = checkPropertiesVPS(frames);
304                         CHECK_STATUS(m_abort, ok);
305                         break;
306                 }
307         }
308
309         //Run encoding passes
310         if(m_options->rcMode() == OptionsModel::RCMode_2Pass)
311         {
312                 QFileInfo info(m_outputFileName);
313                 QString passLogFile = QString("%1/%2.stats").arg(info.path(), info.completeBaseName());
314
315                 if(QFileInfo(passLogFile).exists())
316                 {
317                         int n = 2;
318                         while(QFileInfo(passLogFile).exists())
319                         {
320                                 passLogFile = QString("%1/%2.%3.stats").arg(info.path(), info.completeBaseName(), QString::number(n++));
321                         }
322                 }
323                 
324                 log(tr("\n--- PASS 1 ---\n"));
325                 ok = runEncodingPass(inputType, frames, indexFile, 1, passLogFile);
326                 CHECK_STATUS(m_abort, ok);
327
328                 log(tr("\n--- PASS 2 ---\n"));
329                 ok = runEncodingPass(inputType, frames, indexFile, 2, passLogFile);
330                 CHECK_STATUS(m_abort, ok);
331         }
332         else
333         {
334                 log(tr("\n--- ENCODING ---\n"));
335                 ok = runEncodingPass(inputType, frames, indexFile);
336                 CHECK_STATUS(m_abort, ok);
337         }
338
339         log(tr("\n--- DONE ---\n"));
340         int timePassed = startTime.secsTo(QDateTime::currentDateTime());
341         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)));
342         setStatus(JobStatus_Completed);
343 }
344
345 unsigned int EncodeThread::checkVersionAvs2yuv(void)
346 {
347         if(!m_sysinfo->hasAVSSupport())
348         {
349                 log(tr("\nAVS INPUT REQUIRES VAPOURSYNTH, BUT IT IS *NOT* AVAILABLE !!!"));
350                 return false;
351         }
352
353         QProcess process;
354
355         log("\nCreating process:");
356         if(!startProcess(process, AVS_BINARY(m_sysinfo, m_preferences), QStringList()))
357         {
358                 return false;;
359         }
360
361         QRegExp regExpVersionMod("\\bAvs2YUV (\\d+).(\\d+)bm(\\d)\\b", Qt::CaseInsensitive);
362         QRegExp regExpVersionOld("\\bAvs2YUV (\\d+).(\\d+)\\b", Qt::CaseInsensitive);
363         
364         bool bTimeout = false;
365         bool bAborted = false;
366
367         unsigned int ver_maj = UINT_MAX;
368         unsigned int ver_min = UINT_MAX;
369         unsigned int ver_mod = 0;
370
371         while(process.state() != QProcess::NotRunning)
372         {
373                 if(m_abort)
374                 {
375                         process.kill();
376                         bAborted = true;
377                         break;
378                 }
379                 if(!process.waitForReadyRead())
380                 {
381                         if(process.state() == QProcess::Running)
382                         {
383                                 process.kill();
384                                 qWarning("Avs2YUV process timed out <-- killing!");
385                                 log("\nPROCESS TIMEOUT !!!");
386                                 bTimeout = true;
387                                 break;
388                         }
389                 }
390                 while(process.bytesAvailable() > 0)
391                 {
392                         QList<QByteArray> lines = process.readLine().split('\r');
393                         while(!lines.isEmpty())
394                         {
395                                 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
396                                 int offset = -1;
397                                 if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX) || (ver_mod == UINT_MAX))
398                                 {
399                                         if(!text.isEmpty())
400                                         {
401                                                 log(text);
402                                         }
403                                 }
404                                 if((offset = regExpVersionMod.lastIndexIn(text)) >= 0)
405                                 {
406                                         bool ok1 = false, ok2 = false, ok3 = false;
407                                         unsigned int temp1 = regExpVersionMod.cap(1).toUInt(&ok1);
408                                         unsigned int temp2 = regExpVersionMod.cap(2).toUInt(&ok2);
409                                         unsigned int temp3 = regExpVersionMod.cap(3).toUInt(&ok3);
410                                         if(ok1) ver_maj = temp1;
411                                         if(ok2) ver_min = temp2;
412                                         if(ok3) ver_mod = temp3;
413                                 }
414                                 else if((offset = regExpVersionOld.lastIndexIn(text)) >= 0)
415                                 {
416                                         bool ok1 = false, ok2 = false;
417                                         unsigned int temp1 = regExpVersionOld.cap(1).toUInt(&ok1);
418                                         unsigned int temp2 = regExpVersionOld.cap(2).toUInt(&ok2);
419                                         if(ok1) ver_maj = temp1;
420                                         if(ok2) ver_min = temp2;
421                                 }
422                         }
423                 }
424         }
425
426         process.waitForFinished();
427         if(process.state() != QProcess::NotRunning)
428         {
429                 process.kill();
430                 process.waitForFinished(-1);
431         }
432
433         if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 2)))
434         {
435                 if(!(bTimeout || bAborted))
436                 {
437                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
438                 }
439                 return UINT_MAX;
440         }
441
442         if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX))
443         {
444                 log(tr("\nFAILED TO DETERMINE AVS2YUV VERSION !!!"));
445                 return UINT_MAX;
446         }
447         
448         return (ver_maj * REV_MULT) + ((ver_min % REV_MULT) * 10) + (ver_mod % 10);
449 }
450
451 bool EncodeThread::checkVersionVapoursynth(void)
452 {
453         //Is VapourSynth available at all?
454         if((!m_sysinfo->hasVPSSupport()) || (!QFileInfo(VPS_BINARY(m_sysinfo, m_preferences)).isFile()))
455         {
456                 log(tr("\nVPY INPUT REQUIRES VAPOURSYNTH, BUT IT IS *NOT* AVAILABLE !!!"));
457                 return false;
458         }
459
460         QProcess process;
461
462         log("\nCreating process:");
463         if(!startProcess(process, VPS_BINARY(m_sysinfo, m_preferences), QStringList()))
464         {
465                 return false;;
466         }
467
468         QRegExp regExpSignature("\\bVSPipe\\s+usage\\b", Qt::CaseInsensitive);
469         
470         bool bTimeout = false;
471         bool bAborted = false;
472
473         bool vspipeSignature = false;
474
475         while(process.state() != QProcess::NotRunning)
476         {
477                 if(m_abort)
478                 {
479                         process.kill();
480                         bAborted = true;
481                         break;
482                 }
483                 if(!process.waitForReadyRead())
484                 {
485                         if(process.state() == QProcess::Running)
486                         {
487                                 process.kill();
488                                 qWarning("VSPipe process timed out <-- killing!");
489                                 log("\nPROCESS TIMEOUT !!!");
490                                 bTimeout = true;
491                                 break;
492                         }
493                 }
494                 while(process.bytesAvailable() > 0)
495                 {
496                         QList<QByteArray> lines = process.readLine().split('\r');
497                         while(!lines.isEmpty())
498                         {
499                                 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
500                                 if(regExpSignature.lastIndexIn(text) >= 0)
501                                 {
502                                         vspipeSignature = true;
503                                 }
504                                 if(!text.isEmpty())
505                                 {
506                                         log(text);
507                                 }
508                         }
509                 }
510         }
511
512         process.waitForFinished();
513         if(process.state() != QProcess::NotRunning)
514         {
515                 process.kill();
516                 process.waitForFinished(-1);
517         }
518
519         if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 1)))
520         {
521                 if(!(bTimeout || bAborted))
522                 {
523                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
524                 }
525                 return false;
526         }
527
528         if(!vspipeSignature)
529         {
530                 log(tr("\nFAILED TO DETECT VSPIPE SIGNATURE !!!"));
531                 return false;
532         }
533         
534         return vspipeSignature;
535 }
536
537 bool EncodeThread::checkPropertiesAVS(unsigned int &frames)
538 {
539         QProcess process;
540         QStringList cmdLine;
541
542         if(!m_options->customAvs2YUV().isEmpty())
543         {
544                 cmdLine.append(splitParams(m_options->customAvs2YUV()));
545         }
546
547         cmdLine << "-frames" << "1";
548         cmdLine << QDir::toNativeSeparators(x264_path2ansi(m_sourceFileName, true)) << "NUL";
549
550         log("Creating process:");
551         if(!startProcess(process, AVS_BINARY(m_sysinfo, m_preferences), cmdLine))
552         {
553                 return false;;
554         }
555
556         QRegExp regExpInt(": (\\d+)x(\\d+), (\\d+) fps, (\\d+) frames");
557         QRegExp regExpFrc(": (\\d+)x(\\d+), (\\d+)/(\\d+) fps, (\\d+) frames");
558         
559         QTextCodec *localCodec = QTextCodec::codecForName("System");
560
561         bool bTimeout = false;
562         bool bAborted = false;
563
564         frames = 0;
565         
566         unsigned int fpsNom = 0;
567         unsigned int fpsDen = 0;
568         unsigned int fSizeW = 0;
569         unsigned int fSizeH = 0;
570         
571         unsigned int waitCounter = 0;
572
573         while(process.state() != QProcess::NotRunning)
574         {
575                 if(m_abort)
576                 {
577                         process.kill();
578                         bAborted = true;
579                         break;
580                 }
581                 if(!process.waitForReadyRead(m_processTimeoutInterval))
582                 {
583                         if(process.state() == QProcess::Running)
584                         {
585                                 if(++waitCounter > m_processTimeoutMaxCounter)
586                                 {
587                                         if(m_preferences->getAbortOnTimeout())
588                                         {
589                                                 process.kill();
590                                                 qWarning("Avs2YUV process timed out <-- killing!");
591                                                 log("\nPROCESS TIMEOUT !!!");
592                                                 log("\nAvisynth has encountered a deadlock or your script takes EXTREMELY long to initialize!");
593                                                 bTimeout = true;
594                                                 break;
595                                         }
596                                 }
597                                 else if(waitCounter == m_processTimeoutWarning)
598                                 {
599                                         unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
600                                         log(tr("Warning: Avisynth did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
601                                 }
602                         }
603                         continue;
604                 }
605                 
606                 waitCounter = 0;
607                 
608                 while(process.bytesAvailable() > 0)
609                 {
610                         QList<QByteArray> lines = process.readLine().split('\r');
611                         while(!lines.isEmpty())
612                         {
613                                 QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
614                                 int offset = -1;
615                                 if((offset = regExpInt.lastIndexIn(text)) >= 0)
616                                 {
617                                         bool ok1 = false, ok2 = false;
618                                         bool ok3 = false, ok4 = false;
619                                         unsigned int temp1 = regExpInt.cap(1).toUInt(&ok1);
620                                         unsigned int temp2 = regExpInt.cap(2).toUInt(&ok2);
621                                         unsigned int temp3 = regExpInt.cap(3).toUInt(&ok3);
622                                         unsigned int temp4 = regExpInt.cap(4).toUInt(&ok4);
623                                         if(ok1) fSizeW = temp1;
624                                         if(ok2) fSizeH = temp2;
625                                         if(ok3) fpsNom = temp3;
626                                         if(ok4) frames = temp4;
627                                 }
628                                 else if((offset = regExpFrc.lastIndexIn(text)) >= 0)
629                                 {
630                                         bool ok1 = false, ok2 = false;
631                                         bool ok3 = false, ok4 = false, ok5 = false;
632                                         unsigned int temp1 = regExpFrc.cap(1).toUInt(&ok1);
633                                         unsigned int temp2 = regExpFrc.cap(2).toUInt(&ok2);
634                                         unsigned int temp3 = regExpFrc.cap(3).toUInt(&ok3);
635                                         unsigned int temp4 = regExpFrc.cap(4).toUInt(&ok4);
636                                         unsigned int temp5 = regExpFrc.cap(5).toUInt(&ok5);
637                                         if(ok1) fSizeW = temp1;
638                                         if(ok2) fSizeH = temp2;
639                                         if(ok3) fpsNom = temp3;
640                                         if(ok4) fpsDen = temp4;
641                                         if(ok5) frames = temp5;
642                                 }
643                                 if(!text.isEmpty())
644                                 {
645                                         log(text);
646                                 }
647                                 if(text.contains("failed to load avisynth.dll", Qt::CaseInsensitive))
648                                 {
649                                         log(tr("\nWarning: It seems that %1-Bit Avisynth is not currently installed !!!").arg(m_preferences->getUseAvisyth64Bit() ? "64" : "32"));
650                                 }
651                                 if(text.contains(QRegExp("couldn't convert input clip to (YV16|YV24)", Qt::CaseInsensitive)))
652                                 {
653                                         log(tr("\nWarning: YV16 (4:2:2) and YV24 (4:4:4) color-spaces only supported in Avisynth 2.6 !!!"));
654                                 }
655                         }
656                 }
657         }
658
659         process.waitForFinished();
660         if(process.state() != QProcess::NotRunning)
661         {
662                 process.kill();
663                 process.waitForFinished(-1);
664         }
665
666         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
667         {
668                 if(!(bTimeout || bAborted))
669                 {
670                         const int exitCode = process.exitCode();
671                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(exitCode)));
672                         if((exitCode < 0) || (exitCode >= 32))
673                         {
674                                 log(tr("\nIMPORTANT: The Avs2YUV process terminated abnormally. This means Avisynth or one of your Avisynth-Plugin's just crashed."));
675                                 log(tr("IMPORTANT: Please fix your Avisynth script and try again! If you use Avisynth-MT, try using a *stable* Avisynth instead!"));
676                         }
677                 }
678                 return false;
679         }
680
681         if(frames == 0)
682         {
683                 log(tr("\nFAILED TO DETERMINE AVS PROPERTIES !!!"));
684                 return false;
685         }
686         
687         log("");
688
689         if((fSizeW > 0) && (fSizeH > 0))
690         {
691                 log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
692         }
693         if((fpsNom > 0) && (fpsDen > 0))
694         {
695                 log(tr("Frame Rate: %1/%2").arg(QString::number(fpsNom), QString::number(fpsDen)));
696         }
697         if((fpsNom > 0) && (fpsDen == 0))
698         {
699                 log(tr("Frame Rate: %1").arg(QString::number(fpsNom)));
700         }
701         if(frames > 0)
702         {
703                 log(tr("No. Frames: %1").arg(QString::number(frames)));
704         }
705
706         return true;
707 }
708
709 bool EncodeThread::checkPropertiesVPS(unsigned int &frames)
710 {
711         QProcess process;
712         QStringList cmdLine;
713
714         cmdLine << QDir::toNativeSeparators(x264_path2ansi(m_sourceFileName, true));
715         cmdLine << "-" << "-info";
716
717         log("Creating process:");
718         if(!startProcess(process, VPS_BINARY(m_sysinfo, m_preferences), cmdLine))
719         {
720                 return false;;
721         }
722
723         QRegExp regExpFrm("\\bFrames:\\s+(\\d+)\\b");
724         QRegExp regExpSzW("\\bWidth:\\s+(\\d+)\\b");
725         QRegExp regExpSzH("\\bHeight:\\s+(\\d+)\\b");
726         
727         QTextCodec *localCodec = QTextCodec::codecForName("System");
728
729         bool bTimeout = false;
730         bool bAborted = false;
731
732         frames = 0;
733         
734         unsigned int fSizeW = 0;
735         unsigned int fSizeH = 0;
736         
737         unsigned int waitCounter = 0;
738
739         while(process.state() != QProcess::NotRunning)
740         {
741                 if(m_abort)
742                 {
743                         process.kill();
744                         bAborted = true;
745                         break;
746                 }
747                 if(!process.waitForReadyRead(m_processTimeoutInterval))
748                 {
749                         if(process.state() == QProcess::Running)
750                         {
751                                 if(++waitCounter > m_processTimeoutMaxCounter)
752                                 {
753                                         if(m_preferences->getAbortOnTimeout())
754                                         {
755                                                 process.kill();
756                                                 qWarning("VSPipe process timed out <-- killing!");
757                                                 log("\nPROCESS TIMEOUT !!!");
758                                                 log("\nVapoursynth has encountered a deadlock or your script takes EXTREMELY long to initialize!");
759                                                 bTimeout = true;
760                                                 break;
761                                         }
762                                 }
763                                 else if(waitCounter == m_processTimeoutWarning)
764                                 {
765                                         unsigned int timeOut = (waitCounter * m_processTimeoutInterval) / 1000U;
766                                         log(tr("Warning: nVapoursynth did not respond for %1 seconds, potential deadlock...").arg(QString::number(timeOut)));
767                                 }
768                         }
769                         continue;
770                 }
771                 
772                 waitCounter = 0;
773                 
774                 while(process.bytesAvailable() > 0)
775                 {
776                         QList<QByteArray> lines = process.readLine().split('\r');
777                         while(!lines.isEmpty())
778                         {
779                                 QString text = localCodec->toUnicode(lines.takeFirst().constData()).simplified();
780                                 int offset = -1;
781                                 if((offset = regExpFrm.lastIndexIn(text)) >= 0)
782                                 {
783                                         bool ok = false;
784                                         unsigned int temp = regExpFrm.cap(1).toUInt(&ok);
785                                         if(ok) frames = temp;
786                                 }
787                                 if((offset = regExpSzW.lastIndexIn(text)) >= 0)
788                                 {
789                                         bool ok = false;
790                                         unsigned int temp = regExpSzW.cap(1).toUInt(&ok);
791                                         if(ok) fSizeW = temp;
792                                 }
793                                 if((offset = regExpSzH.lastIndexIn(text)) >= 0)
794                                 {
795                                         bool ok = false;
796                                         unsigned int temp = regExpSzH.cap(1).toUInt(&ok);
797                                         if(ok) fSizeH = temp;
798                                 }
799                                 if(!text.isEmpty())
800                                 {
801                                         log(text);
802                                 }
803                         }
804                 }
805         }
806
807         process.waitForFinished();
808         if(process.state() != QProcess::NotRunning)
809         {
810                 process.kill();
811                 process.waitForFinished(-1);
812         }
813
814         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
815         {
816                 if(!(bTimeout || bAborted))
817                 {
818                         const int exitCode = process.exitCode();
819                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(exitCode)));
820                         if((exitCode < 0) || (exitCode >= 32))
821                         {
822                                 log(tr("\nIMPORTANT: The Vapoursynth process terminated abnormally. This means Vapoursynth or one of your Vapoursynth-Plugin's just crashed."));
823                         }
824                 }
825                 return false;
826         }
827
828         if(frames == 0)
829         {
830                 log(tr("\nFAILED TO DETERMINE VPY PROPERTIES !!!"));
831                 return false;
832         }
833         
834         log("");
835
836         if((fSizeW > 0) && (fSizeH > 0))
837         {
838                 log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
839         }
840         if(frames > 0)
841         {
842                 log(tr("No. Frames: %1").arg(QString::number(frames)));
843         }
844
845         return true;
846 }
847
848 ///////////////////////////////////////////////////////////////////////////////
849 // Misc functions
850 ///////////////////////////////////////////////////////////////////////////////
851
852 void EncodeThread::log(const QString &text)
853 {
854         emit messageLogged(m_jobId, text);
855 }
856
857 void EncodeThread::setStatus(const JobStatus &newStatus)
858 {
859         if(m_status != newStatus)
860         {
861                 if((newStatus != JobStatus_Completed) && (newStatus != JobStatus_Failed) && (newStatus != JobStatus_Aborted) && (newStatus != JobStatus_Paused))
862                 {
863                         if(m_status != JobStatus_Paused) setProgress(0);
864                 }
865                 if(newStatus == JobStatus_Failed)
866                 {
867                         setDetails("The job has failed. See log for details!");
868                 }
869                 if(newStatus == JobStatus_Aborted)
870                 {
871                         setDetails("The job was aborted by the user!");
872                 }
873                 m_status = newStatus;
874                 emit statusChanged(m_jobId, newStatus);
875         }
876 }
877
878 void EncodeThread::setProgress(const unsigned int &newProgress)
879 {
880         if(m_progress != newProgress)
881         {
882                 m_progress = newProgress;
883                 emit progressChanged(m_jobId, m_progress);
884         }
885 }
886
887 void EncodeThread::setDetails(const QString &text)
888 {
889         emit detailsChanged(m_jobId, text);
890 }
891
892 int EncodeThread::getInputType(const QString &fileExt)
893 {
894         int type = INPUT_NATIVE;
895         if(fileExt.compare("avs", Qt::CaseInsensitive) == 0)       type = INPUT_AVISYN;
896         else if(fileExt.compare("avsi", Qt::CaseInsensitive) == 0) type = INPUT_AVISYN;
897         else if(fileExt.compare("vpy", Qt::CaseInsensitive) == 0)  type = INPUT_VAPOUR;
898         else if(fileExt.compare("py", Qt::CaseInsensitive) == 0)   type = INPUT_VAPOUR;
899         return type;
900 }