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.
k9copy/libk9copy/k9mp4enc.cpp

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"