X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fencoder_x265.cpp;h=3bd209d8869de459b2e674af8ef2bc5960afbe3b;hb=ea40bc5f1c9ddc31624b1a755ed3805d8d2fe183;hp=64767fed97fcc4a48ebb5e870f987096ee04817c;hpb=38e7dbae56338623d5eb7942c5a13fe63961961e;p=x264-launcher%2Fx264-launcher.git diff --git a/src/encoder_x265.cpp b/src/encoder_x265.cpp index 64767fe..3bd209d 100644 --- a/src/encoder_x265.cpp +++ b/src/encoder_x265.cpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // Simple x264 Launcher -// Copyright (C) 2004-2014 LoRd_MuldeR +// Copyright (C) 2004-2018 LoRd_MuldeR // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,30 +21,49 @@ #include "encoder_x265.h" +//Internal +#include "global.h" #include "model_options.h" #include "model_status.h" -#include "binaries.h" -#include "binaries.h" +#include "mediainfo.h" +#include "model_sysinfo.h" +#include "model_clipInfo.h" +//MUtils +#include +#include + +//Qt #include #include #include +#include //x265 version info -static const unsigned int X265_VERSION_X264_MINIMUM_VER = 7; -static const unsigned int X265_VERSION_X264_MINIMUM_REV = 167; +static const unsigned int VERSION_X265_MINIMUM_VER = 29; +static const unsigned int VERSION_X265_MINIMUM_REV = 1; + +// ------------------------------------------------------------ +// Helper Macros +// ------------------------------------------------------------ -#define X264_UPDATE_PROGRESS(X) do \ +#define X265_UPDATE_PROGRESS(X) do \ { \ - bool ok = false; qint64 size_estimate = 0; \ - unsigned int progress = (X)->cap(1).toUInt(&ok); \ + bool ok[2] = { false, false }; \ + unsigned int progressInt = (X)->cap(1).toUInt(&ok[0]); \ + unsigned int progressFrc = (X)->cap(2).toUInt(&ok[1]); \ setStatus((pass == 2) ? JobStatus_Running_Pass2 : ((pass == 1) ? JobStatus_Running_Pass1 : JobStatus_Running)); \ - if(ok) \ + if(ok[0] && ok[1]) \ { \ - setProgress(progress); \ - size_estimate = estimateSize(m_outputFile, progress); \ + const double progress = (double(progressInt) / 100.0) + (double(progressFrc) / 1000.0); \ + if(!qFuzzyCompare(progress, last_progress)) \ + { \ + setProgress(floor(progress * 100.0)); \ + size_estimate = qFuzzyIsNull(size_estimate) ? estimateSize(m_outputFile, progress) : ((0.667 * size_estimate) + (0.333 * estimateSize(m_outputFile, progress))); \ + last_progress = progress; \ + } \ } \ - setDetails(tr("%1, est. file size %2").arg(line.mid(offset).trimmed(), sizeToString(size_estimate))); \ + setDetails(tr("%1, est. file size %2").arg(line.mid(offset).trimmed(), sizeToString(qRound64(size_estimate)))); \ } \ while(0) @@ -66,14 +85,124 @@ while(0) } \ while(0) +// ------------------------------------------------------------ +// Encoder Info +// ------------------------------------------------------------ + +class X265EncoderInfo : public AbstractEncoderInfo +{ +public: + virtual QString getName(void) const + { + return "x265 (HEVC/H.265)"; + } + + virtual QList getArchitectures(void) const + { + return QList() + << qMakePair(QString("32-Bit (x86)"), ARCH_TYPE_X86) + << qMakePair(QString("64-Bit (x64)"), ARCH_TYPE_X64); + } + + virtual QStringList getVariants(void) const + { + return QStringList() << "8-Bit" << "10-Bit" << "12-Bit"; + } + virtual QList getRCModes(void) const + { + return QList() + << qMakePair(QString("CRF"), RC_TYPE_QUANTIZER) + << qMakePair(QString("CQ"), RC_TYPE_QUANTIZER) + << qMakePair(QString("2-Pass"), RC_TYPE_MULTIPASS) + << qMakePair(QString("ABR"), RC_TYPE_RATE_KBPS); + } + + virtual QStringList getTunings(void) const + { + return QStringList() << "Grain" << "PSNR" << "SSIM" << "FastDecode" << "ZeroLatency"; + } + + virtual QStringList getPresets(void) const + { + return QStringList() + << "ultrafast" << "superfast" << "veryfast" << "faster" << "fast" + << "medium" << "slow" << "slower" << "veryslow" << "placebo"; + } + + virtual QStringList getProfiles(const quint32 &variant) const + { + QStringList profiles; + switch(variant) + { + case 0: profiles << "main" << "main-intra" << "mainstillpicture" << "main444-8" << "main444-intra" << "main444-stillpicture"; break; + case 1: profiles << "main10" << "main10-intra" << "main422-10" << "main422-10-intra" << "main444-10" << "main444-10-intra"; break; + case 2: profiles << "main12" << "main12-intra" << "main422-12" << "main422-12-intra" << "main444-12" << "main444-12-intra"; break; + default: MUTILS_THROW("Unknown encoder variant!"); + } + return profiles; + } + + virtual QStringList supportedOutputFormats(void) const + { + return QStringList() << "hevc"; + } + + virtual bool isInputTypeSupported(const int format) const + { + switch(format) + { + case MediaInfo::FILETYPE_YUV4MPEG2: + return true; + default: + return false; + } + } + + virtual QString getBinaryPath(const SysinfoModel *sysinfo, const quint32 &encArch, const quint32 &encVariant) const + { + QString arch; + switch(encArch) + { + case 0: arch = "x86"; break; + case 1: arch = "x64"; break; + default: MUTILS_THROW("Unknown encoder arch!"); + } + if((encVariant < 0) || (encVariant > 2)) + { + MUTILS_THROW("Unknown encoder variant!"); + } + return QString("%1/toolset/%2/x265_%2.exe").arg(sysinfo->getAppPath(), arch); + } + + virtual QString getHelpCommand(void) const + { + return "--fullhelp"; + } +}; + +static const X265EncoderInfo s_x265EncoderInfo; + +const AbstractEncoderInfo& X265Encoder::encoderInfo(void) +{ + return s_x265EncoderInfo; +} + +const AbstractEncoderInfo &X265Encoder::getEncoderInfo(void) const +{ + return encoderInfo(); +} + +// ------------------------------------------------------------ +// Constructor & Destructor +// ------------------------------------------------------------ + X265Encoder::X265Encoder(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) : - AbstractEncoder(jobObject, options, sysinfo, preferences, jobStatus, abort, pause, semaphorePause, sourceFile, outputFile), - m_binaryFile(ENC_BINARY(sysinfo, options)) + AbstractEncoder(jobObject, options, sysinfo, preferences, jobStatus, abort, pause, semaphorePause, sourceFile, outputFile) { if(options->encType() != OptionsModel::EncType_X265) { - throw "Invalid encoder type!"; + MUTILS_THROW("Invalid encoder type!"); } } @@ -82,63 +211,117 @@ X265Encoder::~X265Encoder(void) /*Nothing to do here*/ } +QString X265Encoder::getName(void) const +{ + return s_x265EncoderInfo.getFullName(m_options->encArch(), m_options->encVariant()); +} + +// ------------------------------------------------------------ +// Check Version +// ------------------------------------------------------------ + void X265Encoder::checkVersion_init(QList &patterns, QStringList &cmdLine) { cmdLine << "--version"; - patterns << new QRegExp("\\bHEVC\\s+encoder\\s+version\\s+0\\.(\\d+)\\+(\\d+)-[a-f0-9]+\\b", Qt::CaseInsensitive); + patterns << new QRegExp("\\bHEVC\\s+encoder\\s+version\\s+(\\d)\\.(\\d+)\\+(\\d+)\\b", Qt::CaseInsensitive); + patterns << new QRegExp("\\bHEVC\\s+encoder\\s+version\\s+(\\d)\\.(\\d+)\\b", Qt::CaseInsensitive); } -void X265Encoder::checkVersion_parseLine(const QString &line, QList &patterns, unsigned int &coreVers, unsigned int &revision, bool &modified) +void X265Encoder::checkVersion_parseLine(const QString &line, const QList &patterns, unsigned int &core, unsigned int &build, bool &modified) { int offset = -1; + if((offset = patterns[0]->lastIndexIn(line)) >= 0) { - bool ok1 = false, ok2 = false; - unsigned int temp1 = patterns[0]->cap(1).toUInt(&ok1); - unsigned int temp2 = patterns[0]->cap(2).toUInt(&ok2); - if(ok1) coreVers = temp1; - if(ok2) revision = temp2; + unsigned int temp[3]; + if(MUtils::regexp_parse_uint32(*patterns[0], temp, 3)) + { + core = (10 * temp[0]) + temp[1]; + build = temp[2]; + } + } + else if((offset = patterns[1]->lastIndexIn(line)) >= 0) + { + unsigned int temp[2]; + if (MUtils::regexp_parse_uint32(*patterns[0], temp, 2)) + { + core = (10 * temp[0]) + temp[1]; + } + build = 0; + } + + if(!line.isEmpty()) + { + log(line); } } -void X265Encoder::printVersion(const unsigned int &revision, const bool &modified) +QString X265Encoder::printVersion(const unsigned int &revision, const bool &modified) { - log(tr("\nx265 version: 0.%1+%2").arg(QString::number(revision / REV_MULT), QString::number(revision % REV_MULT))); + unsigned int core, build; + splitRevision(revision, core, build); + + return tr("x265 version: %1.%2+%3").arg(QString::number(core / 10), QString::number(core % 10), QString::number(build)); } bool X265Encoder::isVersionSupported(const unsigned int &revision, const bool &modified) { - const unsigned int ver = (revision / REV_MULT); - const unsigned int rev = (revision % REV_MULT); + unsigned int core, build; + splitRevision(revision, core, build); - if((ver < X265_VERSION_X264_MINIMUM_VER) || (rev < X265_VERSION_X264_MINIMUM_REV)) + if((core < VERSION_X265_MINIMUM_VER) || ((core == VERSION_X265_MINIMUM_VER) && (build < VERSION_X265_MINIMUM_REV))) { - log(tr("\nERROR: Your version of x265 is too old! (Minimum required revision is 0.%1+%2)").arg(QString::number(X265_VERSION_X264_MINIMUM_VER), QString::number(X265_VERSION_X264_MINIMUM_REV))); + log(tr("\nERROR: Your version of x265 is too old! (Minimum required revision is %1.%2+%3)").arg(QString::number(VERSION_X265_MINIMUM_VER / 10), QString::number(VERSION_X265_MINIMUM_VER % 10), QString::number(VERSION_X265_MINIMUM_REV))); return false; } + else if(core > VERSION_X265_MINIMUM_VER) + { + log(tr("\nWARNING: Your version of x265 is newer than the latest tested version, take care!")); + log(tr("This application works best with x265 version %1.%2. Newer versions may work or not.").arg(QString::number(VERSION_X265_MINIMUM_VER / 10), QString::number(VERSION_X265_MINIMUM_VER % 10))); + } return true; } -void X265Encoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, const unsigned int &frames, const QString &indexFile, const int &pass, const QString &passLogFile) +// ------------------------------------------------------------ +// Encoding Functions +// ------------------------------------------------------------ + +void X265Encoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, const ClipInfo &clipInfo, const QString &indexFile, const int &pass, const QString &passLogFile) { double crf_int = 0.0, crf_frc = 0.0; - switch(m_options->rcMode()) + cmdLine << "-D"; + switch (m_options->encVariant()) { - case OptionsModel::RCMode_CQ: - cmdLine << "--qp" << QString::number(qRound(m_options->quantizer())); + case 0: + cmdLine << QString::number(8); break; - case OptionsModel::RCMode_CRF: + case 1: + cmdLine << QString::number(10); + break; + case 2: + cmdLine << QString::number(12); + break; + default: + MUTILS_THROW("Unknown encoder variant!"); + } + + switch(m_options->rcMode()) + { + case 0: crf_frc = modf(m_options->quantizer(), &crf_int); cmdLine << "--crf" << QString("%1.%2").arg(QString::number(qRound(crf_int)), QString::number(qRound(crf_frc * 10.0))); break; - case OptionsModel::RCMode_2Pass: - case OptionsModel::RCMode_ABR: + case 1: + cmdLine << "--qp" << QString::number(qRound(m_options->quantizer())); + break; + case 2: + case 3: cmdLine << "--bitrate" << QString::number(m_options->bitrate()); break; default: - throw "Bad rate-control mode !!!"; + MUTILS_THROW("Bad rate-control mode !!!"); break; } @@ -148,18 +331,30 @@ void X265Encoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, co cmdLine << "--stats" << QDir::toNativeSeparators(passLogFile); } - cmdLine << "--preset" << m_options->preset().toLower(); + const QString preset = m_options->preset().simplified().toLower(); + if(!preset.isEmpty()) + { + if(preset.compare(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED), Qt::CaseInsensitive) != 0) + { + cmdLine << "--preset" << preset; + } + } - if(m_options->tune().compare("none", Qt::CaseInsensitive)) + const QString tune = m_options->tune().simplified().toLower(); + if(!tune.isEmpty()) { - cmdLine << "--tune" << m_options->tune().toLower(); + if(tune.compare(QString::fromLatin1(OptionsModel::SETTING_UNSPECIFIED), Qt::CaseInsensitive) != 0) + { + cmdLine << "--tune" << tune; + } } - if(m_options->profile().compare("auto", Qt::CaseInsensitive) != 0) + const QString profile = m_options->profile().simplified().toLower(); + if(!profile.isEmpty()) { - if((m_options->encType() == OptionsModel::EncType_X264) && (m_options->encVariant() == OptionsModel::EncVariant_LoBit)) + if(profile.compare(QString::fromLatin1(OptionsModel::PROFILE_UNRESTRICTED), Qt::CaseInsensitive) != 0) { - cmdLine << "--profile" << m_options->profile().toLower(); + cmdLine << "--profile" << profile; } } @@ -184,14 +379,15 @@ void X265Encoder::buildCommandLine(QStringList &cmdLine, const bool &usePipe, co if(usePipe) { - if(frames < 1) throw "Frames not set!"; - cmdLine << "--frames" << QString::number(frames); - cmdLine << "--demuxer" << "y4m"; - cmdLine << "--stdin" << "y4m" << "-"; + if (clipInfo.getFrameCount() < 1) + { + MUTILS_THROW("Frames not set!"); + } + cmdLine << "--frames" << QString::number(clipInfo.getFrameCount()); + cmdLine << "--y4m" << "-"; } else { - cmdLine << "--index" << QDir::toNativeSeparators(indexFile); cmdLine << QDir::toNativeSeparators(m_sourceFile); } } @@ -204,12 +400,12 @@ void X265Encoder::runEncodingPass_init(QList &patterns) patterns << new QRegExp("\\[\\s*(\\d+)\\.(\\d+)%\\]\\s+(\\d+)/(\\d+)\\s(\\d+).(\\d+)\\s(\\d+).(\\d+)\\s+(\\d+):(\\d+):(\\d+)\\s+(\\d+):(\\d+):(\\d+)"); //regExpModified } -void X265Encoder::runEncodingPass_parseLine(const QString &line, QList &patterns, const int &pass) +void X265Encoder::runEncodingPass_parseLine(const QString &line, const QList &patterns, const ClipInfo &clipInfo, const int &pass, double &last_progress, double &size_estimate) { int offset = -1; if((offset = patterns[0]->lastIndexIn(line)) >= 0) { - X264_UPDATE_PROGRESS(patterns[0]); + X265_UPDATE_PROGRESS(patterns[0]); } else if((offset = patterns[1]->lastIndexIn(line)) >= 0) { @@ -229,7 +425,7 @@ void X265Encoder::runEncodingPass_parseLine(const QString &line, QList } else if((offset = patterns[3]->lastIndexIn(line)) >= 0) { - X264_UPDATE_PROGRESS(patterns[3]); + X265_UPDATE_PROGRESS(patterns[3]); } else if(!line.isEmpty()) {