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 BYTE m_bgFillRed,m_bgFillGreen,m_bgFillBlue;
00364
00365 PBYTEArray m_frameStore;
00366 size_t m_lastStreamCount;
00367 };
00368
00369 #endif // OPAL_VIDEO
00370
00371
00373
00374
00379 struct OpalMixerNodeInfo
00380 {
00381 OpalMixerNodeInfo(const char * name = NULL)
00382 : m_name(name)
00383 , m_listenOnly(false)
00384 , m_sampleRate(OpalMediaFormat::AudioClockRate)
00385 #if OPAL_VIDEO
00386 , m_audioOnly(false)
00387 , m_style(OpalVideoMixer::eGrid)
00388 , m_width(PVideoFrameInfo::CIFWidth)
00389 , m_height(PVideoFrameInfo::CIFHeight)
00390 , m_rate(15)
00391 #endif
00392 , m_mediaPassThru(false)
00393 { }
00394
00395 virtual ~OpalMixerNodeInfo() { }
00396
00397 virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
00398
00399 PString m_name;
00400 bool m_listenOnly;
00401 unsigned m_sampleRate;
00402 #if OPAL_VIDEO
00403 bool m_audioOnly;
00404 OpalVideoMixer::Styles m_style;
00405 unsigned m_width;
00406 unsigned m_height;
00407 unsigned m_rate;
00408 #endif
00409 bool m_mediaPassThru;
00411 };
00412
00413
00415
00416 class OpalMixerNode;
00417
00418
00423 class OpalMixerNodeManager : public PObject
00424 {
00425 PCLASSINFO(OpalMixerNodeManager, PObject);
00426 public:
00431 OpalMixerNodeManager();
00432
00436 virtual ~OpalMixerNodeManager();
00437
00440 virtual void ShutDown();
00441
00447 virtual PBoolean GarbageCollection();
00449
00458 virtual OpalMixerNode * CreateNode(
00459 OpalMixerNodeInfo * info
00460 );
00461
00467 virtual PSafePtr<OpalMixerNode> AddNode(
00468 OpalMixerNodeInfo * info
00469 );
00470
00473 void AddNode(OpalMixerNode * node);
00474
00478 PSafePtr<OpalMixerNode> GetFirstNode(
00479 PSafetyMode mode = PSafeReference
00480 ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
00481
00485 virtual PSafePtr<OpalMixerNode> FindNode(
00486 const PString & name,
00487 PSafetyMode mode = PSafeReference
00488 );
00489
00494 virtual void RemoveNode(
00495 OpalMixerNode & node
00496 );
00497
00500 void AddNodeName(
00501 PString name,
00502 OpalMixerNode * node
00503 );
00504
00507 void RemoveNodeName(
00508 PString name
00509 );
00510
00514 void RemoveNodeNames(
00515 PStringList names
00516 );
00518
00519 protected:
00520 PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
00521 PDictionary<PString, OpalMixerNode> m_nodesByName;
00522 };
00523
00524
00526
00527 class OpalMixerConnection;
00528
00533 class OpalMixerEndPoint : public OpalLocalEndPoint
00534 {
00535 PCLASSINFO(OpalMixerEndPoint, OpalLocalEndPoint);
00536 public:
00541 OpalMixerEndPoint(
00542 OpalManager & manager,
00543 const char * prefix
00544 );
00545
00548 ~OpalMixerEndPoint();
00549
00554 virtual void ShutDown();
00556
00569 virtual OpalMediaFormatList GetMediaFormats() const;
00570
00600 virtual PSafePtr<OpalConnection> MakeConnection(
00601 OpalCall & call,
00602 const PString & party,
00603 void * userData = NULL,
00604 unsigned options = 0,
00605 OpalConnection::StringOptions * stringOptions = NULL
00606 );
00607
00612 virtual PBoolean GarbageCollection();
00614
00623 PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
00624 const PString & token,
00625 PSafetyMode mode = PSafeReadWrite
00626 ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
00627
00631 virtual OpalMixerConnection * CreateConnection(
00632 PSafePtr<OpalMixerNode> node,
00633 OpalCall & call,
00634 void * userData,
00635 unsigned options,
00636 OpalConnection::StringOptions * stringOptions
00637 );
00639
00647 PSafePtr<OpalMixerNode> AddNode(
00648 OpalMixerNodeInfo * info
00649 );
00650
00657 virtual OpalMixerNode * CreateNode(
00658 OpalMixerNodeInfo * info
00659 );
00660
00664 PSafePtr<OpalMixerNode> GetFirstNode(
00665 PSafetyMode mode = PSafeReference
00666 ) const { return m_nodeManager.GetFirstNode(mode); }
00667
00671 PSafePtr<OpalMixerNode> FindNode(
00672 const PString & name,
00673 PSafetyMode mode = PSafeReference
00674 ) { return m_nodeManager.FindNode(name, mode); }
00675
00680 void RemoveNode(
00681 OpalMixerNode & node
00682 ) { m_nodeManager.RemoveNode(node); }
00684
00699 void SetAdHocNodeInfo(
00700 const OpalMixerNodeInfo & info
00701 );
00702 void SetAdHocNodeInfo(
00703 OpalMixerNodeInfo * info
00704 );
00705
00717 OpalMixerNodeInfo * GetAdHocNodeInfo() { return m_adHocNodeInfo; }
00718
00721 const OpalMixerNodeManager & GetNodeManager() const { return m_nodeManager; }
00722 OpalMixerNodeManager & GetNodeManager() { return m_nodeManager; }
00724
00725 protected:
00726 OpalMixerNodeInfo * m_adHocNodeInfo;
00727 OpalMixerNodeManager m_nodeManager;
00728 };
00729
00730
00732
00735 class OpalMixerConnection : public OpalLocalConnection
00736 {
00737 PCLASSINFO(OpalMixerConnection, OpalLocalConnection);
00738 public:
00743 OpalMixerConnection(
00744 PSafePtr<OpalMixerNode> node,
00745 OpalCall & call,
00746 OpalMixerEndPoint & endpoint,
00747 void * userData,
00748 unsigned options = 0,
00749 OpalConnection::StringOptions * stringOptions = NULL
00750 );
00751
00754 ~OpalMixerConnection();
00756
00776 virtual void OnReleased();
00777
00784 virtual OpalMediaFormatList GetMediaFormats() const;
00785
00800 virtual OpalMediaStream * CreateMediaStream(
00801 const OpalMediaFormat & mediaFormat,
00802 unsigned sessionID,
00803 PBoolean isSource
00804 );
00805
00808 virtual void OnStartMediaPatch(
00809 OpalMediaPatch & patch
00810 );
00811
00813 virtual void OnApplyStringOptions();
00814
00821 virtual PBoolean SendUserInputString(
00822 const PString & value
00823 );
00824
00841 virtual PBoolean SendUserInputTone(
00842 char tone,
00843 unsigned duration = 0
00844 );
00846
00851 void SetListenOnly(
00852 bool listenOnly
00853 );
00854
00857 bool GetListenOnly() const { return m_listenOnly; }
00858
00861 PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
00863
00864 protected:
00865 OpalMixerEndPoint & m_endpoint;
00866 PSafePtr<OpalMixerNode> m_node;
00867 bool m_listenOnly;
00868 };
00869
00870
00874 class OpalMixerMediaStream : public OpalMediaStream
00875 {
00876 PCLASSINFO(OpalMixerMediaStream, OpalMediaStream);
00877 public:
00882 OpalMixerMediaStream(
00883 OpalConnection & conn,
00884 const OpalMediaFormat & mediaFormat,
00885 unsigned sessionID,
00886 bool isSource,
00887 PSafePtr<OpalMixerNode> node,
00888 bool listenOnly
00889 );
00890
00893 ~OpalMixerMediaStream();
00895
00900 virtual PBoolean Open();
00901
00904 virtual PBoolean Close();
00905
00911 virtual PBoolean WritePacket(
00912 RTP_DataFrame & packet
00913 );
00914
00918 virtual PBoolean IsSynchronous() const;
00919
00930 virtual PBoolean RequiresPatchThread() const;
00931
00939 virtual bool EnableJitterBuffer(bool enab = true) const;
00941
00946 PSafePtr<OpalMixerNode> GetNode() { return m_node; }
00948
00949 protected:
00950 PSafePtr<OpalMixerNode> m_node;
00951 #if OPAL_VIDEO
00952 bool m_video;
00953 #endif
00954 };
00955
00956
00960 class OpalMixerNode : public PSafeObject
00961 {
00962 PCLASSINFO(OpalMixerNode, PSafeObject);
00963 public:
00968 OpalMixerNode(
00969 OpalMixerNodeManager & manager,
00970 OpalMixerNodeInfo * info
00971 );
00972 OpalMixerNode(
00973 OpalMixerEndPoint & endpoint,
00974 OpalMixerNodeInfo * info
00975 );
00976
00979 ~OpalMixerNode();
00980
00985 void ShutDown();
00987
00994 void PrintOn(
00995 ostream & strm
00996 ) const;
00998
01003 void AttachConnection(
01004 OpalConnection * connection
01005 );
01006
01009 void DetachConnection(
01010 OpalConnection * connection
01011 );
01012
01015 bool AttachStream(
01016 OpalMixerMediaStream * stream
01017 );
01018
01021 void DetachStream(
01022 OpalMixerMediaStream * stream
01023 );
01024
01027 void UseMediaPassThrough(
01028 unsigned sessionID,
01029 OpalConnection * connection = NULL
01030 );
01031
01038 bool SetJitterBufferSize(
01039 const OpalBaseMixer::Key_T & key,
01040 unsigned minJitterDelay,
01041 unsigned maxJitterDelay
01042 ) { return m_audioMixer.SetJitterBufferSize(key, minJitterDelay, maxJitterDelay); }
01043
01046 bool WriteAudio(
01047 const OpalBaseMixer::Key_T & key,
01048 const RTP_DataFrame & input
01049 ) { return m_audioMixer.WriteStream(key, input); }
01050
01051 #if OPAL_VIDEO
01052
01054 bool WriteVideo(
01055 const OpalBaseMixer::Key_T & key,
01056 const RTP_DataFrame & input
01057 ) { return m_videoMixer.WriteStream(key, input); }
01058 #endif // OPAL_VIDEO
01059
01062 virtual void BroadcastUserInput(
01063 const OpalConnection * connection,
01064 const PString & value
01065 );
01067
01072 const PGloballyUniqueID & GetGUID() const { return m_guid; }
01073
01076 const PStringList & GetNames() const { return m_names; }
01077
01080 void AddName(
01081 const PString & name
01082 );
01083
01086 void RemoveName(
01087 const PString & name
01088 );
01089
01095 PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
01096
01099 template <class Subclass>
01100 PSafePtr<Subclass> GetFirstConnectionAs(
01101 PSafetyMode mode = PSafeReference
01102 ) const { return PSafePtr<Subclass>(m_connections, mode); }
01103
01106 PSafePtr<OpalConnection> GetFirstConnection(
01107 PSafetyMode mode = PSafeReference
01108 ) const { return GetFirstConnectionAs<OpalConnection>(mode); }
01109
01112 const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
01113
01116 const PTime & GetCreationTime() const { return m_creationTime; }
01118
01119 protected:
01120 void Construct();
01121
01122 OpalMixerNodeManager & m_manager;
01123 PGloballyUniqueID m_guid;
01124 PStringList m_names;
01125 OpalMixerNodeInfo * m_info;
01126 PTime m_creationTime;
01127
01128 PSafeList<OpalConnection> m_connections;
01129
01130 struct MediaMixer
01131 {
01132 MediaMixer();
01133
01134 PSafeList<OpalMixerMediaStream> m_outputStreams;
01135 };
01136
01137 struct AudioMixer : public OpalAudioMixer, public MediaMixer
01138 {
01139 AudioMixer(const OpalMixerNodeInfo & info);
01140 ~AudioMixer();
01141
01142 virtual bool OnPush();
01143
01144 struct CachedAudio {
01145 CachedAudio();
01146 ~CachedAudio();
01147 enum { Collecting, Collected, Completed } m_state;
01148 RTP_DataFrame m_raw;
01149 RTP_DataFrame m_encoded;
01150 OpalTranscoder * m_transcoder;
01151 };
01152 std::map<PString, CachedAudio> m_cache;
01153
01154 void PushOne(
01155 OpalMixerMediaStream & stream,
01156 CachedAudio & cache,
01157 const short * audioToSubtract
01158 );
01159 #ifdef OPAL_MIXER_AUDIO_DEBUG
01160 class PAudioMixerDebug * m_audioDebug;
01161 #endif
01162 };
01163 AudioMixer m_audioMixer;
01164
01165 #if OPAL_VIDEO
01166 struct VideoMixer : public OpalVideoMixer, public MediaMixer
01167 {
01168 VideoMixer(const OpalMixerNodeInfo & info);
01169 ~VideoMixer();
01170
01171 virtual bool OnMixed(RTP_DataFrame * & output);
01172
01173 PDictionary<PString, OpalTranscoder> m_transcoders;
01174 };
01175 VideoMixer m_videoMixer;
01176 #endif // OPAL_VIDEO
01177 };
01178
01179
01180 #endif // OPAL_OPAL_OPAL_MIXER
01181
01182