added initial implementation of file dump cutting, re #3
This commit is contained in:
parent
cf2d4c85f0
commit
ee4ae648d0
|
@ -1,5 +1,7 @@
|
||||||
DarkIce next release
|
DarkIce next release
|
||||||
|
|
||||||
|
o added logging facility - [file-X] targets will cut the saved file
|
||||||
|
and rename it as needed when darkice recieves the SIGUSR1 signal
|
||||||
o added default configuration file handling - if no configuration file
|
o added default configuration file handling - if no configuration file
|
||||||
is specified, /etc/darkice.cfg is used
|
is specified, /etc/darkice.cfg is used
|
||||||
o fix to enable compiling on 64 bit platforms
|
o fix to enable compiling on 64 bit platforms
|
||||||
|
|
|
@ -71,6 +71,11 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Sink to dump the encoded data to
|
||||||
|
*/
|
||||||
|
Ref<Sink> sink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sample rate of the input.
|
* Sample rate of the input.
|
||||||
*/
|
*/
|
||||||
|
@ -119,6 +124,7 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
|
* @param sink the sink to send encoded output to
|
||||||
* @param inSampleRate sample rate of the input.
|
* @param inSampleRate sample rate of the input.
|
||||||
* @param inBitsPerSample number of bits per sample of the input.
|
* @param inBitsPerSample number of bits per sample of the input.
|
||||||
* @param inChannel number of channels of the input.
|
* @param inChannel number of channels of the input.
|
||||||
|
@ -130,7 +136,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
inline void
|
inline void
|
||||||
init ( unsigned int inSampleRate,
|
init ( Sink * sink,
|
||||||
|
unsigned int inSampleRate,
|
||||||
unsigned int inBitsPerSample,
|
unsigned int inBitsPerSample,
|
||||||
unsigned int inChannel,
|
unsigned int inChannel,
|
||||||
bool inBigEndian,
|
bool inBigEndian,
|
||||||
|
@ -140,6 +147,7 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
unsigned int outSampleRate,
|
unsigned int outSampleRate,
|
||||||
unsigned int outChannel ) throw ( Exception )
|
unsigned int outChannel ) throw ( Exception )
|
||||||
{
|
{
|
||||||
|
this->sink = sink;
|
||||||
this->inSampleRate = inSampleRate;
|
this->inSampleRate = inSampleRate;
|
||||||
this->inBitsPerSample = inBitsPerSample;
|
this->inBitsPerSample = inBitsPerSample;
|
||||||
this->inChannel = inChannel;
|
this->inChannel = inChannel;
|
||||||
|
@ -182,6 +190,7 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
* @param sink the sink to send encoded output to
|
||||||
* @param inSampleRate sample rate of the input.
|
* @param inSampleRate sample rate of the input.
|
||||||
* @param inBitsPerSample number of bits per sample of the input.
|
* @param inBitsPerSample number of bits per sample of the input.
|
||||||
* @param inChannel number of channels of the input.
|
* @param inChannel number of channels of the input.
|
||||||
|
@ -196,7 +205,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
AudioEncoder ( unsigned int inSampleRate,
|
AudioEncoder ( Sink * sink,
|
||||||
|
unsigned int inSampleRate,
|
||||||
unsigned int inBitsPerSample,
|
unsigned int inBitsPerSample,
|
||||||
unsigned int inChannel,
|
unsigned int inChannel,
|
||||||
bool inBigEndian,
|
bool inBigEndian,
|
||||||
|
@ -207,7 +217,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
unsigned int outChannel = 0 )
|
unsigned int outChannel = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
{
|
{
|
||||||
init ( inSampleRate,
|
init ( sink,
|
||||||
|
inSampleRate,
|
||||||
inBitsPerSample,
|
inBitsPerSample,
|
||||||
inChannel,
|
inChannel,
|
||||||
inBigEndian,
|
inBigEndian,
|
||||||
|
@ -221,6 +232,7 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
|
* @param sink the sink to send encoded output to
|
||||||
* @param as get input sample rate, bits per sample and channels
|
* @param as get input sample rate, bits per sample and channels
|
||||||
* from this AudioSource.
|
* from this AudioSource.
|
||||||
* @param outBitrateMode the bit rate mode of the output.
|
* @param outBitrateMode the bit rate mode of the output.
|
||||||
|
@ -233,7 +245,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
AudioEncoder ( const AudioSource * as,
|
AudioEncoder ( Sink * sink,
|
||||||
|
const AudioSource * as,
|
||||||
BitrateMode outBitrateMode,
|
BitrateMode outBitrateMode,
|
||||||
unsigned int outBitrate,
|
unsigned int outBitrate,
|
||||||
double outQuality,
|
double outQuality,
|
||||||
|
@ -241,7 +254,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
unsigned int outChannel = 0 )
|
unsigned int outChannel = 0 )
|
||||||
throw ( Exception)
|
throw ( Exception)
|
||||||
{
|
{
|
||||||
init( as->getSampleRate(),
|
init( sink,
|
||||||
|
as->getSampleRate(),
|
||||||
as->getBitsPerSample(),
|
as->getBitsPerSample(),
|
||||||
as->getChannel(),
|
as->getChannel(),
|
||||||
as->isBigEndian(),
|
as->isBigEndian(),
|
||||||
|
@ -260,7 +274,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
inline
|
inline
|
||||||
AudioEncoder ( const AudioEncoder & encoder ) throw ( Exception )
|
AudioEncoder ( const AudioEncoder & encoder ) throw ( Exception )
|
||||||
{
|
{
|
||||||
init ( encoder.inSampleRate,
|
init ( encoder.sink.get(),
|
||||||
|
encoder.inSampleRate,
|
||||||
encoder.inBitsPerSample,
|
encoder.inBitsPerSample,
|
||||||
encoder.inChannel,
|
encoder.inChannel,
|
||||||
encoder.inBigEndian,
|
encoder.inBigEndian,
|
||||||
|
@ -284,7 +299,8 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
if ( this != &encoder ) {
|
if ( this != &encoder ) {
|
||||||
strip();
|
strip();
|
||||||
|
|
||||||
init ( encoder.inSampleRate,
|
init ( encoder.sink.get(),
|
||||||
|
encoder.inSampleRate,
|
||||||
encoder.inBitsPerSample,
|
encoder.inBitsPerSample,
|
||||||
encoder.inChannel,
|
encoder.inChannel,
|
||||||
encoder.inBigEndian,
|
encoder.inBigEndian,
|
||||||
|
@ -312,6 +328,17 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
strip();
|
strip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying sink, that the encoded content is sent to.
|
||||||
|
*
|
||||||
|
* @return the underlying sink
|
||||||
|
*/
|
||||||
|
inline virtual Ref<Sink>
|
||||||
|
getSink(void) throw ()
|
||||||
|
{
|
||||||
|
return sink;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of channels of the input.
|
* Get the number of channels of the input.
|
||||||
*
|
*
|
||||||
|
@ -438,6 +465,20 @@ class AudioEncoder : public Sink, public virtual Referable
|
||||||
*/
|
*/
|
||||||
virtual void
|
virtual void
|
||||||
stop ( void ) throw ( Exception ) = 0;
|
stop ( void ) throw ( Exception ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*
|
||||||
|
* Typically this action is delegated to the underlying sink.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
sink->cut();
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -366,6 +366,18 @@ class BufferedSink : public Sink, public virtual Reporter
|
||||||
write( b, 0);
|
write( b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
sink->cut();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the BufferedSink. Closes the underlying Sink.
|
* Close the BufferedSink. Closes the underlying Sink.
|
||||||
*
|
*
|
||||||
|
|
|
@ -362,6 +362,19 @@ class CastSink : public Sink, public virtual Reporter
|
||||||
return getSink()->flush();
|
return getSink()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
if ( streamDump != 0 ) {
|
||||||
|
streamDump->cut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the CastSink.
|
* Close the CastSink.
|
||||||
*
|
*
|
||||||
|
|
|
@ -325,6 +325,22 @@ Connector :: transfer ( unsigned long bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Signal to each sink to cut what they've done so far, and start anew.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
Connector :: cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
unsigned int u;
|
||||||
|
|
||||||
|
for ( u = 0; u < numSinks; ++u ) {
|
||||||
|
if ( sinks[u]->isOpen() ) {
|
||||||
|
sinks[u]->cut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Close the source and all the sinks if needed
|
* Close the source and all the sinks if needed
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -234,6 +234,15 @@ class Connector : public virtual Referable, public virtual Reporter
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception );
|
unsigned int usec ) throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal to each sink we have that they need to cut what they are
|
||||||
|
* doing, and start again. For FileSinks, this usually means to
|
||||||
|
* save the archive file recorded so far, and start a new archive
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
virtual void
|
||||||
|
cut ( void ) throw ();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the Connector. The Source and all Sinks are closed.
|
* Close the Connector. The Source and all Sinks are closed.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1137,6 +1137,20 @@ DarkIce :: run ( void ) throw ( Exception )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Tell each sink to cut what they are doing, and start again.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
DarkIce :: cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
reportEvent( 5, "cutting");
|
||||||
|
|
||||||
|
encConnector->cut();
|
||||||
|
|
||||||
|
reportEvent( 5, "cutting ends");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
|
|
||||||
$Source$
|
$Source$
|
||||||
|
|
|
@ -296,6 +296,15 @@ class DarkIce : public virtual Referable, public virtual Reporter
|
||||||
virtual int
|
virtual int
|
||||||
run ( void ) throw ( Exception );
|
run ( void ) throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal to each sink we have that they need to cut what they are
|
||||||
|
* doing, and start again. For FileSinks, this usually means to
|
||||||
|
* save the archive file recorded so far, and start a new archive
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
virtual void
|
||||||
|
cut ( void ) throw ();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ FaacEncoder :: open ( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the underlying sink
|
// open the underlying sink
|
||||||
if ( !sink->open() ) {
|
if ( !getSink()->open() ) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
"faac lib opening underlying sink error");
|
"faac lib opening underlying sink error");
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ FaacEncoder :: write ( const void * buf,
|
||||||
inSamples,
|
inSamples,
|
||||||
faacBuf,
|
faacBuf,
|
||||||
maxOutputBytes);
|
maxOutputBytes);
|
||||||
sink->write(faacBuf, outputBytes);
|
getSink()->write(faacBuf, outputBytes);
|
||||||
|
|
||||||
processedSamples += inSamples;
|
processedSamples += inSamples;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ FaacEncoder :: flush ( void )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sink->flush();
|
getSink()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ FaacEncoder :: close ( void ) throw ( Exception )
|
||||||
faacEncClose(encoderHandle);
|
faacEncClose(encoderHandle);
|
||||||
faacOpen = false;
|
faacOpen = false;
|
||||||
|
|
||||||
sink->close();
|
getSink()->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,15 +98,9 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
int lowpass;
|
int lowpass;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Sink to dump mp3 data to
|
|
||||||
*/
|
|
||||||
Ref<Sink> sink;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
* @param sink the sink to send mp3 output to
|
|
||||||
* @param lowpass frequency threshold for the lowpass filter.
|
* @param lowpass frequency threshold for the lowpass filter.
|
||||||
* Input above this frequency is cut.
|
* Input above this frequency is cut.
|
||||||
* If 0, faac's default values are used,
|
* If 0, faac's default values are used,
|
||||||
|
@ -114,11 +108,9 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
inline void
|
inline void
|
||||||
init ( Sink * sink,
|
init ( int lowpass) throw (Exception)
|
||||||
int lowpass) throw (Exception)
|
|
||||||
{
|
{
|
||||||
this->faacOpen = false;
|
this->faacOpen = false;
|
||||||
this->sink = sink;
|
|
||||||
this->lowpass = lowpass;
|
this->lowpass = lowpass;
|
||||||
|
|
||||||
if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) {
|
if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) {
|
||||||
|
@ -205,7 +197,8 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
int lowpass = 0)
|
int lowpass = 0)
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( inSampleRate,
|
: AudioEncoder ( sink,
|
||||||
|
inSampleRate,
|
||||||
inBitsPerSample,
|
inBitsPerSample,
|
||||||
inChannel,
|
inChannel,
|
||||||
inBigEndian,
|
inBigEndian,
|
||||||
|
@ -215,7 +208,7 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, lowpass);
|
init( lowpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,14 +241,15 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
int lowpass = 0)
|
int lowpass = 0)
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( as,
|
: AudioEncoder ( sink,
|
||||||
|
as,
|
||||||
outBitrateMode,
|
outBitrateMode,
|
||||||
outBitrate,
|
outBitrate,
|
||||||
outQuality,
|
outQuality,
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, lowpass);
|
init( lowpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,7 +262,7 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
: AudioEncoder( encoder )
|
: AudioEncoder( encoder )
|
||||||
{
|
{
|
||||||
init( encoder.sink.get(), encoder.lowpass);
|
init( encoder.lowpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -299,7 +293,7 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter
|
||||||
if ( this != &encoder ) {
|
if ( this != &encoder ) {
|
||||||
strip();
|
strip();
|
||||||
AudioEncoder::operator=( encoder);
|
AudioEncoder::operator=( encoder);
|
||||||
init( encoder.sink.get(), encoder.lowpass);
|
init( encoder.lowpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -238,6 +238,17 @@ class FileCast : public CastSink
|
||||||
return targetFile->flush();
|
return targetFile->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
targetFile->cut();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the FileCast.
|
* Close the FileCast.
|
||||||
*
|
*
|
||||||
|
|
|
@ -81,6 +81,17 @@
|
||||||
#error need string.h
|
#error need string.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error need signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
|
@ -242,7 +253,8 @@ FileSink :: canWrite ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -251,10 +263,15 @@ FileSink :: canWrite ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( fileDescriptor, &fdset);
|
FD_SET( fileDescriptor, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( fileDescriptor + 1, NULL, &fdset, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( fileDescriptor + 1, NULL, &fdset, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
@ -291,6 +308,58 @@ FileSink :: write ( const void * buf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Get the file name to where to move the data saved so far.
|
||||||
|
* The trick is to read the file name from a file named
|
||||||
|
* /tmp/darkice.$PID , where $PID is the current process id
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
std::string
|
||||||
|
FileSink :: getArchiveFileName ( void ) throw ( Exception )
|
||||||
|
{
|
||||||
|
pid_t pid = getpid();
|
||||||
|
std::stringstream metaFileName;
|
||||||
|
|
||||||
|
metaFileName << "/tmp/darkice." << pid;
|
||||||
|
|
||||||
|
std::ifstream ifs(metaFileName.str().c_str());
|
||||||
|
if (!ifs.good()) {
|
||||||
|
throw Exception(__FILE__, __LINE__,
|
||||||
|
"can't find file ", metaFileName.str().c_str(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string archiveFileName;
|
||||||
|
ifs >> archiveFileName;
|
||||||
|
ifs.close();
|
||||||
|
|
||||||
|
return archiveFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Cut what we've done so far, and start anew.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
FileSink :: cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string archiveFileName = getArchiveFileName();
|
||||||
|
|
||||||
|
if (::rename(fileName, archiveFileName.c_str()) != 0) {
|
||||||
|
reportEvent(2, "couldn't move file", fileName,
|
||||||
|
"to", archiveFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ( Exception &e ) {
|
||||||
|
reportEvent(2, "error during archive cut", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
create();
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Close the FileSink
|
* Close the FileSink
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -80,6 +80,16 @@ class FileSink : public Sink, public virtual Reporter
|
||||||
void
|
void
|
||||||
strip ( void ) throw ( Exception );
|
strip ( void ) throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file name to where to move the data saved so far.
|
||||||
|
* Used in cut().
|
||||||
|
*
|
||||||
|
* @return the file name where to move the data saved so far.
|
||||||
|
* @throws Exception on file operation errors
|
||||||
|
*/
|
||||||
|
std::string
|
||||||
|
getArchiveFileName( void ) throw ( Exception );
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -227,6 +237,14 @@ class FileSink : public Sink, public virtual Reporter
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
virtual void
|
||||||
|
cut ( void ) throw ();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the FileSink.
|
* Close the FileSink.
|
||||||
*
|
*
|
||||||
|
|
|
@ -69,6 +69,12 @@
|
||||||
#error need string.h
|
#error need string.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error need signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
@ -197,7 +203,8 @@ FileSource :: canRead ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -206,10 +213,15 @@ FileSource :: canRead ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( fileDescriptor, &fdset);
|
FD_SET( fileDescriptor, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( fileDescriptor + 1, &fdset, NULL, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( fileDescriptor + 1, &fdset, NULL, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
|
|
@ -71,7 +71,7 @@ LameLibEncoder :: open ( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the underlying sink
|
// open the underlying sink
|
||||||
if ( !sink->open() ) {
|
if ( !getSink()->open() ) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
"lame lib opening underlying sink error");
|
"lame lib opening underlying sink error");
|
||||||
}
|
}
|
||||||
|
@ -331,7 +331,7 @@ LameLibEncoder :: write ( const void * buf,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int written = sink->write( mp3Buf, ret);
|
unsigned int written = getSink()->write( mp3Buf, ret);
|
||||||
delete[] mp3Buf;
|
delete[] mp3Buf;
|
||||||
// just let go data that could not be written
|
// just let go data that could not be written
|
||||||
if ( written < (unsigned int) ret ) {
|
if ( written < (unsigned int) ret ) {
|
||||||
|
@ -362,7 +362,7 @@ LameLibEncoder :: flush ( void )
|
||||||
|
|
||||||
ret = lame_encode_flush( lameGlobalFlags, mp3Buf, mp3Size );
|
ret = lame_encode_flush( lameGlobalFlags, mp3Buf, mp3Size );
|
||||||
|
|
||||||
unsigned int written = sink->write( mp3Buf, ret);
|
unsigned int written = getSink()->write( mp3Buf, ret);
|
||||||
delete[] mp3Buf;
|
delete[] mp3Buf;
|
||||||
|
|
||||||
// just let go data that could not be written
|
// just let go data that could not be written
|
||||||
|
@ -372,7 +372,7 @@ LameLibEncoder :: flush ( void )
|
||||||
ret - written);
|
ret - written);
|
||||||
}
|
}
|
||||||
|
|
||||||
sink->flush();
|
getSink()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ LameLibEncoder :: close ( void ) throw ( Exception )
|
||||||
lame_close( lameGlobalFlags);
|
lame_close( lameGlobalFlags);
|
||||||
lameGlobalFlags = 0;
|
lameGlobalFlags = 0;
|
||||||
|
|
||||||
sink->close();
|
getSink()->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,6 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
lame_global_flags * lameGlobalFlags;
|
lame_global_flags * lameGlobalFlags;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Sink to dump mp3 data to
|
|
||||||
*/
|
|
||||||
Ref<Sink> sink;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lowpass filter. Sound frequency in Hz, from where up the
|
* Lowpass filter. Sound frequency in Hz, from where up the
|
||||||
* input is cut.
|
* input is cut.
|
||||||
|
@ -98,7 +93,6 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
* @param sink the sink to send mp3 output to
|
|
||||||
* @param lowpass frequency threshold for the lowpass filter.
|
* @param lowpass frequency threshold for the lowpass filter.
|
||||||
* Input above this frequency is cut.
|
* Input above this frequency is cut.
|
||||||
* If 0, lame's default values are used,
|
* If 0, lame's default values are used,
|
||||||
|
@ -110,12 +104,10 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
inline void
|
inline void
|
||||||
init ( Sink * sink,
|
init ( int lowpass,
|
||||||
int lowpass,
|
|
||||||
int highpass ) throw ( Exception )
|
int highpass ) throw ( Exception )
|
||||||
{
|
{
|
||||||
this->lameGlobalFlags = NULL;
|
this->lameGlobalFlags = NULL;
|
||||||
this->sink = sink;
|
|
||||||
this->lowpass = lowpass;
|
this->lowpass = lowpass;
|
||||||
this->highpass = highpass;
|
this->highpass = highpass;
|
||||||
|
|
||||||
|
@ -209,7 +201,8 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
int highpass = 0 )
|
int highpass = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( inSampleRate,
|
: AudioEncoder ( sink,
|
||||||
|
inSampleRate,
|
||||||
inBitsPerSample,
|
inBitsPerSample,
|
||||||
inChannel,
|
inChannel,
|
||||||
inBigEndian,
|
inBigEndian,
|
||||||
|
@ -219,7 +212,7 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, lowpass, highpass);
|
init( lowpass, highpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -257,14 +250,15 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
int highpass = 0 )
|
int highpass = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( as,
|
: AudioEncoder ( sink,
|
||||||
|
as,
|
||||||
outBitrateMode,
|
outBitrateMode,
|
||||||
outBitrate,
|
outBitrate,
|
||||||
outQuality,
|
outQuality,
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, lowpass, highpass);
|
init( lowpass, highpass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,7 +271,7 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
: AudioEncoder( encoder )
|
: AudioEncoder( encoder )
|
||||||
{
|
{
|
||||||
init( encoder.sink.get(), encoder.lowpass, encoder.highpass );
|
init( encoder.lowpass, encoder.highpass );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -308,7 +302,7 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
if ( this != &encoder ) {
|
if ( this != &encoder ) {
|
||||||
strip();
|
strip();
|
||||||
AudioEncoder::operator=( encoder);
|
AudioEncoder::operator=( encoder);
|
||||||
init( encoder.sink.get(), encoder.lowpass, encoder.highpass );
|
init( encoder.lowpass, encoder.highpass );
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -293,6 +293,11 @@ MultiThreadedConnector :: sinkThread( int ixSink )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( threadData->cut) {
|
||||||
|
sink->cut();
|
||||||
|
threadData->cut = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( threadData->accepting ) {
|
if ( threadData->accepting ) {
|
||||||
if ( sink->canWrite( 0, 0) ) {
|
if ( sink->canWrite( 0, 0) ) {
|
||||||
try {
|
try {
|
||||||
|
@ -337,6 +342,22 @@ MultiThreadedConnector :: sinkThread( int ixSink )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Signal to each sink to cut what they've done so far, and start anew.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
MultiThreadedConnector :: cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
for ( unsigned int i = 0; i < numSinks; ++i ) {
|
||||||
|
threads[i].cut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: it might be more appropriate to signal all the threads here
|
||||||
|
// but, they'll get signaled on new data anyway, and it might be
|
||||||
|
// enough for them to cut at that time
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Stop the treads
|
* Stop the treads
|
||||||
* Close the source and all the sinks if needed
|
* Close the source and all the sinks if needed
|
||||||
|
|
|
@ -107,6 +107,12 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
*/
|
*/
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag to show that the sink should be made to cut in the
|
||||||
|
* next iteration.
|
||||||
|
*/
|
||||||
|
bool cut;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -118,6 +124,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
this->thread = 0;
|
this->thread = 0;
|
||||||
this->accepting = false;
|
this->accepting = false;
|
||||||
this->isDone = false;
|
this->isDone = false;
|
||||||
|
this->cut = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,6 +320,15 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
unsigned int sec,
|
unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception );
|
unsigned int usec ) throw ( Exception );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal to each sink we have that they need to cut what they are
|
||||||
|
* doing, and start again. For FileSinks, this usually means to
|
||||||
|
* save the archive file recorded so far, and start a new archive
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
virtual void
|
||||||
|
cut ( void ) throw ();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the Connector. The Source and all Sinks are closed.
|
* Close the Connector. The Source and all Sinks are closed.
|
||||||
*
|
*
|
||||||
|
@ -322,7 +338,7 @@ class MultiThreadedConnector : public virtual Connector
|
||||||
close ( void ) throw ( Exception );
|
close ( void ) throw ( Exception );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the function for each thread.
|
* This is the worker function for each thread.
|
||||||
* This function has to return fast
|
* This function has to return fast
|
||||||
*
|
*
|
||||||
* @param ixSink the index of the sink this thread works on.
|
* @param ixSink the index of the sink this thread works on.
|
||||||
|
|
|
@ -80,6 +80,12 @@
|
||||||
#error need sys/ioctl.h
|
#error need sys/ioctl.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error need signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_SOUNDCARD_H
|
#ifdef HAVE_SYS_SOUNDCARD_H
|
||||||
#include <sys/soundcard.h>
|
#include <sys/soundcard.h>
|
||||||
#else
|
#else
|
||||||
|
@ -230,7 +236,8 @@ OssDspSource :: canRead ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -247,10 +254,15 @@ OssDspSource :: canRead ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( fileDescriptor, &fdset);
|
FD_SET( fileDescriptor, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( fileDescriptor + 1, &fdset, NULL, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( fileDescriptor + 1, &fdset, NULL, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
|
|
@ -156,6 +156,14 @@ class Sink : public virtual Referable
|
||||||
virtual void
|
virtual void
|
||||||
flush ( void ) throw ( Exception ) = 0;
|
flush ( void ) throw ( Exception ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*/
|
||||||
|
virtual void
|
||||||
|
cut ( void ) throw () = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the Sink.
|
* Close the Sink.
|
||||||
*
|
*
|
||||||
|
|
|
@ -80,6 +80,12 @@
|
||||||
#error need sys/ioctl.h
|
#error need sys/ioctl.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error need signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined( HAVE_SYS_AUDIO_H )
|
#if defined( HAVE_SYS_AUDIO_H )
|
||||||
#include <sys/audio.h>
|
#include <sys/audio.h>
|
||||||
#elif defined( HAVE_SYS_AUDIOIO_H )
|
#elif defined( HAVE_SYS_AUDIOIO_H )
|
||||||
|
@ -202,7 +208,8 @@ SolarisDspSource :: canRead ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -211,10 +218,15 @@ SolarisDspSource :: canRead ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( fileDescriptor, &fdset);
|
FD_SET( fileDescriptor, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( fileDescriptor + 1, &fdset, NULL, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( fileDescriptor + 1, &fdset, NULL, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
|
|
@ -81,6 +81,12 @@
|
||||||
#error need sys/time.h
|
#error need sys/time.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error need signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
|
@ -243,7 +249,8 @@ TcpSocket :: canRead ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -252,10 +259,15 @@ TcpSocket :: canRead ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( sockfd, &fdset);
|
FD_SET( sockfd, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( sockfd + 1, &fdset, NULL, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( sockfd + 1, &fdset, NULL, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
@ -305,7 +317,8 @@ TcpSocket :: canWrite ( unsigned int sec,
|
||||||
unsigned int usec ) throw ( Exception )
|
unsigned int usec ) throw ( Exception )
|
||||||
{
|
{
|
||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
struct timeval tv;
|
struct timespec timespec;
|
||||||
|
sigset_t sigset;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ( !isOpen() ) {
|
if ( !isOpen() ) {
|
||||||
|
@ -314,10 +327,15 @@ TcpSocket :: canWrite ( unsigned int sec,
|
||||||
|
|
||||||
FD_ZERO( &fdset);
|
FD_ZERO( &fdset);
|
||||||
FD_SET( sockfd, &fdset);
|
FD_SET( sockfd, &fdset);
|
||||||
tv.tv_sec = sec;
|
|
||||||
tv.tv_usec = usec;
|
|
||||||
|
|
||||||
ret = select( sockfd + 1, NULL, &fdset, NULL, &tv);
|
timespec.tv_sec = sec;
|
||||||
|
timespec.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
|
// mask out SIGUSR1, as we're expecting that signal for other reasons
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGUSR1);
|
||||||
|
|
||||||
|
ret = pselect( sockfd + 1, NULL, &fdset, NULL, ×pec, &sigset);
|
||||||
|
|
||||||
if ( ret == -1 ) {
|
if ( ret == -1 ) {
|
||||||
throw Exception( __FILE__, __LINE__, "select error");
|
throw Exception( __FILE__, __LINE__, "select error");
|
||||||
|
|
|
@ -258,6 +258,18 @@ class TcpSocket : public Source, public Sink
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut what the sink has been doing so far, and start anew.
|
||||||
|
* This usually means separating the data sent to the sink up
|
||||||
|
* until now, and start saving a new chunk of data.
|
||||||
|
*
|
||||||
|
* For TcpSocket, this is a no-op.
|
||||||
|
*/
|
||||||
|
inline virtual void
|
||||||
|
cut ( void ) throw ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the TcpSocket.
|
* Close the TcpSocket.
|
||||||
*
|
*
|
||||||
|
|
|
@ -65,10 +65,9 @@ static const char fileid[] = "$Id$";
|
||||||
* Initialize the object
|
* Initialize the object
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
TwoLameLibEncoder :: init ( Sink * sink ) throw ( Exception )
|
TwoLameLibEncoder :: init ( void ) throw ( Exception )
|
||||||
{
|
{
|
||||||
this->twolame_opts = NULL;
|
this->twolame_opts = NULL;
|
||||||
this->sink = sink;
|
|
||||||
|
|
||||||
if ( getInBitsPerSample() != 16 ) {
|
if ( getInBitsPerSample() != 16 ) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
|
@ -106,7 +105,7 @@ TwoLameLibEncoder :: open ( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the underlying sink
|
// open the underlying sink
|
||||||
if ( !sink->open() ) {
|
if ( !getSink()->open() ) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
"TwoLAME lib opening underlying sink error");
|
"TwoLAME lib opening underlying sink error");
|
||||||
}
|
}
|
||||||
|
@ -239,7 +238,7 @@ TwoLameLibEncoder :: write ( const void * buf,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int written = sink->write( mp2Buf, ret);
|
unsigned int written = getSink()->write( mp2Buf, ret);
|
||||||
delete[] mp2Buf;
|
delete[] mp2Buf;
|
||||||
// just let go data that could not be written
|
// just let go data that could not be written
|
||||||
if ( written < (unsigned int) ret ) {
|
if ( written < (unsigned int) ret ) {
|
||||||
|
@ -270,7 +269,7 @@ TwoLameLibEncoder :: flush ( void )
|
||||||
|
|
||||||
ret = twolame_encode_flush( twolame_opts, mp2Buf, mp2Size );
|
ret = twolame_encode_flush( twolame_opts, mp2Buf, mp2Size );
|
||||||
|
|
||||||
unsigned int written = sink->write( mp2Buf, ret);
|
unsigned int written = getSink()->write( mp2Buf, ret);
|
||||||
delete[] mp2Buf;
|
delete[] mp2Buf;
|
||||||
|
|
||||||
// just let go data that could not be written
|
// just let go data that could not be written
|
||||||
|
@ -280,7 +279,7 @@ TwoLameLibEncoder :: flush ( void )
|
||||||
ret - written);
|
ret - written);
|
||||||
}
|
}
|
||||||
|
|
||||||
sink->flush();
|
getSink()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -293,7 +292,7 @@ TwoLameLibEncoder :: close ( void ) throw ( Exception )
|
||||||
if ( isOpen() ) {
|
if ( isOpen() ) {
|
||||||
flush();
|
flush();
|
||||||
twolame_close( &twolame_opts );
|
twolame_close( &twolame_opts );
|
||||||
sink->close();
|
getSink()->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,6 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
twolame_options * twolame_opts;
|
twolame_options * twolame_opts;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Sink to dump mp2 data to
|
|
||||||
*/
|
|
||||||
Ref<Sink> sink;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
|
@ -90,7 +85,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init ( Sink * sink ) throw ( Exception );
|
init ( void ) throw ( Exception );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* De-initialize the object.
|
* De-initialize the object.
|
||||||
|
@ -147,7 +142,8 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
unsigned int outChannel = 0 )
|
unsigned int outChannel = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( inSampleRate,
|
: AudioEncoder ( sink,
|
||||||
|
inSampleRate,
|
||||||
inBitsPerSample,
|
inBitsPerSample,
|
||||||
inChannel,
|
inChannel,
|
||||||
inBigEndian,
|
inBigEndian,
|
||||||
|
@ -157,7 +153,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink );
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,14 +179,15 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
unsigned int outChannel = 0 )
|
unsigned int outChannel = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( as,
|
: AudioEncoder ( sink,
|
||||||
|
as,
|
||||||
outBitrateMode,
|
outBitrateMode,
|
||||||
outBitrate,
|
outBitrate,
|
||||||
0.0f, // outQuality
|
0.0f, // outQuality
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink );
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,7 +200,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
: AudioEncoder( encoder )
|
: AudioEncoder( encoder )
|
||||||
{
|
{
|
||||||
init( encoder.sink.get() );
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +231,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
if ( this != &encoder ) {
|
if ( this != &encoder ) {
|
||||||
strip();
|
strip();
|
||||||
AudioEncoder::operator=( encoder);
|
AudioEncoder::operator=( encoder);
|
||||||
init( encoder.sink.get() );
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -62,11 +62,9 @@ static const char fileid[] = "$Id$";
|
||||||
* Initialize the encoder
|
* Initialize the encoder
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
VorbisLibEncoder :: init ( CastSink * sink,
|
VorbisLibEncoder :: init ( unsigned int outMaxBitrate )
|
||||||
unsigned int outMaxBitrate )
|
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
{
|
{
|
||||||
this->sink = sink;
|
|
||||||
this->outMaxBitrate = outMaxBitrate;
|
this->outMaxBitrate = outMaxBitrate;
|
||||||
|
|
||||||
if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) {
|
if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) {
|
||||||
|
@ -140,7 +138,7 @@ VorbisLibEncoder :: open ( void )
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the underlying sink
|
// open the underlying sink
|
||||||
if ( !sink->open() ) {
|
if ( !getSink()->open() ) {
|
||||||
throw Exception( __FILE__, __LINE__,
|
throw Exception( __FILE__, __LINE__,
|
||||||
"vorbis lib opening underlying sink error");
|
"vorbis lib opening underlying sink error");
|
||||||
}
|
}
|
||||||
|
@ -206,11 +204,17 @@ VorbisLibEncoder :: open ( void )
|
||||||
|
|
||||||
// create an empty vorbis_comment structure
|
// create an empty vorbis_comment structure
|
||||||
vorbis_comment_init( &vorbisComment);
|
vorbis_comment_init( &vorbisComment);
|
||||||
|
/* FIXME: removed title metadata when the sink type was changed from
|
||||||
|
* CastSink to the more generic Sink.
|
||||||
|
* make sure to add metadata somehow
|
||||||
// Add comment to vorbis headers to show title in players
|
// Add comment to vorbis headers to show title in players
|
||||||
// stupid cast to (char*) because of stupid vorbis API
|
// stupid cast to (char*) because of stupid vorbis API
|
||||||
if ( sink->getName() ) {
|
if ( getSink()->getName() ) {
|
||||||
vorbis_comment_add_tag(&vorbisComment, "TITLE", (char*)sink->getName());
|
vorbis_comment_add_tag(&vorbisComment,
|
||||||
|
"TITLE",
|
||||||
|
(char*) getSink()->getName());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// create the vorbis stream headers and send them to the underlying sink
|
// create the vorbis stream headers and send them to the underlying sink
|
||||||
ogg_packet header;
|
ogg_packet header;
|
||||||
|
@ -231,8 +235,8 @@ VorbisLibEncoder :: open ( void )
|
||||||
|
|
||||||
ogg_page oggPage;
|
ogg_page oggPage;
|
||||||
while ( ogg_stream_flush( &oggStreamState, &oggPage) ) {
|
while ( ogg_stream_flush( &oggStreamState, &oggPage) ) {
|
||||||
sink->write( oggPage.header, oggPage.header_len);
|
getSink()->write( oggPage.header, oggPage.header_len);
|
||||||
sink->write( oggPage.body, oggPage.body_len);
|
getSink()->write( oggPage.body, oggPage.body_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
vorbis_comment_clear( &vorbisComment );
|
vorbis_comment_clear( &vorbisComment );
|
||||||
|
@ -346,7 +350,7 @@ VorbisLibEncoder :: flush ( void )
|
||||||
|
|
||||||
vorbis_analysis_wrote( &vorbisDspState, 0);
|
vorbis_analysis_wrote( &vorbisDspState, 0);
|
||||||
vorbisBlocksOut();
|
vorbisBlocksOut();
|
||||||
sink->flush();
|
getSink()->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -370,8 +374,8 @@ VorbisLibEncoder :: vorbisBlocksOut ( void ) throw ( Exception )
|
||||||
while ( ogg_stream_pageout( &oggStreamState, &oggPage) ) {
|
while ( ogg_stream_pageout( &oggStreamState, &oggPage) ) {
|
||||||
int written;
|
int written;
|
||||||
|
|
||||||
written = sink->write( oggPage.header, oggPage.header_len);
|
written = getSink()->write(oggPage.header, oggPage.header_len);
|
||||||
written += sink->write( oggPage.body, oggPage.body_len);
|
written += getSink()->write( oggPage.body, oggPage.body_len);
|
||||||
|
|
||||||
if ( written < oggPage.header_len + oggPage.body_len ) {
|
if ( written < oggPage.header_len + oggPage.body_len ) {
|
||||||
// just let go data that could not be written
|
// just let go data that could not be written
|
||||||
|
@ -402,7 +406,7 @@ VorbisLibEncoder :: close ( void ) throw ( Exception )
|
||||||
|
|
||||||
encoderOpen = false;
|
encoderOpen = false;
|
||||||
|
|
||||||
sink->close();
|
getSink()->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,11 +104,6 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
*/
|
*/
|
||||||
ogg_stream_state oggStreamState;
|
ogg_stream_state oggStreamState;
|
||||||
|
|
||||||
/**
|
|
||||||
* The Sink to dump encoded data to
|
|
||||||
*/
|
|
||||||
Ref<CastSink> sink;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum bitrate of the output in kbits/sec. If 0, don't care.
|
* Maximum bitrate of the output in kbits/sec. If 0, don't care.
|
||||||
*/
|
*/
|
||||||
|
@ -127,13 +122,11 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
/**
|
/**
|
||||||
* Initialize the object.
|
* Initialize the object.
|
||||||
*
|
*
|
||||||
* @param sink the sink to send encoded output to
|
|
||||||
* @param the maximum bit rate
|
* @param the maximum bit rate
|
||||||
* @exception Exception
|
* @exception Exception
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init ( CastSink * sink,
|
init ( unsigned int outMaxBitrate ) throw ( Exception );
|
||||||
unsigned int outMaxBitrate ) throw ( Exception );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* De-initialize the object.
|
* De-initialize the object.
|
||||||
|
@ -204,7 +197,8 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
unsigned int outMaxBitrate = 0 )
|
unsigned int outMaxBitrate = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( inSampleRate,
|
: AudioEncoder ( sink,
|
||||||
|
inSampleRate,
|
||||||
inBitsPerSample,
|
inBitsPerSample,
|
||||||
inChannel,
|
inChannel,
|
||||||
inBigEndian,
|
inBigEndian,
|
||||||
|
@ -214,7 +208,7 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, outMaxBitrate);
|
init( outMaxBitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,14 +239,15 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
unsigned int outMaxBitrate = 0 )
|
unsigned int outMaxBitrate = 0 )
|
||||||
throw ( Exception )
|
throw ( Exception )
|
||||||
|
|
||||||
: AudioEncoder ( as,
|
: AudioEncoder ( sink,
|
||||||
|
as,
|
||||||
outBitrateMode,
|
outBitrateMode,
|
||||||
outBitrate,
|
outBitrate,
|
||||||
outQuality,
|
outQuality,
|
||||||
outSampleRate,
|
outSampleRate,
|
||||||
outChannel )
|
outChannel )
|
||||||
{
|
{
|
||||||
init( sink, outMaxBitrate);
|
init( outMaxBitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,7 +263,7 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
if( encoder.isOpen() ) {
|
if( encoder.isOpen() ) {
|
||||||
throw Exception(__FILE__, __LINE__, "don't copy open encoders");
|
throw Exception(__FILE__, __LINE__, "don't copy open encoders");
|
||||||
}
|
}
|
||||||
init( encoder.sink.get(), encoder.getOutMaxBitrate() );
|
init( encoder.getOutMaxBitrate() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -302,7 +297,7 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter
|
||||||
if ( this != &encoder ) {
|
if ( this != &encoder ) {
|
||||||
strip();
|
strip();
|
||||||
AudioEncoder::operator=( encoder);
|
AudioEncoder::operator=( encoder);
|
||||||
init( encoder.sink.get(), encoder.getOutMaxBitrate() );
|
init( encoder.getOutMaxBitrate() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
@ -43,6 +43,12 @@
|
||||||
#error needs stdlib.h
|
#error needs stdlib.h
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#else
|
||||||
|
#error needs signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
@ -54,6 +60,11 @@
|
||||||
|
|
||||||
/* =================================================== local data structures */
|
/* =================================================== local data structures */
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* The DarkIce object we're running
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
static Ref<DarkIce> darkice;
|
||||||
|
|
||||||
|
|
||||||
/* ================================================ local constants & macros */
|
/* ================================================ local constants & macros */
|
||||||
|
|
||||||
|
@ -76,6 +87,12 @@ static const char *DEFAULT_CONFIG_FILE = "/etc/darkice.cfg";
|
||||||
static void
|
static void
|
||||||
showUsage ( std::ostream & os );
|
showUsage ( std::ostream & os );
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Handler for the SIGUSR1 signal
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
sigusr1Handler(int value);
|
||||||
|
|
||||||
|
|
||||||
/* ============================================================= module code */
|
/* ============================================================= module code */
|
||||||
|
|
||||||
|
@ -126,9 +143,12 @@ main (
|
||||||
Reporter::setReportVerbosity( verbosity );
|
Reporter::setReportVerbosity( verbosity );
|
||||||
Reporter::setReportOutputStream( std::cout );
|
Reporter::setReportOutputStream( std::cout );
|
||||||
Config config( configFile);
|
Config config( configFile);
|
||||||
Ref<DarkIce> di = new DarkIce( config);
|
|
||||||
|
|
||||||
res = di->run();
|
darkice = new DarkIce( config);
|
||||||
|
|
||||||
|
signal(SIGUSR1, sigusr1Handler);
|
||||||
|
|
||||||
|
res = darkice->run();
|
||||||
|
|
||||||
} catch ( Exception & e ) {
|
} catch ( Exception & e ) {
|
||||||
std::cout << "DarkIce: " << e << std::endl << std::flush;
|
std::cout << "DarkIce: " << e << std::endl << std::flush;
|
||||||
|
@ -162,6 +182,16 @@ showUsage ( std::ostream & os )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------
|
||||||
|
* Handle the SIGUSR1 signal here
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
sigusr1Handler(int value)
|
||||||
|
{
|
||||||
|
darkice->cut();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
|
|
||||||
$Source$
|
$Source$
|
||||||
|
|
Loading…
Reference in New Issue