opalmixer.h

Go to the documentation of this file.
00001 /*
00002  * opalmixer.h
00003  *
00004  * OPAL media mixers
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): Craig Southeren (craigs@postincrement.com)
00026  *                 Robert Jongbloed (robertj@voxlucida.com.au)
00027  *
00028  * $Revision: 23953 $
00029  * $Author: rjongbloed $
00030  * $Date: 2010-01-20 02:08:46 -0600 (Wed, 20 Jan 2010) $
00031  */
00032 
00033 
00034 #ifndef OPAL_OPAL_OPALMIXER_H
00035 #define OPAL_OPAL_OPALMIXER_H
00036 
00037 #ifndef _PTLIB_H
00038 #include <ptlib.h>
00039 #endif
00040 
00041 #include <opal/buildopts.h>
00042 
00043 #include <queue>
00044 
00045 #include <opal/localep.h>
00046 #include <codec/vidcodec.h>
00047 
00048 
00049 class RTP_DataFrame;
00050 class OpalJitterBuffer;
00051 
00052 
00053 //#define OPAL_MIXER_AUDIO_DEBUG 1
00054 
00055 
00056 #define OPAL_OPT_LISTEN_ONLY "Listen-Only" 
00057 
00058 
00060 
00070 class OpalBaseMixer
00071 {
00072   public:
00073     OpalBaseMixer(
00074       bool pushThread,    
00075       unsigned periodMS,  
00076       unsigned periodTS   
00077     );
00078 
00079     virtual ~OpalBaseMixer();
00080 
00081     typedef PString Key_T;
00082 
00085     virtual bool AddStream(
00086       const Key_T & key   
00087     );
00088 
00091     virtual void RemoveStream(
00092       const Key_T & key   
00093     );
00094 
00097     virtual void RemoveAllStreams();
00098 
00104     virtual bool WriteStream(
00105       const Key_T & key,          
00106       const RTP_DataFrame & input 
00107     );
00108 
00118     virtual RTP_DataFrame * ReadMixed();
00119     virtual bool ReadMixed(RTP_DataFrame & mixed);
00120 
00131     virtual bool OnMixed(
00132       RTP_DataFrame * & mixed   
00133     );
00134 
00138     void StartPushThread();
00139 
00144     void StopPushThread(bool lock = true);
00145 
00148     unsigned GetPeriodTS() const { return m_periodTS; }
00149 
00150   protected:
00151     struct Stream {
00152       virtual ~Stream() { }
00153       virtual void QueuePacket(const RTP_DataFrame & rtp) = 0;
00154       queue<RTP_DataFrame> m_queue;
00155     };
00156     typedef std::map<Key_T, Stream *> StreamMap_T;
00157 
00158     virtual Stream * CreateStream() = 0;
00159     virtual bool MixStreams(RTP_DataFrame & frame) = 0;
00160     virtual size_t GetOutputSize() const = 0;
00161 
00162     virtual bool OnPush();
00163     void PushThreadMain();
00164 
00165     bool      m_pushThread;      // true if to use a thread to push data out
00166     unsigned  m_periodMS;        // Mixing interval in milliseconds
00167     unsigned  m_periodTS;        // Mixing interval in timestamp units
00168 
00169     StreamMap_T     m_inputStreams;     // Map of key to stream for input RTP frame queues
00170     unsigned        m_outputTimestamp;  // RTP timestamp for output data
00171     RTP_DataFrame * m_pushFrame;        // Cached frame for pushing RTP
00172     PThread *       m_workerThread;     // reader thread handle
00173     bool            m_threadRunning;    // used to stop reader thread
00174     PMutex          m_mutex;            // mutex for list of streams and thread handle
00175 };
00176 
00178 
00187 class OpalAudioMixer : public OpalBaseMixer
00188 {
00189   public:
00190     OpalAudioMixer(
00191       bool stereo = false,    
00192       unsigned sampleRate = OpalMediaFormat::AudioClockRate, 
00193       bool pushThread = true, 
00194       unsigned period = 10    
00195     );
00196 
00197     ~OpalAudioMixer() { StopPushThread(); }
00198 
00201     virtual void RemoveStream(
00202       const Key_T & key   
00203     );
00204 
00207     virtual void RemoveAllStreams();
00208 
00211     bool IsStereo() const { return m_stereo; }
00212 
00215     unsigned GetSampleRate() const { return m_sampleRate; }
00216 
00223     bool SetSampleRate(
00224       unsigned rate   
00225     );
00226 
00233     bool SetJitterBufferSize(
00234       const Key_T & key,       
00235       unsigned minJitterDelay, 
00236       unsigned maxJitterDelay  
00237     );
00238 
00239   protected:
00240     struct AudioStream : public Stream
00241     {
00242       AudioStream(OpalAudioMixer & mixer);
00243       ~AudioStream();
00244 
00245       virtual void QueuePacket(const RTP_DataFrame & rtp);
00246       const short * GetAudioDataPtr();
00247 
00248       OpalAudioMixer   & m_mixer;
00249       OpalJitterBuffer * m_jitter;
00250       unsigned           m_nextTimestamp;
00251       PShortArray        m_cacheSamples;
00252       size_t             m_samplesUsed;
00253     };
00254 
00255     virtual Stream * CreateStream();
00256     virtual bool MixStreams(RTP_DataFrame & frame);
00257     virtual size_t GetOutputSize() const;
00258 
00259     void PreMixStreams();
00260     void MixStereo(RTP_DataFrame & frame);
00261     void MixAdditive(RTP_DataFrame & frame, const short * audioToSubtract);
00262 
00263   protected:
00264     bool     m_stereo;
00265     unsigned m_sampleRate;
00266 
00267     AudioStream    * m_left;
00268     AudioStream    * m_right;
00269     std::vector<int> m_mixedAudio;
00270 };
00271 
00272 
00274 
00275 #if OPAL_VIDEO
00276 
00283 class OpalVideoMixer : public OpalBaseMixer
00284 {
00285   public:
00286     enum Styles {
00287       eSideBySideLetterbox, 
00291       eSideBySideScaled,    
00295       eStackedPillarbox,    
00299       eStackedScaled,       
00303       eGrid,                
00305     };
00306 
00307     OpalVideoMixer(
00308       Styles style,           
00309       unsigned width,         
00310       unsigned height,        
00311       unsigned rate = 15,     
00312       bool pushThread = true  
00313     );
00314 
00315     ~OpalVideoMixer() { StopPushThread(); }
00316 
00319     unsigned GetFrameWidth() const { return m_width; }
00320 
00323     unsigned GetFrameHeight() const { return m_height; }
00324 
00327     unsigned GetFrameRate() const { return 1000/m_periodMS; }
00328 
00332     bool SetFrameRate(
00333       unsigned rate   // New frames per second.
00334     );
00335 
00339     bool SetFrameSize(
00340       unsigned width,   
00341       unsigned height   
00342     );
00343 
00344   protected:
00345     struct VideoStream : public Stream
00346     {
00347       VideoStream(OpalVideoMixer & mixer);
00348       virtual void QueuePacket(const RTP_DataFrame & rtp);
00349       void InsertVideoFrame(unsigned x, unsigned y, unsigned w, unsigned h);
00350 
00351       OpalVideoMixer     & m_mixer;
00352     };
00353 
00354     friend struct VideoStream;
00355 
00356     virtual Stream * CreateStream();
00357     virtual bool MixStreams(RTP_DataFrame & frame);
00358     virtual size_t GetOutputSize() const;
00359 
00360   protected:
00361     Styles     m_style;
00362     unsigned   m_width, m_height;
00363 
00364     PBYTEArray m_frameStore;
00365 };
00366 
00367 #endif // OPAL_VIDEO
00368 
00369 
00371 
00372 
00377 struct OpalMixerNodeInfo
00378 {
00379   OpalMixerNodeInfo()
00380     : m_listenOnly(false)
00381     , m_sampleRate(OpalMediaFormat::AudioClockRate)
00382 #if OPAL_VIDEO
00383     , m_audioOnly(false)
00384     , m_style(OpalVideoMixer::eGrid)
00385     , m_width(PVideoFrameInfo::CIFWidth)
00386     , m_height(PVideoFrameInfo::CIFHeight)
00387     , m_rate(15)
00388 #endif
00389     , m_noMediaBypass(false)
00390   { }
00391 
00392   virtual ~OpalMixerNodeInfo() { }
00393 
00394   virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
00395 
00396   PString  m_name;                
00397   bool     m_listenOnly;          
00398   unsigned m_sampleRate;          
00399 #if OPAL_VIDEO
00400   bool     m_audioOnly;           
00401   OpalVideoMixer::Styles m_style; 
00402   unsigned m_width;               
00403   unsigned m_height;              
00404   unsigned m_rate;                
00405 #endif
00406   bool     m_noMediaBypass;       
00408 };
00409 
00410 
00412 
00413 class OpalMixerNode;
00414 
00415 
00420 class OpalMixerNodeManager : public PObject
00421 {
00422     PCLASSINFO(OpalMixerNodeManager, PObject);
00423   public:
00428     OpalMixerNodeManager();
00429 
00433         virtual ~OpalMixerNodeManager();
00434 
00437     virtual void ShutDown();
00438 
00444     virtual PBoolean GarbageCollection();
00446 
00455     virtual OpalMixerNode * CreateNode(
00456       OpalMixerNodeInfo * info 
00457     );
00458 
00464     virtual PSafePtr<OpalMixerNode> AddNode(
00465       OpalMixerNodeInfo * info 
00466     );
00467 
00470     void AddNode(OpalMixerNode * node);
00471 
00475     PSafePtr<OpalMixerNode> GetFirstNode(
00476       PSafetyMode mode = PSafeReference 
00477     ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
00478 
00482     virtual PSafePtr<OpalMixerNode> FindNode(
00483       const PString & name,             
00484       PSafetyMode mode = PSafeReference 
00485     );
00486 
00491     virtual void RemoveNode(
00492       OpalMixerNode & node
00493     );
00494 
00497     void AddNodeName(
00498       PString name,        
00499       OpalMixerNode * node 
00500     );
00501 
00504     void RemoveNodeName(
00505       PString name        
00506     );
00507 
00511     void RemoveNodeNames(
00512       PStringList names   
00513     );
00515 
00516   protected:
00517     PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
00518     PDictionary<PString, OpalMixerNode>               m_nodesByName;
00519 };
00520 
00521 
00523 
00524 class OpalMixerConnection;
00525 
00530 class OpalMixerEndPoint : public OpalLocalEndPoint
00531 {
00532     PCLASSINFO(OpalMixerEndPoint, OpalLocalEndPoint);
00533   public:
00538     OpalMixerEndPoint(
00539       OpalManager & manager,  
00540       const char * prefix     
00541     );
00542 
00545     ~OpalMixerEndPoint();
00546 
00551     virtual void ShutDown();
00553 
00566     virtual OpalMediaFormatList GetMediaFormats() const;
00567 
00597     virtual PSafePtr<OpalConnection> MakeConnection(
00598       OpalCall & call,           
00599       const PString & party,     
00600       void * userData = NULL,    
00601       unsigned int options = 0,  
00602       OpalConnection::StringOptions * stringOptions  = NULL
00603     );
00604 
00609     virtual PBoolean GarbageCollection();
00611 
00620     PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
00621       const PString & token,     
00622       PSafetyMode mode = PSafeReadWrite
00623     ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
00624 
00628     virtual OpalMixerConnection * CreateConnection(
00629       PSafePtr<OpalMixerNode> node, 
00630       OpalCall & call,              
00631       void * userData,              
00632       unsigned options,
00633       OpalConnection::StringOptions * stringOptions
00634     );
00636 
00644     PSafePtr<OpalMixerNode> AddNode(
00645       OpalMixerNodeInfo * info 
00646     );
00647 
00654     virtual OpalMixerNode * CreateNode(
00655       OpalMixerNodeInfo * info 
00656     );
00657 
00661     PSafePtr<OpalMixerNode> GetFirstNode(
00662       PSafetyMode mode = PSafeReference 
00663     ) const { return m_nodeManager.GetFirstNode(mode); }
00664 
00668     PSafePtr<OpalMixerNode> FindNode(
00669       const PString & name,             
00670       PSafetyMode mode = PSafeReference 
00671     ) { return m_nodeManager.FindNode(name, mode); }
00672 
00677     void RemoveNode(
00678       OpalMixerNode & node 
00679     ) { m_nodeManager.RemoveNode(node); }
00681 
00696     void SetAdHocNodeInfo(
00697       const OpalMixerNodeInfo & info
00698     );
00699     void SetAdHocNodeInfo(
00700       OpalMixerNodeInfo * info
00701     );
00702 
00714     OpalMixerNodeInfo * GetAdHocNodeInfo() { return m_adHocNodeInfo; }
00715 
00718     const OpalMixerNodeManager & GetNodeManager() const { return m_nodeManager; }
00719           OpalMixerNodeManager & GetNodeManager()       { return m_nodeManager; }
00721 
00722   protected:
00723     OpalMixerNodeInfo  * m_adHocNodeInfo;
00724     OpalMixerNodeManager m_nodeManager;
00725 };
00726 
00727 
00729 
00732 class OpalMixerConnection : public OpalLocalConnection
00733 {
00734     PCLASSINFO(OpalMixerConnection, OpalLocalConnection);
00735   public:
00740     OpalMixerConnection(
00741       PSafePtr<OpalMixerNode> node, 
00742       OpalCall & call,              
00743       OpalMixerEndPoint & endpoint, 
00744       void * userData,              
00745       unsigned options,
00746       OpalConnection::StringOptions * stringOptions
00747     );
00748 
00751     ~OpalMixerConnection();
00753 
00773     virtual void OnReleased();
00774 
00781     virtual OpalMediaFormatList GetMediaFormats() const;
00782 
00797     virtual OpalMediaStream * CreateMediaStream(
00798       const OpalMediaFormat & mediaFormat, 
00799       unsigned sessionID,                  
00800       PBoolean isSource                    
00801     );
00802 
00805     virtual void OnStartMediaPatch(
00806       OpalMediaPatch & patch    
00807     );
00808 
00809     virtual void ApplyStringOptions(OpalConnection::StringOptions & stringOptions);
00811 
00816     void SetListenOnly(
00817       bool listenOnly   
00818     );
00819 
00822     bool GetListenOnly() const { return m_listenOnly; }
00823 
00826     PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
00828 
00829   protected:
00830     OpalMixerEndPoint     & m_endpoint;
00831     PSafePtr<OpalMixerNode> m_node;
00832     bool                    m_listenOnly;
00833 };
00834 
00835 
00839 class OpalMixerMediaStream : public OpalMediaStream
00840 {
00841     PCLASSINFO(OpalMixerMediaStream, OpalMediaStream);
00842   public:
00847     OpalMixerMediaStream(
00848       OpalConnection & conn,               
00849       const OpalMediaFormat & mediaFormat, 
00850       unsigned sessionID,                  
00851       bool isSource,                       
00852       PSafePtr<OpalMixerNode> node,        
00853       bool listenOnly                      
00854     );
00855 
00858     ~OpalMixerMediaStream();
00860 
00865     virtual PBoolean Open();
00866 
00869     virtual PBoolean Close();
00870 
00876     virtual PBoolean WritePacket(
00877       RTP_DataFrame & packet
00878     );
00879 
00883     virtual PBoolean IsSynchronous() const;
00884 
00895     virtual PBoolean RequiresPatchThread() const;
00897 
00902     PSafePtr<OpalMixerNode> GetNode() { return m_node; }
00904 
00905   protected:
00906     PSafePtr<OpalMixerNode> m_node;
00907 #if OPAL_VIDEO
00908     bool m_video;
00909 #endif
00910 };
00911 
00912 
00916 class OpalMixerNode : public PSafeObject
00917 {
00918     PCLASSINFO(OpalMixerNode, PSafeObject);
00919   public:
00924     OpalMixerNode(
00925       OpalMixerNodeManager & manager, 
00926       OpalMixerNodeInfo * info        
00927     );
00928     OpalMixerNode(
00929       OpalMixerEndPoint & endpoint,   
00930       OpalMixerNodeInfo * info        
00931     );
00932 
00935     ~OpalMixerNode();
00936 
00941     void ShutDown();
00943 
00950     void PrintOn(
00951       ostream & strm    
00952     ) const;
00954 
00959     void AttachConnection(
00960       OpalConnection * connection  
00961     );
00962 
00965     void DetachConnection(
00966       OpalConnection * connection  
00967     );
00968 
00971     bool AttachStream(
00972       OpalMixerMediaStream * stream     
00973     );
00974 
00977     void DetachStream(
00978       OpalMixerMediaStream * stream     
00979     );
00980 
00983     void UseMediaBypass(
00984       unsigned sessionID,                 
00985       OpalConnection * connection = NULL  
00986     );
00987 
00990     bool WriteAudio(
00991       const OpalBaseMixer::Key_T & key, 
00992       const RTP_DataFrame & input       
00993     ) { return m_audioMixer.WriteStream(key, input); }
00994 
00995 #if OPAL_VIDEO
00996 
00998     bool WriteVideo(
00999       const OpalBaseMixer::Key_T & key, 
01000       const RTP_DataFrame & input       
01001     ) { return m_videoMixer.WriteStream(key, input); }
01002 #endif // OPAL_VIDEO
01003 
01004 
01009     const PGloballyUniqueID & GetGUID() const { return m_guid; }
01010 
01013     const PStringList & GetNames() const { return m_names; }
01014 
01017     void AddName(
01018       const PString & name
01019     );
01020 
01023     void RemoveName(
01024       const PString & name
01025     );
01026 
01032     PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
01033 
01036     template <class Subclass>
01037     PSafePtr<Subclass> GetFirstConnectionAs(
01038       PSafetyMode mode = PSafeReference
01039     ) const { return PSafePtr<Subclass>(m_connections, mode); }
01040 
01043     PSafePtr<OpalConnection> GetFirstConnection(
01044       PSafetyMode mode = PSafeReference
01045     ) const { return GetFirstConnectionAs<OpalConnection>(mode); }
01046 
01049     const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
01050 
01053     const PTime & GetCreationTime() const { return m_creationTime; }
01055 
01056   protected:
01057     void Construct();
01058 
01059     OpalMixerNodeManager & m_manager;
01060     PGloballyUniqueID      m_guid;
01061     PStringList            m_names;
01062     OpalMixerNodeInfo    * m_info;
01063     PTime                  m_creationTime;
01064 
01065     PSafeList<OpalConnection> m_connections;
01066 
01067     struct MediaMixer
01068     {
01069       MediaMixer();
01070 
01071       PSafeList<OpalMixerMediaStream> m_outputStreams;
01072     };
01073 
01074     struct AudioMixer : public OpalAudioMixer, public MediaMixer
01075     {
01076       AudioMixer(const OpalMixerNodeInfo & info);
01077       ~AudioMixer();
01078 
01079       virtual bool OnPush();
01080 
01081       struct CachedAudio {
01082         CachedAudio();
01083         ~CachedAudio();
01084         enum { Collecting, Collected, Completed } m_state;
01085         RTP_DataFrame    m_raw;
01086         RTP_DataFrame    m_encoded;
01087         OpalTranscoder * m_transcoder;
01088       };
01089       std::map<PString, CachedAudio> m_cache;
01090 
01091       void PushOne(
01092         OpalMixerMediaStream & stream,
01093         CachedAudio & cache,
01094         const short * audioToSubtract
01095       );
01096 #ifdef OPAL_MIXER_AUDIO_DEBUG
01097       class PAudioMixerDebug * m_audioDebug;
01098 #endif
01099     };
01100     AudioMixer m_audioMixer;
01101 
01102 #if OPAL_VIDEO
01103     struct VideoMixer : public OpalVideoMixer, public MediaMixer
01104     {
01105       VideoMixer(const OpalMixerNodeInfo & info);
01106       ~VideoMixer();
01107 
01108       virtual bool OnMixed(RTP_DataFrame * & output);
01109 
01110       PDictionary<OpalMediaFormat, OpalTranscoder> m_transcoders;
01111     };
01112     VideoMixer m_videoMixer;
01113 #endif // OPAL_VIDEO
01114 };
01115 
01116 
01117 #endif // OPAL_OPAL_OPAL_MIXER
01118 
01119 

Generated on Mon Feb 21 20:19:21 2011 for OPAL by  doxygen 1.4.7