You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
572 lines
15 KiB
572 lines
15 KiB
//
|
|
// C++ Implementation: k9mp4enc
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Author: Jean-Michel PETIT <k9copy@free.fr>, (C) 2006
|
|
//
|
|
// Copyright: See COPYING file that comes with this distribution
|
|
//
|
|
//
|
|
#include "k9mp4enc.h"
|
|
#include "k9mp4dlg.h"
|
|
#include "k9config.h"
|
|
#include <tqcstring.h>
|
|
#include <tqapplication.h>
|
|
#include <klocale.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqdir.h>
|
|
#include <kfiledialog.h>
|
|
#include <kmessagebox.h>
|
|
#include <tqstringlist.h>
|
|
#include <ktempfile.h>
|
|
#include <kstandarddirs.h>
|
|
#include "k9tools.h"
|
|
#include "k9audiocodecs.h"
|
|
#include "k9videocodecs.h"
|
|
#include <tqcstring.h>
|
|
|
|
k9MP4Enc::k9MP4Enc ( TQObject *parent, const char *name,const TQStringList& )
|
|
: TQObject ( parent, name )
|
|
{
|
|
m_fourcc=m_height=m_width=m_audioBitrate=m_videoBitrate=m_filename="";
|
|
m_codec=0; //lavc_mp4;
|
|
m_audioCodec=0;
|
|
m_cpt=-1;
|
|
m_parts=1;
|
|
|
|
TQStringList laudio;
|
|
TQStringList llabels;
|
|
TQStringList lvideo;
|
|
|
|
k9Config config;
|
|
m_lstAudio=config.getCodecAudio();
|
|
m_lstCodecs=config.getCodecLabels();
|
|
m_lstVideo=config.getCodecVideo();
|
|
|
|
timer = new TQTimer ( this );
|
|
connect ( timer, TQT_SIGNAL ( timeout() ), this, TQT_SLOT ( timerDone() ) );
|
|
m_progress=new k9MP4Dlg ( tqApp->mainWidget(),0 );
|
|
|
|
}
|
|
|
|
TQString k9MP4Enc::round16 ( TQString _wh )
|
|
{
|
|
if ( _wh !="" )
|
|
{
|
|
int value=_wh.toInt() /16;
|
|
return TQString::number ( value*16 );
|
|
|
|
}
|
|
else
|
|
return _wh;
|
|
|
|
|
|
}
|
|
|
|
TQString k9MP4Enc::getChapterList ( k9DVDTitle *_title )
|
|
{
|
|
TQString res="";
|
|
TQPtrList <k9DVDChapter> chapters=_title->getChapters();
|
|
for ( k9DVDChapter *chapter=chapters.first();chapter;chapter=chapters.next() )
|
|
{
|
|
if ( chapter->getSelected() )
|
|
{
|
|
res+=res=="" ? TQString::number ( chapter->getnum() ) : ","+TQString::number ( chapter->getnum() );
|
|
}
|
|
}
|
|
TQPtrList <k9DVDTitle> titles=_title->getTitles();
|
|
|
|
for ( k9DVDTitle *title=titles.first();title;title=titles.next() )
|
|
{
|
|
chapters=title->getChapters();
|
|
for ( k9DVDChapter *chapter=chapters.first();chapter;chapter=chapters.next() )
|
|
{
|
|
if ( chapter->getSelected() )
|
|
{
|
|
res+=res=="" ? TQString::number ( chapter->getnum() ) : ","+TQString::number ( chapter->getnum() );
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
int k9MP4Enc::getselectedSubp ( k9DVDTitle *_title )
|
|
{
|
|
for ( int i=0;i< _title->getsubPictureCount();i++ )
|
|
{
|
|
if ( _title->getsubtitle ( i )->getselected() )
|
|
{
|
|
return _title->getsubtitle ( i )->getID().first()-1;
|
|
}
|
|
}
|
|
//nos subtitle selected
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
void k9MP4Enc::execute ( k9DVDTitle *_title )
|
|
{
|
|
bool error=false;
|
|
|
|
if ( m_mpeg2 )
|
|
{
|
|
m_parts=1;
|
|
m_2pass=false;
|
|
}
|
|
|
|
if ( ! k9Tools::checkProgram ( "mencoder" ) && ! m_mpeg2 )
|
|
{
|
|
KMessageBox::error ( tqApp->mainWidget(),i18n ( "Unable to run %1" ).arg ( "mencoder" ) , i18n ( "Encoding error" ) );
|
|
error = TRUE;
|
|
return;
|
|
}
|
|
|
|
time = new TQTime ( 0,0 );
|
|
m_percent=0;
|
|
m_remain="--:--:--";
|
|
|
|
m_totalSize=_title->getChaptersSize ( true );
|
|
|
|
TQString injectName;
|
|
KTempFile injectFile ( locateLocal ( "tmp", "k9copy/k9v" ), "" );
|
|
injectFile.setAutoDelete ( true );
|
|
injectFile.close();
|
|
injectName=injectFile.name();
|
|
|
|
|
|
int maxPass=0;
|
|
int pass=0;
|
|
|
|
//build the cell list for mpeg2 extraction
|
|
TQMap<TQString,int> chapterCells;
|
|
TQMap<TQString, int>::iterator ichapterCells;
|
|
TQStringList chapters;
|
|
if ( m_mpeg2 && m_mpegChapters )
|
|
{
|
|
m_parts=0;
|
|
chapters=TQStringList::split ( ",", getChapterList ( _title ) );
|
|
for ( unsigned int idxChap = 0; idxChap < chapters.size(); idxChap++ )
|
|
{
|
|
TQString chapter = chapters[idxChap];
|
|
//foreach (TQString chapter,chapters) {
|
|
int iCell=0;
|
|
k9DVDChapter *chap=_title->getChapter ( chapter.toInt()-1 );
|
|
//foreach(k9ChapterCell *cell ,chap->cells) {
|
|
iCell++;
|
|
chapterCells.insert ( chapter,iCell );
|
|
m_parts++;
|
|
//}
|
|
}
|
|
ichapterCells = chapterCells.begin();
|
|
}
|
|
|
|
for ( int m_part =1 ; ( m_part <=m_parts ) && !error ;m_part++ )
|
|
{
|
|
if ( m_2pass )
|
|
{
|
|
maxPass=2;
|
|
pass=1;
|
|
}
|
|
KTempFile passLogFile ( locateLocal ( "tmp", "k9copy/k9v" ), "" );
|
|
passLogFile.setAutoDelete ( true );
|
|
passLogFile.close();
|
|
|
|
do
|
|
{
|
|
uint32_t nbSectors= m_totalSize / m_parts ;
|
|
|
|
uint32_t startSector= nbSectors* ( m_part-1 );
|
|
uint32_t endSector= startSector+nbSectors;
|
|
|
|
//calculer le bitrate en faisant la somme des cells compris entre startSector et endSector
|
|
//FIXME Mettre en place la sélection par chapitres
|
|
m_stderr="";
|
|
m_title=_title;
|
|
if ( m_height=="" || m_height=="0" )
|
|
m_height="-2";
|
|
if ( m_width=="" )
|
|
m_width="640";
|
|
if ( m_audioBitrate=="" )
|
|
m_audioBitrate="128";
|
|
if ( m_size=="" )
|
|
m_size="700";
|
|
if ( m_filename=="" )
|
|
m_filename=KFileDialog::getSaveFileName ( TQDir::homeDirPath(),"*.avi", 0,i18n ( "Save file to disk" ) );
|
|
if ( m_filename =="" )
|
|
return;
|
|
|
|
TQDir d=TQDir::root();
|
|
if ( d.exists ( m_filename ) )
|
|
d.remove ( m_filename );
|
|
|
|
m_progress->setbitrate ( TQString::number ( getBitRate ( _title ) ) );
|
|
if ( !m_mpeg2 )
|
|
m_progress->setsize ( m_size +i18n ( "MB" ) +" X " +TQString::number ( m_parts ) );
|
|
else
|
|
m_progress->setsize ( m_size +i18n ( "MB" ) +" X " );
|
|
|
|
m_process=new k9Process ( this,0 );
|
|
m_process->setUseShell ( true );
|
|
*m_process << "k9copy" << "--play" << "--endsector" << TQString::number ( endSector ) ;
|
|
*m_process << "--inject" << injectName; //"/tmp/kde-jmp/inject";
|
|
*m_process << "--input" << "'"+m_device+"'";
|
|
*m_process << "--dvdtitle" << TQString::number ( _title->getnumTitle() );
|
|
|
|
if ( m_mpegChapters && m_mpeg2 )
|
|
{
|
|
*m_process << "--chapter" << ichapterCells.key();//chapters.at(m_part-1);
|
|
//*m_process << "--cell" << TQString::number(ichapterCells.value());
|
|
}
|
|
else
|
|
*m_process << "--chapterlist" << getChapterList ( _title );
|
|
|
|
if ( m_part==1 || m_mpeg2 )
|
|
*m_process << "--initstatus";
|
|
else
|
|
*m_process << "--continue";
|
|
|
|
if ( pass==1 )
|
|
*m_process << "--firstpass";
|
|
|
|
for ( int i=0;i<_title->getaudioStreamCount();i++ )
|
|
{
|
|
if ( _title->getaudioStream ( i )->getselected() )
|
|
{
|
|
*m_process << "--audiofilter" << TQString::number ( _title->getaudioStream ( i )->getID() );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( getselectedSubp ( _title ) !=-1 )
|
|
{
|
|
*m_process << "--subpicturefilter" ;
|
|
TQString s="";
|
|
for ( int i=1; i<=_title->getsubPictureCount();i++ )
|
|
s+= ( i>1?",":"" ) + TQString::number ( i );
|
|
*m_process << s;
|
|
}
|
|
|
|
if ( m_usecache )
|
|
*m_process << "--usecache";
|
|
|
|
if ( m_mpeg2 )
|
|
{
|
|
m_progress->setbitrate ( "--" );
|
|
double size;
|
|
|
|
if ( _title->getforceFactor() )
|
|
{
|
|
size = _title->getChaptersSize_mb ( true ) /_title->getfactor();
|
|
*m_process << "--vampsfactor" << TQString::number ( _title->getfactor() ) << "--ffactor";
|
|
}
|
|
else
|
|
size = _title->getChaptersSize_mb ( true );
|
|
m_progress->setsize ( TQString::number ( size ) +i18n ( "MB" ) );
|
|
TQString path=m_filename;
|
|
if ( m_parts>1 )
|
|
{
|
|
TQString ext=m_filename.section ( ".",-1 );
|
|
if ( ext!="" )
|
|
ext="."+ext;
|
|
path=m_filename.left ( m_filename.length()-ext.length() );
|
|
//path=TQString("%1-chapter%2-cell%3%4").tqarg(path).tqarg(ichapterCells.key()).tqarg(ichapterCells.value()).tqarg(ext);
|
|
path=TQString ( "%1-chapter%2%3" ).arg ( path ).arg ( ichapterCells.key() ).arg ( ext );
|
|
++ichapterCells;
|
|
}
|
|
*m_process << "> "+path;
|
|
}
|
|
else
|
|
{
|
|
*m_process << "| mencoder" << "/dev/stdin";
|
|
*m_process << "-passlogfile" << passLogFile.name();
|
|
|
|
bool audio=false;
|
|
TQString sPass="";
|
|
TQString sCodec="";
|
|
|
|
k9AudioCodecs *audioCodecs=new k9AudioCodecs ( 0,0 );
|
|
k9VideoCodecs *videoCodecs=new k9VideoCodecs ( 0,0 );
|
|
|
|
TQString sVOption;
|
|
m_pass=pass;
|
|
switch ( pass )
|
|
{
|
|
case 1:
|
|
sVOption=replaceParams ( videoCodecs->getOptions1 ( m_codec ) );
|
|
break;
|
|
case 2:
|
|
sVOption=replaceParams ( videoCodecs->getOptions2 ( m_codec ) );
|
|
break;
|
|
default:
|
|
sVOption=replaceParams ( videoCodecs->getOptions0 ( m_codec ) );
|
|
break;
|
|
}
|
|
sCodec=videoCodecs->getCodecName ( m_codec );
|
|
sVOption=sVOption.simplifyWhiteSpace();
|
|
int pos;
|
|
//*m_process << "-ovc" << sVOption;
|
|
/* int pos=sVOption.find("-vf");
|
|
if (pos==-1)
|
|
*m_process <<"-vf" << TQString("scale=%1:%2").tqarg(m_width).tqarg(m_height);
|
|
else
|
|
sVOption=sVOption.insert(pos+4,TQString("scale=%1:%2,").tqarg(m_width).tqarg(m_height));
|
|
*/
|
|
*m_process << sVOption;
|
|
|
|
TQString sAOption=replaceParams ( audioCodecs->getOptions ( m_audioCodec ) ).simplifyWhiteSpace();
|
|
|
|
|
|
|
|
if ( pass >0 )
|
|
m_progress->setTitleLabel ( i18n ( "Encoding %1" ).arg ( sCodec ) +" - "+i18n ( "pass %1" ).arg ( pass ) );
|
|
else
|
|
m_progress->setTitleLabel ( i18n ( "Encoding %1" ).arg ( sCodec ) );
|
|
|
|
if ( m_fourcc !="" )
|
|
*m_process << "-ffourcc" << m_fourcc;
|
|
else if ( videoCodecs->getFourcc ( m_codec ) !="" )
|
|
*m_process << "-ffourcc" << videoCodecs->getFourcc ( m_codec );
|
|
|
|
delete audioCodecs;
|
|
delete videoCodecs;
|
|
|
|
//looking for first audio selected
|
|
for ( int i=0;i<_title->getaudioStreamCount();i++ )
|
|
{
|
|
if ( _title->getaudioStream ( i )->getselected() )
|
|
{
|
|
//*m_process << "-oac" << sAOption;
|
|
pos=sAOption.find ( "-af" );
|
|
if ( pos==-1 )
|
|
*m_process << TQString ( "-af volume=%1" ).arg ( m_audioGain );
|
|
else
|
|
sAOption=sAOption.insert ( pos+4,TQString ( "volume=%1," ).arg ( m_audioGain ) );
|
|
*m_process << sAOption;
|
|
|
|
audio=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( getselectedSubp ( _title ) !=-1 )
|
|
{
|
|
*m_process << "-sid" << TQString::number ( getselectedSubp ( _title ) );
|
|
}
|
|
if ( !audio )
|
|
*m_process << "-nosound";
|
|
|
|
TQString path=m_filename;
|
|
|
|
if ( m_parts>1 )
|
|
{
|
|
TQString ext=m_filename.section ( ".",-1 );
|
|
if ( ext!="" )
|
|
ext="."+ext;
|
|
path=m_filename.left ( m_filename.length()-ext.length() );
|
|
path=path+TQString::number ( m_part ) +ext;
|
|
}
|
|
if ( pass==1 )
|
|
*m_process << "-o" << "/dev/null";
|
|
else
|
|
*m_process <<"-o" << "'"+path+"'";
|
|
if ( path.upper().endsWith ( "MPEG" ) || path.upper().endsWith ( "MPG" ) )
|
|
*m_process << "-of" << "mpeg";
|
|
else if ( path.upper().endsWith ( "AVI" ) )
|
|
*m_process << "-of" << "avi";
|
|
else
|
|
{
|
|
*m_process << "-of" << "lavf";
|
|
*m_process << "-lavfopts" << "i_certify_that_my_video_stream_does_not_use_b_frames";
|
|
}
|
|
//*m_process << "-of" << "avi";
|
|
|
|
}
|
|
TQString s="";
|
|
for ( uint i=0; i< m_process->args().count();i++ )
|
|
{
|
|
TQCString str=* ( m_process->args().at ( i ) );
|
|
s +=TQString ( str ) +" ";
|
|
}
|
|
qDebug ( s );
|
|
time->start();
|
|
m_timer3.start();
|
|
connect ( m_process, TQT_SIGNAL ( receivedStdout ( KProcess *, char *, int ) ),this, TQT_SLOT ( getStdout ( KProcess *, char *, int ) ) );
|
|
connect ( m_process, TQT_SIGNAL ( receivedStderr ( KProcess *, char *, int ) ),this, TQT_SLOT ( getStderr ( KProcess *, char *, int ) ) );
|
|
//connect(m_process, TQT_SIGNAL(processExited(KProcess*)),this,TQT_SLOT(exited(KProcess*)));
|
|
connect ( m_progress,TQT_SIGNAL ( sigCancel() ),this,TQT_SLOT ( slotCancel() ) );
|
|
m_canceled=false;
|
|
m_progress->show();
|
|
m_process->start ( KProcess::OwnGroup, KProcess::All );
|
|
timer->start ( 500, 0 );
|
|
m_process->sync();
|
|
//if application is exiting, kill the encoding process
|
|
if ( m_process->isRunning() )
|
|
{
|
|
m_process->kill();
|
|
return;
|
|
}
|
|
if ( m_canceled )
|
|
{
|
|
KMessageBox::information ( tqApp->mainWidget(),i18n ( "MPEG-4 Encoding cancelled" ), i18n ( "MPEG-4 Encoding" ) );
|
|
error=true;
|
|
}
|
|
else if ( !m_process->normalExit() )
|
|
{
|
|
KMessageBox::error ( tqApp->mainWidget(),"<b>"+i18n ( "Error while running mencoder :" ) +"</b><br>"+m_stderr, i18n ( "Encoding error" ) );
|
|
error=true;
|
|
}
|
|
if ( maxPass >0 )
|
|
pass++;
|
|
}
|
|
while ( pass<=maxPass && !error && m_2pass );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void k9MP4Enc::slotCancel()
|
|
{
|
|
m_canceled=true;
|
|
m_process->kill();
|
|
}
|
|
|
|
|
|
TQString k9MP4Enc::replaceParams ( TQString _value )
|
|
{
|
|
TQString str=_value;
|
|
str.replace ( "$PASS",TQString::number ( m_pass ) );
|
|
str.replace ( "$WIDTH",m_width );
|
|
str.replace ( "$HEIGHT",m_height );
|
|
str.replace ( "$VIDBR",TQString::number ( getBitRate ( m_title ) ) );
|
|
str.replace ( "$AUDBR",m_audioBitrate );
|
|
return str;
|
|
}
|
|
|
|
|
|
int k9MP4Enc::getBitRate ( k9DVDTitle *_title )
|
|
{
|
|
// bitrate video = (MB *8388.608) /SEC - bitrate audio
|
|
|
|
if ( m_videoBitrate!="" )
|
|
{
|
|
return m_videoBitrate.toInt();
|
|
}
|
|
else
|
|
{
|
|
int size=m_size.toInt();
|
|
float titleSize=_title->getChaptersSize_mb ( true );
|
|
if ( titleSize< ( float ) size )
|
|
size= ( int ) ( titleSize/m_parts ) ;
|
|
m_progress->setsize ( TQString::number ( size ) +i18n ( "MB" ) +" X " +TQString::number ( m_parts ) );
|
|
TQTime t1 ( 0,0 );
|
|
int sec=t1.secsTo ( _title->getSelectedLength() );
|
|
//int bitrate=(int)( ((size*m_parts) * 8388.608)/sec - m_audioBitrate.toInt());
|
|
int bitrate=8* ( ( ( size*m_parts*1024 )- ( m_audioBitrate.toInt() *sec/8 ) ) /sec );
|
|
|
|
return bitrate;
|
|
}
|
|
}
|
|
|
|
|
|
void k9MP4Enc::getStdout ( KProcess *, char *buffer, int buflen )
|
|
{
|
|
TQCString tmp ( buffer,buflen );
|
|
m_cpt++;
|
|
if ( m_cpt==100 )
|
|
m_cpt=0;
|
|
|
|
if ( m_cpt!=0 )
|
|
return;
|
|
|
|
int pos=tmp.find ( "Pos:" );
|
|
if ( pos!=-1 )
|
|
{
|
|
TQString tmp2=tmp.mid ( pos );
|
|
float t;
|
|
int frame;
|
|
int fps;
|
|
sscanf ( tmp2.latin1(),"Pos: %f%*s%d",&t,&frame );
|
|
tmp2=tmp2.mid ( tmp2.find ( "(" ) +1 );
|
|
tmp2=tmp2.mid ( tmp2.find ( ")" ) +1 );
|
|
sscanf ( tmp2.latin1(),"%d",&fps );
|
|
|
|
m_progress->setfps ( TQString::number ( fps ) );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void k9MP4Enc::getStderr ( KProcess *proc, char *buffer, int buflen )
|
|
{
|
|
//m_stderr=TQString::tqfromLatin1(buffer,buflen);
|
|
TQCString cstderr ( buffer,buflen+1 );
|
|
|
|
if ( cstderr.find ( "FATAL:" ) !=-1 )
|
|
{
|
|
proc->kill();
|
|
}
|
|
|
|
int pos=cstderr.find ( "INFOPOS:" );
|
|
if ( pos!=-1 )
|
|
{
|
|
if ( m_timer3.elapsed() >500 )
|
|
{
|
|
m_timer3.restart();
|
|
TQString tmp=cstderr.mid ( pos );
|
|
uint32_t totalBytes,totalSize;
|
|
sscanf ( tmp.latin1(),"INFOPOS: %d %d",&totalBytes,&totalSize );
|
|
if ( totalSize !=0 )
|
|
m_percent= ( float ) totalBytes / ( float ) m_totalSize;
|
|
|
|
|
|
TQTime time2 ( 0,0 );
|
|
time2=time2.addMSecs ( time->elapsed() );
|
|
if ( m_percent>0 )
|
|
{
|
|
TQTime time3 ( 0,0 );
|
|
time3=time3.addMSecs ( ( uint32_t ) ( time->elapsed() * ( 1/m_percent ) ) );
|
|
m_remain=time3.toString ( "hh:mm:ss" );
|
|
}
|
|
|
|
m_percent*=100;
|
|
m_progress->setProgress ( ( int ) m_percent );
|
|
m_progress->setremain ( time2.toString ( "hh:mm:ss" ) +" / " +m_remain );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pos=cstderr.find ( "INFOIMAGE:" );
|
|
if ( pos!=-1 )
|
|
{
|
|
m_progress->setImage ( cstderr.mid ( pos+10 ) );
|
|
}
|
|
else
|
|
qDebug ( "[%s]",buffer );
|
|
}
|
|
m_stderr=cstderr;
|
|
}
|
|
|
|
void k9MP4Enc::timerDone()
|
|
{
|
|
TQTime time2 ( 0,0 );
|
|
time2=time2.addMSecs ( time->elapsed() );
|
|
m_progress->setremain ( time2.toString ( "hh:mm:ss" ) +" / " +m_remain );
|
|
|
|
}
|
|
|
|
bool k9MP4Enc::isCanceled()
|
|
{
|
|
return m_canceled;
|
|
}
|
|
|
|
|
|
k9MP4Enc::~k9MP4Enc() {}
|
|
|
|
|
|
#include "k9mp4enc.moc"
|