OSDN Git Service

Implemented rudimental encoding support.
[x264-launcher/x264-launcher.git] / src / thread_encode.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2012 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
27 #include <QDate>
28 #include <QTime>
29 #include <QProcess>
30 #include <QMutex>
31 #include <QLibrary>
32
33 /*
34  * Win32 API definitions
35  */
36 typedef HANDLE (WINAPI *CreateJobObjectFun)(__in_opt LPSECURITY_ATTRIBUTES lpJobAttributes, __in_opt LPCSTR lpName);
37 typedef BOOL (WINAPI *SetInformationJobObjectFun)(__in HANDLE hJob, __in JOBOBJECTINFOCLASS JobObjectInformationClass, __in_bcount(cbJobObjectInformationLength) LPVOID lpJobObjectInformation, __in DWORD cbJobObjectInformationLength);
38 typedef BOOL (WINAPI *AssignProcessToJobObjectFun)(__in HANDLE hJob, __in HANDLE hProcess);
39
40 /*
41  * Static vars
42  */
43 QMutex EncodeThread::m_mutex_startProcess;
44 HANDLE EncodeThread::m_handle_jobObject = NULL;
45
46 ///////////////////////////////////////////////////////////////////////////////
47 // Constructor & Destructor
48 ///////////////////////////////////////////////////////////////////////////////
49
50 EncodeThread::EncodeThread(const QString &sourceFileName, const QString &outputFileName, const OptionsModel *options, const QString &binDir)
51 :
52         m_jobId(QUuid::createUuid()),
53         m_sourceFileName(sourceFileName),
54         m_outputFileName(outputFileName),
55         m_options(new OptionsModel(*options)),
56         m_binDir(binDir)
57 {
58         m_abort = false;
59 }
60
61 EncodeThread::~EncodeThread(void)
62 {
63         X264_DELETE(m_options);
64 }
65
66 ///////////////////////////////////////////////////////////////////////////////
67 // Thread entry point
68 ///////////////////////////////////////////////////////////////////////////////
69
70 void EncodeThread::run(void)
71 {
72         try
73         {
74                 encode();
75         }
76         catch(char *msg)
77         {
78                 emit messageLogged(m_jobId, QString("EXCEPTION ERROR: ").append(QString::fromLatin1(msg)));
79         }
80         catch(...)
81         {
82                 emit messageLogged(m_jobId, QString("EXCEPTION ERROR !!!"));
83         }
84 }
85
86 ///////////////////////////////////////////////////////////////////////////////
87 // Encode functions
88 ///////////////////////////////////////////////////////////////////////////////
89
90 void EncodeThread::encode(void)
91 {
92         Sleep(500);
93
94         //Print some basic info
95         log(tr("Job started at %1, %2.\n").arg(QDate::currentDate().toString(Qt::ISODate), QTime::currentTime().toString( Qt::ISODate)));
96         log(tr("Source file: %1").arg(m_sourceFileName));
97         log(tr("Output file: %1").arg(m_outputFileName));
98         log(tr("\n[Encoder Options]"));
99         log(tr("RC Mode: %1").arg(OptionsModel::rcMode2String(m_options->rcMode())));
100         log(tr("Preset: %1").arg(m_options->preset()));
101         log(tr("Tuning: %1").arg(m_options->tune()));
102         log(tr("Profile: %1").arg(m_options->profile()));
103         log(tr("Custom: %1").arg(m_options->custom().isEmpty() ? tr("(None)") : m_options->custom()));
104         
105         //Detect source info
106         log(tr("\n[Input Properties]"));
107         log(tr("Not implemented yet, sorry ;-)\n"));
108
109         QStringList cmdLine;
110         QProcess process;
111
112         cmdLine = buildCommandLine();
113
114         log("Creating process:");
115         if(!startProcess(process, QString("%1/x264.exe").arg(m_binDir), cmdLine))
116         {
117                 emit statusChanged(m_jobId, JobStatus_Failed);
118                 return;
119         }
120
121         emit statusChanged(m_jobId, JobStatus_Running);
122         QRegExp regExp("\\[(\\d+)\\.\\d+%\\].+frames");
123         
124         bool bTimeout = false;
125         bool bAborted = false;
126
127         while(process.state() != QProcess::NotRunning)
128         {
129                 if(m_abort)
130                 {
131                         process.kill();
132                         bAborted = true;
133                         log("\nABORTED BY USER !!!");
134                         break;
135                 }
136                 process.waitForReadyRead(m_processTimeoutInterval);
137                 if(!process.bytesAvailable() && process.state() == QProcess::Running)
138                 {
139                         process.kill();
140                         qWarning("x264 process timed out <-- killing!");
141                         log("\nPROCESS TIMEOUT !!!");
142                         bTimeout = true;
143                         break;
144                 }
145                 while(process.bytesAvailable() > 0)
146                 {
147                         QByteArray line = process.readLine();
148                         QString text = QString::fromUtf8(line.constData()).simplified();
149                         if(regExp.lastIndexIn(text) >= 0)
150                         {
151                                 bool ok = false;
152                                 unsigned int progress = regExp.cap(1).toUInt(&ok);
153                                 if(ok)
154                                 {
155                                         emit progressChanged(m_jobId, progress);
156                                         emit detailsChanged(m_jobId, line);
157                                 }
158                         }
159                         else if(!text.isEmpty())
160                         {
161                                 log(text);
162                         }
163                 }
164         }
165
166         process.waitForFinished();
167         if(process.state() != QProcess::NotRunning)
168         {
169                 process.kill();
170                 process.waitForFinished(-1);
171         }
172
173         if(bTimeout || bAborted || process.exitCode() != EXIT_SUCCESS)
174         {
175                 emit statusChanged(m_jobId, JobStatus_Failed);
176                 return;
177         }
178         
179         emit progressChanged(m_jobId, 100);
180         emit statusChanged(m_jobId, JobStatus_Completed);
181 }
182
183 QStringList EncodeThread::buildCommandLine(void)
184 {
185         QStringList cmdLine;
186
187         cmdLine << "--crf" << QString::number(m_options->quantizer());
188         
189         if(m_options->tune().compare("none", Qt::CaseInsensitive))
190         {
191                 cmdLine << "--tune" << m_options->tune().toLower();
192         }
193         
194         cmdLine << "--preset" << m_options->preset().toLower();
195         cmdLine << "--output" << m_outputFileName;
196         cmdLine << m_sourceFileName;
197
198         return cmdLine;
199 }
200
201 ///////////////////////////////////////////////////////////////////////////////
202 // Misc functions
203 ///////////////////////////////////////////////////////////////////////////////
204
205 bool EncodeThread::startProcess(QProcess &process, const QString &program, const QStringList &args)
206 {
207         static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr = NULL;
208         
209         QMutexLocker lock(&m_mutex_startProcess);
210         log(commandline2string(program, args) + "\n");
211         
212         if(!AssignProcessToJobObjectPtr)
213         {
214                 QLibrary Kernel32Lib("kernel32.dll");
215                 AssignProcessToJobObjectPtr = (AssignProcessToJobObjectFun) Kernel32Lib.resolve("AssignProcessToJobObject");
216         }
217         
218         process.setProcessChannelMode(QProcess::MergedChannels);
219         process.setReadChannel(QProcess::StandardOutput);
220         process.start(program, args);
221         
222         if(process.waitForStarted())
223         {
224                 
225                 if(AssignProcessToJobObjectPtr)
226                 {
227                         AssignProcessToJobObjectPtr(m_handle_jobObject, process.pid()->hProcess);
228                 }
229                 if(!SetPriorityClass(process.pid()->hProcess, BELOW_NORMAL_PRIORITY_CLASS))
230                 {
231                         SetPriorityClass(process.pid()->hProcess, IDLE_PRIORITY_CLASS);
232                 }
233                 
234                 lock.unlock();
235                 return true;
236         }
237
238         log("Process creation has failed :-(");
239         QString errorMsg= process.errorString().trimmed();
240         if(!errorMsg.isEmpty()) log(errorMsg);
241
242         process.kill();
243         process.waitForFinished(-1);
244         return false;
245 }
246
247 QString EncodeThread::commandline2string(const QString &program, const QStringList &arguments)
248 {
249         QString commandline = (program.contains(' ') ? QString("\"%1\"").arg(program) : program);
250         
251         for(int i = 0; i < arguments.count(); i++)
252         {
253                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
254         }
255
256         return commandline;
257 }