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"