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  * $Revision: 21283 $
00028  * $Author: rjongbloed $
00029  * $Date: 2008-10-11 07:10:58 +0000 (Sat, 11 Oct 2008) $
00030  */
00031 
00032 
00033 #ifndef OPAL_OPAL_OPALMIXER_H
00034 #define OPAL_OPAL_OPALMIXER_H
00035 
00036 #ifndef _PTLIB_H
00037 #include <ptlib.h>
00038 #endif
00039 
00040 #include <opal/buildopts.h>
00041 
00042 #include <queue>
00043 
00044 #include <ptlib/psync.h>
00045 #include <ptclib/delaychan.h>
00046 
00047 #include <rtp/rtp.h>
00048 #include <codec/opalwavfile.h>
00049 
00050 template <typename Locker_T = PSyncNULL>
00051 class PMemBuffer
00052 {
00053   public:
00054     struct Common {
00055       Common(size_t size)
00056         : base(size)
00057       { 
00058         refCount = 1; 
00059       }
00060 
00061       Common(BYTE * ptr, size_t size)
00062         : base(ptr, size)
00063       { 
00064         refCount = 1; 
00065       }
00066 
00067       mutable int refCount;
00068       mutable Locker_T mutex;
00069       mutable PBYTEArray base;
00070     };
00071 
00072     Common * common;
00073 
00074   protected:
00075     BYTE * data;
00076     PINDEX dataLen;
00077 
00078   public:
00079     PMemBuffer()
00080     { 
00081       common  = NULL;
00082       data    = NULL;
00083       dataLen = 0;
00084     }
00085 
00086     PMemBuffer(PINDEX size)
00087     { 
00088       common = new Common(size);
00089       data    = common->base.GetPointer();
00090       dataLen = size;
00091     }
00092 
00093     PMemBuffer(BYTE * ptr, size_t size)
00094     { 
00095       common = new Common(ptr, size);
00096       data    = common->base.GetPointer();
00097       dataLen = size;
00098     }
00099 
00100     PMemBuffer(const PBYTEArray & obj)
00101     { 
00102       common = new Common(obj.GetPointer(), obj.GetSize());
00103       data    = common->base.GetPointer();
00104       dataLen = obj.GetSize();
00105     }
00106 
00107     PMemBuffer(const PMemBuffer & obj)
00108     { 
00109       PWaitAndSignal m(obj.common->mutex);
00110       common = obj.common;
00111       ++common->refCount;
00112       data    = obj.data;
00113       dataLen = obj.dataLen;
00114     }
00115 
00116     ~PMemBuffer()
00117     {
00118       if (common != NULL) {
00119         common->mutex.Wait();
00120         PBoolean last = common->refCount == 1;
00121         if (last) {
00122           common->mutex.Signal();
00123           delete common;
00124         } 
00125         else {
00126           --common->refCount;
00127           common->mutex.Signal();
00128         }
00129         common = NULL;
00130         data    = NULL;
00131         dataLen = 0;
00132       }
00133     }
00134 
00135     PMemBuffer & operator = (const PMemBuffer & obj)
00136     {
00137       if (&obj == this)
00138         return *this;
00139 
00140       if (common != NULL) {
00141         common->mutex.Wait();
00142         PBoolean last = common->refCount == 1;
00143         if (last) {
00144           common->mutex.Signal();
00145           delete common;
00146         }
00147         else
00148         {
00149           --common->refCount;
00150           common->mutex.Signal();
00151         }
00152         common = NULL;
00153         data    = NULL;
00154         dataLen = 0;
00155       }
00156       {
00157         PWaitAndSignal m(obj.common->mutex);
00158         common = obj.common;
00159         ++common->refCount;
00160         data    = obj.data;
00161         dataLen = obj.dataLen;
00162       }
00163 
00164       return *this;
00165     }
00166 
00167     void MakeUnique()
00168     {
00169       PWaitAndSignal m(common->mutex);
00170       if (common->refCount == 1) 
00171         return;
00172 
00173       Common * newCommon = new Common(common->base.GetPointer(), common->base.GetSize());
00174       data = newCommon->base.GetPointer() + (data - common->base.GetPointer());
00175       --common->refCount;
00176       common = newCommon;
00177     }
00178 
00179     // set absolute base of data
00180     // length is unchanged
00181     void SetBase(PINDEX offs)
00182     { 
00183       PWaitAndSignal m(common->mutex);
00184       data = common->base.GetPointer() + offs;
00185       if (offs + dataLen > common->base.GetSize())
00186         dataLen = common->base.GetSize() - offs;
00187     }
00188 
00189     // adjust base of data relative to current base
00190     // length is unchanged
00191     void Rebase(PINDEX offs)
00192     { 
00193       PWaitAndSignal m(common->mutex);
00194       SetBase(offs + data - common->base.GetPointer());
00195     }
00196 
00197     // set the sbsolute length of the data
00198     void SetSize(PINDEX size)
00199     { 
00200       if (common == NULL) {
00201         common = new Common(size);
00202         data    = common->base.GetPointer();
00203         dataLen = size;
00204       }
00205       else {
00206         PWaitAndSignal m(common->mutex);
00207         if (size < dataLen)
00208           dataLen = size;
00209         else {
00210           PINDEX offs = data - common->base.GetPointer();
00211           if (offs + size < common->base.GetSize())
00212             dataLen = size;
00213           else
00214             dataLen = common->base.GetSize() - offs;
00215         }
00216       }
00217     }
00218 
00219     BYTE * GetPointerAndLock()
00220     { 
00221       PAssert(common != NULL, "NULL pointer");
00222       common->mutex.Wait();
00223       return data; 
00224     }
00225 
00226     inline const BYTE * GetPointerAndLock() const
00227     { 
00228       PAssert(common != NULL, "NULL pointer");
00229       common->mutex.Wait();
00230       return data; 
00231     }
00232 
00233     inline PINDEX GetSize() const
00234     { return dataLen; }
00235 
00236     inline void Lock() const
00237     {
00238       common->mutex.Wait();
00239     }
00240 
00241     inline void Unlock() const
00242     {
00243       common->mutex.Signal();
00244     }
00245 
00246     inline PSync & GetMutex()
00247     {
00248       return common->mutex;
00249     }
00250 };
00251 
00253 
00255 //
00256 //  the mixer operates by re-buffering the input streams into 10ms chunks
00257 //  each with an associated timestamp. A main mixer thread then reads from each 
00258 //  stream at regular intervals, mixes the audio and creates the output
00259 //
00260 //  There are several complications:
00261 //
00262 //    1) the timestamps must be used so that breaks in the input audio are 
00263 //       dealt with correctly
00264 //
00265 //    2) Using a single worker thread to read all of the streams doesn't work because
00266 //       it tends to get starved of CPU time and the output either gets behind or has
00267 //       breaks in it. To avoid this, the creation of the output data is triggered 
00268 //       by whatever thread (write or read) occurs after each 10ms interval
00269 //
00270 
00272 //
00273 //  define a class that encapsulates an audio stream for the purposes of the mixer
00274 //
00275 
00276 class OpalAudioMixerStream {
00277   public:
00278     class StreamFrame : public PMemBuffer<PMutex> {
00279       public:
00280         DWORD timestamp;
00281         unsigned channelNumber;
00282         StreamFrame()
00283         { }
00284 
00285         StreamFrame(const RTP_DataFrame & rtp);
00286     };
00287     typedef std::queue<StreamFrame> StreamFrameQueue_T;
00288 
00289     PMutex mutex;
00290     StreamFrameQueue_T frameQueue;
00291     StreamFrame frameCache;
00292     DWORD cacheTimeStamp;
00293 
00294     PBoolean active;
00295     PBoolean first;
00296     unsigned channelNumber;
00297 
00298     OpalAudioMixerStream();
00299     void WriteFrame(const StreamFrame & frame);
00300     void FillSilence(StreamFrame & retFrame, PINDEX ms);
00301     void PopFrame(StreamFrame & retFrame, PINDEX ms);
00302     PBoolean ReadFrame(StreamFrame & retFrame, PINDEX ms);
00303 };
00304 
00306 //
00307 //  Define the audio mixer. This class extracts audio from a list of 
00308 //  OpalAudioMixerStream instances
00309 //
00310 
00311 class OpalAudioMixer
00312 {
00313   public:
00314     typedef std::string Key_T;
00315     typedef std::map<Key_T, OpalAudioMixerStream *> StreamInfoMap_T;
00316     typedef std::map<Key_T, OpalAudioMixerStream::StreamFrame> MixerPCMMap_T;
00317 
00318     class MixerFrame
00319     {
00320       public:
00321         MixerPCMMap_T channelData;
00322 
00323         DWORD timeStamp;
00324         PINDEX frameLengthSamples;
00325         mutable PIntArray mixedData;
00326         mutable PMutex mutex;
00327 
00328         MixerFrame(PINDEX _frameLength);
00329         void CreateMixedData() const;
00330         PBoolean GetMixedFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00331         PBoolean GetStereoFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00332         PBoolean GetChannelFrame(Key_T key, OpalAudioMixerStream::StreamFrame & frame) const;
00333     };
00334 
00335   protected:
00336     PINDEX frameLengthMs;                  
00337 
00338     PMutex mutex;                          
00339     StreamInfoMap_T streamInfoMap;         
00340     unsigned channelNumber;                
00341 
00342     PBoolean realTime;                         
00343     PBoolean pushThread;                       
00344     PThread * thread;                      
00345     PBoolean threadRunning;                    
00346 
00347     PBoolean audioStarted;                     
00348     PBoolean firstRead;                        
00349 
00350     PTime timeOfNextRead;                  
00351     DWORD outputTimestamp;                 
00352 
00353   public:
00354     OpalAudioMixer(PBoolean realTime = PTrue, PBoolean _pushThread = PTrue);
00355     virtual ~OpalAudioMixer() { }
00356     virtual PBoolean OnWriteAudio(const MixerFrame &);
00357     PBoolean AddStream(const Key_T & key, OpalAudioMixerStream * stream);
00358     void RemoveStream(const Key_T & key);
00359     void RemoveAllStreams();
00360     void StartThread();
00361     void ThreadMain();
00362     void ReadRoutine();
00363     void WriteMixedFrame();
00364     PBoolean Write(const Key_T & key, const RTP_DataFrame & rtp);
00365 };
00366 
00367 #endif // OPAL_OPAL_OPAL_MIXER
00368 

Generated on Mon Feb 23 02:01:38 2009 for OPAL by  doxygen 1.5.1