From ee4ae648d08e1b5cec4377e38935525c9d777f51 Mon Sep 17 00:00:00 2001 From: darkeye Date: Sun, 14 Jan 2007 16:12:32 +0000 Subject: [PATCH] added initial implementation of file dump cutting, re #3 --- darkice/trunk/ChangeLog | 12 +-- darkice/trunk/src/AudioEncoder.h | 55 ++++++++++++-- darkice/trunk/src/BufferedSink.h | 12 +++ darkice/trunk/src/CastSink.h | 13 ++++ darkice/trunk/src/Connector.cpp | 16 ++++ darkice/trunk/src/Connector.h | 9 +++ darkice/trunk/src/DarkIce.cpp | 14 ++++ darkice/trunk/src/DarkIce.h | 9 +++ darkice/trunk/src/FaacEncoder.cpp | 8 +- darkice/trunk/src/FaacEncoder.h | 24 +++--- darkice/trunk/src/FileCast.h | 11 +++ darkice/trunk/src/FileSink.cpp | 77 +++++++++++++++++++- darkice/trunk/src/FileSink.h | 18 +++++ darkice/trunk/src/FileSource.cpp | 20 ++++- darkice/trunk/src/LameLibEncoder.cpp | 10 +-- darkice/trunk/src/LameLibEncoder.h | 24 +++--- darkice/trunk/src/MultiThreadedConnector.cpp | 21 ++++++ darkice/trunk/src/MultiThreadedConnector.h | 18 ++++- darkice/trunk/src/OssDspSource.cpp | 20 ++++- darkice/trunk/src/Sink.h | 8 ++ darkice/trunk/src/SolarisDspSource.cpp | 20 ++++- darkice/trunk/src/TcpSocket.cpp | 36 ++++++--- darkice/trunk/src/TcpSocket.h | 12 +++ darkice/trunk/src/TwoLameLibEncoder.cpp | 13 ++-- darkice/trunk/src/TwoLameLibEncoder.h | 21 +++--- darkice/trunk/src/VorbisLibEncoder.cpp | 28 ++++--- darkice/trunk/src/VorbisLibEncoder.h | 23 +++--- darkice/trunk/src/main.cpp | 34 ++++++++- 28 files changed, 462 insertions(+), 124 deletions(-) diff --git a/darkice/trunk/ChangeLog b/darkice/trunk/ChangeLog index 28568b7..b6fdad2 100644 --- a/darkice/trunk/ChangeLog +++ b/darkice/trunk/ChangeLog @@ -1,12 +1,14 @@ 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 - is specified, /etc/darkice.cfg is used - o fix to enable compiling on 64 bit platforms - thanks to Alexander Vlasov and + is specified, /etc/darkice.cfg is used + o fix to enable compiling on 64 bit platforms + thanks to Alexander Vlasov and Mariusz Mazur - o fix to enable file dump feature using ogg vorbis. - thanks to + o fix to enable file dump feature using ogg vorbis. + thanks to 19-05-2006 DarkIce 0.17.1 released diff --git a/darkice/trunk/src/AudioEncoder.h b/darkice/trunk/src/AudioEncoder.h index 88328dc..cddb022 100644 --- a/darkice/trunk/src/AudioEncoder.h +++ b/darkice/trunk/src/AudioEncoder.h @@ -71,6 +71,11 @@ class AudioEncoder : public Sink, public virtual Referable private: + /** + * The Sink to dump the encoded data to + */ + Ref sink; + /** * Sample rate of the input. */ @@ -119,6 +124,7 @@ class AudioEncoder : public Sink, public virtual Referable /** * Initialize the object. * + * @param sink the sink to send encoded output to * @param inSampleRate sample rate of the input. * @param inBitsPerSample number of bits per sample of the input. * @param inChannel number of channels of the input. @@ -130,7 +136,8 @@ class AudioEncoder : public Sink, public virtual Referable * @exception Exception */ inline void - init ( unsigned int inSampleRate, + init ( Sink * sink, + unsigned int inSampleRate, unsigned int inBitsPerSample, unsigned int inChannel, bool inBigEndian, @@ -140,6 +147,7 @@ class AudioEncoder : public Sink, public virtual Referable unsigned int outSampleRate, unsigned int outChannel ) throw ( Exception ) { + this->sink = sink; this->inSampleRate = inSampleRate; this->inBitsPerSample = inBitsPerSample; this->inChannel = inChannel; @@ -182,6 +190,7 @@ class AudioEncoder : public Sink, public virtual Referable /** * Constructor. * + * @param sink the sink to send encoded output to * @param inSampleRate sample rate of the input. * @param inBitsPerSample number of bits per sample of the input. * @param inChannel number of channels of the input. @@ -196,7 +205,8 @@ class AudioEncoder : public Sink, public virtual Referable * @exception Exception */ inline - AudioEncoder ( unsigned int inSampleRate, + AudioEncoder ( Sink * sink, + unsigned int inSampleRate, unsigned int inBitsPerSample, unsigned int inChannel, bool inBigEndian, @@ -207,7 +217,8 @@ class AudioEncoder : public Sink, public virtual Referable unsigned int outChannel = 0 ) throw ( Exception ) { - init ( inSampleRate, + init ( sink, + inSampleRate, inBitsPerSample, inChannel, inBigEndian, @@ -221,6 +232,7 @@ class AudioEncoder : public Sink, public virtual Referable /** * Constructor. * + * @param sink the sink to send encoded output to * @param as get input sample rate, bits per sample and channels * from this AudioSource. * @param outBitrateMode the bit rate mode of the output. @@ -233,7 +245,8 @@ class AudioEncoder : public Sink, public virtual Referable * @exception Exception */ inline - AudioEncoder ( const AudioSource * as, + AudioEncoder ( Sink * sink, + const AudioSource * as, BitrateMode outBitrateMode, unsigned int outBitrate, double outQuality, @@ -241,7 +254,8 @@ class AudioEncoder : public Sink, public virtual Referable unsigned int outChannel = 0 ) throw ( Exception) { - init( as->getSampleRate(), + init( sink, + as->getSampleRate(), as->getBitsPerSample(), as->getChannel(), as->isBigEndian(), @@ -260,7 +274,8 @@ class AudioEncoder : public Sink, public virtual Referable inline AudioEncoder ( const AudioEncoder & encoder ) throw ( Exception ) { - init ( encoder.inSampleRate, + init ( encoder.sink.get(), + encoder.inSampleRate, encoder.inBitsPerSample, encoder.inChannel, encoder.inBigEndian, @@ -284,7 +299,8 @@ class AudioEncoder : public Sink, public virtual Referable if ( this != &encoder ) { strip(); - init ( encoder.inSampleRate, + init ( encoder.sink.get(), + encoder.inSampleRate, encoder.inBitsPerSample, encoder.inChannel, encoder.inBigEndian, @@ -312,6 +328,17 @@ class AudioEncoder : public Sink, public virtual Referable strip(); } + /** + * Get the underlying sink, that the encoded content is sent to. + * + * @return the underlying sink + */ + inline virtual Ref + getSink(void) throw () + { + return sink; + } + /** * Get the number of channels of the input. * @@ -438,6 +465,20 @@ class AudioEncoder : public Sink, public virtual Referable */ virtual void 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(); + } + }; diff --git a/darkice/trunk/src/BufferedSink.h b/darkice/trunk/src/BufferedSink.h index 129cabc..177622c 100644 --- a/darkice/trunk/src/BufferedSink.h +++ b/darkice/trunk/src/BufferedSink.h @@ -366,6 +366,18 @@ class BufferedSink : public Sink, public virtual Reporter 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. * diff --git a/darkice/trunk/src/CastSink.h b/darkice/trunk/src/CastSink.h index cbec626..117e36f 100644 --- a/darkice/trunk/src/CastSink.h +++ b/darkice/trunk/src/CastSink.h @@ -362,6 +362,19 @@ class CastSink : public Sink, public virtual Reporter 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. * diff --git a/darkice/trunk/src/Connector.cpp b/darkice/trunk/src/Connector.cpp index 7e17ced..bfacb92 100644 --- a/darkice/trunk/src/Connector.cpp +++ b/darkice/trunk/src/Connector.cpp @@ -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 *----------------------------------------------------------------------------*/ diff --git a/darkice/trunk/src/Connector.h b/darkice/trunk/src/Connector.h index d4af39c..eea3215 100644 --- a/darkice/trunk/src/Connector.h +++ b/darkice/trunk/src/Connector.h @@ -234,6 +234,15 @@ class Connector : public virtual Referable, public virtual Reporter unsigned int sec, 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. * diff --git a/darkice/trunk/src/DarkIce.cpp b/darkice/trunk/src/DarkIce.cpp index 47c99b2..0bb3e8f 100644 --- a/darkice/trunk/src/DarkIce.cpp +++ b/darkice/trunk/src/DarkIce.cpp @@ -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$ diff --git a/darkice/trunk/src/DarkIce.h b/darkice/trunk/src/DarkIce.h index c6edabf..5729bb7 100644 --- a/darkice/trunk/src/DarkIce.h +++ b/darkice/trunk/src/DarkIce.h @@ -296,6 +296,15 @@ class DarkIce : public virtual Referable, public virtual Reporter virtual int 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 (); + }; diff --git a/darkice/trunk/src/FaacEncoder.cpp b/darkice/trunk/src/FaacEncoder.cpp index 66588dd..b8f7c7b 100644 --- a/darkice/trunk/src/FaacEncoder.cpp +++ b/darkice/trunk/src/FaacEncoder.cpp @@ -71,7 +71,7 @@ FaacEncoder :: open ( void ) } // open the underlying sink - if ( !sink->open() ) { + if ( !getSink()->open() ) { throw Exception( __FILE__, __LINE__, "faac lib opening underlying sink error"); } @@ -145,7 +145,7 @@ FaacEncoder :: write ( const void * buf, inSamples, faacBuf, maxOutputBytes); - sink->write(faacBuf, outputBytes); + getSink()->write(faacBuf, outputBytes); processedSamples += inSamples; } @@ -167,7 +167,7 @@ FaacEncoder :: flush ( void ) return; } - sink->flush(); + getSink()->flush(); } @@ -182,7 +182,7 @@ FaacEncoder :: close ( void ) throw ( Exception ) faacEncClose(encoderHandle); faacOpen = false; - sink->close(); + getSink()->close(); } } diff --git a/darkice/trunk/src/FaacEncoder.h b/darkice/trunk/src/FaacEncoder.h index a01c4dd..647a716 100644 --- a/darkice/trunk/src/FaacEncoder.h +++ b/darkice/trunk/src/FaacEncoder.h @@ -98,15 +98,9 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter */ int lowpass; - /** - * The Sink to dump mp3 data to - */ - Ref sink; - /** * Initialize the object. * - * @param sink the sink to send mp3 output to * @param lowpass frequency threshold for the lowpass filter. * Input above this frequency is cut. * If 0, faac's default values are used, @@ -114,11 +108,9 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter * @exception Exception */ inline void - init ( Sink * sink, - int lowpass) throw (Exception) + init ( int lowpass) throw (Exception) { this->faacOpen = false; - this->sink = sink; this->lowpass = lowpass; if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) { @@ -205,7 +197,8 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter int lowpass = 0) throw ( Exception ) - : AudioEncoder ( inSampleRate, + : AudioEncoder ( sink, + inSampleRate, inBitsPerSample, inChannel, inBigEndian, @@ -215,7 +208,7 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter outSampleRate, outChannel ) { - init( sink, lowpass); + init( lowpass); } /** @@ -248,14 +241,15 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter int lowpass = 0) throw ( Exception ) - : AudioEncoder ( as, + : AudioEncoder ( sink, + as, outBitrateMode, outBitrate, outQuality, outSampleRate, outChannel ) { - init( sink, lowpass); + init( lowpass); } /** @@ -268,7 +262,7 @@ class FaacEncoder : public AudioEncoder, public virtual Reporter throw ( Exception ) : 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 ) { strip(); AudioEncoder::operator=( encoder); - init( encoder.sink.get(), encoder.lowpass); + init( encoder.lowpass); } return *this; diff --git a/darkice/trunk/src/FileCast.h b/darkice/trunk/src/FileCast.h index c61afbf..2926ef1 100644 --- a/darkice/trunk/src/FileCast.h +++ b/darkice/trunk/src/FileCast.h @@ -238,6 +238,17 @@ class FileCast : public CastSink 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. * diff --git a/darkice/trunk/src/FileSink.cpp b/darkice/trunk/src/FileSink.cpp index c6c369c..e20cbfc 100644 --- a/darkice/trunk/src/FileSink.cpp +++ b/darkice/trunk/src/FileSink.cpp @@ -81,6 +81,17 @@ #error need string.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error need signal.h +#endif + + +#include +#include +#include + #include "Util.h" #include "Exception.h" @@ -242,7 +253,8 @@ FileSink :: canWrite ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -251,10 +263,15 @@ FileSink :: canWrite ( unsigned int sec, FD_ZERO( &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 ) { 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 *----------------------------------------------------------------------------*/ diff --git a/darkice/trunk/src/FileSink.h b/darkice/trunk/src/FileSink.h index 01bd562..c45cafe 100644 --- a/darkice/trunk/src/FileSink.h +++ b/darkice/trunk/src/FileSink.h @@ -80,6 +80,16 @@ class FileSink : public Sink, public virtual Reporter void 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: @@ -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. * diff --git a/darkice/trunk/src/FileSource.cpp b/darkice/trunk/src/FileSource.cpp index 18a22c2..6ec47e9 100644 --- a/darkice/trunk/src/FileSource.cpp +++ b/darkice/trunk/src/FileSource.cpp @@ -69,6 +69,12 @@ #error need string.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error need signal.h +#endif + #include "Exception.h" #include "Util.h" @@ -197,7 +203,8 @@ FileSource :: canRead ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -206,10 +213,15 @@ FileSource :: canRead ( unsigned int sec, FD_ZERO( &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 ) { throw Exception( __FILE__, __LINE__, "select error"); diff --git a/darkice/trunk/src/LameLibEncoder.cpp b/darkice/trunk/src/LameLibEncoder.cpp index e294c25..8bdf50d 100644 --- a/darkice/trunk/src/LameLibEncoder.cpp +++ b/darkice/trunk/src/LameLibEncoder.cpp @@ -71,7 +71,7 @@ LameLibEncoder :: open ( void ) } // open the underlying sink - if ( !sink->open() ) { + if ( !getSink()->open() ) { throw Exception( __FILE__, __LINE__, "lame lib opening underlying sink error"); } @@ -331,7 +331,7 @@ LameLibEncoder :: write ( const void * buf, return 0; } - unsigned int written = sink->write( mp3Buf, ret); + unsigned int written = getSink()->write( mp3Buf, ret); delete[] mp3Buf; // just let go data that could not be written if ( written < (unsigned int) ret ) { @@ -362,7 +362,7 @@ LameLibEncoder :: flush ( void ) ret = lame_encode_flush( lameGlobalFlags, mp3Buf, mp3Size ); - unsigned int written = sink->write( mp3Buf, ret); + unsigned int written = getSink()->write( mp3Buf, ret); delete[] mp3Buf; // just let go data that could not be written @@ -372,7 +372,7 @@ LameLibEncoder :: flush ( void ) ret - written); } - sink->flush(); + getSink()->flush(); } @@ -387,7 +387,7 @@ LameLibEncoder :: close ( void ) throw ( Exception ) lame_close( lameGlobalFlags); lameGlobalFlags = 0; - sink->close(); + getSink()->close(); } } diff --git a/darkice/trunk/src/LameLibEncoder.h b/darkice/trunk/src/LameLibEncoder.h index ada80d5..2a9c246 100644 --- a/darkice/trunk/src/LameLibEncoder.h +++ b/darkice/trunk/src/LameLibEncoder.h @@ -78,11 +78,6 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter */ lame_global_flags * lameGlobalFlags; - /** - * The Sink to dump mp3 data to - */ - Ref sink; - /** * Lowpass filter. Sound frequency in Hz, from where up the * input is cut. @@ -98,7 +93,6 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter /** * Initialize the object. * - * @param sink the sink to send mp3 output to * @param lowpass frequency threshold for the lowpass filter. * Input above this frequency is cut. * If 0, lame's default values are used, @@ -110,12 +104,10 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter * @exception Exception */ inline void - init ( Sink * sink, - int lowpass, + init ( int lowpass, int highpass ) throw ( Exception ) { this->lameGlobalFlags = NULL; - this->sink = sink; this->lowpass = lowpass; this->highpass = highpass; @@ -209,7 +201,8 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter int highpass = 0 ) throw ( Exception ) - : AudioEncoder ( inSampleRate, + : AudioEncoder ( sink, + inSampleRate, inBitsPerSample, inChannel, inBigEndian, @@ -219,7 +212,7 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter outSampleRate, outChannel ) { - init( sink, lowpass, highpass); + init( lowpass, highpass); } /** @@ -257,14 +250,15 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter int highpass = 0 ) throw ( Exception ) - : AudioEncoder ( as, + : AudioEncoder ( sink, + as, outBitrateMode, outBitrate, outQuality, outSampleRate, outChannel ) { - init( sink, lowpass, highpass); + init( lowpass, highpass); } /** @@ -277,7 +271,7 @@ class LameLibEncoder : public AudioEncoder, public virtual Reporter throw ( Exception ) : 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 ) { strip(); AudioEncoder::operator=( encoder); - init( encoder.sink.get(), encoder.lowpass, encoder.highpass ); + init( encoder.lowpass, encoder.highpass ); } return *this; diff --git a/darkice/trunk/src/MultiThreadedConnector.cpp b/darkice/trunk/src/MultiThreadedConnector.cpp index 5480feb..ef92b21 100644 --- a/darkice/trunk/src/MultiThreadedConnector.cpp +++ b/darkice/trunk/src/MultiThreadedConnector.cpp @@ -293,6 +293,11 @@ MultiThreadedConnector :: sinkThread( int ixSink ) break; } + if ( threadData->cut) { + sink->cut(); + threadData->cut = false; + } + if ( threadData->accepting ) { if ( sink->canWrite( 0, 0) ) { 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 * Close the source and all the sinks if needed diff --git a/darkice/trunk/src/MultiThreadedConnector.h b/darkice/trunk/src/MultiThreadedConnector.h index fbbe336..5bbdb4c 100644 --- a/darkice/trunk/src/MultiThreadedConnector.h +++ b/darkice/trunk/src/MultiThreadedConnector.h @@ -107,6 +107,12 @@ class MultiThreadedConnector : public virtual Connector */ bool isDone; + /** + * A flag to show that the sink should be made to cut in the + * next iteration. + */ + bool cut; + /** * Default constructor. */ @@ -118,6 +124,7 @@ class MultiThreadedConnector : public virtual Connector this->thread = 0; this->accepting = false; this->isDone = false; + this->cut = false; } /** @@ -313,6 +320,15 @@ class MultiThreadedConnector : public virtual Connector unsigned int sec, 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. * @@ -322,7 +338,7 @@ class MultiThreadedConnector : public virtual Connector 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 * * @param ixSink the index of the sink this thread works on. diff --git a/darkice/trunk/src/OssDspSource.cpp b/darkice/trunk/src/OssDspSource.cpp index f39a41e..2bc0e92 100644 --- a/darkice/trunk/src/OssDspSource.cpp +++ b/darkice/trunk/src/OssDspSource.cpp @@ -80,6 +80,12 @@ #error need sys/ioctl.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error need signal.h +#endif + #ifdef HAVE_SYS_SOUNDCARD_H #include #else @@ -230,7 +236,8 @@ OssDspSource :: canRead ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -247,10 +254,15 @@ OssDspSource :: canRead ( unsigned int sec, FD_ZERO( &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 ) { throw Exception( __FILE__, __LINE__, "select error"); diff --git a/darkice/trunk/src/Sink.h b/darkice/trunk/src/Sink.h index fac29e6..f9ff4bc 100644 --- a/darkice/trunk/src/Sink.h +++ b/darkice/trunk/src/Sink.h @@ -156,6 +156,14 @@ class Sink : public virtual Referable virtual void 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. * diff --git a/darkice/trunk/src/SolarisDspSource.cpp b/darkice/trunk/src/SolarisDspSource.cpp index 18d9c7a..a67b431 100644 --- a/darkice/trunk/src/SolarisDspSource.cpp +++ b/darkice/trunk/src/SolarisDspSource.cpp @@ -80,6 +80,12 @@ #error need sys/ioctl.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error need signal.h +#endif + #if defined( HAVE_SYS_AUDIO_H ) #include #elif defined( HAVE_SYS_AUDIOIO_H ) @@ -202,7 +208,8 @@ SolarisDspSource :: canRead ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -211,10 +218,15 @@ SolarisDspSource :: canRead ( unsigned int sec, FD_ZERO( &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 ) { throw Exception( __FILE__, __LINE__, "select error"); diff --git a/darkice/trunk/src/TcpSocket.cpp b/darkice/trunk/src/TcpSocket.cpp index d4c29db..8e13912 100644 --- a/darkice/trunk/src/TcpSocket.cpp +++ b/darkice/trunk/src/TcpSocket.cpp @@ -81,6 +81,12 @@ #error need sys/time.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error need signal.h +#endif + #include "Util.h" #include "Exception.h" @@ -243,7 +249,8 @@ TcpSocket :: canRead ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -252,11 +259,16 @@ TcpSocket :: canRead ( unsigned int sec, FD_ZERO( &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 ) { throw Exception( __FILE__, __LINE__, "select error"); } @@ -305,7 +317,8 @@ TcpSocket :: canWrite ( unsigned int sec, unsigned int usec ) throw ( Exception ) { fd_set fdset; - struct timeval tv; + struct timespec timespec; + sigset_t sigset; int ret; if ( !isOpen() ) { @@ -314,10 +327,15 @@ TcpSocket :: canWrite ( unsigned int sec, FD_ZERO( &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 ) { throw Exception( __FILE__, __LINE__, "select error"); diff --git a/darkice/trunk/src/TcpSocket.h b/darkice/trunk/src/TcpSocket.h index df8d4cb..79af588 100644 --- a/darkice/trunk/src/TcpSocket.h +++ b/darkice/trunk/src/TcpSocket.h @@ -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. * diff --git a/darkice/trunk/src/TwoLameLibEncoder.cpp b/darkice/trunk/src/TwoLameLibEncoder.cpp index 1e1ced2..1d2f13e 100644 --- a/darkice/trunk/src/TwoLameLibEncoder.cpp +++ b/darkice/trunk/src/TwoLameLibEncoder.cpp @@ -65,10 +65,9 @@ static const char fileid[] = "$Id$"; * Initialize the object *----------------------------------------------------------------------------*/ void -TwoLameLibEncoder :: init ( Sink * sink ) throw ( Exception ) +TwoLameLibEncoder :: init ( void ) throw ( Exception ) { this->twolame_opts = NULL; - this->sink = sink; if ( getInBitsPerSample() != 16 ) { throw Exception( __FILE__, __LINE__, @@ -106,7 +105,7 @@ TwoLameLibEncoder :: open ( void ) } // open the underlying sink - if ( !sink->open() ) { + if ( !getSink()->open() ) { throw Exception( __FILE__, __LINE__, "TwoLAME lib opening underlying sink error"); } @@ -239,7 +238,7 @@ TwoLameLibEncoder :: write ( const void * buf, return 0; } - unsigned int written = sink->write( mp2Buf, ret); + unsigned int written = getSink()->write( mp2Buf, ret); delete[] mp2Buf; // just let go data that could not be written if ( written < (unsigned int) ret ) { @@ -270,7 +269,7 @@ TwoLameLibEncoder :: flush ( void ) ret = twolame_encode_flush( twolame_opts, mp2Buf, mp2Size ); - unsigned int written = sink->write( mp2Buf, ret); + unsigned int written = getSink()->write( mp2Buf, ret); delete[] mp2Buf; // just let go data that could not be written @@ -280,7 +279,7 @@ TwoLameLibEncoder :: flush ( void ) ret - written); } - sink->flush(); + getSink()->flush(); } @@ -293,7 +292,7 @@ TwoLameLibEncoder :: close ( void ) throw ( Exception ) if ( isOpen() ) { flush(); twolame_close( &twolame_opts ); - sink->close(); + getSink()->close(); } } diff --git a/darkice/trunk/src/TwoLameLibEncoder.h b/darkice/trunk/src/TwoLameLibEncoder.h index 0e519e1..afbf0be 100644 --- a/darkice/trunk/src/TwoLameLibEncoder.h +++ b/darkice/trunk/src/TwoLameLibEncoder.h @@ -78,11 +78,6 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter */ twolame_options * twolame_opts; - /** - * The Sink to dump mp2 data to - */ - Ref sink; - /** * Initialize the object. * @@ -90,7 +85,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter * @exception Exception */ void - init ( Sink * sink ) throw ( Exception ); + init ( void ) throw ( Exception ); /** * De-initialize the object. @@ -147,7 +142,8 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter unsigned int outChannel = 0 ) throw ( Exception ) - : AudioEncoder ( inSampleRate, + : AudioEncoder ( sink, + inSampleRate, inBitsPerSample, inChannel, inBigEndian, @@ -157,7 +153,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter outSampleRate, outChannel ) { - init( sink ); + init(); } /** @@ -183,14 +179,15 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter unsigned int outChannel = 0 ) throw ( Exception ) - : AudioEncoder ( as, + : AudioEncoder ( sink, + as, outBitrateMode, outBitrate, 0.0f, // outQuality outSampleRate, outChannel ) { - init( sink ); + init(); } /** @@ -203,7 +200,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter throw ( Exception ) : AudioEncoder( encoder ) { - init( encoder.sink.get() ); + init(); } @@ -234,7 +231,7 @@ class TwoLameLibEncoder : public AudioEncoder, public virtual Reporter if ( this != &encoder ) { strip(); AudioEncoder::operator=( encoder); - init( encoder.sink.get() ); + init(); } return *this; diff --git a/darkice/trunk/src/VorbisLibEncoder.cpp b/darkice/trunk/src/VorbisLibEncoder.cpp index 373f949..e0a111a 100644 --- a/darkice/trunk/src/VorbisLibEncoder.cpp +++ b/darkice/trunk/src/VorbisLibEncoder.cpp @@ -62,11 +62,9 @@ static const char fileid[] = "$Id$"; * Initialize the encoder *----------------------------------------------------------------------------*/ void -VorbisLibEncoder :: init ( CastSink * sink, - unsigned int outMaxBitrate ) +VorbisLibEncoder :: init ( unsigned int outMaxBitrate ) throw ( Exception ) { - this->sink = sink; this->outMaxBitrate = outMaxBitrate; if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) { @@ -140,7 +138,7 @@ VorbisLibEncoder :: open ( void ) } // open the underlying sink - if ( !sink->open() ) { + if ( !getSink()->open() ) { throw Exception( __FILE__, __LINE__, "vorbis lib opening underlying sink error"); } @@ -206,11 +204,17 @@ VorbisLibEncoder :: open ( void ) // create an empty vorbis_comment structure 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 // stupid cast to (char*) because of stupid vorbis API - if ( sink->getName() ) { - vorbis_comment_add_tag(&vorbisComment, "TITLE", (char*)sink->getName()); + if ( getSink()->getName() ) { + vorbis_comment_add_tag(&vorbisComment, + "TITLE", + (char*) getSink()->getName()); } + */ // create the vorbis stream headers and send them to the underlying sink ogg_packet header; @@ -231,8 +235,8 @@ VorbisLibEncoder :: open ( void ) ogg_page oggPage; while ( ogg_stream_flush( &oggStreamState, &oggPage) ) { - sink->write( oggPage.header, oggPage.header_len); - sink->write( oggPage.body, oggPage.body_len); + getSink()->write( oggPage.header, oggPage.header_len); + getSink()->write( oggPage.body, oggPage.body_len); } vorbis_comment_clear( &vorbisComment ); @@ -346,7 +350,7 @@ VorbisLibEncoder :: flush ( void ) vorbis_analysis_wrote( &vorbisDspState, 0); vorbisBlocksOut(); - sink->flush(); + getSink()->flush(); } @@ -370,8 +374,8 @@ VorbisLibEncoder :: vorbisBlocksOut ( void ) throw ( Exception ) while ( ogg_stream_pageout( &oggStreamState, &oggPage) ) { int written; - written = sink->write( oggPage.header, oggPage.header_len); - written += sink->write( oggPage.body, oggPage.body_len); + written = getSink()->write(oggPage.header, oggPage.header_len); + written += getSink()->write( oggPage.body, oggPage.body_len); if ( written < oggPage.header_len + oggPage.body_len ) { // just let go data that could not be written @@ -402,7 +406,7 @@ VorbisLibEncoder :: close ( void ) throw ( Exception ) encoderOpen = false; - sink->close(); + getSink()->close(); } } diff --git a/darkice/trunk/src/VorbisLibEncoder.h b/darkice/trunk/src/VorbisLibEncoder.h index a956f42..5ddcee4 100644 --- a/darkice/trunk/src/VorbisLibEncoder.h +++ b/darkice/trunk/src/VorbisLibEncoder.h @@ -104,11 +104,6 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter */ ogg_stream_state oggStreamState; - /** - * The Sink to dump encoded data to - */ - Ref sink; - /** * 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. * - * @param sink the sink to send encoded output to * @param the maximum bit rate * @exception Exception */ void - init ( CastSink * sink, - unsigned int outMaxBitrate ) throw ( Exception ); + init ( unsigned int outMaxBitrate ) throw ( Exception ); /** * De-initialize the object. @@ -204,7 +197,8 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter unsigned int outMaxBitrate = 0 ) throw ( Exception ) - : AudioEncoder ( inSampleRate, + : AudioEncoder ( sink, + inSampleRate, inBitsPerSample, inChannel, inBigEndian, @@ -214,7 +208,7 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter outSampleRate, outChannel ) { - init( sink, outMaxBitrate); + init( outMaxBitrate); } /** @@ -245,14 +239,15 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter unsigned int outMaxBitrate = 0 ) throw ( Exception ) - : AudioEncoder ( as, + : AudioEncoder ( sink, + as, outBitrateMode, outBitrate, outQuality, outSampleRate, outChannel ) { - init( sink, outMaxBitrate); + init( outMaxBitrate); } /** @@ -268,7 +263,7 @@ class VorbisLibEncoder : public AudioEncoder, public virtual Reporter if( encoder.isOpen() ) { 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 ) { strip(); AudioEncoder::operator=( encoder); - init( encoder.sink.get(), encoder.getOutMaxBitrate() ); + init( encoder.getOutMaxBitrate() ); } return *this; diff --git a/darkice/trunk/src/main.cpp b/darkice/trunk/src/main.cpp index b234b10..5d9687d 100644 --- a/darkice/trunk/src/main.cpp +++ b/darkice/trunk/src/main.cpp @@ -43,6 +43,12 @@ #error needs stdlib.h #endif +#ifdef HAVE_SIGNAL_H +#include +#else +#error needs signal.h +#endif + #include #include @@ -54,6 +60,11 @@ /* =================================================== local data structures */ +/*------------------------------------------------------------------------------ + * The DarkIce object we're running + *----------------------------------------------------------------------------*/ +static Ref darkice; + /* ================================================ local constants & macros */ @@ -76,6 +87,12 @@ static const char *DEFAULT_CONFIG_FILE = "/etc/darkice.cfg"; static void showUsage ( std::ostream & os ); +/*------------------------------------------------------------------------------ + * Handler for the SIGUSR1 signal + *----------------------------------------------------------------------------*/ +static void +sigusr1Handler(int value); + /* ============================================================= module code */ @@ -126,9 +143,12 @@ main ( Reporter::setReportVerbosity( verbosity ); Reporter::setReportOutputStream( std::cout ); Config config( configFile); - Ref di = new DarkIce( config); - res = di->run(); + darkice = new DarkIce( config); + + signal(SIGUSR1, sigusr1Handler); + + res = darkice->run(); } catch ( Exception & e ) { 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$