1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
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.
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.
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.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "thread_encode.h"
25 #include "model_options.h"
39 QMutex EncodeThread::m_mutex_startProcess;
44 #define CHECK_STATUS(ABORT_FLAG, OK_FLAG) \
48 log("\nPROCESS ABORTED BY USER !!!"); \
49 setStatus(JobStatus_Aborted); \
50 if(QFileInfo(indexFile).exists()) QFile::remove(indexFile); \
55 setStatus(JobStatus_Failed); \
56 if(QFileInfo(indexFile).exists()) QFile::remove(indexFile); \
64 static const unsigned int REV_MULT = 10000;
66 ///////////////////////////////////////////////////////////////////////////////
67 // Constructor & Destructor
68 ///////////////////////////////////////////////////////////////////////////////
70 EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir, bool x64)
72 m_jobId(QUuid::createUuid()),
73 m_sourceFileName(sourceFileName),
74 m_outputFileName(outputFileName),
75 m_options(new OptionsModel(*options)),
78 m_handle_jobObject(NULL),
85 EncodeThread::~EncodeThread(void)
87 X264_DELETE(m_options);
89 if(m_handle_jobObject)
91 CloseHandle(m_handle_jobObject);
92 m_handle_jobObject = NULL;
96 ///////////////////////////////////////////////////////////////////////////////
98 ///////////////////////////////////////////////////////////////////////////////
100 void EncodeThread::run(void)
108 qWarning("STRUCTURED EXCEPTION ERROR IN ENCODE THREAD !!!");
111 if(m_handle_jobObject)
113 TerminateJobObject(m_handle_jobObject, 42);
114 m_handle_jobObject = NULL;
118 void EncodeThread::checkedRun(void)
121 m_status = JobStatus_Starting;
131 log(tr("EXCEPTION ERROR IN THREAD: ").append(QString::fromLatin1(msg)));
132 setStatus(JobStatus_Failed);
136 log(tr("UNHANDLED EXCEPTION ERROR IN THREAD !!!"));
137 setStatus(JobStatus_Failed);
142 RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
146 void EncodeThread::start(Priority priority)
148 qDebug("Thread starting...");
153 while(m_semaphorePaused.tryAcquire(1, 0));
154 QThread::start(priority);
157 ///////////////////////////////////////////////////////////////////////////////
159 ///////////////////////////////////////////////////////////////////////////////
161 void EncodeThread::encode(void)
163 QDateTime startTime = QDateTime::currentDateTime();
165 //Print some basic info
166 log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
167 log(tr("Source file: %1").arg(m_sourceFileName));
168 log(tr("Output file: %1").arg(m_outputFileName));
170 //Print encoder settings
171 log(tr("\n--- SETTINGS ---\n"));
172 log(tr("RC Mode: %1").arg(OptionsModel::rcMode2String(m_options->rcMode())));
173 log(tr("Preset: %1").arg(m_options->preset()));
174 log(tr("Tuning: %1").arg(m_options->tune()));
175 log(tr("Profile: %1").arg(m_options->profile()));
176 log(tr("Custom: %1").arg(m_options->custom().isEmpty() ? tr("(None)") : m_options->custom()));
179 unsigned int frames = 0;
182 const bool usePipe = (QFileInfo(m_sourceFileName).suffix().compare("avs", Qt::CaseInsensitive) == 0);
183 const QString indexFile = QString("%1/%2.ffindex").arg(QDir::tempPath(), m_jobId.toString());
185 //Checking x264 version
186 log(tr("\n--- CHECK VERSION ---\n"));
187 unsigned int revision_x264 = UINT_MAX;
188 bool x264_modified = false;
189 ok = ((revision_x264 = checkVersionX264(m_x64, x264_modified)) != UINT_MAX);
190 CHECK_STATUS(m_abort, ok);
192 //Checking avs2yuv version
193 unsigned int revision_avs2yuv = UINT_MAX;
196 ok = ((revision_avs2yuv = checkVersionAvs2yuv()) != UINT_MAX);
197 CHECK_STATUS(m_abort, ok);
201 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()));
202 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)));
204 //Is x264 revision supported?
205 if((revision_x264 % REV_MULT) < VER_X264_MINIMUM_REV)
207 log(tr("\nERROR: Your revision of x264 is too old! (Minimum required revision is %2)").arg(QString::number(VER_X264_MINIMUM_REV)));
208 setStatus(JobStatus_Failed);
211 if((revision_x264 / REV_MULT) != VER_X264_CURRENT_API)
213 log(tr("\nWARNING: Your revision of x264 uses an unsupported core (API) version, take care!"));
214 log(tr("This application works best with x264 core (API) version %2.").arg(QString::number(VER_X264_CURRENT_API)));
216 if((revision_avs2yuv != UINT_MAX) && ((revision_avs2yuv % REV_MULT) != VER_x264_AVS2YUV_VER))
218 log(tr("\nERROR: Your version of avs2yuv is unsupported (Required version: v0.24 BugMaster's mod 2)"));
219 log(tr("You can find the required version at: http://komisar.gin.by/tools/avs2yuv/"));
220 setStatus(JobStatus_Failed);
227 log(tr("\n--- AVS INFO ---\n"));
228 ok = checkProperties(frames);
229 CHECK_STATUS(m_abort, ok);
232 //Run encoding passes
233 if(m_options->rcMode() == OptionsModel::RCMode_2Pass)
235 QFileInfo info(m_outputFileName);
236 QString passLogFile = QString("%1/%2.stats").arg(info.path(), info.completeBaseName());
238 if(QFileInfo(passLogFile).exists())
241 while(QFileInfo(passLogFile).exists())
243 passLogFile = QString("%1/%2.%3.stats").arg(info.path(), info.completeBaseName(), QString::number(n++));
247 log(tr("\n--- PASS 1 ---\n"));
248 ok = runEncodingPass(m_x64, usePipe, frames, indexFile, 1, passLogFile);
249 CHECK_STATUS(m_abort, ok);
251 log(tr("\n--- PASS 2 ---\n"));
252 ok = runEncodingPass(m_x64, usePipe, frames, indexFile, 2, passLogFile);
253 CHECK_STATUS(m_abort, ok);
257 log(tr("\n--- ENCODING ---\n"));
258 ok = runEncodingPass(m_x64, usePipe, frames, indexFile);
259 CHECK_STATUS(m_abort, ok);
262 log(tr("\n--- DONE ---\n"));
263 if(QFileInfo(indexFile).exists()) QFile::remove(indexFile);
264 int timePassed = startTime.secsTo(QDateTime::currentDateTime());
265 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)));
266 setStatus(JobStatus_Completed);
269 bool EncodeThread::runEncodingPass(bool x64, bool usePipe, unsigned int frames, const QString &indexFile, int pass, const QString &passLogFile)
271 QProcess processEncode, processAvisynth;
275 QStringList cmdLine_Avisynth;
276 cmdLine_Avisynth << QDir::toNativeSeparators(m_sourceFileName);
277 cmdLine_Avisynth << "-";
278 processAvisynth.setStandardOutputProcess(&processEncode);
280 log("Creating Avisynth process:");
281 if(!startProcess(processAvisynth, QString("%1/avs2yuv.exe").arg(m_binDir), cmdLine_Avisynth, false))
287 QStringList cmdLine_Encode = buildCommandLine(usePipe, frames, indexFile, pass, passLogFile);
289 log("Creating x264 process:");
290 if(!startProcess(processEncode, QString("%1/%2.exe").arg(m_binDir, x64 ? "x264_x64" : "x264"), cmdLine_Encode))
295 QRegExp regExpIndexing("indexing.+\\[(\\d+)\\.\\d+%\\]");
296 QRegExp regExpProgress("\\[(\\d+)\\.\\d+%\\].+frames");
297 QRegExp regExpFrameCnt("^(\\d+) frames:");
299 bool bTimeout = false;
300 bool bAborted = false;
302 //Main processing loop
303 while(processEncode.state() != QProcess::NotRunning)
305 unsigned int waitCounter = 0;
307 //Wait until new output is available
312 processEncode.kill();
313 processAvisynth.kill();
317 if(m_pause && (processEncode.state() == QProcess::Running))
319 JobStatus previousStatus = m_status;
320 setStatus(JobStatus_Paused);
321 log(tr("Job paused by user at %1, %2.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
322 bool ok[2] = {false, false};
323 Q_PID pid[2] = {processEncode.pid(), processAvisynth.pid()};
324 if(pid[0]) { ok[0] = (SuspendThread(pid[0]->hThread) != (DWORD)(-1)); }
325 if(pid[1]) { ok[1] = (SuspendThread(pid[1]->hThread) != (DWORD)(-1)); }
326 while(m_pause) m_semaphorePaused.acquire();
327 while(m_semaphorePaused.tryAcquire(1, 0));
328 if(pid[0]) { if(ok[0]) ResumeThread(pid[0]->hThread); }
329 if(pid[1]) { if(ok[1]) ResumeThread(pid[1]->hThread); }
330 if(!m_abort) setStatus(previousStatus);
331 log(tr("Job resumed by user at %1, %2.").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
335 if(!processEncode.waitForReadyRead(2500))
337 if(processEncode.state() == QProcess::Running)
339 if(waitCounter++ > m_processTimeoutCounter)
341 processEncode.kill();
342 qWarning("x264 process timed out <-- killing!");
343 log("\nPROCESS TIMEOUT !!!");
350 if(m_abort || (m_pause && (processEncode.state() == QProcess::Running)))
357 //Exit main processing loop now?
358 if(bAborted || bTimeout)
364 while(processEncode.bytesAvailable() > 0)
366 QList<QByteArray> lines = processEncode.readLine().split('\r');
367 while(!lines.isEmpty())
369 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
371 if((offset = regExpProgress.lastIndexIn(text)) >= 0)
374 unsigned int progress = regExpProgress.cap(1).toUInt(&ok);
375 setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running));
376 setDetails(text.mid(offset).trimmed());
377 if(ok) setProgress(progress);
379 else if((offset = regExpIndexing.lastIndexIn(text)) >= 0)
382 unsigned int progress = regExpIndexing.cap(1).toUInt(&ok);
383 setStatus(JobStatus_Indexing);
384 setDetails(text.mid(offset).trimmed());
385 if(ok) setProgress(progress);
387 else if((offset = regExpFrameCnt.lastIndexIn(text)) >= 0)
389 setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running));
390 setDetails(text.mid(offset).trimmed());
392 else if(!text.isEmpty())
400 processEncode.waitForFinished(5000);
401 if(processEncode.state() != QProcess::NotRunning)
403 qWarning("x264 process still running, going to kill it!");
404 processEncode.kill();
405 processEncode.waitForFinished(-1);
408 processAvisynth.waitForFinished(5000);
409 if(processAvisynth.state() != QProcess::NotRunning)
411 qWarning("Avisynth process still running, going to kill it!");
412 processAvisynth.kill();
413 processAvisynth.waitForFinished(-1);
416 if(!(bTimeout || bAborted))
418 while(processAvisynth.bytesAvailable() > 0)
420 log(tr("av2y [info]: %1").arg(QString::fromUtf8(processAvisynth.readLine()).simplified()));
424 if(usePipe && (processAvisynth.exitCode() != EXIT_SUCCESS))
426 if(!(bTimeout || bAborted))
428 log(tr("\nWARNING: Avisynth process exited with error code: %1").arg(QString::number(processAvisynth.exitCode())));
432 if(bTimeout || bAborted || processEncode.exitCode() != EXIT_SUCCESS)
434 if(!(bTimeout || bAborted))
436 log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(processEncode.exitCode())));
438 processEncode.close();
439 processAvisynth.close();
446 setStatus(JobStatus_Running_Pass1);
447 setDetails(tr("First pass completed. Preparing for second pass..."));
450 setStatus(JobStatus_Running_Pass2);
451 setDetails(tr("Second pass completed successfully."));
454 setStatus(JobStatus_Running);
455 setDetails(tr("Encode completed successfully."));
460 processEncode.close();
461 processAvisynth.close();
465 QStringList EncodeThread::buildCommandLine(bool usePipe, unsigned int frames, const QString &indexFile, int pass, const QString &passLogFile)
469 switch(m_options->rcMode())
471 case OptionsModel::RCMode_CRF:
472 cmdLine << "--crf" << QString::number(m_options->quantizer());
474 case OptionsModel::RCMode_CQ:
475 cmdLine << "--qp" << QString::number(m_options->quantizer());
477 case OptionsModel::RCMode_2Pass:
478 case OptionsModel::RCMode_ABR:
479 cmdLine << "--bitrate" << QString::number(m_options->bitrate());
482 throw "Bad rate-control mode !!!";
486 if((pass == 1) || (pass == 2))
488 cmdLine << "--pass" << QString::number(pass);
489 cmdLine << "--stats" << QDir::toNativeSeparators(passLogFile);
492 cmdLine << "--preset" << m_options->preset().toLower();
494 if(m_options->tune().compare("none", Qt::CaseInsensitive))
496 cmdLine << "--tune" << m_options->tune().toLower();
499 if(m_options->profile().compare("auto", Qt::CaseInsensitive))
501 cmdLine << "--profile" << m_options->profile().toLower();
504 if(!m_options->custom().isEmpty())
506 //FIXME: Handle custom parameters that contain withspaces within quotes!
507 cmdLine.append(m_options->custom().split(" "));
510 cmdLine << "--output" << QDir::toNativeSeparators(m_outputFileName);
514 if(frames < 1) throw "Frames not set!";
515 cmdLine << "--frames" << QString::number(frames);
516 cmdLine << "--demuxer" << "y4m";
517 cmdLine << "--stdin" << "y4m" << "-";
521 cmdLine << "--index" << QDir::toNativeSeparators(indexFile);
522 cmdLine << QDir::toNativeSeparators(m_sourceFileName);
528 unsigned int EncodeThread::checkVersionX264(bool x64, bool &modified)
531 QStringList cmdLine = QStringList() << "--version";
533 log("Creating process:");
534 if(!startProcess(process, QString("%1/%2.exe").arg(m_binDir, x64 ? "x264_x64" : "x264"), cmdLine))
539 QRegExp regExpVersion("\\bx264\\s(\\d)\\.(\\d+)\\.(\\d+)\\s([a-f0-9]{7})", Qt::CaseInsensitive);
540 QRegExp regExpVersionMod("\\bx264 (\\d)\\.(\\d+)\\.(\\d+)", Qt::CaseInsensitive);
542 bool bTimeout = false;
543 bool bAborted = false;
545 unsigned int revision = UINT_MAX;
546 unsigned int coreVers = UINT_MAX;
549 while(process.state() != QProcess::NotRunning)
557 if(!process.waitForReadyRead())
559 if(process.state() == QProcess::Running)
562 qWarning("x264 process timed out <-- killing!");
563 log("\nPROCESS TIMEOUT !!!");
568 while(process.bytesAvailable() > 0)
570 QList<QByteArray> lines = process.readLine().split('\r');
571 while(!lines.isEmpty())
573 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
575 if((offset = regExpVersion.lastIndexIn(text)) >= 0)
577 bool ok1 = false, ok2 = false;
578 unsigned int temp1 = regExpVersion.cap(2).toUInt(&ok1);
579 unsigned int temp2 = regExpVersion.cap(3).toUInt(&ok2);
580 if(ok1) coreVers = temp1;
581 if(ok2) revision = temp2;
583 else if((offset = regExpVersionMod.lastIndexIn(text)) >= 0)
585 bool ok1 = false, ok2 = false;
586 unsigned int temp1 = regExpVersionMod.cap(2).toUInt(&ok1);
587 unsigned int temp2 = regExpVersionMod.cap(3).toUInt(&ok2);
588 if(ok1) coreVers = temp1;
589 if(ok2) revision = temp2;
600 process.waitForFinished();
601 if(process.state() != QProcess::NotRunning)
604 process.waitForFinished(-1);
607 if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
609 if(!(bTimeout || bAborted))
611 log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
616 if((revision == UINT_MAX) || (coreVers == UINT_MAX))
618 log(tr("\nFAILED TO DETERMINE X264 VERSION !!!"));
622 return (coreVers * REV_MULT) + (revision % REV_MULT);
625 unsigned int EncodeThread::checkVersionAvs2yuv(void)
629 log("\nCreating process:");
630 if(!startProcess(process, QString("%1/avs2yuv.exe").arg(m_binDir), QStringList()))
635 QRegExp regExpVersionMod("\\bAvs2YUV (\\d+).(\\d+)bm(\\d)\\b", Qt::CaseInsensitive);
636 QRegExp regExpVersionOld("\\bAvs2YUV (\\d+).(\\d+)\\b", Qt::CaseInsensitive);
638 bool bTimeout = false;
639 bool bAborted = false;
641 unsigned int ver_maj = UINT_MAX;
642 unsigned int ver_min = UINT_MAX;
643 unsigned int ver_mod = 0;
645 while(process.state() != QProcess::NotRunning)
653 if(!process.waitForReadyRead())
655 if(process.state() == QProcess::Running)
658 qWarning("Avs2YUV process timed out <-- killing!");
659 log("\nPROCESS TIMEOUT !!!");
664 while(process.bytesAvailable() > 0)
666 QList<QByteArray> lines = process.readLine().split('\r');
667 while(!lines.isEmpty())
669 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
671 if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX) || (ver_mod == UINT_MAX))
678 if((offset = regExpVersionMod.lastIndexIn(text)) >= 0)
680 bool ok1 = false, ok2 = false, ok3 = false;
681 unsigned int temp1 = regExpVersionMod.cap(1).toUInt(&ok1);
682 unsigned int temp2 = regExpVersionMod.cap(2).toUInt(&ok2);
683 unsigned int temp3 = regExpVersionMod.cap(3).toUInt(&ok3);
684 if(ok1) ver_maj = temp1;
685 if(ok2) ver_min = temp2;
686 if(ok3) ver_mod = temp3;
688 else if((offset = regExpVersionOld.lastIndexIn(text)) >= 0)
690 bool ok1 = false, ok2 = false;
691 unsigned int temp1 = regExpVersionOld.cap(1).toUInt(&ok1);
692 unsigned int temp2 = regExpVersionOld.cap(2).toUInt(&ok2);
693 if(ok1) ver_maj = temp1;
694 if(ok2) ver_min = temp2;
700 process.waitForFinished();
701 if(process.state() != QProcess::NotRunning)
704 process.waitForFinished(-1);
707 if(bTimeout || bAborted || ((process.exitCode() != EXIT_SUCCESS) && (process.exitCode() != 2)))
709 if(!(bTimeout || bAborted))
711 log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
716 if((ver_maj == UINT_MAX) || (ver_min == UINT_MAX))
718 log(tr("\nFAILED TO DETERMINE AVS2YUV VERSION !!!"));
722 return (ver_maj * REV_MULT) + ((ver_min % REV_MULT) * 10) + (ver_mod % 10);
725 bool EncodeThread::checkProperties(unsigned int &frames)
729 QStringList cmdLine = QStringList() << "-frames" << "1";
730 cmdLine << QDir::toNativeSeparators(m_sourceFileName) << "NUL";
732 log("Creating process:");
733 if(!startProcess(process, QString("%1/avs2yuv.exe").arg(m_binDir), cmdLine))
738 QRegExp regExpInt(": (\\d+)x(\\d+), (\\d+) fps, (\\d+) frames");
739 QRegExp regExpFrc(": (\\d+)x(\\d+), (\\d+)/(\\d+) fps, (\\d+) frames");
741 bool bTimeout = false;
742 bool bAborted = false;
746 unsigned int fpsNom = 0;
747 unsigned int fpsDen = 0;
748 unsigned int fSizeW = 0;
749 unsigned int fSizeH = 0;
751 while(process.state() != QProcess::NotRunning)
759 if(!process.waitForReadyRead())
761 if(process.state() == QProcess::Running)
764 qWarning("Avs2YUV process timed out <-- killing!");
765 log("\nPROCESS TIMEOUT !!!");
770 while(process.bytesAvailable() > 0)
772 QList<QByteArray> lines = process.readLine().split('\r');
773 while(!lines.isEmpty())
775 QString text = QString::fromUtf8(lines.takeFirst().constData()).simplified();
777 if((offset = regExpInt.lastIndexIn(text)) >= 0)
779 bool ok1 = false, ok2 = false;
780 bool ok3 = false, ok4 = false;
781 unsigned int temp1 = regExpInt.cap(1).toUInt(&ok1);
782 unsigned int temp2 = regExpInt.cap(2).toUInt(&ok2);
783 unsigned int temp3 = regExpInt.cap(3).toUInt(&ok3);
784 unsigned int temp4 = regExpInt.cap(4).toUInt(&ok4);
785 if(ok1) fSizeW = temp1;
786 if(ok2) fSizeH = temp2;
787 if(ok3) fpsNom = temp3;
788 if(ok4) frames = temp4;
790 else if((offset = regExpFrc.lastIndexIn(text)) >= 0)
792 bool ok1 = false, ok2 = false;
793 bool ok3 = false, ok4 = false, ok5 = false;
794 unsigned int temp1 = regExpFrc.cap(1).toUInt(&ok1);
795 unsigned int temp2 = regExpFrc.cap(2).toUInt(&ok2);
796 unsigned int temp3 = regExpFrc.cap(3).toUInt(&ok3);
797 unsigned int temp4 = regExpFrc.cap(4).toUInt(&ok4);
798 unsigned int temp5 = regExpFrc.cap(5).toUInt(&ok5);
799 if(ok1) fSizeW = temp1;
800 if(ok2) fSizeH = temp2;
801 if(ok3) fpsNom = temp3;
802 if(ok4) fpsDen = temp4;
803 if(ok5) frames = temp5;
813 process.waitForFinished();
814 if(process.state() != QProcess::NotRunning)
817 process.waitForFinished(-1);
820 if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
822 if(!(bTimeout || bAborted))
824 log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
831 log(tr("\nFAILED TO DETERMINE AVS PROPERTIES !!!"));
837 if((fSizeW > 0) && (fSizeH > 0))
839 log(tr("Resolution: %1x%2").arg(QString::number(fSizeW), QString::number(fSizeH)));
841 if((fpsNom > 0) && (fpsDen > 0))
843 log(tr("Frame Rate: %1/%2").arg(QString::number(fpsNom), QString::number(fpsDen)));
845 if((fpsNom > 0) && (fpsDen == 0))
847 log(tr("Frame Rate: %1").arg(QString::number(fpsNom)));
851 log(tr("No. Frames: %1").arg(QString::number(frames)));
857 ///////////////////////////////////////////////////////////////////////////////
859 ///////////////////////////////////////////////////////////////////////////////
861 void EncodeThread::setStatus(JobStatus newStatus)
863 if(m_status != newStatus)
865 if((newStatus != JobStatus_Completed) && (newStatus != JobStatus_Failed) && (newStatus != JobStatus_Aborted) && (newStatus != JobStatus_Paused))
867 if(m_status != JobStatus_Paused) setProgress(0);
869 if(newStatus == JobStatus_Failed)
871 setDetails("The job has failed. See log for details!");
873 if(newStatus == JobStatus_Aborted)
875 setDetails("The job was aborted by the user!");
877 m_status = newStatus;
878 emit statusChanged(m_jobId, newStatus);
882 void EncodeThread::setProgress(unsigned int newProgress)
884 if(m_progress != newProgress)
886 m_progress = newProgress;
887 emit progressChanged(m_jobId, m_progress);
891 void EncodeThread::setDetails(const QString &text)
893 emit detailsChanged(m_jobId, text);
896 bool EncodeThread::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels)
898 QMutexLocker lock(&m_mutex_startProcess);
899 log(commandline2string(program, args) + "\n");
901 //Create a new job object, if not done yet
902 if(!m_handle_jobObject)
904 m_handle_jobObject = CreateJobObject(NULL, NULL);
905 if(m_handle_jobObject == INVALID_HANDLE_VALUE)
907 m_handle_jobObject = NULL;
909 if(m_handle_jobObject)
911 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo;
912 memset(&jobExtendedLimitInfo, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
913 jobExtendedLimitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
914 SetInformationJobObject(m_handle_jobObject, JobObjectExtendedLimitInformation, &jobExtendedLimitInfo, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
920 process.setProcessChannelMode(QProcess::MergedChannels);
921 process.setReadChannel(QProcess::StandardOutput);
925 process.setProcessChannelMode(QProcess::SeparateChannels);
926 process.setReadChannel(QProcess::StandardError);
929 process.start(program, args);
931 if(process.waitForStarted())
933 Q_PID pid = process.pid();
934 AssignProcessToJobObject(m_handle_jobObject, process.pid()->hProcess);
937 if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
939 SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
947 log("Process creation has failed :-(");
948 QString errorMsg= process.errorString().trimmed();
949 if(!errorMsg.isEmpty()) log(errorMsg);
952 process.waitForFinished(-1);
956 QString EncodeThread::commandline2string(const QString &program, const QStringList &arguments)
958 QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
960 for(int i = 0; i < arguments.count(); i++)
962 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));