OSDN Git Service

Updated CA certificates file for cURL.
[x264-launcher/x264-launcher.git] / src / encoder_nvencc.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2021 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_nvencc.h"
23
24 //Internal
25 #include "global.h"
26 #include "model_options.h"
27 #include "model_status.h"
28 #include "mediainfo.h"
29 #include "model_sysinfo.h"
30 #include "model_clipInfo.h"
31
32 //MUtils
33 #include <MUtils/Global.h>
34 #include <MUtils/Exception.h>
35
36 //Qt
37 #include <QStringList>
38 #include <QDir>
39 #include <QRegExp>
40 #include <QPair>
41
42 //x265 version info
43 static const unsigned int VERSION_NVENCC_MINIMUM_VER = 605;
44
45 // ------------------------------------------------------------
46 // Helper Macros
47 // ------------------------------------------------------------
48
49 #define NVENCC_UPDATE_PROGRESS(X) do \
50 { \
51         bool ok[2] = { false, false }; \
52         unsigned int progressInt = (X)->cap(1).toUInt(&ok[0]); \
53         unsigned int progressFrc = (X)->cap(2).toUInt(&ok[1]); \
54         setStatus(JobStatus_Running); \
55         if(ok[0] && ok[1]) \
56         { \
57                 const double progress = (double(progressInt) / 100.0) + (double(progressFrc) / 1000.0); \
58                 if(!qFuzzyCompare(progress, last_progress)) \
59                 { \
60                         setProgress(floor(progress * 100.0)); \
61                         size_estimate = qFuzzyIsNull(size_estimate) ? estimateSize(m_outputFile, progress) : ((0.667 * size_estimate) + (0.333 * estimateSize(m_outputFile, progress))); \
62                         last_progress = progress; \
63                 } \
64         } \
65         setDetails(line.mid(offset).trimmed()); \
66 } \
67 while(0)
68
69 #define NVENCC_UPDATE_PROGRESS_OLD(X) do \
70 { \
71         bool ok = false; \
72         unsigned int progressFrames = (X)->cap(1).toUInt(&ok); \
73         double progress = 0.0; \
74         setStatus(JobStatus_Running); \
75         if(ok && (clipInfo.getFrameCount() > 0)) \
76         { \
77                 progress = (double(progressFrames) / double(clipInfo.getFrameCount())); \
78                 if(!qFuzzyCompare(progress, last_progress)) \
79                 { \
80                         setProgress(floor(progress * 100.0)); \
81                         size_estimate = qFuzzyIsNull(size_estimate) ? estimateSize(m_outputFile, progress) : ((0.667 * size_estimate) + (0.333 * estimateSize(m_outputFile, progress))); \
82                         last_progress = progress; \
83                 } \
84         } \
85         setDetails(tr("[%1] %2, est. file size %3").arg(QString().sprintf("%.1f%%", 100.0 * progress), line.mid(offset).trimmed(), sizeToString(qRound64(size_estimate)))); \
86 } \
87 while(0)
88
89 #define REMOVE_CUSTOM_ARG(LIST, ITER, FLAG, PARAM) do \
90 { \
91         if(ITER != LIST.end()) \
92         { \
93                 if((*ITER).compare(PARAM, Qt::CaseInsensitive) == 0) \
94                 { \
95                         log(tr("WARNING: Custom parameter \"" PARAM "\" will be ignored in Pipe'd mode!\n")); \
96                         ITER = LIST.erase(ITER); \
97                         if(ITER != LIST.end()) \
98                         { \
99                                 if(!((*ITER).startsWith("--", Qt::CaseInsensitive))) ITER = LIST.erase(ITER); \
100                         } \
101                         FLAG = true; \
102                 } \
103         } \
104 } \
105 while(0)
106
107 // ------------------------------------------------------------
108 // Encoder Info
109 // ------------------------------------------------------------
110
111 #define NVENCCC_PATH "%1/toolset/%2/nvencc/"
112
113 class NVEncEncoderInfo : public AbstractEncoderInfo
114 {
115 public:
116         virtual QString getName(void) const
117         {
118                 return "NVEncC";
119         }
120
121         virtual QList<ArchId> getArchitectures(void) const
122         {
123                 return QList<ArchId>()
124                 << qMakePair(QString("32-Bit (x86)"), ARCH_TYPE_X86)
125                 << qMakePair(QString("64-Bit (x64)"), ARCH_TYPE_X64);
126         }
127
128         virtual QStringList getVariants(void) const
129         {
130                 return QStringList() << "AVC" << "HEVC";
131         }
132
133         virtual QList<RCMode> getRCModes(void) const
134         {
135                 return QList<RCMode>()
136                 << qMakePair(QString("CQP"),  RC_TYPE_QUANTIZER)
137                 << qMakePair(QString("VBR"),  RC_TYPE_RATE_KBPS)
138                 << qMakePair(QString("VBR2"), RC_TYPE_RATE_KBPS)
139                 << qMakePair(QString("CBR"),  RC_TYPE_RATE_KBPS);
140         }
141
142         virtual QStringList getTunings(void) const
143         {
144                 return QStringList();
145         }
146
147         virtual QStringList getPresets(void) const
148         {
149                 return QStringList() << "performance" << "quality";
150         }
151
152         virtual QStringList getProfiles(const quint32 &variant) const
153         {
154                 QStringList profiles;
155                 switch(variant)
156                 {
157                         case 0: profiles << "baseline" << "main" << "high" << "high444"; break;
158                         case 1: profiles << "main" << "main10" << "main444";             break;
159                         default: MUTILS_THROW("Unknown encoder variant!");
160                 }
161                 return profiles;
162         }
163
164         virtual QStringList supportedOutputFormats(void) const
165         {
166                 QStringList extLst;
167                 extLst << "mp4" << "264" << "hevc";
168                 return extLst;
169         }
170
171         virtual bool isInputTypeSupported(const int format) const
172         {
173                 switch(format)
174                 {
175                 case MediaInfo::FILETYPE_AVISYNTH:
176                 case MediaInfo::FILETYPE_YUV4MPEG2:
177                 case MediaInfo::FILETYPE_UNKNOWN:
178                         return true;
179                 default:
180                         return false;
181                 }
182         }
183
184         virtual QString getBinaryPath(const SysinfoModel *sysinfo, const quint32 &encArch, const quint32 &encVariant) const
185         {
186                 QString arch, ext;
187                 switch(encArch)
188                 {
189                         case 0: arch = "x86";             break;
190                         case 1: arch = "x64"; ext = "64"; break;
191                         default: MUTILS_THROW("Unknown encoder arch!");
192                 }
193                 switch(encVariant)
194                 {
195                         case 0: break;
196                         case 1: break;
197                         default: MUTILS_THROW("Unknown encoder variant!");
198                 }
199                 return QString("%1/toolset/%2/nvencc/nvencc%3.exe").arg(sysinfo->getAppPath(), arch, ext);
200         }
201
202         virtual QStringList getDependencies(const SysinfoModel *sysinfo, const quint32 &encArch, const quint32 &encVariant) const
203         {
204                 QString arch;
205                 switch (encArch)
206                 {
207                         case 0: arch = "x86"; break;
208                         case 1: arch = "x64"; break;
209                         default: MUTILS_THROW("Unknown encoder arch!");
210                 }
211                 switch (encVariant)
212                 {
213                         case 0: break;
214                         case 1: break;
215                         default: MUTILS_THROW("Unknown encoder variant!");
216
217                 }
218                 QStringList dependencies;
219                 dependencies << QString(NVENCCC_PATH "avcodec-59.dll"  ).arg(sysinfo->getAppPath(), arch);
220                 dependencies << QString(NVENCCC_PATH "avfilter-8.dll"  ).arg(sysinfo->getAppPath(), arch);
221                 dependencies << QString(NVENCCC_PATH "avformat-59.dll" ).arg(sysinfo->getAppPath(), arch);
222                 dependencies << QString(NVENCCC_PATH "avutil-57.dll"   ).arg(sysinfo->getAppPath(), arch);
223                 dependencies << QString(NVENCCC_PATH "libass-9.dll"    ).arg(sysinfo->getAppPath(), arch);
224                 dependencies << QString(NVENCCC_PATH "swresample-4.dll").arg(sysinfo->getAppPath(), arch);
225                 if (encArch)
226                 {
227                         dependencies << QString(NVENCCC_PATH "libvmaf.dll"             ).arg(sysinfo->getAppPath(), arch);
228                         dependencies << QString(NVENCCC_PATH "nvrtc64_101_0.dll"       ).arg(sysinfo->getAppPath(), arch);
229                         dependencies << QString(NVENCCC_PATH "nvrtc-builtins64_101.dll").arg(sysinfo->getAppPath(), arch);
230                 }
231                 return dependencies;
232         }
233
234         virtual QString getHelpCommand(void) const
235         {
236                 return "--help";
237         }
238 };
239
240 static const NVEncEncoderInfo s_nvencEncoderInfo;
241
242 const AbstractEncoderInfo& NVEncEncoder::encoderInfo(void)
243 {
244         return s_nvencEncoderInfo;
245 }
246
247 const AbstractEncoderInfo &NVEncEncoder::getEncoderInfo(void) const
248 {
249         return encoderInfo();
250 }
251
252 // ------------------------------------------------------------
253 // Constructor & Destructor
254 // ------------------------------------------------------------
255
256 NVEncEncoder::NVEncEncoder(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)
257 :
258         AbstractEncoder(jobObject, options, sysinfo, preferences, jobStatus, abort, pause, semaphorePause, sourceFile, outputFile)
259 {
260         if(options->encType() != OptionsModel::EncType_NVEnc)
261         {
262                 MUTILS_THROW("Invalid encoder type!");
263         }
264 }
265
266 NVEncEncoder::~NVEncEncoder(void)
267 {
268         /*Nothing to do here*/
269 }
270
271 QString NVEncEncoder::getName(void) const
272 {
273         return s_nvencEncoderInfo.getFullName(m_options->encArch(), m_options->encVariant());
274 }
275
276 // ------------------------------------------------------------
277 // Check Version
278 // ------------------------------------------------------------
279
280 void NVEncEncoder::checkVersion_init(QList<QRegExp*> &patterns, QStringList &cmdLine)
281 {
282         cmdLine << "--version";
283         patterns << new QRegExp("\\bNVEncC\\s+\\(x\\d+\\)\\s+(\\d)\\.(\\d+)\\s+\\(r(\\d+)\\)", Qt::CaseInsensitive);
284 }
285
286 void NVEncEncoder::checkVersion_parseLine(const QString &line, const QList<QRegExp*> &patterns, unsigned int &core, unsigned int &build, bool &modified)
287 {
288         if(patterns[0]->lastIndexIn(line) >= 0)
289         {
290                 unsigned int temp[3];
291                 if(MUtils::regexp_parse_uint32(*patterns[0], temp, 3))
292                 {
293                         core = (100 * temp[0]) + temp[1];
294                         build = temp[2];
295                 }
296         }
297
298         if(!line.isEmpty())
299         {
300                 log(line);
301         }
302 }
303
304 bool NVEncEncoder::checkVersion_succeeded(const int &exitCode)
305 {
306         return (exitCode == 0) || (exitCode == 1);
307 }
308
309 QString NVEncEncoder::printVersion(const unsigned int &revision, const bool &modified)
310 {
311         unsigned int core, build;
312         splitRevision(revision, core, build);
313
314         return tr("NVEncC version: %1.%2 [rev #%3]").arg(QString::number(core / 100), QString::number(core % 100).leftJustified(2, QLatin1Char('0')), QString::number(build));
315 }
316
317 bool NVEncEncoder::isVersionSupported(const unsigned int &revision, const bool &modified)
318 {
319         unsigned int core, build;
320         splitRevision(revision, core, build);
321
322         if(core < VERSION_NVENCC_MINIMUM_VER)
323         {
324                 log(tr("\nERROR: Your version of NVEncC is too old! (Minimum required version is %1.%2)").arg(QString::number(VERSION_NVENCC_MINIMUM_VER / 100), QString::number(VERSION_NVENCC_MINIMUM_VER % 100)));
325                 return false;
326         }
327         else if(core > VERSION_NVENCC_MINIMUM_VER)
328         {
329                 log(tr("\nWARNING: Your version of NVEncC is newer than the latest tested version, take care!"));
330                 log(tr("This application works best with NVEncC version %1.%2. Newer versions may work or not.").arg(QString::number(VERSION_NVENCC_MINIMUM_VER / 100), QString::number(VERSION_NVENCC_MINIMUM_VER % 100)));
331         }
332
333         return true;
334 }
335
336 // ------------------------------------------------------------
337 // Encoding Functions
338 // ------------------------------------------------------------
339
340 void NVEncEncoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, const ClipInfo &clipInfo, const QString &indexFile, const int &pass, const QString &passLogFile)
341 {
342         switch (m_options->encVariant())
343         {
344         case 0:
345                 cmdLine << "--codec" << "avc";
346                 break;
347         case 1:
348                 cmdLine << "--codec" << "hevc";
349                 break;
350         default:
351                 MUTILS_THROW("Bad encoder variant !!!");
352         }
353
354         const QString preset = m_options->preset().simplified().toLower();
355         if(!preset.isEmpty())
356         {
357                 if(preset.compare(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED), Qt::CaseInsensitive) != 0)
358                 {
359                         cmdLine << "--preset" << preset;
360                 }
361         }
362
363         switch(m_options->rcMode())
364         {
365         case 0:
366                 cmdLine << "--cqp" << QString::number(qRound(m_options->quantizer()));
367                 break;
368         case 1:
369                 cmdLine << "--vbr" << QString::number(m_options->bitrate());
370                 break;
371         case 2:
372                 cmdLine << "--vbr2" << QString::number(m_options->bitrate());
373                 break;
374         case 3:
375                 cmdLine << "--cbr" << QString::number(m_options->bitrate());
376                 break;
377         default:
378                 MUTILS_THROW("Bad rate-control mode !!!");
379         }
380         
381         const QString profile = m_options->profile().simplified().toLower();
382         if(!profile.isEmpty())
383         {
384                 if(profile.compare(QString::fromLatin1(OptionsModel::PROFILE_UNRESTRICTED), Qt::CaseInsensitive) != 0)
385                 {
386                         cmdLine << "--profile" << profile;
387                 }
388         }
389
390         if(!m_options->customEncParams().isEmpty())
391         {
392                 QStringList customArgs = splitParams(m_options->customEncParams(), m_sourceFile, m_outputFile);
393                 if(usePipe)
394                 {
395                         QStringList::iterator i = customArgs.begin();
396                         while(i != customArgs.end())
397                         {
398                                 bool bModified = false;
399                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--fps");
400                                 REMOVE_CUSTOM_ARG(customArgs, i, bModified, "--frames");
401                                 if(!bModified) i++;
402                         }
403                 }
404                 cmdLine.append(customArgs);
405         }
406
407         cmdLine << "--output" << QDir::toNativeSeparators(m_outputFile);
408         
409         if(usePipe)
410         {
411                 cmdLine << "--y4m" << "--input" << "-";
412         }
413         else
414         {
415                 cmdLine << "--input" << QDir::toNativeSeparators(m_sourceFile);
416         }
417 }
418
419 void NVEncEncoder::runEncodingPass_init(QList<QRegExp*> &patterns)
420 {
421         patterns << new QRegExp("\\[(\\d+)\\.(\\d+)%\\].+frames", Qt::CaseInsensitive);
422         patterns << new QRegExp("^(\\d+) frames:", Qt::CaseInsensitive);
423         patterns << new QRegExp("Selected\\s+codec\\s+is\\s+not\\s+supported", Qt::CaseInsensitive);
424         patterns << new QRegExp("nvEncodeAPI.dll\\s+does\\s+not\\s+exists\\s+in\\s+your\\s+system", Qt::CaseInsensitive);
425 }
426
427 void NVEncEncoder::runEncodingPass_parseLine(const QString &line, const QList<QRegExp*> &patterns, const ClipInfo &clipInfo, const int &pass, double &last_progress, double &size_estimate)
428 {
429         int offset = -1;
430         if((offset = patterns[0]->lastIndexIn(line)) >= 0)
431         {
432                 NVENCC_UPDATE_PROGRESS(patterns[0]);
433         }
434         else if ((offset = patterns[1]->lastIndexIn(line)) >= 0)
435         {
436                 NVENCC_UPDATE_PROGRESS_OLD(patterns[1]);
437         }
438         else if ((offset = patterns[2]->lastIndexIn(line)) >= 0)
439         {
440                 log(QString("ERROR: YOUR HARDWARE DOES *NOT* SUPPORT THE '%1' CODEC !!!\n").arg(s_nvencEncoderInfo.variantToString(m_options->encVariant())));
441         }
442         else if ((offset = patterns[3]->lastIndexIn(line)) >= 0)
443         {
444                 log("ERROR: NVIDIA ENCODER API (NVENCODEAPI.DLL) IS *NOT* AVAILABLE !!!\n");
445         }
446         else if(!line.isEmpty())
447         {
448                 log(line);
449         }
450 }