File indexing completed on 2024-06-16 07:42:06

0001 /*
0002     SPDX-FileCopyrightText: 2006-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "k3bvideodvdtitledetectclippingjob.h"
0009 
0010 #include "k3bexternalbinmanager.h"
0011 #include "k3bprocess.h"
0012 #include "k3bcore.h"
0013 #include "k3bglobals.h"
0014 #include "k3b_i18n.h"
0015 
0016 #include <QDebug>
0017 
0018 
0019 static const int s_unrealisticHighClippingValue = 100000;
0020 
0021 
0022 class K3b::VideoDVDTitleDetectClippingJob::Private
0023 {
0024 public:
0025     const K3b::ExternalBin* usedTranscodeBin;
0026 
0027     K3b::Process* process;
0028 
0029     bool canceled;
0030 
0031     unsigned int currentChapter;
0032     unsigned int currentFrames;
0033     unsigned int totalChapters;
0034 
0035     int lastProgress;
0036     int lastSubProgress;
0037 };
0038 
0039 
0040 
0041 K3b::VideoDVDTitleDetectClippingJob::VideoDVDTitleDetectClippingJob( K3b::JobHandler* hdl, QObject* parent )
0042     : K3b::Job( hdl, parent ),
0043       m_clippingTop( 0 ),
0044       m_clippingBottom( 0 ),
0045       m_clippingLeft( 0 ),
0046       m_clippingRight( 0 ),
0047       m_lowPriority( true )
0048 {
0049     d = new Private;
0050     d->process = 0;
0051 }
0052 
0053 
0054 K3b::VideoDVDTitleDetectClippingJob::~VideoDVDTitleDetectClippingJob()
0055 {
0056     delete d->process;
0057     delete d;
0058 }
0059 
0060 
0061 void K3b::VideoDVDTitleDetectClippingJob::start()
0062 {
0063     jobStarted();
0064 
0065     d->canceled = false;
0066     d->lastProgress = 0;
0067 
0068     //
0069     // It seems as if the last chapter is often way too short
0070     //
0071     d->totalChapters = m_dvd[m_titleNumber-1].numPTTs();
0072     if( d->totalChapters > 1 && m_dvd[m_titleNumber-1][d->totalChapters-1].playbackTime().totalFrames() < 200 )
0073         d->totalChapters--;
0074 
0075     // initial values (some way to big value)
0076     m_clippingTop = s_unrealisticHighClippingValue;
0077     m_clippingBottom = s_unrealisticHighClippingValue;
0078     m_clippingLeft = s_unrealisticHighClippingValue;
0079     m_clippingRight = s_unrealisticHighClippingValue;
0080 
0081     d->usedTranscodeBin = k3bcore->externalBinManager()->binObject("transcode");
0082     if( !d->usedTranscodeBin ) {
0083         emit infoMessage( i18n("%1 executable could not be found.",QString("transcode")), MessageError );
0084         jobFinished( false );
0085         return;
0086     }
0087 
0088     if( d->usedTranscodeBin->version() < K3b::Version( 1, 0, 0 ) ){
0089         emit infoMessage( i18n("%1 version %2 is too old.",
0090                                QString("transcode")
0091                                ,d->usedTranscodeBin->version()), MessageError );
0092         jobFinished( false );
0093         return;
0094     }
0095 
0096     emit debuggingOutput( QLatin1String( "Used versions" ), QString::fromLatin1( "transcode: %1" ).arg(d->usedTranscodeBin->version()) );
0097 
0098     if( !d->usedTranscodeBin->copyright().isEmpty() )
0099         emit infoMessage( i18n("Using %1 %2 – Copyright © %3"
0100                                ,d->usedTranscodeBin->name()
0101                                ,d->usedTranscodeBin->version()
0102                                ,d->usedTranscodeBin->copyright()), MessageInfo );
0103 
0104     emit newTask( i18n("Analysing Title %1 of Video DVD %2",m_titleNumber,m_dvd.volumeIdentifier()) );
0105 
0106     startTranscode( 1 );
0107 }
0108 
0109 
0110 void K3b::VideoDVDTitleDetectClippingJob::startTranscode( int chapter )
0111 {
0112     d->currentChapter = chapter;
0113     d->lastSubProgress = 0;
0114 
0115     //
0116     // If we have only one chapter and it is not longer than 2 minutes (value guessed based on some test DVD)
0117     // use the whole chapter
0118     //
0119     if( d->totalChapters == 1 )
0120         d->currentFrames = qMin( 3000, qMax( 1, ( int )m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );
0121     else
0122         d->currentFrames = qMin( 200, qMax( 1, ( int )m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );
0123 
0124     //
0125     // prepare the process
0126     //
0127     delete d->process;
0128     d->process = new K3b::Process();
0129     d->process->setSuppressEmptyLines(true);
0130     d->process->setSplitStdout(true);
0131     connect( d->process, SIGNAL(stdoutLine(QString)), this, SLOT(slotTranscodeStderr(QString)) );
0132     connect( d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotTranscodeExited(int,QProcess::ExitStatus)) );
0133 
0134     // the executable
0135     *d->process << d->usedTranscodeBin;
0136 
0137     // low priority
0138     if( m_lowPriority )
0139         *d->process << "--nice" << "19";
0140 
0141     if ( d->usedTranscodeBin->version() >= Version( 1, 1, 0 ) )
0142         *d->process << "--log_no_color";
0143 
0144     // the input
0145     *d->process << "-i" << m_dvd.device()->blockDeviceName();
0146 
0147     // select the title number and chapter
0148     *d->process << "-T" << QString("%1,%2").arg(m_titleNumber).arg(chapter);
0149 
0150     // null output
0151     *d->process << "-y" << "null,null" << "-o" << "/dev/null";
0152 
0153     // analyze the first 200 frames
0154     *d->process << "-J" << QString("detectclipping=range=0-%1/5").arg(d->currentFrames);
0155 
0156     // also only decode the first 200 frames
0157     *d->process << "-c" << QString("0-%1").arg(d->currentFrames+1);
0158 
0159     // additional user parameters from config
0160     const QStringList& params = d->usedTranscodeBin->userParameters();
0161     for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
0162         *d->process << *it;
0163 
0164     // produce some debugging output
0165     qDebug() << "***** transcode parameters:\n";
0166     QString s = d->process->joinedArgs();
0167     qDebug() << s << Qt::flush;
0168     emit debuggingOutput( d->usedTranscodeBin->name() + " command:", s);
0169 
0170     // start the process
0171     if( !d->process->start( KProcess::MergedChannels ) ) {
0172         // something went wrong when starting the program
0173         // it "should" be the executable
0174         emit infoMessage( i18n("Could not start %1.",d->usedTranscodeBin->name()), K3b::Job::MessageError );
0175         jobFinished(false);
0176     }
0177     else {
0178         emit newSubTask( i18n("Analysing Chapter %1 of %2",chapter,m_dvd[m_titleNumber-1].numPTTs()) );
0179         emit subPercent( 0 );
0180     }
0181 }
0182 
0183 
0184 void K3b::VideoDVDTitleDetectClippingJob::cancel()
0185 {
0186     d->canceled = true;
0187     if( d->process && d->process->isRunning() )
0188         d->process->kill();
0189 }
0190 
0191 
0192 void K3b::VideoDVDTitleDetectClippingJob::slotTranscodeStderr( const QString& line )
0193 {
0194     emit debuggingOutput( "transcode", line );
0195 
0196     // parse progress
0197     // encoding frame [185],  24.02 fps, 93.0%, ETA: 0:00:00, ( 0| 0| 0)
0198     if( line.startsWith( "encoding frame" ) ) {
0199         int pos1 = line.indexOf( '[', 15 );
0200         int pos2 = line.indexOf( ']', pos1+1 );
0201         if( pos1 > 0 && pos2 > 0 ) {
0202             bool ok;
0203             int encodedFrames = line.mid( pos1+1, pos2-pos1-1 ).toInt( &ok );
0204             if( ok ) {
0205                 int progress = 100 * encodedFrames / d->currentFrames;
0206 
0207                 if( progress > d->lastSubProgress ) {
0208                     d->lastSubProgress = progress;
0209                     emit subPercent( progress );
0210                 }
0211 
0212                 double part = 100.0 / (double)d->totalChapters;
0213 
0214                 progress = (int)( ( (double)(d->currentChapter-1) * part )
0215                                   + ( (double)progress / (double)d->totalChapters )
0216                                   + 0.5 );
0217 
0218                 if( progress > d->lastProgress ) {
0219                     d->lastProgress = progress;
0220                     emit percent( progress );
0221                 }
0222             }
0223         }
0224     }
0225 
0226     // [detectclipping#0] valid area: X: 5..719 Y: 72..507  -> -j 72,6,68,0
0227     else if( line.startsWith( "[detectclipping" ) ) {
0228         int pos = line.indexOf( "-j" );
0229         if( pos > 0 ) {
0230             QStringList values = line.mid( pos+3 ).split( ',' );
0231             m_clippingTop = qMin( m_clippingTop, values[0].toInt() );
0232             m_clippingLeft = qMin( m_clippingLeft, values[1].toInt() );
0233             m_clippingBottom = qMin( m_clippingBottom, values[2].toInt() );
0234             m_clippingRight = qMin( m_clippingRight, values[3].toInt() );
0235         }
0236         else
0237             qDebug() << "(K3b::VideoDVDTitleDetectClippingJob) failed to parse line: " << line;
0238     }
0239 }
0240 
0241 
0242 void K3b::VideoDVDTitleDetectClippingJob::slotTranscodeExited( int exitCode, QProcess::ExitStatus )
0243 {
0244     switch( exitCode ) {
0245     case 0:
0246         d->currentChapter++;
0247         if( d->currentChapter > d->totalChapters ) {
0248             //
0249             // check if we did set any values at all
0250             //
0251             if( m_clippingTop == s_unrealisticHighClippingValue )
0252                 m_clippingTop = m_clippingLeft = m_clippingBottom = m_clippingRight = 0;
0253 
0254             if( d->totalChapters < m_dvd[m_titleNumber-1].numPTTs() )
0255                 emit infoMessage( i18n("Ignoring clipping values of last chapter due to its short playback time."), MessageInfo );
0256 
0257             jobFinished( true );
0258         }
0259         else {
0260             startTranscode( d->currentChapter );
0261         }
0262         break;
0263 
0264     default:
0265         // FIXME: error handling
0266 
0267         if( d->canceled ) {
0268             emit canceled();
0269         }
0270         else {
0271             emit infoMessage( i18n("%1 returned an unknown error (code %2).",
0272                                    d->usedTranscodeBin->name(), exitCode ),
0273                               K3b::Job::MessageError );
0274             emit infoMessage( i18n("Please send me an email with the last output."), K3b::Job::MessageError );
0275         }
0276 
0277         jobFinished( false );
0278     }
0279 }
0280 
0281 #include "moc_k3bvideodvdtitledetectclippingjob.cpp"