Warning, file /multimedia/k3b/plugins/encoder/external/k3bexternalencoder.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3bexternalencoder.h"
0010 #include "k3bexternalencodercommand.h"
0011 #include "k3bplugin_i18n.h"
0012 
0013 #include <config-k3b.h>
0014 
0015 #include "k3bcore.h"
0016 #include "k3bprocess.h"
0017 
0018 #include <KConfig>
0019 
0020 #include <QDebug>
0021 #include <QFile>
0022 #include <QList>
0023 #include <QRegularExpression>
0024 #include <QStandardPaths>
0025 
0026 #include <sys/types.h>
0027 
0028 
0029 K_PLUGIN_CLASS_WITH_JSON(K3bExternalEncoder, "k3bexternalencoder.json")
0030 
0031 
0032 Q_DECLARE_METATYPE( QProcess::ExitStatus )
0033 
0034 
0035 static const unsigned char s_riffHeader[] =
0036 {
0037     0x52, 0x49, 0x46, 0x46, // 0  "RIFF"
0038     0x00, 0x00, 0x00, 0x00, // 4  wavSize
0039     0x57, 0x41, 0x56, 0x45, // 8  "WAVE"
0040     0x66, 0x6d, 0x74, 0x20, // 12 "fmt "
0041     0x10, 0x00, 0x00, 0x00, // 16
0042     0x01, 0x00, 0x02, 0x00, // 20
0043     0x44, 0xac, 0x00, 0x00, // 24
0044     0x10, 0xb1, 0x02, 0x00, // 28
0045     0x04, 0x00, 0x10, 0x00, // 32
0046     0x64, 0x61, 0x74, 0x61, // 36 "data"
0047     0x00, 0x00, 0x00, 0x00  // 40 byteCount
0048 };
0049 
0050 
0051 
0052 
0053 
0054 static K3bExternalEncoderCommand commandByExtension( const QString& extension )
0055 {
0056     QList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() );
0057     for( QList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it )
0058         if( (*it).extension == extension )
0059             return *it;
0060 
0061     qDebug() << "(K3bExternalEncoder) could not find command for extension " << extension;
0062 
0063     return K3bExternalEncoderCommand();
0064 }
0065 
0066 
0067 class K3bExternalEncoder::Private
0068 {
0069 public:
0070     K3b::Process* process;
0071     QString fileName;
0072     K3b::Msf length;
0073 
0074     K3bExternalEncoderCommand cmd;
0075 
0076     bool initialized;
0077 };
0078 
0079 
0080 K3bExternalEncoder::K3bExternalEncoder( QObject* parent, const QVariantList& )
0081     : K3b::AudioEncoder( parent ),
0082       d( new Private() )
0083 {
0084     d->process = 0;
0085     d->initialized = false;
0086 
0087     qRegisterMetaType<QProcess::ExitStatus>();
0088 }
0089 
0090 
0091 K3bExternalEncoder::~K3bExternalEncoder()
0092 {
0093     if( d->process ) {
0094         disconnect( d->process );
0095         d->process->deleteLater();
0096     }
0097     delete d;
0098 }
0099 
0100 
0101 void K3bExternalEncoder::finishEncoderInternal()
0102 {
0103     if( d->process && d->process->state() == QProcess::Running ) {
0104         d->process->closeWriteChannel();
0105 
0106         // this is kind of evil...
0107         // but we need to be sure the process exited when this method returns
0108         d->process->waitForFinished(-1);
0109     }
0110     d->initialized = false;
0111 }
0112 
0113 
0114 void K3bExternalEncoder::slotExternalProgramFinished( int exitCode, QProcess::ExitStatus exitStatus )
0115 {
0116     if( (exitStatus != QProcess::NormalExit) || (exitCode != 0) )
0117         qDebug() << "(K3bExternalEncoder) program exited with error.";
0118 }
0119 
0120 
0121 bool K3bExternalEncoder::openFile( const QString& ext, const QString& filename, const K3b::Msf& length, const MetaData& metaData )
0122 {
0123     d->fileName = filename;
0124     d->length = length;
0125 
0126     // delete existing files as some programs (like flac for example) might refuse to overwrite files
0127     if( QFile::exists( filename ) )
0128         QFile::remove( filename );
0129 
0130     return initEncoderInternal( ext, length, metaData );
0131 }
0132 
0133 
0134 bool K3bExternalEncoder::isOpen() const
0135 {
0136     return d->initialized;
0137 }
0138 
0139 
0140 void K3bExternalEncoder::closeFile()
0141 {
0142     finishEncoderInternal();
0143 }
0144 
0145 
0146 bool K3bExternalEncoder::initEncoderInternal( const QString& extension, const K3b::Msf& /*length*/, const MetaData& metaData )
0147 {
0148     // find the correct command
0149     d->cmd = commandByExtension( extension );
0150 
0151     if( !d->cmd.command.isEmpty() ) {
0152         // create the commandline
0153         QStringList params = d->cmd.command.split( ' ' );
0154         for( QStringList::iterator it = params.begin(); it != params.end(); ++it ) {
0155             (*it).replace( "%f", d->fileName );
0156             (*it).replace( "%a", metaData[META_TRACK_ARTIST].toString() );
0157             (*it).replace( "%t", metaData[META_TRACK_TITLE].toString() );
0158             (*it).replace( "%c", metaData[META_TRACK_COMMENT].toString() );
0159             (*it).replace( "%y", metaData[META_YEAR].toString() );
0160             (*it).replace( "%m", metaData[META_ALBUM_TITLE].toString() );
0161             (*it).replace( "%r", metaData[META_ALBUM_ARTIST].toString() );
0162             (*it).replace( "%x", metaData[META_ALBUM_COMMENT].toString() );
0163             (*it).replace( "%n", metaData[META_TRACK_NUMBER].toString() );
0164             (*it).replace( "%g", metaData[META_GENRE].toString() );
0165         }
0166 
0167 
0168         qDebug() << "***** external parameters:";
0169         qDebug() << params.join( " " ) << Qt::flush;
0170 
0171         // set one general error message
0172         setLastError( i18n("Command failed: %1", params.join( " " ) ) );
0173 
0174         // always create a new process since we are called in a separate thread
0175         if( d->process ) {
0176             disconnect( d->process );
0177             d->process->deleteLater();
0178         }
0179         d->process = new K3b::Process();
0180         d->process->setSplitStdout( true );
0181         connect( d->process, SIGNAL(finished(int,QProcess::ExitStatus)),
0182                 this, SLOT(slotExternalProgramFinished(int,QProcess::ExitStatus)) );
0183         connect( d->process, SIGNAL(stderrLine(QString)),
0184                  this, SLOT(slotExternalProgramOutput(QString)) );
0185         connect( d->process, SIGNAL(stdoutLine(QString)),
0186                  this, SLOT(slotExternalProgramOutput(QString)) );
0187 
0188         d->process->setProgram( params );
0189         d->process->start( KProcess::SeparateChannels );
0190 
0191         if( d->process->waitForStarted() ) {
0192             if( d->cmd.writeWaveHeader )
0193                 d->initialized = writeWaveHeader();
0194             else
0195                 d->initialized = true;
0196         }
0197         else {
0198             static const QRegularExpression rx("\\s+");
0199             QString commandName = d->cmd.command.section( rx, 0 );
0200             if( !QStandardPaths::findExecutable( commandName ).isEmpty() )
0201                 setLastError( i18n("Could not find program '%1'",commandName) );
0202 
0203             d->initialized = false;
0204         }
0205 
0206     }
0207     else {
0208         setLastError( i18n("Invalid command: the command is empty.") );
0209         d->initialized = false;
0210     }
0211 
0212     return d->initialized;
0213 }
0214 
0215 
0216 bool K3bExternalEncoder::writeWaveHeader()
0217 {
0218     qDebug() << "(K3bExternalEncoder) writing wave header";
0219 
0220     // write the RIFF thing
0221     if( d->process->write( (const char*) s_riffHeader, 4 ) != 4 ) {
0222         qDebug() << "(K3bExternalEncoder) failed to write riff header.";
0223         return false;
0224     }
0225 
0226     // write the wave size
0227     qint32 dataSize( d->length.audioBytes() );
0228     qint32 wavSize( dataSize + 44 - 8 );
0229     char c[4];
0230 
0231     c[0] = (wavSize   >> 0 ) & 0xff;
0232     c[1] = (wavSize   >> 8 ) & 0xff;
0233     c[2] = (wavSize   >> 16) & 0xff;
0234     c[3] = (wavSize   >> 24) & 0xff;
0235 
0236     if( d->process->write( c, 4 ) != 4 ) {
0237         qDebug() << "(K3bExternalEncoder) failed to write wave size.";
0238         return false;
0239     }
0240 
0241     // write static part of the header
0242     if( d->process->write( (const char*) s_riffHeader + 8, 32 ) != 32 ) {
0243         qDebug() << "(K3bExternalEncoder) failed to write wave header.";
0244         return false;
0245     }
0246 
0247     c[0] = (dataSize   >> 0 ) & 0xff;
0248     c[1] = (dataSize   >> 8 ) & 0xff;
0249     c[2] = (dataSize   >> 16) & 0xff;
0250     c[3] = (dataSize   >> 24) & 0xff;
0251 
0252     if( d->process->write( c, 4 ) != 4 ) {
0253         qDebug() << "(K3bExternalEncoder) failed to write data size.";
0254         return false;
0255     }
0256 
0257     return d->process->waitForBytesWritten( -1 );
0258 }
0259 
0260 
0261 qint64 K3bExternalEncoder::encodeInternal( const char* data, qint64 len )
0262 {
0263     if( !d->initialized )
0264         return -1;
0265 
0266     if( d->process->state() == QProcess::Running ) {
0267 
0268         long written = 0;
0269 
0270         if( d->cmd.swapByteOrder ) {
0271             char* buffer = new char[len];
0272             for( unsigned int i = 0; i < len-1; i+=2 ) {
0273                 buffer[i] = data[i+1];
0274                 buffer[i+1] = data[i];
0275             }
0276 
0277             written = d->process->write( buffer, len );
0278             delete [] buffer;
0279         }
0280         else
0281             written = d->process->write( data, len );
0282 
0283         d->process->waitForBytesWritten( -1 );
0284 
0285         return written;
0286     }
0287     else
0288         return -1;
0289 }
0290 
0291 
0292 void K3bExternalEncoder::slotExternalProgramOutput( const QString& line )
0293 {
0294     qDebug() << "(" << d->cmd.name << ") " << line;
0295 }
0296 
0297 
0298 QStringList K3bExternalEncoder::extensions() const
0299 {
0300     QStringList el;
0301     QList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() );
0302     for( QList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it )
0303         el.append( (*it).extension );
0304 
0305     return el;
0306 }
0307 
0308 
0309 QString K3bExternalEncoder::fileTypeComment( const QString& ext ) const
0310 {
0311     return commandByExtension( ext ).name;
0312 }
0313 
0314 #include "k3bexternalencoder.moc"
0315 
0316 #include "moc_k3bexternalencoder.cpp"