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: 23023 $
00028  * $Author: rjongbloed $
00029  * $Date: 2009-07-01 12:40:58 +0000 (Wed, 01 Jul 2009) $
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 
00283         StreamFrame()
00284           : timestamp(0)
00285           , channelNumber(0)
00286         { }
00287 
00288         StreamFrame(const RTP_DataFrame & rtp);
00289     };
00290     typedef std::queue<StreamFrame> StreamFrameQueue_T;
00291 
00292     PMutex mutex;
00293     StreamFrameQueue_T frameQueue;
00294     StreamFrame frameCache;
00295     DWORD cacheTimeStamp;
00296     DWORD writtenTimeStamp;
00297 
00298     PBoolean active;
00299     PBoolean first;
00300     unsigned channelNumber;
00301 
00302     OpalAudioMixerStream();
00303     void WriteFrame(const StreamFrame & frame);
00304     void FillSilence(StreamFrame & retFrame, PINDEX ms);
00305     void PopFrame(StreamFrame & retFrame, PINDEX ms);
00306     PBoolean ReadFrame(StreamFrame & retFrame, PINDEX ms);
00307 };
00308 
00310 //
00311 //  Define the audio mixer. This class extracts audio from a list of 
00312 //  OpalAudioMixerStream instances
00313 //
00314 
00315 class OpalAudioMixer
00316 {
00317   public:
00318     typedef std::string Key_T;
00319     typedef std::map<Key_T, OpalAudioMixerStream *> StreamInfoMap_T;
00320     typedef std::map<Key_T, OpalAudioMixerStream::StreamFrame> MixerPCMMap_T;
00321 
00322     class MixerFrame
00323     {
00324       protected:
00325         MixerPCMMap_T channelData;
00326 
00327         PINDEX frameLengthSamples;
00328         mutable PIntArray mixedData;
00329         mutable PMutex mutex;
00330 
00331       public:
00332                 MixerFrame(PINDEX _frameLength);
00333         void CreateMixedData() const;
00334         PBoolean GetMixedFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00335         PBoolean GetStereoFrame(OpalAudioMixerStream::StreamFrame & frame) const;
00336         PBoolean GetChannelFrame(Key_T key, OpalAudioMixerStream::StreamFrame & frame) const;
00337         void InsertFrame(Key_T key, OpalAudioMixerStream::StreamFrame & frame);
00338         };
00339 
00340   protected:
00341     PINDEX frameLengthMs;                  
00342 
00343     PMutex mutex;                          
00344     StreamInfoMap_T streamInfoMap;         
00345     unsigned channelNumber;                
00346 
00347     PBoolean realTime;                         
00348     PBoolean pushThread;                       
00349     PThread * mixerWorkerThread;                      
00350     PBoolean threadRunning;                    
00351 
00352     PBoolean audioStarted;                     
00353     PBoolean firstRead;                        
00354 
00355     PTime timeOfNextRead;                  
00356     DWORD outputTimestamp;                 
00357 
00358   public:
00359     OpalAudioMixer(PBoolean realTime = PTrue, PBoolean _pushThread = PTrue);
00360     virtual ~OpalAudioMixer() { }
00361     virtual PBoolean OnWriteAudio(const MixerFrame &);
00362     void 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     PBoolean Write(const Key_T & key, const RTP_DataFrame & rtp);
00370 };
00371 
00372 #endif // OPAL_OPAL_OPAL_MIXER
00373 

Generated on Mon Feb 1 00:25:53 2010 for OPAL by  doxygen 1.5.1