00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
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;
00166 unsigned m_periodMS;
00167 unsigned m_periodTS;
00168
00169 StreamMap_T m_inputStreams;
00170 unsigned m_outputTimestamp;
00171 RTP_DataFrame * m_pushFrame;
00172 PThread * m_workerThread;
00173 bool m_threadRunning;
00174 PMutex m_mutex;
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
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