2016-05-07 17:39:12 +02:00
///////////////////////////////////////////////////////////////////////////////
// Simple x264 Launcher
// Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
//
// 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
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
# include "encoder_nvenc.h"
//Internal
# include "global.h"
# include "model_options.h"
# include "model_status.h"
# include "mediainfo.h"
# include "model_sysinfo.h"
2016-05-08 18:33:48 +02:00
# include "model_clipInfo.h"
2016-05-07 17:39:12 +02:00
//MUtils
# include <MUtils/Exception.h>
//Qt
# include <QStringList>
# include <QDir>
# include <QRegExp>
2016-05-08 15:33:09 +02:00
# include <QPair>
2016-05-07 17:39:12 +02:00
//x265 version info
static const unsigned int VERSION_NVENCC_MINIMUM_VER = 206 ;
2016-05-08 18:33:48 +02:00
static const unsigned int VERSION_NVENCC_MINIMUM_API = 60 ;
2016-05-07 17:39:12 +02:00
// ------------------------------------------------------------
// Helper Macros
// ------------------------------------------------------------
# define NVENCC_UPDATE_PROGRESS(X) do \
{ \
bool ok = false ; \
unsigned int progressFrames = ( X ) - > cap ( 1 ) . toUInt ( & ok ) ; \
2016-05-08 18:33:48 +02:00
double progress = 0.0 ; \
2016-05-07 17:39:12 +02:00
setStatus ( JobStatus_Running ) ; \
2016-05-08 18:33:48 +02:00
if ( ok & & ( clipInfo . getFrameCount ( ) > 0 ) ) \
2016-05-07 17:39:12 +02:00
{ \
2016-05-08 18:33:48 +02:00
progress = ( double ( progressFrames ) / double ( clipInfo . getFrameCount ( ) ) ) ; \
2016-05-07 17:39:12 +02:00
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 ; \
} \
} \
2016-05-08 18:33:48 +02:00
setDetails ( tr ( " [%1] %2, est. file size %3 " ) . arg ( QString ( ) . sprintf ( " %.1f%% " , 100.0 * progress ) , line . mid ( offset ) . trimmed ( ) , sizeToString ( qRound64 ( size_estimate ) ) ) ) ; \
2016-05-07 17:39:12 +02:00
} \
while ( 0 )
# define REMOVE_CUSTOM_ARG(LIST, ITER, FLAG, PARAM) do \
{ \
if ( ITER ! = LIST . end ( ) ) \
{ \
if ( ( * ITER ) . compare ( PARAM , Qt : : CaseInsensitive ) = = 0 ) \
{ \
log ( tr ( " WARNING: Custom parameter \" " PARAM " \" will be ignored in Pipe'd mode! \n " ) ) ; \
ITER = LIST . erase ( ITER ) ; \
if ( ITER ! = LIST . end ( ) ) \
{ \
if ( ! ( ( * ITER ) . startsWith ( " -- " , Qt : : CaseInsensitive ) ) ) ITER = LIST . erase ( ITER ) ; \
} \
FLAG = true ; \
} \
} \
} \
while ( 0 )
// ------------------------------------------------------------
// Encoder Info
// ------------------------------------------------------------
class NVEncEncoderInfo : public AbstractEncoderInfo
{
public :
2016-05-08 15:33:09 +02:00
virtual QString getName ( void ) const
2016-05-07 17:39:12 +02:00
{
2016-05-08 15:33:09 +02:00
return " NVEncC " ;
}
2016-05-08 16:30:31 +02:00
virtual QList < ArchId > getArchitectures ( void ) const
2016-05-08 15:33:09 +02:00
{
2016-05-08 16:30:31 +02:00
return QList < ArchId > ( )
< < qMakePair ( QString ( " 32-Bit (x86) " ) , ARCH_TYPE_X86 )
< < qMakePair ( QString ( " 64-Bit (x64) " ) , ARCH_TYPE_X64 ) ;
2016-05-08 15:33:09 +02:00
}
virtual QStringList getVariants ( void ) const
{
return QStringList ( ) < < " AVC " < < " HEVC " ;
}
virtual QList < RCMode > getRCModes ( void ) const
{
return QList < RCMode > ( )
< < qMakePair ( QString ( " CQP " ) , RC_TYPE_QUANTIZER )
< < qMakePair ( QString ( " VBR " ) , RC_TYPE_RATE_KBPS )
< < qMakePair ( QString ( " VBR2 " ) , RC_TYPE_RATE_KBPS )
< < qMakePair ( QString ( " CBR " ) , RC_TYPE_RATE_KBPS ) ;
2016-05-07 17:39:12 +02:00
}
virtual QStringList getTunings ( void ) const
{
return QStringList ( ) ;
}
virtual QStringList getPresets ( void ) const
{
return QStringList ( ) ;
}
2016-05-08 15:33:09 +02:00
virtual QStringList getProfiles ( const quint32 & variant ) const
2016-05-07 17:39:12 +02:00
{
QStringList profiles ;
switch ( variant )
{
2016-05-08 15:33:09 +02:00
case 0 : profiles < < " baseline " < < " main " < < " high " ; break ;
case 1 : profiles < < " main " ; break ;
default : MUTILS_THROW ( " Unknown encoder variant! " ) ;
2016-05-07 17:39:12 +02:00
}
return profiles ;
}
virtual QStringList supportedOutputFormats ( void ) const
{
QStringList extLst ;
2016-05-08 20:56:17 +02:00
extLst < < " mp4 " < < " 264 " < < " hevc " ;
2016-05-07 17:39:12 +02:00
return extLst ;
}
virtual bool isInputTypeSupported ( const int format ) const
{
switch ( format )
{
case MediaInfo : : FILETYPE_YUV4MPEG2 :
return true ;
default :
return false ;
}
}
2016-05-08 15:33:09 +02:00
virtual QString getBinaryPath ( const SysinfoModel * sysinfo , const quint32 & encArch , const quint32 & encVariant ) const
2016-05-07 17:39:12 +02:00
{
2016-05-08 15:33:09 +02:00
QString arch ;
2016-05-07 17:39:12 +02:00
switch ( encArch )
{
2016-05-08 15:33:09 +02:00
case 0 : arch = " x86 " ; break ;
case 1 : arch = " x64 " ; break ;
2016-05-07 17:39:12 +02:00
default : MUTILS_THROW ( " Unknown encoder arch! " ) ;
}
switch ( encVariant )
{
2016-05-08 15:33:09 +02:00
case 0 : break ;
case 1 : break ;
default : MUTILS_THROW ( " Unknown encoder variant! " ) ;
2016-05-07 17:39:12 +02:00
}
return QString ( " %1/toolset/%2/nvencc_%2.exe " ) . arg ( sysinfo - > getAppPath ( ) , arch ) ;
}
2016-05-08 15:33:09 +02:00
virtual QStringList getDependencies ( const SysinfoModel * sysinfo , const quint32 & encArch , const quint32 & encVariant ) const
2016-05-07 17:39:12 +02:00
{
2016-05-08 15:33:09 +02:00
QString arch ;
2016-05-07 17:39:12 +02:00
switch ( encArch )
{
2016-05-08 15:33:09 +02:00
case 0 : arch = " x86 " ; break ;
case 1 : arch = " x64 " ; break ;
2016-05-07 17:39:12 +02:00
default : MUTILS_THROW ( " Unknown encoder arch! " ) ;
}
switch ( encVariant )
{
2016-05-08 15:33:09 +02:00
case 0 : break ;
case 1 : break ;
default : MUTILS_THROW ( " Unknown encoder variant! " ) ;
2016-05-07 17:39:12 +02:00
}
2016-05-08 15:33:09 +02:00
return QStringList ( )
< < QString ( " %1/toolset/%2/avcodec-57.dll " ) . arg ( sysinfo - > getAppPath ( ) , arch )
< < QString ( " %1/toolset/%2/avfilter-6.dll " ) . arg ( sysinfo - > getAppPath ( ) , arch )
< < QString ( " %1/toolset/%2/avformat-57.dll " ) . arg ( sysinfo - > getAppPath ( ) , arch )
< < QString ( " %1/toolset/%2/avutil-55.dll " ) . arg ( sysinfo - > getAppPath ( ) , arch )
< < QString ( " %1/toolset/%2/swresample-2.dll " ) . arg ( sysinfo - > getAppPath ( ) , arch ) ;
2016-05-07 17:39:12 +02:00
}
2016-05-08 22:51:10 +02:00
virtual QString getHelpCommand ( void ) const
{
return " --help " ;
}
2016-05-07 17:39:12 +02:00
} ;
2016-05-08 15:33:09 +02:00
static const NVEncEncoderInfo s_nvencEncoderInfo ;
2016-05-07 17:39:12 +02:00
2016-05-08 16:30:31 +02:00
const AbstractEncoderInfo & NVEncEncoder : : encoderInfo ( void )
2016-05-07 17:39:12 +02:00
{
2016-05-08 15:33:09 +02:00
return s_nvencEncoderInfo ;
2016-05-07 17:39:12 +02:00
}
2016-05-08 16:30:31 +02:00
const AbstractEncoderInfo & NVEncEncoder : : getEncoderInfo ( void ) const
{
return encoderInfo ( ) ;
}
2016-05-07 17:39:12 +02:00
// ------------------------------------------------------------
// Constructor & Destructor
// ------------------------------------------------------------
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 )
:
AbstractEncoder ( jobObject , options , sysinfo , preferences , jobStatus , abort , pause , semaphorePause , sourceFile , outputFile )
{
if ( options - > encType ( ) ! = OptionsModel : : EncType_NVEnc )
{
MUTILS_THROW ( " Invalid encoder type! " ) ;
}
}
NVEncEncoder : : ~ NVEncEncoder ( void )
{
/*Nothing to do here*/
}
QString NVEncEncoder : : getName ( void ) const
{
2016-05-08 15:33:09 +02:00
return s_nvencEncoderInfo . getFullName ( m_options - > encArch ( ) , m_options - > encVariant ( ) ) ;
2016-05-07 17:39:12 +02:00
}
// ------------------------------------------------------------
// Check Version
// ------------------------------------------------------------
void NVEncEncoder : : checkVersion_init ( QList < QRegExp * > & patterns , QStringList & cmdLine )
{
cmdLine < < " --version " ;
patterns < < new QRegExp ( " \\ bNVEncC \\ s+ \\ ( \\ w+ \\ ) \\ s+( \\ d) \\ .( \\ d+) \\ s+by \\ s+rigaya \\ s+ \\ [NVENC \\ s+API \\ s+v( \\ d+) \\ .( \\ d+) \\ ] " , Qt : : CaseInsensitive ) ;
}
void NVEncEncoder : : checkVersion_parseLine ( const QString & line , QList < QRegExp * > & patterns , unsigned int & core , unsigned int & build , bool & modified )
{
int offset = - 1 ;
if ( ( offset = patterns [ 0 ] - > lastIndexIn ( line ) ) > = 0 )
{
bool ok [ 4 ] = { false , false , false , false } ;
unsigned int temp [ 4 ] ;
temp [ 0 ] = patterns [ 0 ] - > cap ( 1 ) . toUInt ( & ok [ 0 ] ) ;
temp [ 1 ] = patterns [ 0 ] - > cap ( 2 ) . toUInt ( & ok [ 1 ] ) ;
temp [ 2 ] = patterns [ 0 ] - > cap ( 2 ) . toUInt ( & ok [ 2 ] ) ;
temp [ 3 ] = patterns [ 0 ] - > cap ( 2 ) . toUInt ( & ok [ 3 ] ) ;
if ( ok [ 0 ] & & ok [ 1 ] )
{
core = ( 100 * temp [ 0 ] ) + temp [ 1 ] ;
}
if ( ok [ 2 ] & & ok [ 3 ] )
{
build = ( 10 * temp [ 2 ] ) + temp [ 3 ] ;
}
}
if ( ! line . isEmpty ( ) )
{
log ( line ) ;
}
}
bool NVEncEncoder : : checkVersion_succeeded ( const int & exitCode )
{
return ( exitCode = = 0 ) | | ( exitCode = = 1 ) ;
}
QString NVEncEncoder : : printVersion ( const unsigned int & revision , const bool & modified )
{
unsigned int core , build ;
splitRevision ( revision , core , build ) ;
return tr ( " NVEncC version: %1.%2 " ) . arg ( QString : : number ( core / 100 ) , QString : : number ( core % 100 ) . leftJustified ( 2 , QLatin1Char ( ' 0 ' ) ) ) ;
}
bool NVEncEncoder : : isVersionSupported ( const unsigned int & revision , const bool & modified )
{
unsigned int core , build ;
splitRevision ( revision , core , build ) ;
if ( core < VERSION_NVENCC_MINIMUM_VER )
{
log ( tr ( " \n ERROR: 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 ) ) ) ;
return false ;
}
else if ( core > VERSION_NVENCC_MINIMUM_VER )
{
log ( tr ( " \n WARNING: Your version of NVEncC is newer than the latest tested version, take care! " ) ) ;
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 ) ) ) ;
}
if ( build < VERSION_NVENCC_MINIMUM_API )
{
log ( tr ( " \n ERROR: Your version of NVENC API is too old! (Minimum required version is %1.%2) " ) . arg ( QString : : number ( VERSION_NVENCC_MINIMUM_API / 10 ) , QString : : number ( VERSION_NVENCC_MINIMUM_API % 10 ) ) ) ;
return false ;
}
return true ;
}
// ------------------------------------------------------------
// Encoding Functions
// ------------------------------------------------------------
2016-05-08 18:33:48 +02:00
void NVEncEncoder : : buildCommandLine ( QStringList & cmdLine , const bool & usePipe , const ClipInfo & clipInfo , const QString & indexFile , const int & pass , const QString & passLogFile )
2016-05-07 17:39:12 +02:00
{
2016-05-08 15:33:09 +02:00
switch ( m_options - > encVariant ( ) )
{
case 0 :
cmdLine < < " --codec " < < " avc " ;
break ;
case 1 :
cmdLine < < " --codec " < < " hevc " ;
break ;
default :
MUTILS_THROW ( " Bad encoder variant !!! " ) ;
}
2016-05-07 17:39:12 +02:00
switch ( m_options - > rcMode ( ) )
{
2016-05-08 15:33:09 +02:00
case 0 :
cmdLine < < " --cqp " < < QString : : number ( qRound ( m_options - > quantizer ( ) ) ) ;
break ;
case 1 :
2016-05-07 17:39:12 +02:00
cmdLine < < " --vbr " < < QString : : number ( m_options - > bitrate ( ) ) ;
break ;
2016-05-08 15:33:09 +02:00
case 2 :
cmdLine < < " --vbr2 " < < QString : : number ( m_options - > bitrate ( ) ) ;
break ;
case 3 :
cmdLine < < " --cbr " < < QString : : number ( m_options - > bitrate ( ) ) ;
2016-05-07 17:39:12 +02:00
break ;
default :
MUTILS_THROW ( " Bad rate-control mode !!! " ) ;
}
const QString profile = m_options - > profile ( ) . simplified ( ) . toLower ( ) ;
if ( ! profile . isEmpty ( ) )
{
if ( profile . compare ( QString : : fromLatin1 ( OptionsModel : : PROFILE_UNRESTRICTED ) , Qt : : CaseInsensitive ) ! = 0 )
{
cmdLine < < " --profile " < < profile ;
}
}
if ( ! m_options - > customEncParams ( ) . isEmpty ( ) )
{
QStringList customArgs = splitParams ( m_options - > customEncParams ( ) , m_sourceFile , m_outputFile ) ;
if ( usePipe )
{
QStringList : : iterator i = customArgs . begin ( ) ;
while ( i ! = customArgs . end ( ) )
{
bool bModified = false ;
REMOVE_CUSTOM_ARG ( customArgs , i , bModified , " --fps " ) ;
REMOVE_CUSTOM_ARG ( customArgs , i , bModified , " --frames " ) ;
if ( ! bModified ) i + + ;
}
}
cmdLine . append ( customArgs ) ;
}
cmdLine < < " --output " < < QDir : : toNativeSeparators ( m_outputFile ) ;
if ( usePipe )
{
cmdLine < < " --y4m " < < " --input " < < " - " ;
}
else
{
cmdLine < < " --input " < < QDir : : toNativeSeparators ( m_sourceFile ) ;
}
}
void NVEncEncoder : : runEncodingPass_init ( QList < QRegExp * > & patterns )
{
2016-05-08 18:33:48 +02:00
patterns < < new QRegExp ( " ^( \\ d+) frames: " ) ;
patterns < < new QRegExp ( " Selected \\ s+codec \\ s+is \\ s+not \\ s+supported " , Qt : : CaseInsensitive ) ;
2016-05-08 21:52:46 +02:00
patterns < < new QRegExp ( " nvEncodeAPI.dll \\ s+does \\ s+not \\ s+exists \\ s+in \\ s+your \\ s+system " , Qt : : CaseInsensitive ) ;
2016-05-07 17:39:12 +02:00
}
2016-05-08 18:33:48 +02:00
void NVEncEncoder : : runEncodingPass_parseLine ( const QString & line , QList < QRegExp * > & patterns , const ClipInfo & clipInfo , const int & pass , double & last_progress , double & size_estimate )
2016-05-07 17:39:12 +02:00
{
int offset = - 1 ;
if ( ( offset = patterns [ 0 ] - > lastIndexIn ( line ) ) > = 0 )
{
NVENCC_UPDATE_PROGRESS ( patterns [ 0 ] ) ;
}
2016-05-08 18:33:48 +02:00
else if ( ( offset = patterns [ 1 ] - > lastIndexIn ( line ) ) > = 0 )
{
2016-05-08 21:52:46 +02:00
log ( QString ( " ERROR: YOUR HARDWARE DOES *NOT* SUPPORT THE '%1' CODEC !!! \n " ) . arg ( s_nvencEncoderInfo . variantToString ( m_options - > encVariant ( ) ) ) ) ;
}
else if ( ( offset = patterns [ 2 ] - > lastIndexIn ( line ) ) > = 0 )
{
log ( " ERROR: NVIDIA ENCODER API (NVENCODEAPI.DLL) IS *NOT* AVAILABLE !!! \n " ) ;
2016-05-08 18:33:48 +02:00
}
2016-05-07 17:39:12 +02:00
else if ( ! line . isEmpty ( ) )
{
log ( line ) ;
}
}