2012-01-28 18:55:40 +01:00
///////////////////////////////////////////////////////////////////////////////
// Simple x264 Launcher
// Copyright (C) 2004-2012 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 "thread_encode.h"
2012-01-29 19:14:46 +01:00
2012-01-28 19:59:04 +01:00
# include "global.h"
2012-01-29 19:14:46 +01:00
# include "model_options.h"
2012-01-30 17:50:19 +01:00
# include "version.h"
2012-01-29 19:14:46 +01:00
# include <QDate>
# include <QTime>
2012-01-31 00:13:32 +01:00
# include <QDateTime>
2012-01-30 04:58:42 +01:00
# include <QFileInfo>
# include <QDir>
2012-01-29 21:31:09 +01:00
# include <QProcess>
# include <QMutex>
# include <QLibrary>
2012-01-28 18:55:40 +01:00
2012-01-29 21:31:09 +01:00
/*
* Win32 API definitions
*/
typedef HANDLE ( WINAPI * CreateJobObjectFun ) ( __in_opt LPSECURITY_ATTRIBUTES lpJobAttributes , __in_opt LPCSTR lpName ) ;
typedef BOOL ( WINAPI * SetInformationJobObjectFun ) ( __in HANDLE hJob , __in JOBOBJECTINFOCLASS JobObjectInformationClass , __in_bcount ( cbJobObjectInformationLength ) LPVOID lpJobObjectInformation , __in DWORD cbJobObjectInformationLength ) ;
typedef BOOL ( WINAPI * AssignProcessToJobObjectFun ) ( __in HANDLE hJob , __in HANDLE hProcess ) ;
/*
* Static vars
*/
QMutex EncodeThread : : m_mutex_startProcess ;
2012-01-30 17:50:19 +01:00
/*
* Macros
*/
# define CHECK_STATUS(ABORT_FLAG, OK_FLAG) \
{ \
if ( ABORT_FLAG ) \
{ \
log ( " \n PROCESS ABORTED BY USER !!! " ) ; \
setStatus ( JobStatus_Aborted ) ; \
return ; \
} \
else if ( ! ( OK_FLAG ) ) \
{ \
setStatus ( JobStatus_Failed ) ; \
return ; \
} \
}
/*
* Static vars
*/
static const unsigned int REV_MULT = 10000 ;
2012-01-29 21:31:09 +01:00
///////////////////////////////////////////////////////////////////////////////
// Constructor & Destructor
///////////////////////////////////////////////////////////////////////////////
2012-01-30 17:50:19 +01:00
EncodeThread : : EncodeThread ( const QString & sourceFileName , const QString & outputFileName , const OptionsModel * options , const QString & binDir , bool x64 )
2012-01-28 18:55:40 +01:00
:
2012-01-29 15:57:23 +01:00
m_jobId ( QUuid : : createUuid ( ) ) ,
m_sourceFileName ( sourceFileName ) ,
2012-01-29 19:14:46 +01:00
m_outputFileName ( outputFileName ) ,
2012-01-29 21:31:09 +01:00
m_options ( new OptionsModel ( * options ) ) ,
2012-01-30 17:50:19 +01:00
m_binDir ( binDir ) ,
2012-01-31 00:13:32 +01:00
m_x64 ( x64 ) ,
m_handle_jobObject ( NULL )
2012-01-28 18:55:40 +01:00
{
2012-01-28 23:24:41 +01:00
m_abort = false ;
2012-01-28 18:55:40 +01:00
}
EncodeThread : : ~ EncodeThread ( void )
{
2012-01-29 19:14:46 +01:00
X264_DELETE ( m_options ) ;
2012-01-31 00:13:32 +01:00
if ( m_handle_jobObject )
{
CloseHandle ( m_handle_jobObject ) ;
m_handle_jobObject = NULL ;
}
2012-01-28 18:55:40 +01:00
}
///////////////////////////////////////////////////////////////////////////////
// Thread entry point
///////////////////////////////////////////////////////////////////////////////
void EncodeThread : : run ( void )
{
2012-01-31 00:13:32 +01:00
m_progress = 0 ;
m_status = JobStatus_Starting ;
2012-01-28 19:59:04 +01:00
try
{
encode ( ) ;
}
catch ( char * msg )
{
2012-01-30 04:58:42 +01:00
log ( tr ( " EXCEPTION ERROR: " ) . append ( QString : : fromLatin1 ( msg ) ) ) ;
2012-01-28 19:59:04 +01:00
}
catch ( . . . )
{
2012-01-30 04:58:42 +01:00
log ( tr ( " EXCEPTION ERROR !!! " ) ) ;
2012-01-28 19:59:04 +01:00
}
2012-01-31 00:13:32 +01:00
if ( m_handle_jobObject )
{
CloseHandle ( m_handle_jobObject ) ;
m_handle_jobObject = NULL ;
}
2012-01-28 19:59:04 +01:00
}
2012-01-29 21:31:09 +01:00
///////////////////////////////////////////////////////////////////////////////
// Encode functions
///////////////////////////////////////////////////////////////////////////////
2012-01-28 19:59:04 +01:00
void EncodeThread : : encode ( void )
{
2012-01-31 00:13:32 +01:00
QDateTime startTime = QDateTime : : currentDateTime ( ) ;
2012-01-29 19:14:46 +01:00
//Print some basic info
log ( tr ( " Job started at %1, %2. \n " ) . arg ( QDate : : currentDate ( ) . toString ( Qt : : ISODate ) , QTime : : currentTime ( ) . toString ( Qt : : ISODate ) ) ) ;
log ( tr ( " Source file: %1 " ) . arg ( m_sourceFileName ) ) ;
log ( tr ( " Output file: %1 " ) . arg ( m_outputFileName ) ) ;
2012-01-31 00:13:32 +01:00
//Print encoder settings
log ( tr ( " \n --- SETTINGS --- \n " ) ) ;
2012-01-29 19:14:46 +01:00
log ( tr ( " RC Mode: %1 " ) . arg ( OptionsModel : : rcMode2String ( m_options - > rcMode ( ) ) ) ) ;
2012-01-31 00:13:32 +01:00
log ( tr ( " Preset: %1 " ) . arg ( m_options - > preset ( ) ) ) ;
log ( tr ( " Tuning: %1 " ) . arg ( m_options - > tune ( ) ) ) ;
2012-01-29 19:14:46 +01:00
log ( tr ( " Profile: %1 " ) . arg ( m_options - > profile ( ) ) ) ;
2012-01-31 00:13:32 +01:00
log ( tr ( " Custom: %1 " ) . arg ( m_options - > custom ( ) . isEmpty ( ) ? tr ( " (None) " ) : m_options - > custom ( ) ) ) ;
2012-01-29 21:31:09 +01:00
2012-01-30 04:58:42 +01:00
bool ok = false ;
2012-01-31 00:13:32 +01:00
unsigned int frames = 0 ;
//Detect source info
2012-01-31 15:51:13 +01:00
bool usePipe = ( QFileInfo ( m_sourceFileName ) . suffix ( ) . compare ( " avs " , Qt : : CaseInsensitive ) = = 0 ) ;
2012-01-31 00:13:32 +01:00
if ( usePipe )
{
log ( tr ( " \n --- AVS INFO --- \n " ) ) ;
ok = checkProperties ( frames ) ;
CHECK_STATUS ( m_abort , ok ) ;
}
2012-01-30 04:58:42 +01:00
2012-01-30 17:50:19 +01:00
//Checking version
2012-01-31 00:13:32 +01:00
log ( tr ( " \n --- X264 VERSION --- \n " ) ) ;
2012-01-30 17:50:19 +01:00
unsigned int revision ;
ok = ( ( revision = checkVersion ( m_x64 ) ) ! = UINT_MAX ) ;
CHECK_STATUS ( m_abort , ok ) ;
//Is revision supported?
log ( tr ( " \n x264 revision: %1 (core #%2) " ) . arg ( QString : : number ( revision % REV_MULT ) , QString : : number ( revision / REV_MULT ) ) ) ;
if ( ( revision % REV_MULT ) < VER_X264_MINIMUM_REV )
{
log ( tr ( " \n ERROR: Your revision of x264 is too old! (Minimum required revision is %2) " ) . arg ( QString : : number ( VER_X264_MINIMUM_REV ) ) ) ;
setStatus ( JobStatus_Failed ) ;
return ;
}
if ( ( revision / REV_MULT ) ! = VER_X264_CURRENT_API )
{
log ( tr ( " \n WARNING: Your revision of x264 uses an unsupported core (API) version, take care! " ) ) ;
log ( tr ( " This application works best with x264 core (API) version %2. " ) . arg ( QString : : number ( VER_X264_CURRENT_API ) ) ) ;
}
2012-01-31 00:13:32 +01:00
2012-01-30 04:58:42 +01:00
//Run encoding passes
if ( m_options - > rcMode ( ) = = OptionsModel : : RCMode_2Pass )
{
QFileInfo info ( m_outputFileName ) ;
QString passLogFile = QString ( " %1/%2.stats " ) . arg ( info . path ( ) , info . completeBaseName ( ) ) ;
if ( QFileInfo ( passLogFile ) . exists ( ) )
{
int n = 2 ;
while ( QFileInfo ( passLogFile ) . exists ( ) )
{
passLogFile = QString ( " %1/%2.%3.stats " ) . arg ( info . path ( ) , info . completeBaseName ( ) , QString : : number ( n + + ) ) ;
}
}
2012-01-30 17:50:19 +01:00
log ( tr ( " \n --- PASS 1 --- \n " ) ) ;
2012-01-31 00:13:32 +01:00
ok = runEncodingPass ( m_x64 , usePipe , frames , 1 , passLogFile ) ;
2012-01-30 17:50:19 +01:00
CHECK_STATUS ( m_abort , ok ) ;
2012-01-30 04:58:42 +01:00
2012-01-30 17:50:19 +01:00
log ( tr ( " \n --- PASS 2 --- \n " ) ) ;
2012-01-31 00:13:32 +01:00
ok = runEncodingPass ( m_x64 , usePipe , frames , 2 , passLogFile ) ;
2012-01-30 17:50:19 +01:00
CHECK_STATUS ( m_abort , ok ) ;
2012-01-30 04:58:42 +01:00
}
else
{
2012-01-30 17:50:19 +01:00
log ( tr ( " \n --- ENCODING --- \n " ) ) ;
2012-01-31 00:13:32 +01:00
ok = runEncodingPass ( m_x64 , usePipe , frames ) ;
2012-01-30 17:50:19 +01:00
CHECK_STATUS ( m_abort , ok ) ;
2012-01-30 04:58:42 +01:00
}
2012-01-30 17:50:19 +01:00
log ( tr ( " \n --- DONE --- \n " ) ) ;
2012-01-31 00:13:32 +01:00
int timePassed = startTime . secsTo ( QDateTime : : currentDateTime ( ) ) ;
log ( tr ( " Job finished at %1, %2. Process took %3 minutes, %4 seconds. " ) . arg ( QDate : : currentDate ( ) . toString ( Qt : : ISODate ) , QTime : : currentTime ( ) . toString ( Qt : : ISODate ) , QString : : number ( timePassed / 60 ) , QString : : number ( timePassed % 60 ) ) ) ;
2012-01-30 04:58:42 +01:00
setStatus ( JobStatus_Completed ) ;
}
2012-01-31 00:13:32 +01:00
bool EncodeThread : : runEncodingPass ( bool x64 , bool usePipe , unsigned int frames , int pass , const QString & passLogFile )
2012-01-30 04:58:42 +01:00
{
2012-01-31 15:15:15 +01:00
QProcess processEncode , processAvisynth ;
2012-01-31 00:13:32 +01:00
if ( usePipe )
{
2012-01-31 15:15:15 +01:00
QStringList cmdLine_Avisynth ;
cmdLine_Avisynth < < QDir : : toNativeSeparators ( m_sourceFileName ) ;
cmdLine_Avisynth < < " - " ;
processAvisynth . setStandardOutputProcess ( & processEncode ) ;
log ( " Creating Avisynth process: " ) ;
if ( ! startProcess ( processAvisynth , QString ( " %1/avs2yuv.exe " ) . arg ( m_binDir ) , cmdLine_Avisynth , false ) )
{
return false ;
}
2012-01-31 00:13:32 +01:00
}
2012-01-31 15:15:15 +01:00
QStringList cmdLine_Encode = buildCommandLine ( usePipe , frames , pass , passLogFile ) ;
2012-01-29 19:14:46 +01:00
2012-01-31 15:15:15 +01:00
log ( " Creating x264 process: " ) ;
if ( ! startProcess ( processEncode , QString ( " %1/%2.exe " ) . arg ( m_binDir , x64 ? " x264_x64 " : " x264 " ) , cmdLine_Encode ) )
2012-01-28 19:59:04 +01:00
{
2012-01-31 15:15:15 +01:00
return false ;
2012-01-29 21:31:09 +01:00
}
2012-01-30 04:58:42 +01:00
QRegExp regExpIndexing ( " indexing.+ \\ [( \\ d+) \ \ . \ \ d + % \ \ ] " ) ;
QRegExp regExpProgress ( " \\ [( \\ d+) \ \ . \ \ d + % \ \ ] . + frames " ) ;
2012-01-31 00:13:32 +01:00
QRegExp regExpFrameCnt ( " ^( \\ d+) frames : " ) ;
2012-01-28 23:24:41 +01:00
2012-01-29 21:31:09 +01:00
bool bTimeout = false ;
bool bAborted = false ;
2012-01-28 23:24:41 +01:00
2012-01-31 15:15:15 +01:00
while ( processEncode . state ( ) ! = QProcess : : NotRunning )
2012-01-29 21:31:09 +01:00
{
2012-01-28 23:24:41 +01:00
if ( m_abort )
{
2012-01-31 15:15:15 +01:00
processEncode . kill ( ) ;
processAvisynth . kill ( ) ;
2012-01-29 21:31:09 +01:00
bAborted = true ;
break ;
}
2012-01-31 15:15:15 +01:00
if ( ! processEncode . waitForReadyRead ( m_processTimeoutInterval ) )
2012-01-29 21:31:09 +01:00
{
2012-01-31 15:15:15 +01:00
if ( processEncode . state ( ) = = QProcess : : Running )
2012-01-30 04:58:42 +01:00
{
2012-01-31 15:15:15 +01:00
processEncode . kill ( ) ;
2012-01-30 04:58:42 +01:00
qWarning ( " x264 process timed out <-- killing! " ) ;
log ( " \n PROCESS TIMEOUT !!! " ) ;
bTimeout = true ;
break ;
}
2012-01-28 23:24:41 +01:00
}
2012-01-31 15:15:15 +01:00
while ( processEncode . bytesAvailable ( ) > 0 )
2012-01-29 21:31:09 +01:00
{
2012-01-31 15:15:15 +01:00
QList < QByteArray > lines = processEncode . readLine ( ) . split ( ' \r ' ) ;
2012-01-30 04:58:42 +01:00
while ( ! lines . isEmpty ( ) )
2012-01-29 21:31:09 +01:00
{
2012-01-30 04:58:42 +01:00
QString text = QString : : fromUtf8 ( lines . takeFirst ( ) . constData ( ) ) . simplified ( ) ;
int offset = - 1 ;
if ( ( offset = regExpProgress . lastIndexIn ( text ) ) > = 0 )
2012-01-29 21:31:09 +01:00
{
2012-01-30 04:58:42 +01:00
bool ok = false ;
unsigned int progress = regExpProgress . cap ( 1 ) . toUInt ( & ok ) ;
setStatus ( ( pass = = 2 ) ? JobStatus_Running_Pass2 : ( ( pass = = 1 ) ? JobStatus_Running_Pass1 : JobStatus_Running ) ) ;
setDetails ( text . mid ( offset ) . trimmed ( ) ) ;
2012-01-30 17:50:19 +01:00
if ( ok ) setProgress ( progress ) ;
2012-01-30 04:58:42 +01:00
}
else if ( ( offset = regExpIndexing . lastIndexIn ( text ) ) > = 0 )
{
bool ok = false ;
unsigned int progress = regExpIndexing . cap ( 1 ) . toUInt ( & ok ) ;
setStatus ( JobStatus_Indexing ) ;
setDetails ( text . mid ( offset ) . trimmed ( ) ) ;
2012-01-30 17:50:19 +01:00
if ( ok ) setProgress ( progress ) ;
2012-01-30 04:58:42 +01:00
}
2012-01-31 00:13:32 +01:00
else if ( ( offset = regExpFrameCnt . lastIndexIn ( text ) ) > = 0 )
{
setStatus ( ( pass = = 2 ) ? JobStatus_Running_Pass2 : ( ( pass = = 1 ) ? JobStatus_Running_Pass1 : JobStatus_Running ) ) ;
setDetails ( text . mid ( offset ) . trimmed ( ) ) ;
}
2012-01-30 04:58:42 +01:00
else if ( ! text . isEmpty ( ) )
{
log ( text ) ;
2012-01-29 21:31:09 +01:00
}
}
}
}
2012-01-31 15:15:15 +01:00
processEncode . waitForFinished ( 5000 ) ;
if ( processEncode . state ( ) ! = QProcess : : NotRunning )
2012-01-29 21:31:09 +01:00
{
2012-01-31 15:15:15 +01:00
qWarning ( " x264 process still running, going to kill it! " ) ;
processEncode . kill ( ) ;
processEncode . waitForFinished ( - 1 ) ;
}
processAvisynth . waitForFinished ( 5000 ) ;
if ( processAvisynth . state ( ) ! = QProcess : : NotRunning )
{
qWarning ( " Avisynth process still running, going to kill it! " ) ;
processAvisynth . kill ( ) ;
processAvisynth . waitForFinished ( - 1 ) ;
2012-01-29 21:31:09 +01:00
}
2012-01-31 15:15:15 +01:00
while ( processAvisynth . bytesAvailable ( ) > 0 )
{
log ( tr ( " av2y [info]: %1 " ) . arg ( QString : : fromUtf8 ( processAvisynth . readLine ( ) ) . simplified ( ) ) ) ;
}
if ( usePipe & & ( processAvisynth . exitCode ( ) ! = EXIT_SUCCESS ) )
2012-01-29 21:31:09 +01:00
{
2012-01-31 00:13:32 +01:00
if ( ! ( bTimeout | | bAborted ) )
{
2012-01-31 15:15:15 +01:00
log ( tr ( " \n WARNING: Avisynth process exited with error code: %1 " ) . arg ( QString : : number ( processAvisynth . exitCode ( ) ) ) ) ;
}
}
if ( bTimeout | | bAborted | | processEncode . exitCode ( ) ! = EXIT_SUCCESS )
{
if ( ! ( bTimeout | | bAborted ) )
{
log ( tr ( " \n PROCESS EXITED WITH ERROR CODE: %1 " ) . arg ( QString : : number ( processEncode . exitCode ( ) ) ) ) ;
2012-01-31 00:13:32 +01:00
}
2012-01-31 15:15:15 +01:00
processEncode . close ( ) ;
processAvisynth . close ( ) ;
2012-01-30 04:58:42 +01:00
return false ;
2012-01-29 21:31:09 +01:00
}
2012-01-31 15:15:15 +01:00
2012-01-31 15:51:13 +01:00
switch ( pass )
{
case 1 :
setStatus ( JobStatus_Running_Pass1 ) ;
setDetails ( tr ( " First pass completed. Preparing for second pass... " ) ) ;
break ;
case 2 :
setStatus ( JobStatus_Running_Pass2 ) ;
setDetails ( tr ( " Second pass completed successfully. " ) ) ;
break ;
default :
setStatus ( JobStatus_Running ) ;
setDetails ( tr ( " Encode completed successfully. " ) ) ;
break ;
}
2012-01-30 04:58:42 +01:00
setProgress ( 100 ) ;
2012-01-31 15:15:15 +01:00
processEncode . close ( ) ;
processAvisynth . close ( ) ;
2012-01-30 04:58:42 +01:00
return true ;
2012-01-29 21:31:09 +01:00
}
2012-01-31 00:13:32 +01:00
QStringList EncodeThread : : buildCommandLine ( bool usePipe , unsigned int frames , int pass , const QString & passLogFile )
2012-01-29 21:31:09 +01:00
{
QStringList cmdLine ;
2012-01-30 04:58:42 +01:00
switch ( m_options - > rcMode ( ) )
{
case OptionsModel : : RCMode_CRF :
cmdLine < < " --crf " < < QString : : number ( m_options - > quantizer ( ) ) ;
break ;
case OptionsModel : : RCMode_CQ :
cmdLine < < " --qp " < < QString : : number ( m_options - > quantizer ( ) ) ;
break ;
case OptionsModel : : RCMode_2Pass :
case OptionsModel : : RCMode_ABR :
cmdLine < < " --bitrate " < < QString : : number ( m_options - > bitrate ( ) ) ;
break ;
default :
throw " Bad rate-control mode !!! " ;
break ;
}
2012-01-29 21:31:09 +01:00
2012-01-30 04:58:42 +01:00
if ( ( pass = = 1 ) | | ( pass = = 2 ) )
{
cmdLine < < " --pass " < < QString : : number ( pass ) ;
cmdLine < < " --stats " < < QDir : : toNativeSeparators ( passLogFile ) ;
}
2012-01-29 21:31:09 +01:00
if ( m_options - > tune ( ) . compare ( " none " , Qt : : CaseInsensitive ) )
{
cmdLine < < " --tune " < < m_options - > tune ( ) . toLower ( ) ;
2012-01-28 19:59:04 +01:00
}
2012-01-29 21:31:09 +01:00
cmdLine < < " --preset " < < m_options - > preset ( ) . toLower ( ) ;
2012-01-30 04:58:42 +01:00
if ( ! m_options - > custom ( ) . isEmpty ( ) )
{
//FIXME: Handle custom parameters that contain spaces!
cmdLine . append ( m_options - > custom ( ) . split ( " " ) ) ;
}
cmdLine < < " --output " < < QDir : : toNativeSeparators ( m_outputFileName ) ;
2012-01-31 00:13:32 +01:00
if ( usePipe )
{
2012-01-31 15:15:15 +01:00
if ( frames < 1 ) throw " Frames not set! " ;
2012-01-31 00:13:32 +01:00
cmdLine < < " --frames " < < QString : : number ( frames ) ;
cmdLine < < " --demuxer " < < " y4m " ;
cmdLine < < " --stdin " < < " y4m " < < " - " ;
}
else
{
cmdLine < < QDir : : toNativeSeparators ( m_sourceFileName ) ;
}
2012-01-28 19:59:04 +01:00
2012-01-29 21:31:09 +01:00
return cmdLine ;
}
2012-01-28 19:59:04 +01:00
2012-01-30 17:50:19 +01:00
unsigned int EncodeThread : : checkVersion ( bool x64 )
{
QProcess process ;
QStringList cmdLine = QStringList ( ) < < " --version " ;
log ( " Creating process: " ) ;
if ( ! startProcess ( process , QString ( " %1/%2.exe " ) . arg ( m_binDir , x64 ? " x264_x64 " : " x264 " ) , cmdLine ) )
{
return false ; ;
}
QRegExp regExpVersion ( " x264 ( \\ d) \ \ . ( \ \ d + ) \ \ . ( \ \ d + ) ( [ 0 - 9 A - Fa - f ] { 7 } ) " );
bool bTimeout = false ;
bool bAborted = false ;
unsigned int revision = UINT_MAX ;
unsigned int coreVers = UINT_MAX ;
while ( process . state ( ) ! = QProcess : : NotRunning )
{
if ( m_abort )
{
process . kill ( ) ;
bAborted = true ;
break ;
}
if ( ! process . waitForReadyRead ( m_processTimeoutInterval ) )
{
if ( process . state ( ) = = QProcess : : Running )
{
process . kill ( ) ;
qWarning ( " x264 process timed out <-- killing! " ) ;
log ( " \n PROCESS TIMEOUT !!! " ) ;
bTimeout = true ;
break ;
}
}
while ( process . bytesAvailable ( ) > 0 )
{
QList < QByteArray > lines = process . readLine ( ) . split ( ' \r ' ) ;
while ( ! lines . isEmpty ( ) )
{
QString text = QString : : fromUtf8 ( lines . takeFirst ( ) . constData ( ) ) . simplified ( ) ;
int offset = - 1 ;
if ( ( offset = regExpVersion . lastIndexIn ( text ) ) > = 0 )
{
bool ok1 = false , ok2 = false ;
unsigned int temp1 = regExpVersion . cap ( 2 ) . toUInt ( & ok1 ) ;
unsigned int temp2 = regExpVersion . cap ( 3 ) . toUInt ( & ok2 ) ;
if ( ok1 ) coreVers = temp1 ;
if ( ok2 ) revision = temp2 ;
}
if ( ! text . isEmpty ( ) )
{
log ( text ) ;
}
}
}
}
process . waitForFinished ( ) ;
if ( process . state ( ) ! = QProcess : : NotRunning )
{
process . kill ( ) ;
process . waitForFinished ( - 1 ) ;
}
if ( bTimeout | | bAborted | | process . exitCode ( ) ! = EXIT_SUCCESS )
{
2012-01-31 00:13:32 +01:00
if ( ! ( bTimeout | | bAborted ) )
{
log ( tr ( " \n PROCESS EXITED WITH ERROR CODE: %1 " ) . arg ( QString : : number ( process . exitCode ( ) ) ) ) ;
}
2012-01-30 17:50:19 +01:00
return UINT_MAX ;
}
if ( ( revision = = UINT_MAX ) | | ( coreVers = = UINT_MAX ) )
{
log ( tr ( " \n FAILED TO DETERMINE X264 VERSION !!! " ) ) ;
return UINT_MAX ;
}
return ( coreVers * REV_MULT ) + revision ;
}
2012-01-31 00:13:32 +01:00
bool EncodeThread : : checkProperties ( unsigned int & frames )
{
QProcess process ;
QStringList cmdLine = QStringList ( ) < < " -frames " < < " 1 " ;
cmdLine < < QDir : : toNativeSeparators ( m_sourceFileName ) < < " NUL " ;
log ( " Creating process: " ) ;
if ( ! startProcess ( process , QString ( " %1/avs2yuv.exe " ) . arg ( m_binDir ) , cmdLine ) )
{
return false ; ;
}
QRegExp regExpInt ( " : ( \\ d+) x ( \ \ d + ) , ( \ \ d + ) fps , ( \ \ d + ) frames " ) ;
QRegExp regExpFrc ( " : ( \\ d+) x ( \ \ d + ) , ( \ \ d + ) / ( \ \ d + ) fps , ( \ \ d + ) frames " ) ;
bool bTimeout = false ;
bool bAborted = false ;
frames = 0 ;
unsigned int fpsNom = 0 ;
unsigned int fpsDen = 0 ;
unsigned int fSizeW = 0 ;
unsigned int fSizeH = 0 ;
while ( process . state ( ) ! = QProcess : : NotRunning )
{
if ( m_abort )
{
process . kill ( ) ;
bAborted = true ;
break ;
}
if ( ! process . waitForReadyRead ( m_processTimeoutInterval ) )
{
if ( process . state ( ) = = QProcess : : Running )
{
process . kill ( ) ;
qWarning ( " x264 process timed out <-- killing! " ) ;
log ( " \n PROCESS TIMEOUT !!! " ) ;
bTimeout = true ;
break ;
}
}
while ( process . bytesAvailable ( ) > 0 )
{
QList < QByteArray > lines = process . readLine ( ) . split ( ' \r ' ) ;
while ( ! lines . isEmpty ( ) )
{
QString text = QString : : fromUtf8 ( lines . takeFirst ( ) . constData ( ) ) . simplified ( ) ;
int offset = - 1 ;
if ( ( offset = regExpInt . lastIndexIn ( text ) ) > = 0 )
{
bool ok1 = false , ok2 = false ;
bool ok3 = false , ok4 = false ;
unsigned int temp1 = regExpInt . cap ( 1 ) . toUInt ( & ok1 ) ;
unsigned int temp2 = regExpInt . cap ( 2 ) . toUInt ( & ok2 ) ;
unsigned int temp3 = regExpInt . cap ( 3 ) . toUInt ( & ok3 ) ;
unsigned int temp4 = regExpInt . cap ( 4 ) . toUInt ( & ok4 ) ;
if ( ok1 ) fSizeW = temp1 ;
if ( ok2 ) fSizeH = temp2 ;
if ( ok3 ) fpsNom = temp3 ;
if ( ok4 ) frames = temp4 ;
}
else if ( ( offset = regExpFrc . lastIndexIn ( text ) ) > = 0 )
{
bool ok1 = false , ok2 = false ;
bool ok3 = false , ok4 = false , ok5 = false ;
unsigned int temp1 = regExpFrc . cap ( 1 ) . toUInt ( & ok1 ) ;
unsigned int temp2 = regExpFrc . cap ( 2 ) . toUInt ( & ok2 ) ;
unsigned int temp3 = regExpFrc . cap ( 3 ) . toUInt ( & ok3 ) ;
unsigned int temp4 = regExpFrc . cap ( 4 ) . toUInt ( & ok4 ) ;
unsigned int temp5 = regExpFrc . cap ( 5 ) . toUInt ( & ok5 ) ;
if ( ok1 ) fSizeW = temp1 ;
if ( ok2 ) fSizeH = temp2 ;
if ( ok3 ) fpsNom = temp3 ;
if ( ok4 ) fpsDen = temp4 ;
if ( ok5 ) frames = temp5 ;
}
if ( ! text . isEmpty ( ) )
{
log ( text ) ;
}
}
}
}
process . waitForFinished ( ) ;
if ( process . state ( ) ! = QProcess : : NotRunning )
{
process . kill ( ) ;
process . waitForFinished ( - 1 ) ;
}
if ( bTimeout | | bAborted | | process . exitCode ( ) ! = EXIT_SUCCESS )
{
if ( ! ( bTimeout | | bAborted ) )
{
log ( tr ( " \n PROCESS EXITED WITH ERROR CODE: %1 " ) . arg ( QString : : number ( process . exitCode ( ) ) ) ) ;
}
return false ;
}
if ( frames = = 0 )
{
log ( tr ( " \n FAILED TO DETERMINE AVS PROPERTIES !!! " ) ) ;
return false ;
}
log ( " " ) ;
if ( ( fSizeW > 0 ) & & ( fSizeH > 0 ) )
{
log ( tr ( " Resolution: %1x%2 " ) . arg ( QString : : number ( fSizeW ) , QString : : number ( fSizeH ) ) ) ;
}
if ( ( fpsNom > 0 ) & & ( fpsDen > 0 ) )
{
log ( tr ( " Frame Rate: %1/%2 " ) . arg ( QString : : number ( fpsNom ) , QString : : number ( fpsDen ) ) ) ;
}
if ( ( fpsNom > 0 ) & & ( fpsDen = = 0 ) )
{
log ( tr ( " Frame Rate: %1 " ) . arg ( QString : : number ( fpsNom ) ) ) ;
}
if ( frames > 0 )
{
log ( tr ( " No. Frames: %1 " ) . arg ( QString : : number ( frames ) ) ) ;
}
return true ;
}
2012-01-29 21:31:09 +01:00
///////////////////////////////////////////////////////////////////////////////
// Misc functions
///////////////////////////////////////////////////////////////////////////////
2012-01-30 04:58:42 +01:00
void EncodeThread : : setStatus ( JobStatus newStatus )
{
if ( m_status ! = newStatus )
{
m_status = newStatus ;
2012-01-30 17:50:19 +01:00
if ( ( newStatus ! = JobStatus_Completed ) & & ( newStatus ! = JobStatus_Failed ) & & ( newStatus ! = JobStatus_Aborted ) )
{
setProgress ( 0 ) ;
}
if ( newStatus = = JobStatus_Failed )
{
setDetails ( " The job has failed. See log for details! " ) ;
}
if ( newStatus = = JobStatus_Aborted )
{
setDetails ( " The job was aborted by the user! " ) ;
}
2012-01-30 04:58:42 +01:00
emit statusChanged ( m_jobId , newStatus ) ;
}
}
void EncodeThread : : setProgress ( unsigned int newProgress )
{
if ( m_progress ! = newProgress )
{
m_progress = newProgress ;
emit progressChanged ( m_jobId , m_progress ) ;
}
}
void EncodeThread : : setDetails ( const QString & text )
{
emit detailsChanged ( m_jobId , text ) ;
}
2012-01-31 15:15:15 +01:00
bool EncodeThread : : startProcess ( QProcess & process , const QString & program , const QStringList & args , bool mergeChannels )
2012-01-29 21:31:09 +01:00
{
static AssignProcessToJobObjectFun AssignProcessToJobObjectPtr = NULL ;
2012-01-31 00:13:32 +01:00
static CreateJobObjectFun CreateJobObjectPtr = NULL ;
static SetInformationJobObjectFun SetInformationJobObjectPtr = NULL ;
2012-01-29 21:31:09 +01:00
QMutexLocker lock ( & m_mutex_startProcess ) ;
log ( commandline2string ( program , args ) + " \n " ) ;
2012-01-31 00:13:32 +01:00
//Create a new job object, if not done yet
if ( ! m_handle_jobObject )
{
if ( ! CreateJobObjectPtr | | ! SetInformationJobObjectPtr )
{
QLibrary Kernel32Lib ( " kernel32.dll " ) ;
CreateJobObjectPtr = ( CreateJobObjectFun ) Kernel32Lib . resolve ( " CreateJobObjectA " ) ;
SetInformationJobObjectPtr = ( SetInformationJobObjectFun ) Kernel32Lib . resolve ( " SetInformationJobObject " ) ;
}
if ( CreateJobObjectPtr & & SetInformationJobObjectPtr )
{
m_handle_jobObject = CreateJobObjectPtr ( NULL , NULL ) ;
if ( m_handle_jobObject = = INVALID_HANDLE_VALUE )
{
m_handle_jobObject = NULL ;
}
if ( m_handle_jobObject )
{
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedLimitInfo ;
memset ( & jobExtendedLimitInfo , 0 , sizeof ( JOBOBJECT_EXTENDED_LIMIT_INFORMATION ) ) ;
jobExtendedLimitInfo . BasicLimitInformation . LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION ;
SetInformationJobObjectPtr ( m_handle_jobObject , JobObjectExtendedLimitInformation , & jobExtendedLimitInfo , sizeof ( JOBOBJECT_EXTENDED_LIMIT_INFORMATION ) ) ;
}
}
}
//Initialize AssignProcessToJobObject function
2012-01-29 21:31:09 +01:00
if ( ! AssignProcessToJobObjectPtr )
2012-01-29 00:57:47 +01:00
{
2012-01-29 21:31:09 +01:00
QLibrary Kernel32Lib ( " kernel32.dll " ) ;
AssignProcessToJobObjectPtr = ( AssignProcessToJobObjectFun ) Kernel32Lib . resolve ( " AssignProcessToJobObject " ) ;
}
2012-01-29 00:57:47 +01:00
2012-01-31 15:15:15 +01:00
if ( mergeChannels )
{
process . setProcessChannelMode ( QProcess : : MergedChannels ) ;
process . setReadChannel ( QProcess : : StandardOutput ) ;
}
else
{
process . setProcessChannelMode ( QProcess : : SeparateChannels ) ;
process . setReadChannel ( QProcess : : StandardError ) ;
}
2012-01-29 21:31:09 +01:00
process . start ( program , args ) ;
if ( process . waitForStarted ( ) )
{
if ( AssignProcessToJobObjectPtr )
2012-01-29 00:57:47 +01:00
{
2012-01-29 21:31:09 +01:00
AssignProcessToJobObjectPtr ( m_handle_jobObject , process . pid ( ) - > hProcess ) ;
2012-01-29 00:57:47 +01:00
}
2012-01-29 21:31:09 +01:00
if ( ! SetPriorityClass ( process . pid ( ) - > hProcess , BELOW_NORMAL_PRIORITY_CLASS ) )
2012-01-29 00:57:47 +01:00
{
2012-01-29 21:31:09 +01:00
SetPriorityClass ( process . pid ( ) - > hProcess , IDLE_PRIORITY_CLASS ) ;
2012-01-29 00:57:47 +01:00
}
2012-01-29 21:31:09 +01:00
lock . unlock ( ) ;
return true ;
2012-01-29 00:57:47 +01:00
}
2012-01-29 21:31:09 +01:00
log ( " Process creation has failed :-( " ) ;
QString errorMsg = process . errorString ( ) . trimmed ( ) ;
if ( ! errorMsg . isEmpty ( ) ) log ( errorMsg ) ;
2012-01-29 00:57:47 +01:00
2012-01-29 21:31:09 +01:00
process . kill ( ) ;
process . waitForFinished ( - 1 ) ;
return false ;
}
QString EncodeThread : : commandline2string ( const QString & program , const QStringList & arguments )
{
QString commandline = ( program . contains ( ' ' ) ? QString ( " \" %1 \" " ) . arg ( program ) : program ) ;
for ( int i = 0 ; i < arguments . count ( ) ; i + + )
{
commandline + = ( arguments . at ( i ) . contains ( ' ' ) ? QString ( " \" %1 \" " ) . arg ( arguments . at ( i ) ) : QString ( " %1 " ) . arg ( arguments . at ( i ) ) ) ;
}
return commandline ;
2012-01-28 18:55:40 +01:00
}