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.
k3b/plugins/encoder/external/k3bexternalencoder.cpp

381 lines
9.5 KiB

/*
*
* $Id: k3bexternalencoder.cpp 619556 2007-01-03 17:38:12Z trueg $
* Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
*
* This file is part of the K3b project.
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* See the file "COPYING" for the exact licensing terms.
*/
#include <config.h>
#include "k3bexternalencoder.h"
#include "k3bexternalencodercommand.h"
#include "k3bexternalencoderconfigwidget.h"
#include <k3bpluginfactory.h>
#include <k3bprocess.h>
#include <k3bcore.h>
#include <kdebug.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <tqregexp.h>
#include <tqfile.h>
#include <sys/types.h>
#include <sys/wait.h>
K_EXPORT_COMPONENT_FACTORY( libk3bexternalencoder, K3bPluginFactory<K3bExternalEncoder>( "libk3bexternalencoder" ) )
static const char s_riffHeader[] =
{
'\x52', '\x49', '\x46', '\x46', // 0 "RIFF"
'\x00', '\x00', '\x00', '\x00', // 4 wavSize
'\x57', '\x41', '\x56', '\x45', // 8 "WAVE"
'\x66', '\x6d', '\x74', '\x20', // 12 "fmt "
'\x10', '\x00', '\x00', '\x00', // 16
'\x01', '\x00', '\x02', '\x00', // 20
'\x44', '\xac', '\x00', '\x00', // 24
'\x10', '\xb1', '\x02', '\x00', // 28
'\x04', '\x00', '\x10', '\x00', // 32
'\x64', '\x61', '\x74', '\x61', // 36 "data"
'\x00', '\x00', '\x00', '\x00' // 40 byteCount
};
static K3bExternalEncoderCommand commandByExtension( const TQString& extension )
{
TQValueList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() );
for( TQValueList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it )
if( (*it).extension == extension )
return *it;
kdDebug() << "(K3bExternalEncoder) could not find command for extension " << extension << endl;
return K3bExternalEncoderCommand();
}
class K3bExternalEncoder::Private
{
public:
Private()
: process(0) {
}
K3bProcess* process;
TQString fileName;
TQString extension;
K3b::Msf length;
K3bExternalEncoderCommand cmd;
bool initialized;
// the metaData we support
TQString artist;
TQString title;
TQString comment;
TQString trackNumber;
TQString cdArtist;
TQString cdTitle;
TQString cdComment;
TQString year;
TQString genre;
};
K3bExternalEncoder::K3bExternalEncoder( TQObject* parent, const char* name )
: K3bAudioEncoder( parent, name )
{
d = new Private();
}
K3bExternalEncoder::~K3bExternalEncoder()
{
delete d->process;
delete d;
}
void K3bExternalEncoder::setMetaDataInternal( K3bAudioEncoder::MetaDataField f, const TQString& value )
{
switch( f ) {
case META_TRACK_TITLE:
d->title = value;
break;
case META_TRACK_ARTIST:
d->artist = value;
break;
case META_TRACK_COMMENT:
d->comment = value;
break;
case META_TRACK_NUMBER:
d->trackNumber = value;
break;
case META_ALBUM_TITLE:
d->cdTitle = value;
break;
case META_ALBUM_ARTIST:
d->cdArtist = value;
break;
case META_ALBUM_COMMENT:
d->cdComment = value;
break;
case META_YEAR:
d->year = value;
break;
case META_GENRE:
d->genre = value;
break;
}
}
void K3bExternalEncoder::finishEncoderInternal()
{
if( d->process ) {
if( d->process->isRunning() ) {
::close( d->process->stdinFd() );
// this is kind of evil...
// but we need to be sure the process exited when this method returnes
::waitpid( d->process->pid(), 0, 0 );
}
}
}
void K3bExternalEncoder::slotExternalProgramFinished( TDEProcess* p )
{
if( !p->normalExit() || p->exitStatus() != 0 )
kdDebug() << "(K3bExternalEncoder) program exited with error." << endl;
}
bool K3bExternalEncoder::openFile( const TQString& ext, const TQString& filename, const K3b::Msf& length )
{
d->fileName = filename;
d->extension = ext;
d->initialized = false;
d->length = length;
// delete existing files as some programs (like flac for example) might refuse to overwrite files
if( TQFile::exists( filename ) )
TQFile::remove( filename );
return true;
}
void K3bExternalEncoder::closeFile()
{
finishEncoderInternal();
}
bool K3bExternalEncoder::initEncoderInternal( const TQString& extension )
{
d->initialized = true;
// find the correct command
d->cmd = commandByExtension( extension );
if( d->cmd.command.isEmpty() ) {
setLastError( i18n("Invalid command: the command is empty.") );
return false;
}
// setup the process
delete d->process;
d->process = new K3bProcess();
d->process->setSplitStdout(true);
d->process->setRawStdin(true);
connect( d->process, TQT_SIGNAL(processExited(TDEProcess*)),
this, TQT_SLOT(slotExternalProgramFinished(TDEProcess*)) );
connect( d->process, TQT_SIGNAL(stderrLine(const TQString&)),
this, TQT_SLOT(slotExternalProgramOutputLine(const TQString&)) );
connect( d->process, TQT_SIGNAL(stdoutLine(const TQString&)),
this, TQT_SLOT(slotExternalProgramOutputLine(const TQString&)) );
// create the commandline
TQStringList params = TQStringList::split( ' ', d->cmd.command, false );
for( TQStringList::iterator it = params.begin(); it != params.end(); ++it ) {
(*it).replace( "%f", d->fileName );
(*it).replace( "%a", d->artist );
(*it).replace( "%t", d->title );
(*it).replace( "%c", d->comment );
(*it).replace( "%y", d->year );
(*it).replace( "%m", d->cdTitle );
(*it).replace( "%r", d->cdArtist );
(*it).replace( "%x", d->cdComment );
(*it).replace( "%n", d->trackNumber );
(*it).replace( "%g", d->genre );
*d->process << *it;
}
kdDebug() << "***** external parameters:" << endl;
const TQValueList<TQCString>& args = d->process->args();
TQString s;
for( TQValueList<TQCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
s += *it + " ";
}
kdDebug() << s << flush << endl;
// set one general error message
setLastError( i18n("Command failed: %1").arg( s ) );
if( d->process->start( TDEProcess::NotifyOnExit, TDEProcess::All ) ) {
if( d->cmd.writeWaveHeader )
return writeWaveHeader();
else
return true;
}
else {
TQString commandName = d->cmd.command.section( TQRegExp("\\s+"), 0 );
if( !TDEStandardDirs::findExe( commandName ).isEmpty() )
setLastError( i18n("Could not find program '%1'").arg(commandName) );
return false;
}
}
bool K3bExternalEncoder::writeWaveHeader()
{
kdDebug() << "(K3bExternalEncoder) writing wave header" << endl;
// write the RIFF thing
if( ::write( d->process->stdinFd(), s_riffHeader, 4 ) != 4 ) {
kdDebug() << "(K3bExternalEncoder) failed to write riff header." << endl;
return false;
}
// write the wave size
TQ_INT32 dataSize( d->length.audioBytes() );
TQ_INT32 wavSize( dataSize + 44 - 8 );
char c[4];
c[0] = (wavSize >> 0 ) & 0xff;
c[1] = (wavSize >> 8 ) & 0xff;
c[2] = (wavSize >> 16) & 0xff;
c[3] = (wavSize >> 24) & 0xff;
if( ::write( d->process->stdinFd(), c, 4 ) != 4 ) {
kdDebug() << "(K3bExternalEncoder) failed to write wave size." << endl;
return false;
}
// write static part of the header
if( ::write( d->process->stdinFd(), s_riffHeader+8, 32 ) != 32 ) {
kdDebug() << "(K3bExternalEncoder) failed to write wave header." << endl;
return false;
}
c[0] = (dataSize >> 0 ) & 0xff;
c[1] = (dataSize >> 8 ) & 0xff;
c[2] = (dataSize >> 16) & 0xff;
c[3] = (dataSize >> 24) & 0xff;
if( ::write( d->process->stdinFd(), c, 4 ) != 4 ) {
kdDebug() << "(K3bExternalEncoder) failed to write data size." << endl;
return false;
}
return true;
}
long K3bExternalEncoder::encodeInternal( const char* data, TQ_ULONG len )
{
if( !d->initialized )
if( !initEncoderInternal( d->extension ) )
return -1;
if( d->process ) {
if( d->process->isRunning() ) {
long written = 0;
//
// we swap the bytes to reduce user irritation ;)
// This is a little confused: We used to swap the byte order
// in older versions of this encoder since little endian seems
// to "feel" more natural.
// So now that we have a swap option we have to invert it to ensure
// compatibility
//
if( !d->cmd.swapByteOrder ) {
char* buffer = new char[len];
for( unsigned int i = 0; i < len-1; i+=2 ) {
buffer[i] = data[i+1];
buffer[i+1] = data[i];
}
written = ::write( d->process->stdinFd(), (const void*)buffer, len );
delete [] buffer;
}
else
written = ::write( d->process->stdinFd(), (const void*)data, len );
return written;
}
else
return -1;
}
else
return -1;
}
void K3bExternalEncoder::slotExternalProgramOutputLine( const TQString& line )
{
kdDebug() << "(" << d->cmd.name << ") " << line << endl;
}
TQStringList K3bExternalEncoder::extensions() const
{
TQStringList el;
TQValueList<K3bExternalEncoderCommand> cmds( K3bExternalEncoderCommand::readCommands() );
for( TQValueList<K3bExternalEncoderCommand>::iterator it = cmds.begin(); it != cmds.end(); ++it )
el.append( (*it).extension );
return el;
}
TQString K3bExternalEncoder::fileTypeComment( const TQString& ext ) const
{
return commandByExtension( ext ).name;
}
K3bPluginConfigWidget* K3bExternalEncoder::createConfigWidget( TQWidget* parent,
const char* name ) const
{
return new K3bExternalEncoderSettingsWidget( parent, name );
}
#include "k3bexternalencoder.moc"