OSDN Git Service

Updated encoder versions.
[x264-launcher/x264-launcher.git] / src / encoder_x264.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 "encoder_x264.h"
23
24 #include "model_options.h"
25 #include "model_status.h"
26 #include "binaries.h"
27
28 #include <QStringList>
29 #include <QDir>
30 #include <QRegExp>
31
32 //x264 version info
33 static const unsigned int X264_VERSION_X264_MINIMUM_REV = 2397;
34 static const unsigned int X264_VERSION_X264_CURRENT_API = 142;
35
36 // ------------------------------------------------------------
37 // Helper Macros
38 // ------------------------------------------------------------
39
40 #define REMOVE_CUSTOM_ARG(LIST, ITER, FLAG, PARAM) do \
41 { \
42         if(ITER != LIST.end()) \
43         { \
44                 if((*ITER).compare(PARAM, Qt::CaseInsensitive) == 0) \
45                 { \
46                         log(tr("WARNING: Custom parameter \"" PARAM "\" will be ignored in Pipe'd mode!\n")); \
47                         ITER = LIST.erase(ITER); \
48                         if(ITER != LIST.end()) \
49                         { \
50                                 if(!((*ITER).startsWith("--", Qt::CaseInsensitive))) ITER = LIST.erase(ITER); \
51                         } \
52                         FLAG = true; \
53                 } \
54         } \
55 } \
56 while(0)
57
58 #define X264_UPDATE_PROGRESS(X) do \
59 { \
60         bool ok = false; qint64 size_estimate = 0; \
61         unsigned int progress = (X)->cap(1).toUInt(&ok); \
62         setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running)); \
63         if(ok) \
64         { \
65                 setProgress(progress); \
66                 size_estimate = estimateSize(m_outputFile, progress); \
67         } \
68         setDetails(tr("%1, est. file size %2").arg(line.mid(offset).trimmed(), sizeToString(size_estimate))); \
69 } \
70 while(0)
71
72 static QString MAKE_NAME(const char *baseName, const OptionsModel *options)
73 {
74         const QString arch = (options->encArch() == OptionsModel::EncArch_x64) ? "x64" : "x86";
75         const QString vari = (options->encVariant() == OptionsModel::EncVariant_HiBit ) ? "10-Bit" : "8-Bit";
76         return QString("%1, %2, %3").arg(QString::fromLatin1(baseName), arch, vari);
77 }
78
79 // ------------------------------------------------------------
80 // Constructor & Destructor
81 // ------------------------------------------------------------
82
83 X264Encoder::X264Encoder(JobObject *jobObject, const OptionsModel *options, const SysinfoModel *const sysinfo, const PreferencesModel *const preferences, JobStatus &jobStatus, volatile bool *abort, volatile bool *pause, QSemaphore *semaphorePause, const QString &sourceFile, const QString &outputFile)
84 :
85         AbstractEncoder(jobObject, options, sysinfo, preferences, jobStatus, abort, pause, semaphorePause, sourceFile, outputFile),
86         m_encoderName(MAKE_NAME("x264 (H.264/AVC)", m_options)),
87         m_binaryFile(ENC_BINARY(sysinfo, options))
88 {
89         if(options->encType() != OptionsModel::EncType_X264)
90         {
91                 throw "Invalid encoder type!";
92         }
93 }
94
95 X264Encoder::~X264Encoder(void)
96 {
97         /*Nothing to do here*/
98 }
99
100 const QString &X264Encoder::getName(void)
101 {
102         return m_encoderName;
103 }
104
105 // ------------------------------------------------------------
106 // Check Version
107 // ------------------------------------------------------------
108
109 void X264Encoder::checkVersion_init(QList<QRegExp*> &patterns, QStringList &cmdLine)
110 {
111         cmdLine << "--version";
112         patterns << new QRegExp("\\bx264\\s(\\d)\\.(\\d+)\\.(\\d+)\\s([a-f0-9]{7})", Qt::CaseInsensitive);
113         patterns << new QRegExp("\\bx264 (\\d)\\.(\\d+)\\.(\\d+)", Qt::CaseInsensitive);
114 }
115
116 void X264Encoder::checkVersion_parseLine(const QString &line, QList<QRegExp*> &patterns, unsigned int &coreVers, unsigned int &revision, bool &modified)
117 {
118         int offset = -1;
119
120         if((offset = patterns[0]->lastIndexIn(line)) >= 0)
121         {
122                 bool ok1 = false, ok2 = false;
123                 unsigned int temp1 = patterns[0]->cap(2).toUInt(&ok1);
124                 unsigned int temp2 = patterns[0]->cap(3).toUInt(&ok2);
125                 if(ok1 && ok2)
126                 {
127                         coreVers = temp1;
128                         revision = temp2;
129                 }
130         }
131         else if((offset = patterns[1]->lastIndexIn(line)) >= 0)
132         {
133                 bool ok1 = false, ok2 = false;
134                 unsigned int temp1 = patterns[1]->cap(2).toUInt(&ok1);
135                 unsigned int temp2 = patterns[1]->cap(3).toUInt(&ok2);
136                 if(ok1 && ok2)
137                 {
138                         coreVers = temp1;
139                         revision = temp2;
140                 }
141                 modified = true;
142         }
143
144         if(!line.isEmpty())
145         {
146                 log(line);
147         }
148 }
149
150 QString X264Encoder::printVersion(const unsigned int &revision, const bool &modified)
151 {
152         return tr("x264 revision: %1 (core #%2)").arg(QString::number(revision % REV_MULT), QString::number(revision / REV_MULT)).append(modified ? tr(" - with custom patches!") : QString());
153 }
154
155 bool X264Encoder::isVersionSupported(const unsigned int &revision, const bool &modified)
156 {
157         if((revision % REV_MULT) < X264_VERSION_X264_MINIMUM_REV)
158         {
159                 log(tr("\nERROR: Your revision of x264 is too old! (Minimum required revision is %1)").arg(QString::number(X264_VERSION_X264_MINIMUM_REV)));
160                 return false;
161         }
162         
163         if((revision / REV_MULT) != X264_VERSION_X264_CURRENT_API)
164         {
165                 log(tr("\nWARNING: Your revision of x264 uses an unsupported core (API) version, take care!"));
166                 log(tr("This application works best with x264 core (API) version %2.").arg(QString::number(X264_VERSION_X264_CURRENT_API)));
167         }
168
169         return true;
170 }
171
172 // ------------------------------------------------------------
173 // Encoding Functions
174 // ------------------------------------------------------------
175
176 void X264Encoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, const unsigned int &frames, const QString &indexFile, const int &pass, const QString &passLogFile)
177 {
178         double crf_int = 0.0, crf_frc = 0.0;
179
180         switch(m_options->rcMode())
181         {
182         case OptionsModel::RCMode_CQ:
183                 cmdLine << "--qp" << QString::number(qRound(m_options->quantizer()));
184                 break;
185         case OptionsModel::RCMode_CRF:
186                 crf_frc = modf(m_options->quantizer(), &crf_int);
187                 cmdLine << "--crf" << QString("%1.%2").arg(QString::number(qRound(crf_int)), QString::number(qRound(crf_frc * 10.0)));
188                 break;
189         case OptionsModel::RCMode_2Pass:
190         case OptionsModel::RCMode_ABR:
191                 cmdLine << "--bitrate" << QString::number(m_options->bitrate());
192                 break;
193         default:
194                 throw "Bad rate-control mode !!!";
195                 break;
196         }
197         
198         if((pass == 1) || (pass == 2))
199         {
200                 cmdLine << "--pass" << QString::number(pass);
201                 cmdLine << "--stats" << QDir::toNativeSeparators(passLogFile);
202         }
203
204         cmdLine << "--preset" << m_options->preset().toLower();
205
206         if(m_options->tune().compare("none", Qt::CaseInsensitive))
207         {
208                 cmdLine << "--tune" << m_options->tune().toLower();
209         }
210
211         if(m_options->profile().compare("auto", Qt::CaseInsensitive) != 0)
212         {
213                 if((m_options->encType() == OptionsModel::EncType_X264) && (m_options->encVariant() == OptionsModel::EncVariant_LoBit))
214                 {
215                         cmdLine << "--profile" << m_options->profile().toLower();
216                 }
217         }
218
219         if(!m_options->customEncParams().isEmpty())
220         {
221                 QStringList customArgs = splitParams(m_options->customEncParams(), m_sourceFile, m_outputFile);
222                 if(usePipe)
223                 {
224                         QStringList::iterator i = customArgs.begin();
225                         while(i != customArgs.end())
226                         {
227                                 bool bModified = false;
228                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--fps");
229                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--frames");
230                                 if(!bModified) i++;
231                         }
232                 }
233                 cmdLine.append(customArgs);
234         }
235
236         cmdLine << "--output" << QDir::toNativeSeparators(m_outputFile);
237         
238         if(usePipe)
239         {
240                 if(frames < 1) throw "Frames not set!";
241                 cmdLine << "--frames" << QString::number(frames);
242                 cmdLine << "--demuxer" << "y4m";
243                 cmdLine << "--stdin" << "y4m" << "-";
244         }
245         else
246         {
247                 cmdLine << "--index" << QDir::toNativeSeparators(indexFile);
248                 cmdLine << QDir::toNativeSeparators(m_sourceFile);
249         }
250 }
251
252 void X264Encoder::runEncodingPass_init(QList<QRegExp*> &patterns)
253 {
254         patterns << new QRegExp("\\[(\\d+)\\.(\\d+)%\\].+frames");
255         patterns << new QRegExp("indexing.+\\[(\\d+)\\.(\\d+)%\\]");
256         patterns << new QRegExp("^(\\d+) frames:");
257         patterns << new QRegExp("\\[\\s*(\\d+)\\.(\\d+)%\\]\\s+(\\d+)/(\\d+)\\s(\\d+).(\\d+)\\s(\\d+).(\\d+)\\s+(\\d+):(\\d+):(\\d+)\\s+(\\d+):(\\d+):(\\d+)"); //regExpModified
258 }
259
260 void X264Encoder::runEncodingPass_parseLine(const QString &line, QList<QRegExp*> &patterns, const int &pass)
261 {
262         int offset = -1;
263         if((offset = patterns[0]->lastIndexIn(line)) >= 0)
264         {
265                 X264_UPDATE_PROGRESS(patterns[0]);
266         }
267         else if((offset = patterns[1]->lastIndexIn(line)) >= 0)
268         {
269                 bool ok = false;
270                 unsigned int progress = patterns[1]->cap(1).toUInt(&ok);
271                 setStatus(JobStatus_Indexing);
272                 if(ok)
273                 {
274                         setProgress(progress);
275                 }
276                 setDetails(line.mid(offset).trimmed());
277         }
278         else if((offset = patterns[2]->lastIndexIn(line)) >= 0)
279         {
280                 setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running));
281                 setDetails(line.mid(offset).trimmed());
282         }
283         else if((offset = patterns[3]->lastIndexIn(line)) >= 0)
284         {
285                 X264_UPDATE_PROGRESS(patterns[3]);
286         }
287         else if(!line.isEmpty())
288         {
289                 log(line);
290         }
291 }