Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

DarkIce.cpp

Go to the documentation of this file.
00001 /*------------------------------------------------------------------------------
00002 
00003    Copyright (c) 2000 Tyrell Corporation. All rights reserved.
00004 
00005    Tyrell DarkIce
00006 
00007    File     : DarkIce.cpp
00008    Version  : $Revision: 1.45 $
00009    Author   : $Author: darkeye $
00010    Location : $Source: /cvsroot/darkice/darkice/src/DarkIce.cpp,v $
00011    
00012 
00013    Copyright notice:
00014 
00015     This program is free software; you can redistribute it and/or
00016     modify it under the terms of the GNU General Public License  
00017     as published by the Free Software Foundation; either version 2
00018     of the License, or (at your option) any later version.
00019    
00020     This program is distributed in the hope that it will be useful,
00021     but WITHOUT ANY WARRANTY; without even the implied warranty of 
00022     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
00023     GNU General Public License for more details.
00024    
00025     You should have received a copy of the GNU General Public License
00026     along with this program; if not, write to the Free Software
00027     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 ------------------------------------------------------------------------------*/
00030 
00031 /* ============================================================ include files */
00032 
00033 #ifdef HAVE_CONFIG_H
00034 #include "config.h"
00035 #endif
00036 
00037 #ifdef HAVE_STDLIB_H
00038 #include <stdlib.h>
00039 #else
00040 #error need stdlib.h
00041 #endif
00042 
00043 #ifdef HAVE_UNISTD_H
00044 #include <unistd.h>
00045 #else
00046 #error need unistd.h
00047 #endif
00048 
00049 #ifdef HAVE_SYS_TYPES_H
00050 #include <sys/types.h>
00051 #else
00052 #error need sys/types.h
00053 #endif
00054 
00055 #ifdef HAVE_SYS_WAIT_H
00056 #include <sys/wait.h>
00057 #else
00058 #error need sys/wait.h
00059 #endif
00060 
00061 #ifdef HAVE_ERRNO_H
00062 #include <errno.h>
00063 #else
00064 #error need errno.h
00065 #endif
00066 
00067 #ifdef HAVE_SCHED_H
00068 #include <sched.h>
00069 #else
00070 #error need sched.h
00071 #endif
00072 
00073 
00074 
00075 #include "Util.h"
00076 #include "IceCast.h"
00077 #include "IceCast2.h"
00078 #include "ShoutCast.h"
00079 #include "FileCast.h"
00080 #include "MultiThreadedConnector.h"
00081 #include "DarkIce.h"
00082 
00083 #ifdef HAVE_LAME_LIB
00084 #include "LameLibEncoder.h"
00085 #endif
00086 
00087 #ifdef HAVE_VORBIS_LIB
00088 #include "VorbisLibEncoder.h"
00089 #endif
00090 
00091 #ifdef HAVE_FAAC_LIB
00092 #include "FaacEncoder.h"
00093 #endif
00094 
00095 
00096 /* ===================================================  local data structures */
00097 
00098 
00099 /* ================================================  local constants & macros */
00100 
00101 /*------------------------------------------------------------------------------
00102  *  File identity
00103  *----------------------------------------------------------------------------*/
00104 static const char fileid[] = "$Id: DarkIce.cpp,v 1.45 2005/10/22 10:34:21 darkeye Exp $";
00105 
00106 
00107 /*------------------------------------------------------------------------------
00108  *  Make sure wait-related stuff is what we expect
00109  *----------------------------------------------------------------------------*/
00110 #ifndef WEXITSTATUS
00111 # define WEXITSTATUS(stat_val)      ((unsigned)(stat_val) >> 8)
00112 #endif
00113 #ifndef WIFEXITED
00114 # define WIFEXITED(stat_val)        (((stat_val) & 255) == 0)
00115 #endif
00116 
00117 
00118 
00119 /* ===============================================  local function prototypes */
00120 
00121 
00122 /* =============================================================  module code */
00123 
00124 /*------------------------------------------------------------------------------
00125  *  Initialize the object
00126  *----------------------------------------------------------------------------*/
00127 void
00128 DarkIce :: init ( const Config      & config )              throw ( Exception )
00129 {
00130     unsigned int             bufferSecs;
00131     const ConfigSection    * cs;
00132     const char             * str;
00133     unsigned int             sampleRate;
00134     unsigned int             bitsPerSample;
00135     unsigned int             channel;
00136     bool                     reconnect;
00137     const char             * device;
00138 
00139     // the [general] section
00140     if ( !(cs = config.get( "general")) ) {
00141         throw Exception( __FILE__, __LINE__, "no section [general] in config");
00142     }
00143     str = cs->getForSure( "duration", " missing in section [general]");
00144     duration = Util::strToL( str);
00145     str = cs->getForSure( "bufferSecs", " missing in section [general]");
00146     bufferSecs = Util::strToL( str);
00147     str           = cs->get( "reconnect");
00148     reconnect     = str ? (Util::strEq( str, "yes") ? true : false) : true;
00149 
00150 
00151     // the [input] section
00152     if ( !(cs = config.get( "input")) ) {
00153         throw Exception( __FILE__, __LINE__, "no section [general] in config");
00154     }
00155     
00156     str        = cs->getForSure( "sampleRate", " missing in section [input]");
00157     sampleRate = Util::strToL( str);
00158     str       = cs->getForSure( "bitsPerSample", " missing in section [input]");
00159     bitsPerSample = Util::strToL( str);
00160     str           = cs->getForSure( "channel", " missing in section [input]");
00161     channel       = Util::strToL( str);
00162     device        = cs->getForSure( "device", " missing in section [input]");
00163 
00164     dsp             = AudioSource::createDspSource( device,
00165                                                     sampleRate,
00166                                                     bitsPerSample,
00167                                                     channel );
00168     encConnector    = new MultiThreadedConnector( dsp.get(), reconnect );
00169 
00170     noAudioOuts = 0;
00171     configIceCast( config, bufferSecs);
00172     configIceCast2( config, bufferSecs);
00173     configShoutCast( config, bufferSecs);
00174     configFileCast( config);
00175 }
00176 
00177 
00178 /*------------------------------------------------------------------------------
00179  *  Look for the IceCast stream outputs in the config file
00180  *----------------------------------------------------------------------------*/
00181 void
00182 DarkIce :: configIceCast (  const Config      & config,
00183                             unsigned int        bufferSecs  )
00184                                                         throw ( Exception )
00185 {
00186     // look for IceCast encoder output streams,
00187     // sections [icecast-0], [icecast-1], ...
00188     char            stream[]        = "icecast- ";
00189     size_t          streamLen       = Util::strLen( stream);
00190     unsigned int    u;
00191 
00192     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00193         const ConfigSection    * cs;
00194 
00195         // ugly hack to change the section name to "stream0", "stream1", etc.
00196         stream[streamLen-1] = '0' + (u - noAudioOuts);
00197 
00198         if ( !(cs = config.get( stream)) ) {
00199             break;
00200         }
00201 
00202 #ifndef HAVE_LAME_LIB
00203         throw Exception( __FILE__, __LINE__,
00204                          "DarkIce not compiled with lame support, "
00205                          "thus can't connect to IceCast 1.x, stream: ",
00206                          stream);
00207 #else
00208 
00209         const char                * str;
00210 
00211         unsigned int                sampleRate      = 0;
00212         unsigned int                channel         = 0;
00213         AudioEncoder::BitrateMode   bitrateMode;
00214         unsigned int                bitrate         = 0;
00215         double                      quality         = 0.0;
00216         const char                * server          = 0;
00217         unsigned int                port            = 0;
00218         const char                * password        = 0;
00219         const char                * mountPoint      = 0;
00220         const char                * remoteDumpFile  = 0;
00221         const char                * name            = 0;
00222         const char                * description     = 0;
00223         const char                * url             = 0;
00224         const char                * genre           = 0;
00225         bool                        isPublic        = false;
00226         int                         lowpass         = 0;
00227         int                         highpass        = 0;
00228         const char                * localDumpName   = 0;
00229         FileSink                  * localDumpFile   = 0;
00230         bool                        fileAddDate     = false;
00231 
00232         str         = cs->get( "sampleRate");
00233         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00234         str         = cs->get( "channel");
00235         channel     = str ? Util::strToL( str) : dsp->getChannel();
00236 
00237         str         = cs->get( "bitrate");
00238         bitrate     = str ? Util::strToL( str) : 0;
00239         str         = cs->get( "quality");
00240         quality     = str ? Util::strToD( str) : 0.0;
00241         
00242         str         = cs->getForSure( "bitrateMode",
00243                                       " not specified in section ",
00244                                       stream);
00245         if ( Util::strEq( str, "cbr") ) {
00246             bitrateMode = AudioEncoder::cbr;
00247             
00248             if ( bitrate == 0 ) {
00249                 throw Exception( __FILE__, __LINE__,
00250                                  "bitrate not specified for CBR encoding");
00251             }
00252             if ( cs->get( "quality" ) == 0 ) {
00253                 throw Exception( __FILE__, __LINE__,
00254                                  "quality not specified for CBR encoding");
00255             }
00256         } else if ( Util::strEq( str, "abr") ) {
00257             bitrateMode = AudioEncoder::abr;
00258 
00259             if ( bitrate == 0 ) {
00260                 throw Exception( __FILE__, __LINE__,
00261                                  "bitrate not specified for ABR encoding");
00262             }
00263         } else if ( Util::strEq( str, "vbr") ) {
00264             bitrateMode = AudioEncoder::vbr;
00265 
00266             if ( cs->get( "quality" ) == 0 ) {
00267                 throw Exception( __FILE__, __LINE__,
00268                                  "quality not specified for VBR encoding");
00269             }
00270         } else {
00271             throw Exception( __FILE__, __LINE__,
00272                              "invalid bitrate mode: ", str);
00273         }
00274 
00275         server      = cs->getForSure( "server", " missing in section ", stream);
00276         str         = cs->getForSure( "port", " missing in section ", stream);
00277         port        = Util::strToL( str);
00278         password    = cs->getForSure("password"," missing in section ",stream);
00279         mountPoint  = cs->getForSure( "mountPoint",
00280                                       " missing in section ",
00281                                       stream);
00282         remoteDumpFile = cs->get( "remoteDumpFile");
00283         name        = cs->get( "name");
00284         description = cs->get("description");
00285         url         = cs->get( "url");
00286         genre       = cs->get( "genre");
00287         str         = cs->get( "public");
00288         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00289         str         = cs->get( "lowpass");
00290         lowpass     = str ? Util::strToL( str) : 0;
00291         str         = cs->get( "highpass");
00292         highpass    = str ? Util::strToL( str) : 0;
00293         str         = cs->get("fileAddDate");
00294         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00295 
00296         localDumpName = cs->get( "localDumpFile");
00297 
00298         // go on and create the things
00299 
00300         // check for and create the local dump file if needed
00301         if ( localDumpName != 0 ) {
00302             if ( fileAddDate ) {
00303                 localDumpName = Util::fileAddDate(localDumpName);
00304             }
00305 
00306             localDumpFile = new FileSink( localDumpName);
00307             if ( !localDumpFile->exists() ) {
00308                 if ( !localDumpFile->create() ) {
00309                     reportEvent( 1, "can't create local dump file",
00310                                     localDumpName);
00311                     localDumpFile = 0;
00312                 }
00313             }
00314             if ( fileAddDate ) {
00315                 delete[] localDumpFile;
00316             }
00317         }
00318         // streaming related stuff
00319         audioOuts[u].socket = new TcpSocket( server, port);
00320         audioOuts[u].server = new IceCast( audioOuts[u].socket.get(),
00321                                            password,
00322                                            mountPoint,
00323                                            bitrate,
00324                                            name,
00325                                            description,
00326                                            url,
00327                                            genre,
00328                                            isPublic,
00329                                            remoteDumpFile,
00330                                            localDumpFile,
00331                                            bufferSecs );
00332 
00333         audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(),
00334                                                    dsp.get(),
00335                                                    bitrateMode,
00336                                                    bitrate,
00337                                                    quality,
00338                                                    sampleRate,
00339                                                    channel,
00340                                                    lowpass,
00341                                                    highpass );
00342 
00343         encConnector->attach( audioOuts[u].encoder.get());
00344 #endif // HAVE_LAME_LIB
00345     }
00346 
00347     noAudioOuts += u;
00348 }
00349 
00350 
00351 /*------------------------------------------------------------------------------
00352  *  Look for the IceCast2 stream outputs in the config file
00353  *----------------------------------------------------------------------------*/
00354 void
00355 DarkIce :: configIceCast2 (  const Config      & config,
00356                              unsigned int        bufferSecs  )
00357                                                         throw ( Exception )
00358 {
00359     // look for IceCast2 encoder output streams,
00360     // sections [icecast2-0], [icecast2-1], ...
00361     char            stream[]        = "icecast2- ";
00362     size_t          streamLen       = Util::strLen( stream);
00363     unsigned int    u;
00364 
00365     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00366         const ConfigSection    * cs;
00367 
00368         // ugly hack to change the section name to "stream0", "stream1", etc.
00369         stream[streamLen-1] = '0' + (u - noAudioOuts);
00370 
00371         if ( !(cs = config.get( stream)) ) {
00372             break;
00373         }
00374 
00375         const char                * str;
00376 
00377         IceCast2::StreamFormat      format;
00378         unsigned int                sampleRate      = 0;
00379         unsigned int                channel         = 0;
00380         AudioEncoder::BitrateMode   bitrateMode;
00381         unsigned int                bitrate         = 0;
00382         unsigned int                maxBitrate      = 0;
00383         double                      quality         = 0.0;
00384         const char                * server          = 0;
00385         unsigned int                port            = 0;
00386         const char                * password        = 0;
00387         const char                * mountPoint      = 0;
00388         const char                * name            = 0;
00389         const char                * description     = 0;
00390         const char                * url             = 0;
00391         const char                * genre           = 0;
00392         bool                        isPublic        = false;
00393         int                         lowpass         = 0;
00394         int                         highpass        = 0;
00395         const char                * localDumpName   = 0;
00396         FileSink                  * localDumpFile   = 0;
00397         bool                        fileAddDate     = false;
00398 
00399         str         = cs->getForSure( "format", " missing in section ", stream);
00400         if ( Util::strEq( str, "vorbis") ) {
00401             format = IceCast2::oggVorbis;
00402         } else if ( Util::strEq( str, "mp3") ) {
00403             format = IceCast2::mp3;
00404         } else if ( Util::strEq( str, "aac") ) {
00405             format = IceCast2::aac;
00406         } else {
00407             throw Exception( __FILE__, __LINE__,
00408                              "unsupported stream format: ", str);
00409         }
00410                 
00411         str         = cs->get( "sampleRate");
00412         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00413         str         = cs->get( "channel");
00414         channel     = str ? Util::strToL( str) : dsp->getChannel();
00415 
00416         // determine fixed bitrate or variable bitrate quality
00417         str         = cs->get( "bitrate");
00418         bitrate     = str ? Util::strToL( str) : 0;
00419         str         = cs->get( "maxBitrate");
00420         maxBitrate  = str ? Util::strToL( str) : 0;
00421         str         = cs->get( "quality");
00422         quality     = str ? Util::strToD( str) : 0.0;
00423         
00424         str         = cs->getForSure( "bitrateMode",
00425                                       " not specified in section ",
00426                                       stream);
00427         if ( Util::strEq( str, "cbr") ) {
00428             bitrateMode = AudioEncoder::cbr;
00429             
00430             if ( bitrate == 0 ) {
00431                 throw Exception( __FILE__, __LINE__,
00432                                  "bitrate not specified for CBR encoding");
00433             }
00434         } else if ( Util::strEq( str, "abr") ) {
00435             bitrateMode = AudioEncoder::abr;
00436 
00437             if ( bitrate == 0 ) {
00438                 throw Exception( __FILE__, __LINE__,
00439                                  "bitrate not specified for ABR encoding");
00440             }
00441         } else if ( Util::strEq( str, "vbr") ) {
00442             bitrateMode = AudioEncoder::vbr;
00443 
00444             if ( cs->get( "quality" ) == 0 ) {
00445                 throw Exception( __FILE__, __LINE__,
00446                                  "quality not specified for VBR encoding");
00447             }
00448         } else {
00449             throw Exception( __FILE__, __LINE__,
00450                              "invalid bitrate mode: ", str);
00451         }
00452 
00453         server      = cs->getForSure( "server", " missing in section ", stream);
00454         str         = cs->getForSure( "port", " missing in section ", stream);
00455         port        = Util::strToL( str);
00456         password    = cs->getForSure("password"," missing in section ",stream);
00457         mountPoint  = cs->getForSure( "mountPoint",
00458                                       " missing in section ",
00459                                       stream);
00460         name        = cs->get( "name");
00461         description = cs->get( "description");
00462         url         = cs->get( "url");
00463         genre       = cs->get( "genre");
00464         str         = cs->get( "public");
00465         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00466         str         = cs->get( "lowpass");
00467         lowpass     = str ? Util::strToL( str) : 0;
00468         str         = cs->get( "highpass");
00469         highpass    = str ? Util::strToL( str) : 0;
00470         str         = cs->get( "fileAddDate");
00471         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00472         
00473         localDumpName = cs->get( "localDumpFile");
00474 
00475         // go on and create the things
00476 
00477         // check for and create the local dump file if needed
00478         if ( localDumpName != 0 ) {
00479             if ( fileAddDate ) {
00480                 localDumpName = Util::fileAddDate(localDumpName);
00481             }
00482 
00483             localDumpFile = new FileSink( localDumpName);
00484             if ( !localDumpFile->exists() ) {
00485                 if ( !localDumpFile->create() ) {
00486                     reportEvent( 1, "can't create local dump file",
00487                                     localDumpName);
00488                     localDumpFile = 0;
00489                 }
00490             }
00491             if ( fileAddDate ) {
00492                 delete[] localDumpName;
00493             }
00494         }
00495 
00496         // streaming related stuff
00497         audioOuts[u].socket = new TcpSocket( server, port);
00498         audioOuts[u].server = new IceCast2( audioOuts[u].socket.get(),
00499                                             password,
00500                                             mountPoint,
00501                                             format,
00502                                             bitrate,
00503                                             name,
00504                                             description,
00505                                             url,
00506                                             genre,
00507                                             isPublic,
00508                                             localDumpFile,
00509                                             bufferSecs );
00510 
00511         switch ( format ) {
00512             case IceCast2::mp3:
00513 #ifndef HAVE_LAME_LIB
00514                 throw Exception( __FILE__, __LINE__,
00515                                  "DarkIce not compiled with lame support, "
00516                                  "thus can't create mp3 stream: ",
00517                                  stream);
00518 #else
00519                 audioOuts[u].encoder = new LameLibEncoder(
00520                                                     audioOuts[u].server.get(),
00521                                                     dsp.get(),
00522                                                     bitrateMode,
00523                                                     bitrate,
00524                                                     quality,
00525                                                     sampleRate,
00526                                                     channel,
00527                                                     lowpass,
00528                                                     highpass );
00529 #endif // HAVE_LAME_LIB
00530                 break;
00531 
00532             case IceCast2::oggVorbis:
00533 #ifndef HAVE_VORBIS_LIB
00534                 throw Exception( __FILE__, __LINE__,
00535                                 "DarkIce not compiled with Ogg Vorbis support, "
00536                                 "thus can't Ogg Vorbis stream: ",
00537                                 stream);
00538 #else
00539                 audioOuts[u].encoder = new VorbisLibEncoder(
00540                                                 audioOuts[u].server.get(),
00541                                                 dsp.get(),
00542                                                 bitrateMode,
00543                                                 bitrate,
00544                                                 quality,
00545                                                 sampleRate,
00546                                                 dsp->getChannel(),
00547                                                 maxBitrate);
00548 #endif // HAVE_VORBIS_LIB
00549                 break;
00550 
00551             case IceCast2::aac:
00552 #ifndef HAVE_FAAC_LIB
00553                 throw Exception( __FILE__, __LINE__,
00554                                 "DarkIce not compiled with AAC support, "
00555                                 "thus can't aac stream: ",
00556                                 stream);
00557 #else
00558                 audioOuts[u].encoder = new FaacEncoder(
00559                                                 audioOuts[u].server.get(),
00560                                                 dsp.get(),
00561                                                 bitrateMode,
00562                                                 bitrate,
00563                                                 quality,
00564                                                 sampleRate,
00565                                                 dsp->getChannel());
00566 #endif // HAVE_FAAC_LIB
00567                 break;
00568 
00569             default:
00570                 throw Exception( __FILE__, __LINE__,
00571                                 "Illegal stream format: ", format);
00572         }
00573 
00574         encConnector->attach( audioOuts[u].encoder.get());
00575     }
00576 
00577     noAudioOuts += u;
00578 }
00579 
00580 
00581 /*------------------------------------------------------------------------------
00582  *  Look for the ShoutCast stream outputs in the config file
00583  *----------------------------------------------------------------------------*/
00584 void
00585 DarkIce :: configShoutCast (    const Config      & config,
00586                                 unsigned int        bufferSecs  )
00587                                                         throw ( Exception )
00588 {
00589     // look for Shoutcast encoder output streams,
00590     // sections [shoutcast-0], [shoutcast-1], ...
00591     char            stream[]        = "shoutcast- ";
00592     size_t          streamLen       = Util::strLen( stream);
00593     unsigned int    u;
00594 
00595     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00596         const ConfigSection    * cs;
00597 
00598         // ugly hack to change the section name to "stream0", "stream1", etc.
00599         stream[streamLen-1] = '0' + (u - noAudioOuts);
00600 
00601         if ( !(cs = config.get( stream)) ) {
00602             break;
00603         }
00604 
00605 #ifndef HAVE_LAME_LIB
00606         throw Exception( __FILE__, __LINE__,
00607                          "DarkIce not compiled with lame support, "
00608                          "thus can't connect to ShoutCast, stream: ",
00609                          stream);
00610 #else
00611 
00612         const char                * str;
00613 
00614         unsigned int                sampleRate      = 0;
00615         unsigned int                channel         = 0;
00616         AudioEncoder::BitrateMode   bitrateMode;
00617         unsigned int                bitrate         = 0;
00618         double                      quality         = 0.0;
00619         const char                * server          = 0;
00620         unsigned int                port            = 0;
00621         const char                * password        = 0;
00622         const char                * name            = 0;
00623         const char                * url             = 0;
00624         const char                * genre           = 0;
00625         bool                        isPublic        = false;
00626         int                         lowpass         = 0;
00627         int                         highpass        = 0;
00628         const char                * irc             = 0;
00629         const char                * aim             = 0;
00630         const char                * icq             = 0;
00631         const char                * localDumpName   = 0;
00632         FileSink                  * localDumpFile   = 0;
00633         bool                        fileAddDate     = false;
00634 
00635         str         = cs->get( "sampleRate");
00636         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00637         str         = cs->get( "channel");
00638         channel     = str ? Util::strToL( str) : dsp->getChannel();
00639 
00640         str         = cs->get( "bitrate");
00641         bitrate     = str ? Util::strToL( str) : 0;
00642         str         = cs->get( "quality");
00643         quality     = str ? Util::strToD( str) : 0.0;
00644         
00645         str         = cs->getForSure( "bitrateMode",
00646                                       " not specified in section ",
00647                                       stream);
00648         if ( Util::strEq( str, "cbr") ) {
00649             bitrateMode = AudioEncoder::cbr;
00650             
00651             if ( bitrate == 0 ) {
00652                 throw Exception( __FILE__, __LINE__,
00653                                  "bitrate not specified for CBR encoding");
00654             }
00655             if ( cs->get( "quality" ) == 0 ) {
00656                 throw Exception( __FILE__, __LINE__,
00657                                  "quality not specified for CBR encoding");
00658             }
00659         } else if ( Util::strEq( str, "abr") ) {
00660             bitrateMode = AudioEncoder::abr;
00661 
00662             if ( bitrate == 0 ) {
00663                 throw Exception( __FILE__, __LINE__,
00664                                  "bitrate not specified for ABR encoding");
00665             }
00666         } else if ( Util::strEq( str, "vbr") ) {
00667             bitrateMode = AudioEncoder::vbr;
00668 
00669             if ( cs->get( "quality" ) == 0 ) {
00670                 throw Exception( __FILE__, __LINE__,
00671                                  "quality not specified for VBR encoding");
00672             }
00673         } else {
00674             throw Exception( __FILE__, __LINE__,
00675                              "invalid bitrate mode: ", str);
00676         }
00677 
00678         server      = cs->getForSure( "server", " missing in section ", stream);
00679         str         = cs->getForSure( "port", " missing in section ", stream);
00680         port        = Util::strToL( str);
00681         password    = cs->getForSure("password"," missing in section ",stream);
00682         name        = cs->get( "name");
00683         url         = cs->get( "url");
00684         genre       = cs->get( "genre");
00685         str         = cs->get( "public");
00686         isPublic    = str ? (Util::strEq( str, "yes") ? true : false) : false;
00687         str         = cs->get( "lowpass");
00688         lowpass     = str ? Util::strToL( str) : 0;
00689         str         = cs->get( "highpass");
00690         highpass    = str ? Util::strToL( str) : 0;
00691         irc         = cs->get( "irc");
00692         aim         = cs->get( "aim");
00693         icq         = cs->get( "icq");
00694         str         = cs->get("fileAddDate");
00695         fileAddDate = str ? (Util::strEq( str, "yes") ? true : false) : false;
00696 
00697         localDumpName = cs->get( "localDumpFile");
00698 
00699         // go on and create the things
00700 
00701         // check for and create the local dump file if needed
00702         if ( localDumpName != 0 ) {
00703             if ( fileAddDate ) {
00704                 localDumpName = Util::fileAddDate(localDumpName);
00705             }
00706 
00707             localDumpFile = new FileSink( localDumpName);
00708             if ( !localDumpFile->exists() ) {
00709                 if ( !localDumpFile->create() ) {
00710                     reportEvent( 1, "can't create local dump file",
00711                                     localDumpName);
00712                     localDumpFile = 0;
00713                 }
00714             }
00715             if ( fileAddDate ) {
00716                 delete[] localDumpFile;
00717             }
00718         }
00719 
00720         // streaming related stuff
00721         audioOuts[u].socket = new TcpSocket( server, port);
00722         audioOuts[u].server = new ShoutCast( audioOuts[u].socket.get(),
00723                                              password,
00724                                              bitrate,
00725                                              name,
00726                                              url,
00727                                              genre,
00728                                              isPublic,
00729                                              irc,
00730                                              aim,
00731                                              icq,
00732                                              localDumpFile,
00733                                              bufferSecs );
00734 
00735         audioOuts[u].encoder = new LameLibEncoder( audioOuts[u].server.get(),
00736                                                    dsp.get(),
00737                                                    bitrateMode,
00738                                                    bitrate,
00739                                                    quality,
00740                                                    sampleRate,
00741                                                    channel,
00742                                                    lowpass,
00743                                                    highpass );
00744 
00745         encConnector->attach( audioOuts[u].encoder.get());
00746 #endif // HAVE_LAME_LIB
00747     }
00748 
00749     noAudioOuts += u;
00750 }
00751 
00752 
00753 /*------------------------------------------------------------------------------
00754  *  Look for the FileCast stream outputs in the config file
00755  *----------------------------------------------------------------------------*/
00756 void
00757 DarkIce :: configFileCast (  const Config      & config )
00758                                                         throw ( Exception )
00759 {
00760     // look for FileCast encoder output streams,
00761     // sections [file-0], [file-1], ...
00762     char            stream[]        = "file- ";
00763     size_t          streamLen       = Util::strLen( stream);
00764     unsigned int    u;
00765 
00766     for ( u = noAudioOuts; u < maxOutput; ++u ) {
00767         const ConfigSection    * cs;
00768 
00769         // ugly hack to change the section name to "stream0", "stream1", etc.
00770         stream[streamLen-1] = '0' + (u - noAudioOuts);
00771 
00772         if ( !(cs = config.get( stream)) ) {
00773             break;
00774         }
00775 
00776         const char                * str;
00777 
00778         const char                * format          = 0;
00779         AudioEncoder::BitrateMode   bitrateMode;
00780         unsigned int                bitrate         = 0;
00781         double                      quality         = 0.0;
00782         const char                * targetFileName  = 0;
00783         unsigned int                sampleRate      = 0;
00784         int                         lowpass         = 0;
00785         int                         highpass        = 0;
00786 
00787         format      = cs->getForSure( "format", " missing in section ", stream);
00788         if ( !Util::strEq( format, "vorbis")
00789           && !Util::strEq( format, "mp3")
00790           && !Util::strEq( format, "aac") ) {
00791             throw Exception( __FILE__, __LINE__,
00792                              "unsupported stream format: ", format);
00793         }
00794 
00795         str         = cs->getForSure("bitrate", " missing in section ", stream);
00796         bitrate     = Util::strToL( str);
00797         targetFileName    = cs->getForSure( "fileName",
00798                                             " missing in section ",
00799                                             stream);
00800         str         = cs->get( "sampleRate");
00801         sampleRate  = str ? Util::strToL( str) : dsp->getSampleRate();
00802 
00803         str         = cs->get( "bitrate");
00804         bitrate     = str ? Util::strToL( str) : 0;
00805         str         = cs->get( "quality");
00806         quality     = str ? Util::strToD( str) : 0.0;
00807         
00808         str         = cs->getForSure( "bitrateMode",
00809                                       " not specified in section ",
00810                                       stream);
00811         if ( Util::strEq( str, "cbr") ) {
00812             bitrateMode = AudioEncoder::cbr;
00813             
00814             if ( bitrate == 0 ) {
00815                 throw Exception( __FILE__, __LINE__,
00816                                  "bitrate not specified for CBR encoding");
00817             }
00818             if ( cs->get( "quality" ) == 0 ) {
00819                 throw Exception( __FILE__, __LINE__,
00820                                  "quality not specified for CBR encoding");
00821             }
00822         } else if ( Util::strEq( str, "abr") ) {
00823             bitrateMode = AudioEncoder::abr;
00824 
00825             if ( bitrate == 0 ) {
00826                 throw Exception( __FILE__, __LINE__,
00827                                  "bitrate not specified for ABR encoding");
00828             }
00829         } else if ( Util::strEq( str, "vbr") ) {
00830             bitrateMode = AudioEncoder::vbr;
00831 
00832             if ( cs->get( "quality" ) == 0 ) {
00833                 throw Exception( __FILE__, __LINE__,
00834                                  "quality not specified for VBR encoding");
00835             }
00836         } else {
00837             throw Exception( __FILE__, __LINE__,
00838                              "invalid bitrate mode: ", str);
00839         }
00840 
00841         if (Util::strEq(format, "aac") && bitrateMode != AudioEncoder::abr) {
00842             throw Exception(__FILE__, __LINE__,
00843                             "currently the AAC format only supports "
00844                             "average bitrate mode");
00845         }
00846 
00847         str         = cs->get( "lowpass");
00848         lowpass     = str ? Util::strToL( str) : 0;
00849         str         = cs->get( "highpass");
00850         highpass    = str ? Util::strToL( str) : 0;
00851 
00852         // go on and create the things
00853 
00854         // the underlying file
00855         FileSink  * targetFile = new FileSink( targetFileName);
00856         if ( !targetFile->exists() ) {
00857             if ( !targetFile->create() ) {
00858                 throw Exception( __FILE__, __LINE__,
00859                                  "can't create output file", targetFileName);
00860             }
00861         }
00862 
00863         // streaming related stuff
00864         audioOuts[u].socket = 0;
00865         audioOuts[u].server = new FileCast( targetFile );
00866 
00867         if ( Util::strEq( format, "mp3") ) {
00868 #ifndef HAVE_LAME_LIB
00869                 throw Exception( __FILE__, __LINE__,
00870                                  "DarkIce not compiled with lame support, "
00871                                  "thus can't create mp3 stream: ",
00872                                  stream);
00873 #else
00874                 audioOuts[u].encoder = new LameLibEncoder(
00875                                                     audioOuts[u].server.get(),
00876                                                     dsp.get(),
00877                                                     bitrateMode,
00878                                                     bitrate,
00879                                                     quality,
00880                                                     sampleRate,
00881                                                     dsp->getChannel(),
00882                                                     lowpass,
00883                                                     highpass );
00884 #endif // HAVE_LAME_LIB
00885         } else if ( Util::strEq( format, "vorbis") ) {
00886 #ifndef HAVE_VORBIS_LIB
00887                 throw Exception( __FILE__, __LINE__,
00888                                 "DarkIce not compiled with Ogg Vorbis support, "
00889                                 "thus can't Ogg Vorbis stream: ",
00890                                 stream);
00891 #else
00892                 audioOuts[u].encoder = new VorbisLibEncoder(
00893                                                     audioOuts[u].server.get(),
00894                                                     dsp.get(),
00895                                                     bitrateMode,
00896                                                     bitrate,
00897                                                     quality,
00898                                                     dsp->getSampleRate(),
00899                                                     dsp->getChannel() );
00900 #endif // HAVE_VORBIS_LIB
00901         } else if ( Util::strEq( format, "aac") ) {
00902 #ifndef HAVE_FAAC_LIB
00903                 throw Exception( __FILE__, __LINE__,
00904                                 "DarkIce not compiled with AAC support, "
00905                                 "thus can't aac stream: ",
00906                                 stream);
00907 #else
00908                 audioOuts[u].encoder = new FaacEncoder(
00909                                                 audioOuts[u].server.get(),
00910                                                 dsp.get(),
00911                                                 bitrateMode,
00912                                                 bitrate,
00913                                                 quality,
00914                                                 sampleRate,
00915                                                 dsp->getChannel());
00916 #endif // HAVE_FAAC_LIB
00917         } else {
00918                 throw Exception( __FILE__, __LINE__,
00919                                 "Illegal stream format: ", format);
00920         }
00921 
00922         encConnector->attach( audioOuts[u].encoder.get());
00923     }
00924 
00925     noAudioOuts += u;
00926 }
00927 
00928 
00929 /*------------------------------------------------------------------------------
00930  *  Set POSIX real-time scheduling, if super-user
00931  *----------------------------------------------------------------------------*/
00932 void
00933 DarkIce :: setRealTimeScheduling ( void )               throw ( Exception )
00934 {
00935 // Only if the OS has the POSIX real-time scheduling functions implemented.
00936 #if defined( HAVE_SCHED_GETSCHEDULER ) && defined( HAVE_SCHED_GETPARAM )
00937     uid_t   euid;
00938 
00939     euid = geteuid();
00940 
00941     if ( euid == 0 ) {
00942         int                 high_priority;
00943         struct sched_param  param;
00944 
00945         /* store the old scheduling parameters */
00946         if ( (origSchedPolicy = sched_getscheduler(0)) == -1 ) {
00947             throw Exception( __FILE__, __LINE__, "sched_getscheduler", errno);
00948         }
00949 
00950         if ( sched_getparam( 0, &param) == -1 ) {
00951             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
00952         }
00953         origSchedPriority = param.sched_priority;
00954         
00955         /* set SCHED_FIFO with max - 1 priority */
00956         if ( (high_priority = sched_get_priority_max(SCHED_FIFO)) == -1 ) {
00957             throw Exception(__FILE__,__LINE__,"sched_get_priority_max",errno);
00958         }
00959         reportEvent( 8, "scheduler high priority", high_priority);
00960 
00961         param.sched_priority = high_priority - 1;
00962 
00963         if ( sched_setscheduler( 0, SCHED_FIFO, &param) == -1 ) {
00964             throw Exception( __FILE__, __LINE__, "sched_setscheduler", errno);
00965         }
00966 
00967         /* ask the new priortiy and report it */
00968         if ( sched_getparam( 0, &param) == -1 ) {
00969             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
00970         }
00971 
00972         reportEvent( 1,
00973                      "Using POSIX real-time scheduling, priority",
00974                      param.sched_priority );
00975     } else {
00976         reportEvent( 1,
00977         "Not running as super-user, unable to use POSIX real-time scheduling" );
00978         reportEvent( 1,
00979         "It is recommended that you run this program as super-user");
00980     }
00981 #else
00982     reportEvent( 1, "POSIX scheduling not supported on this system, "
00983                     "this may cause recording skips");
00984 #endif // HAVE_SCHED_GETSCHEDULER && HAVE_SCHED_GETPARAM
00985 }
00986 
00987 
00988 /*------------------------------------------------------------------------------
00989  *  Set the original scheduling of the process, the one prior to the
00990  *  setRealTimeScheduling call.
00991  *  WARNING: make sure you don't call this before setRealTimeScheduling!!
00992  *----------------------------------------------------------------------------*/
00993 void
00994 DarkIce :: setOriginalScheduling ( void )               throw ( Exception )
00995 {
00996 // Only if the OS has the POSIX real-time scheduling functions implemented.
00997 #if defined( HAVE_SCHED_GETSCHEDULER ) && defined( HAVE_SCHED_GETPARAM )
00998     uid_t   euid;
00999 
01000     euid = geteuid();
01001 
01002     if ( euid == 0 ) {
01003         struct sched_param  param;
01004 
01005         if ( sched_getparam( 0, &param) == -1 ) {
01006             throw Exception( __FILE__, __LINE__, "sched_getparam", errno);
01007         }
01008 
01009         param.sched_priority = origSchedPriority;
01010 
01011         if ( sched_setscheduler( 0, origSchedPolicy, &param) == -1 ) {
01012             throw Exception( __FILE__, __LINE__, "sched_setscheduler", errno);
01013         }
01014 
01015         reportEvent( 5, "reverted to original scheduling");
01016     }
01017 #endif // HAVE_SCHED_GETSCHEDULER && HAVE_SCHED_GETPARAM
01018 }
01019 
01020 
01021 /*------------------------------------------------------------------------------
01022  *  Run the encoder
01023  *----------------------------------------------------------------------------*/
01024 bool
01025 DarkIce :: encode ( void )                          throw ( Exception )
01026 {
01027     unsigned int       len;
01028     unsigned long      bytes;
01029 
01030     if ( !encConnector->open() ) {
01031         throw Exception( __FILE__, __LINE__, "can't open connector");
01032     }
01033     
01034     bytes = dsp->getSampleRate() *
01035             (dsp->getBitsPerSample() / 8UL) *
01036             dsp->getChannel() *
01037             duration;
01038                                                 
01039     len = encConnector->transfer( bytes, 4096, 1, 0 );
01040 
01041     reportEvent( 1, len, "bytes transfered to the encoders");
01042 
01043     encConnector->close();
01044 
01045     return true;
01046 }
01047 
01048 
01049 /*------------------------------------------------------------------------------
01050  *  Run
01051  *----------------------------------------------------------------------------*/
01052 int
01053 DarkIce :: run ( void )                             throw ( Exception )
01054 {
01055     reportEvent( 3, "encoding");
01056     setRealTimeScheduling();
01057     encode();
01058     setOriginalScheduling();
01059     reportEvent( 3, "encoding ends");
01060 
01061     return 0;
01062 }
01063 
01064 
01065 /*------------------------------------------------------------------------------
01066  
01067   $Source: /cvsroot/darkice/darkice/src/DarkIce.cpp,v $
01068 
01069   $Log: DarkIce.cpp,v $
01070   Revision 1.45  2005/10/22 10:34:21  darkeye
01071   added highpass and lowpass values to icecast2 sections
01072 
01073   Revision 1.44  2005/04/16 21:57:34  darkeye
01074   added AAC support through the faac codec, http://www.audiocoding.com/
01075 
01076   Revision 1.43  2005/04/11 19:27:43  darkeye
01077   added option to turn off automatic reconnect feature
01078 
01079   Revision 1.42  2005/04/04 08:36:17  darkeye
01080   commited changes to enable Jack support
01081   thanks to Nicholas J. Humfrey, njh@ecs.soton.ac.uk
01082 
01083   Revision 1.41  2005/04/03 05:12:20  jbebel
01084   Changed mechanism for testing the presence of the quality value such that
01085   zero is a valid option.
01086 
01087   Revision 1.40  2004/02/23 19:12:51  darkeye
01088   ported to NetBSD
01089 
01090   Revision 1.39  2004/02/19 06:47:06  darkeye
01091   finalized OpenBSD port
01092 
01093   Revision 1.38  2004/02/18 21:08:11  darkeye
01094   ported to OpenBSD (real-time scheduling not yet supported)
01095 
01096   Revision 1.37  2004/02/15 12:14:38  darkeye
01097   added patch to allow mp3 stream downsampling to mono for icecast2 as well
01098 
01099   Revision 1.36  2004/02/15 12:06:30  darkeye
01100   added ALSA support, thanks to Christian Forster
01101 
01102   Revision 1.35  2003/02/09 13:15:57  darkeye
01103   added feature for setting the TITLE comment field for vorbis streams
01104 
01105   Revision 1.34  2003/02/09 12:57:36  darkeye
01106   cosmetic changes to the fileAddDate option
01107 
01108   Revision 1.33  2002/11/20 16:52:05  wandereq
01109   added fileAddDate function
01110 
01111   Revision 1.32  2002/10/19 12:24:55  darkeye
01112   anged internals so that now each encoding/server connection is
01113   a separate thread
01114 
01115   Revision 1.31  2002/08/20 19:35:37  darkeye
01116   added possibility to specify maximum bitrate for Ogg Vorbis streams
01117 
01118   Revision 1.30  2002/08/20 18:37:49  darkeye
01119   added mp3 streaming possibility for icecast2
01120 
01121   Revision 1.29  2002/08/03 12:41:18  darkeye
01122   added possibility to stream in mono when recording in stereo
01123 
01124   Revision 1.28  2002/07/20 10:59:00  darkeye
01125   added support for Ogg Vorbis 1.0, removed support for rc2
01126 
01127   Revision 1.27  2002/04/13 11:26:00  darkeye
01128   added cbr, abr and vbr setting feature with encoding quality
01129 
01130   Revision 1.26  2002/03/28 16:43:11  darkeye
01131   enabled resampling and variable bitrates for vorbis (icecast2) streams
01132 
01133   Revision 1.25  2002/02/28 09:49:25  darkeye
01134   added possibility to save the encoded stream to a local file only
01135   (no streaming server needed)
01136 
01137   Revision 1.24  2002/02/20 11:54:11  darkeye
01138   added local dump file possibility
01139 
01140   Revision 1.23  2002/02/20 10:35:35  darkeye
01141   updated to work with Ogg Vorbis libs rc3 and current IceCast2 cvs
01142 
01143   Revision 1.22  2001/10/20 10:56:45  darkeye
01144   added possibility to disable highpass and lowpass filters for lame
01145 
01146   Revision 1.21  2001/10/19 12:39:42  darkeye
01147   created configure options to compile with or without lame / Ogg Vorbis
01148 
01149   Revision 1.20  2001/10/19 09:03:39  darkeye
01150   added support for resampling mp3 streams
01151 
01152   Revision 1.19  2001/09/14 19:31:06  darkeye
01153   added IceCast2 / vorbis support
01154 
01155   Revision 1.18  2001/09/11 15:05:21  darkeye
01156   added Solaris support
01157 
01158   Revision 1.17  2001/09/09 11:27:31  darkeye
01159   added support for ShoutCast servers
01160 
01161   Revision 1.16  2001/09/05 20:11:15  darkeye
01162   removed dependency on locally stored SGI STL header files
01163   now compiler-supplied C++ library STL header files are used
01164   compiles under GNU C++ 3
01165   hash_map (an SGI extension to STL) replaced with map
01166   std:: namespace prefix added to all STL class references
01167 
01168   Revision 1.15  2001/08/30 17:25:56  darkeye
01169   renamed configure.h to config.h
01170 
01171   Revision 1.14  2001/08/29 21:08:30  darkeye
01172   made some description options in the darkice config file optional
01173 
01174   Revision 1.13  2001/08/26 20:44:30  darkeye
01175   removed external command-line encoder support
01176   replaced it with a shared-object support for lame with the possibility
01177   of static linkage
01178 
01179   Revision 1.12  2000/12/20 12:36:47  darkeye
01180   added POSIX real-time scheduling
01181 
01182   Revision 1.11  2000/11/18 11:13:27  darkeye
01183   removed direct reference to cout, except from main.cpp
01184   all class use the Reporter interface to report events
01185 
01186   Revision 1.10  2000/11/17 15:50:48  darkeye
01187   added -Wall flag to compiler and eleminated new warnings
01188 
01189   Revision 1.9  2000/11/15 18:37:37  darkeye
01190   changed the transferable number of bytes to unsigned long
01191 
01192   Revision 1.8  2000/11/15 18:08:43  darkeye
01193   added multiple verbosity-level event reporting and verbosity command
01194   line option
01195 
01196   Revision 1.7  2000/11/13 19:38:55  darkeye
01197   moved command line parameter parsing from DarkIce.cpp to main.cpp
01198 
01199   Revision 1.6  2000/11/13 18:46:50  darkeye
01200   added kdoc-style documentation comments
01201 
01202   Revision 1.5  2000/11/10 20:16:21  darkeye
01203   first real tests with multiple streaming
01204 
01205   Revision 1.4  2000/11/09 22:09:46  darkeye
01206   added multiple outputs
01207   added configuration reading
01208   added command line processing
01209 
01210   Revision 1.3  2000/11/08 17:29:50  darkeye
01211   added configuration file reader
01212 
01213   Revision 1.2  2000/11/05 14:08:27  darkeye
01214   changed builting to an automake / autoconf environment
01215 
01216   Revision 1.1.1.1  2000/11/05 10:05:49  darkeye
01217   initial version
01218 
01219   
01220 ------------------------------------------------------------------------------*/
01221 

Generated on Sat Oct 22 13:17:04 2005 for DarkIce by  doxygen 1.4.4