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

Generated on Mon Sep 15 11:49:14 2008 for OPAL by  doxygen 1.5.1