opalmixer.h

Go to the documentation of this file.
00001 /*
00002  * opalmixer.h
00003  *
00004  * OPAL audio mixer
00005  *
00006  * Open Phone Abstraction Library (OPAL)
00007  * Formally known as the Open H323 project.
00008  *
00009  * Copyright (C) 2007 Post Increment
00010  *
00011  * The contents of this file are subject to the Mozilla Public License
00012  * Version 1.0 (the "License"); you may not use this file except in
00013  * compliance with the License. You may obtain a copy of the License at
00014  * http://www.mozilla.org/MPL/
00015  *
00016  * Software distributed under the License is distributed on an "AS IS"
00017  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00018  * the License for the specific language governing rights and limitations
00019  * under the License.
00020  *
00021  * The Original Code is Open Phone Abstraction Library.
00022  *
00023  * The Initial Developer of the Original Code is Post Increment
00024  *
00025  * Contributor(s): ______________________________________.
00026  *
00027  * $Log: opalmixer.h,v $
00028  * Revision 2.3  2007/05/21 04:30:30  dereksmithies
00029  * put #ifndef _PTLIB_H protection around the include of ptlib.h
00030  *
00031  * Revision 2.2  2007/05/08 09:31:23  csoutheren
00032  * Removed warnings on Unix
00033  *
00034  * Revision 2.1  2007/05/07 14:13:51  csoutheren
00035  * Add call record capability
00036  *
00037  */
00038 
00039 
00040 #ifndef _OPALMIXER_H
00041 #define _OPALMIXER_H
00042 
00043 #ifndef _PTLIB_H
00044 #include <ptlib.h>
00045 #endif
00046 
00047 #include <queue>
00048 
00049 #include <ptlib/psync.h>
00050 #include <ptclib/delaychan.h>
00051 
00052 #include <rtp/rtp.h>
00053 #include <codec/opalwavfile.h>
00054 
00055 template <typename Locker_T = PSyncNULL>
00056 class PMemBuffer
00057 {
00058   public:
00059     struct Common {
00060       Common(size_t size)
00061         : base(size)
00062       { 
00063         refCount = 1; 
00064       }
00065 
00066       Common(BYTE * ptr, size_t size)
00067         : base(ptr, size)
00068       { 
00069         refCount = 1; 
00070       }
00071 
00072       mutable int refCount;
00073       mutable Locker_T mutex;
00074       mutable PBYTEArray base;
00075     };
00076 
00077     Common * common;
00078 
00079   protected:
00080     BYTE * data;
00081     PINDEX dataLen;
00082 
00083   public:
00084     PMemBuffer()
00085     { 
00086       common  = NULL;
00087       data    = NULL;
00088       dataLen = 0;
00089     }
00090 
00091     PMemBuffer(PINDEX size)
00092     { 
00093       common = new Common(size);
00094       data    = common->base.GetPointer();
00095       dataLen = size;
00096     }
00097 
00098     PMemBuffer(BYTE * ptr, size_t size)
00099     { 
00100       common = new Common(ptr, size);
00101       data    = common->base.GetPointer();
00102       dataLen = size;
00103     }
00104 
00105     PMemBuffer(const PBYTEArray & obj)
00106     { 
00107       common = new Common(obj.GetPointer(), obj.GetSize());
00108       data    = common->base.GetPointer();
00109       dataLen = obj.GetSize();
00110     }
00111 
00112     PMemBuffer(const PMemBuffer & obj)
00113     { 
00114       PWaitAndSignal m(obj.common->mutex);
00115       common = obj.common;
00116       ++common->refCount;
00117       data    = obj.data;
00118       dataLen = obj.dataLen;
00119     }
00120 
00121     ~PMemBuffer()
00122     {
00123       if (common != NULL) {
00124         common->mutex.Wait();
00125         BOOL last = common->refCount == 1;
00126         if (last) {
00127           common->mutex.Signal();
00128           delete common;
00129         } 
00130         else {
00131           --common->refCount;
00132           common->mutex.Signal();
00133         }
00134         common = NULL;
00135         data    = NULL;
00136         dataLen = 0;
00137       }
00138     }
00139 
00140     PMemBuffer & operator = (const PMemBuffer & obj)
00141     {
00142       if (&obj == this)
00143         return *this;
00144 
00145       if (common != NULL) {
00146         common->mutex.Wait();
00147         BOOL last = common->refCount == 1;
00148         if (last) {
00149           common->mutex.Signal();
00150           delete common;
00151         }
00152         else
00153         {
00154           --common->refCount;
00155           common->mutex.Signal();
00156         }
00157         common = NULL;
00158         data    = NULL;
00159         dataLen = 0;
00160       }
00161       {
00162         PWaitAndSignal m(obj.common->mutex);
00163         common = obj.common;
00164         ++common->refCount;
00165         data    = obj.data;
00166         dataLen = obj.dataLen;
00167       }
00168 
00169       return *this;
00170     }
00171 
00172     void MakeUnique()
00173     {
00174       PWaitAndSignal m(common->mutex);
00175       if (common->refCount == 1) 
00176         return;
00177 
00178       Common * newCommon = new Common(common->base.GetPointer(), common->base.GetSize());
00179       data = newCommon->base.GetPointer() + (data - common->base.GetPointer());
00180       --common->refCount;
00181       common = newCommon;
00182     }
00183 
00184     // set absolute base of data
00185     // length is unchanged
00186     void SetBase(PINDEX offs)
00187     { 
00188       PWaitAndSignal m(common->mutex);
00189       data = common->base.GetPointer() + offs;
00190       if (offs + dataLen > common->base.GetSize())
00191         dataLen = common->base.GetSize() - offs;
00192     }
00193 
00194     // adjust base of data relative to current base
00195     // length is unchanged
00196     void Rebase(PINDEX offs)
00197     { 
00198       PWaitAndSignal m(common->mutex);
00199       SetBase(offs + data - common->base.GetPointer());
00200     }
00201 
00202     // set the sbsolute length of the data
00203     void SetSize(PINDEX size)
00204     { 
00205       if (common == NULL) {
00206         common = new Common(size);
00207         data    = common->base.GetPointer();
00208         dataLen = size;
00209       }
00210       else {
00211         PWaitAndSignal m(common->mutex);
00212         if (size < dataLen)
00213           dataLen = size;
00214         else {
00215           PINDEX offs = data - common->base.GetPointer();
00216           if (offs + size < common->base.GetSize())
00217             dataLen = size;
00218           else
00219             dataLen = common->base.GetSize() - offs;
00220         }
00221       }
00222     }
00223 
00224     BYTE * GetPointerAndLock()
00225     { 
00226       PAssert(common != NULL, "NULL pointer");
00227       common->mutex.Wait();
00228       return data; 
00229     }
00230 
00231     inline const BYTE * GetPointerAndLock() const
00232     { 
00233       PAssert(common != NULL, "NULL pointer");
00234       common->mutex.Wait();
00235       return data; 
00236     }
00237 
00238     inline PINDEX GetSize() const
00239     { return dataLen; }
00240 
00241     inline void Lock() const
00242     {
00243       common->mutex.Wait();
00244     }
00245 
00246     inline void Unlock() const
00247     {
00248       common->mutex.Signal();
00249     }
00250 
00251     inline PSync & GetMutex()
00252     {
00253       return common->mutex;
00254     }
00255 };
00256 
00258 
00260 //
00261 //  the mixer operates by re-buffering the input streams into 10ms chunks
00262 //  each with an associated timestamp. A main mixer thread then reads from each 
00263 //  stream at regular intervals, mixes the audio and creates the output
00264 //
00265 //  There are several complications:
00266 //
00267 //    1) the timestamps must be used so that breaks in the input audio are 
00268 //       dealt with correctly
00269 //
00270 //    2) Using a single worker thread to read all of the streams doesn't work because
00271 //       it tends to get starved of CPU time and the output either gets behind or has
00272 //       breaks in it. To avoid this, the creation of the output data is triggered 
00273 //       by whatever thread (write or read) occurs after each 10ms interval
00274 //
00275 
00277 //
00278 //  define a class that encapsulates an audio stream for the purposes of the mixer
00279 //
00280 
00281 class OpalAudioMixerStream {
00282   public:
00283     class StreamFrame : public PMemBuffer<PMutex> {
00284       public:
00285         DWORD timestamp;
00286         unsigned channelNumber;
00287         StreamFrame()
00288         { }
00289 
00290         StreamFrame(const RTP_DataFrame & rtp);
00291     };
00292     typedef std::queue<StreamFrame> StreamFrameQueue_T;
00293 
00294     PMutex mutex;
00295     StreamFrameQueue_T frameQueue;
00296     StreamFrame frameCache;
00297     DWORD cacheTimeStamp;
00298 
00299     BOOL active;
00300     BOOL first;
00301     unsigned channelNumber;
00302 
00303     OpalAudioMixerStream();
00304     void WriteFrame(const StreamFrame & frame);
00305     void FillSilence(StreamFrame & retFrame, PINDEX ms);
00306     void PopFrame(StreamFrame & retFrame, PINDEX ms);
00307     BOOL ReadFrame(StreamFrame & retFrame, PINDEX ms);
00308 };
00309 
00311 //
00312 //  Define the audio mixer. This class extracts audio from a list of 
00313 //  OpalAudioMixerStream instances
00314 //
00315 
00316 class OpalAudioMixer
00317 {
00318   public:
00319     typedef std::string Key_T;
00320     typedef std::map<Key_T, OpalAudioMixerStream *> StreamInfoMap_T;
00321     typedef std::map<Key_T, OpalAudioMixerStream::StreamFrame> MixerPCMMap_T;
00322 
00323     class MixerFrame
00324     {
00325       public:
00326         MixerPCMMap_T channelData;
00327 
00328         DWORD timeStamp;
00329         PINDEX frameLengthSamples;
00330         mutable PIntArray mixedData;
00331         mutable PMutex mutex;
00332 
00333         MixerFrame(PINDEX _frameLength);
00334         void CreateMixedData() const;
00335         BOOL GetMixedFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00336         BOOL GetStereoFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00337         BOOL GetChannelFrame(Key_T key, OpalAudioMixerStream::StreamFrame & frame) const;
00338     };
00339 
00340   protected:
00341     PINDEX frameLengthMs;                  
00342 
00343     PMutex mutex;                          
00344     StreamInfoMap_T streamInfoMap;         
00345     unsigned channelNumber;                
00346 
00347     BOOL realTime;                         
00348     BOOL pushThread;                       
00349     PThread * thread;                      
00350     BOOL threadRunning;                    
00351 
00352     BOOL audioStarted;                     
00353     BOOL firstRead;                        
00354 
00355     PTime timeOfNextRead;                  
00356     DWORD outputTimestamp;                 
00357 
00358   public:
00359     OpalAudioMixer(BOOL realTime = TRUE, BOOL _pushThread = TRUE);
00360     virtual ~OpalAudioMixer() { }
00361     virtual BOOL OnWriteAudio(const MixerFrame &);
00362     BOOL AddStream(const Key_T & key, OpalAudioMixerStream * stream);
00363     void RemoveStream(const Key_T & key);
00364     void RemoveAllStreams();
00365     void StartThread();
00366     void ThreadMain();
00367     void ReadRoutine();
00368     void WriteMixedFrame();
00369     BOOL Write(const Key_T & key, const RTP_DataFrame & rtp);
00370 };
00371 
00372 #endif // _OPAL_MIXER
00373 

Generated on Fri Mar 7 06:33:40 2008 for OPAL by  doxygen 1.5.1