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
00057
00067 class OpalBaseMixer
00068 {
00069 public:
00070 OpalBaseMixer(
00071 bool pushThread,
00072 unsigned periodMS,
00073 unsigned periodTS
00074 );
00075
00076 virtual ~OpalBaseMixer();
00077
00078 typedef PString Key_T;
00079
00082 virtual bool AddStream(
00083 const Key_T & key
00084 );
00085
00088 virtual void RemoveStream(
00089 const Key_T & key
00090 );
00091
00094 virtual void RemoveAllStreams();
00095
00101 virtual bool WriteStream(
00102 const Key_T & key,
00103 const RTP_DataFrame & input
00104 );
00105
00115 virtual RTP_DataFrame * ReadMixed();
00116 virtual bool ReadMixed(RTP_DataFrame & mixed);
00117
00128 virtual bool OnMixed(
00129 RTP_DataFrame * & mixed
00130 );
00131
00135 void StartPushThread();
00136
00141 void StopPushThread(bool lock = true);
00142
00145 unsigned GetPeriodTS() const { return m_periodTS; }
00146
00147 protected:
00148 struct Stream {
00149 virtual ~Stream() { }
00150 virtual void QueuePacket(const RTP_DataFrame & rtp) = 0;
00151 queue<RTP_DataFrame> m_queue;
00152 };
00153 typedef std::map<Key_T, Stream *> StreamMap_T;
00154
00155 virtual Stream * CreateStream() = 0;
00156 virtual bool MixStreams(RTP_DataFrame & frame) = 0;
00157 virtual size_t GetOutputSize() const = 0;
00158
00159 virtual bool OnPush();
00160 void PushThreadMain();
00161
00162 bool m_pushThread;
00163 unsigned m_periodMS;
00164 unsigned m_periodTS;
00165
00166 StreamMap_T m_inputStreams;
00167 unsigned m_outputTimestamp;
00168 RTP_DataFrame * m_pushFrame;
00169 PThread * m_workerThread;
00170 bool m_threadRunning;
00171 PMutex m_mutex;
00172 };
00173
00175
00184 class OpalAudioMixer : public OpalBaseMixer
00185 {
00186 public:
00187 OpalAudioMixer(
00188 bool stereo = false,
00189 unsigned sampleRate = OpalMediaFormat::AudioClockRate,
00190 bool pushThread = true,
00191 unsigned period = 10
00192 );
00193
00194 ~OpalAudioMixer() { StopPushThread(); }
00195
00198 virtual void RemoveStream(
00199 const Key_T & key
00200 );
00201
00204 virtual void RemoveAllStreams();
00205
00208 bool IsStereo() const { return m_stereo; }
00209
00212 unsigned GetSampleRate() const { return m_sampleRate; }
00213
00220 bool SetSampleRate(
00221 unsigned rate
00222 );
00223
00230 bool SetJitterBufferSize(
00231 const Key_T & key,
00232 unsigned minJitterDelay,
00233 unsigned maxJitterDelay
00234 );
00235
00236 protected:
00237 struct AudioStream : public Stream
00238 {
00239 AudioStream(OpalAudioMixer & mixer);
00240 ~AudioStream();
00241
00242 virtual void QueuePacket(const RTP_DataFrame & rtp);
00243 const short * GetAudioDataPtr();
00244
00245 OpalAudioMixer & m_mixer;
00246 OpalJitterBuffer * m_jitter;
00247 unsigned m_nextTimestamp;
00248 PShortArray m_cacheSamples;
00249 size_t m_samplesUsed;
00250 };
00251
00252 virtual Stream * CreateStream();
00253 virtual bool MixStreams(RTP_DataFrame & frame);
00254 virtual size_t GetOutputSize() const;
00255
00256 void PreMixStreams();
00257 void MixStereo(RTP_DataFrame & frame);
00258 void MixAdditive(RTP_DataFrame & frame, const short * audioToSubtract);
00259
00260 protected:
00261 bool m_stereo;
00262 unsigned m_sampleRate;
00263
00264 AudioStream * m_left;
00265 AudioStream * m_right;
00266 std::vector<int> m_mixedAudio;
00267 };
00268
00269
00271
00272 #if OPAL_VIDEO
00273
00280 class OpalVideoMixer : public OpalBaseMixer
00281 {
00282 public:
00283 enum Styles {
00284 eSideBySideLetterbox,
00288 eSideBySideScaled,
00292 eStackedPillarbox,
00296 eStackedScaled,
00300 eGrid,
00302 };
00303
00304 OpalVideoMixer(
00305 Styles style,
00306 unsigned width,
00307 unsigned height,
00308 unsigned rate = 15,
00309 bool pushThread = true
00310 );
00311
00312 ~OpalVideoMixer() { StopPushThread(); }
00313
00316 unsigned GetFrameWidth() const { return m_width; }
00317
00320 unsigned GetFrameHeight() const { return m_height; }
00321
00324 unsigned GetFrameRate() const { return 1000/m_periodMS; }
00325
00329 bool SetFrameRate(
00330 unsigned rate
00331 );
00332
00336 bool SetFrameSize(
00337 unsigned width,
00338 unsigned height
00339 );
00340
00341 protected:
00342 struct VideoStream : public Stream
00343 {
00344 VideoStream(OpalVideoMixer & mixer);
00345 virtual void QueuePacket(const RTP_DataFrame & rtp);
00346 void InsertVideoFrame(unsigned x, unsigned y, unsigned w, unsigned h);
00347
00348 OpalVideoMixer & m_mixer;
00349 };
00350
00351 friend struct VideoStream;
00352
00353 virtual Stream * CreateStream();
00354 virtual bool MixStreams(RTP_DataFrame & frame);
00355 virtual size_t GetOutputSize() const;
00356
00357 protected:
00358 Styles m_style;
00359 unsigned m_width, m_height;
00360
00361 PBYTEArray m_frameStore;
00362 };
00363
00364 #endif // OPAL_VIDEO
00365
00366
00368
00369 class OpalMixerConnection;
00370 class OpalMixerNode;
00371
00372
00373 struct OpalMixerNodeInfo
00374 {
00375 OpalMixerNodeInfo()
00376 : m_listenOnly(false)
00377 , m_sampleRate(OpalMediaFormat::AudioClockRate)
00378 #if OPAL_VIDEO
00379 , m_audioOnly(false)
00380 , m_style(OpalVideoMixer::eGrid)
00381 , m_width(PVideoFrameInfo::CIFWidth)
00382 , m_height(PVideoFrameInfo::CIFHeight)
00383 , m_rate(15)
00384 #endif
00385 { }
00386
00387 virtual ~OpalMixerNodeInfo() { }
00388
00389 virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
00390
00391 PString m_name;
00392 bool m_listenOnly;
00393 unsigned m_sampleRate;
00394 #if OPAL_VIDEO
00395 bool m_audioOnly;
00396 OpalVideoMixer::Styles m_style;
00397 unsigned m_width;
00398 unsigned m_height;
00399 unsigned m_rate;
00400 #endif
00401 };
00402
00403
00408 class OpalMixerEndPoint : public OpalLocalEndPoint
00409 {
00410 PCLASSINFO(OpalMixerEndPoint, OpalLocalEndPoint);
00411 public:
00416 OpalMixerEndPoint(
00417 OpalManager & manager,
00418 const char * prefix
00419 );
00420
00423 ~OpalMixerEndPoint();
00424
00429 virtual void ShutDown();
00431
00444 virtual OpalMediaFormatList GetMediaFormats() const;
00445
00475 virtual PSafePtr<OpalConnection> MakeConnection(
00476 OpalCall & call,
00477 const PString & party,
00478 void * userData = NULL,
00479 unsigned int options = 0,
00480 OpalConnection::StringOptions * stringOptions = NULL
00481 );
00482
00487 virtual PBoolean GarbageCollection();
00489
00498 PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
00499 const PString & token,
00500 PSafetyMode mode = PSafeReadWrite
00501 ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
00502
00506 virtual OpalMixerConnection * CreateConnection(
00507 PSafePtr<OpalMixerNode> node,
00508 OpalCall & call,
00509 void * userData,
00510 unsigned options,
00511 OpalConnection::StringOptions * stringOptions
00512 );
00513
00518 PSafePtr<OpalMixerNode> AddNode(
00519 OpalMixerNodeInfo * info
00520 );
00521
00526 virtual OpalMixerNode * CreateNode(
00527 OpalMixerNodeInfo * info
00528 );
00529
00533 PSafePtr<OpalMixerNode> GetFirstNode(
00534 PSafetyMode mode = PSafeReference
00535 ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
00536
00540 PSafePtr<OpalMixerNode> FindNode(
00541 const PString & name,
00542 PSafetyMode mode = PSafeReference
00543 );
00544
00548 void RemoveNode(
00549 OpalMixerNode & node
00550 );
00552
00567 void SetAdHocNodeInfo(
00568 const OpalMixerNodeInfo & info
00569 );
00570 void SetAdHocNodeInfo(
00571 OpalMixerNodeInfo * info
00572 );
00573
00585 OpalMixerNodeInfo * GetAdHocNodeInfo() { return m_adHocNodeInfo; }
00587
00588 protected:
00589 OpalMixerNodeInfo * m_adHocNodeInfo;
00590
00591 PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
00592 PDictionary<PString, OpalMixerNode> m_nodesByName;
00593
00594 friend class OpalMixerNode;
00595 };
00596
00597
00599
00602 class OpalMixerConnection : public OpalLocalConnection
00603 {
00604 PCLASSINFO(OpalMixerConnection, OpalLocalConnection);
00605 public:
00610 OpalMixerConnection(
00611 PSafePtr<OpalMixerNode> node,
00612 OpalCall & call,
00613 OpalMixerEndPoint & endpoint,
00614 void * userData,
00615 unsigned options,
00616 OpalConnection::StringOptions * stringOptions
00617 );
00618
00621 ~OpalMixerConnection();
00623
00643 virtual void OnReleased();
00644
00651 virtual OpalMediaFormatList GetMediaFormats() const;
00652
00667 virtual OpalMediaStream * CreateMediaStream(
00668 const OpalMediaFormat & mediaFormat,
00669 unsigned sessionID,
00670 PBoolean isSource
00671 );
00672
00673 virtual void ApplyStringOptions(OpalConnection::StringOptions & stringOptions);
00675
00680 void SetListenOnly(
00681 bool listenOnly
00682 );
00683
00686 bool GetListenOnly() const { return m_listenOnly; }
00687
00690 PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
00692
00693 protected:
00694 OpalMixerEndPoint & m_endpoint;
00695 PSafePtr<OpalMixerNode> m_node;
00696 bool m_listenOnly;
00697 };
00698
00699
00703 class OpalMixerMediaStream : public OpalMediaStream
00704 {
00705 PCLASSINFO(OpalMixerMediaStream, OpalMediaStream);
00706 public:
00711 OpalMixerMediaStream(
00712 OpalMixerConnection & conn,
00713 const OpalMediaFormat & mediaFormat,
00714 unsigned sessionID,
00715 bool isSource,
00716 PSafePtr<OpalMixerNode> node,
00717 bool listenOnly
00718 );
00719
00722 ~OpalMixerMediaStream();
00724
00729 virtual PBoolean Open();
00730
00733 virtual PBoolean Close();
00734
00740 virtual PBoolean WritePacket(
00741 RTP_DataFrame & packet
00742 );
00743
00747 virtual PBoolean IsSynchronous() const;
00748
00758 virtual PBoolean RequiresPatchThread(
00759 OpalMediaStream * sinkStream
00760 ) const;
00762
00767 PSafePtr<OpalMixerNode> GetNode() { return m_node; }
00769
00770 protected:
00771 PSafePtr<OpalMixerNode> m_node;
00772 #if OPAL_VIDEO
00773 bool m_video;
00774 #endif
00775 };
00776
00777
00781 class OpalMixerNode : public PSafeObject
00782 {
00783 PCLASSINFO(OpalMixerNode, PSafeObject);
00784 public:
00789 OpalMixerNode(
00790 OpalMixerEndPoint & endpoint,
00791 OpalMixerNodeInfo * info
00792 );
00793
00796 ~OpalMixerNode();
00797
00802 void ShutDown();
00804
00811 void PrintOn(
00812 ostream & strm
00813 ) const;
00815
00820 void AttachConnection(
00821 OpalMixerConnection * connection
00822 );
00823
00826 void DetachConnection(
00827 OpalMixerConnection * connection
00828 );
00829
00832 bool AttachStream(
00833 OpalMixerMediaStream * stream
00834 );
00835
00838 void DetachStream(
00839 OpalMixerMediaStream * stream
00840 );
00841
00844 bool WriteAudio(
00845 const OpalBaseMixer::Key_T & key,
00846 const RTP_DataFrame & input
00847 ) { return m_audioMixer.WriteStream(key, input); }
00848
00849 #if OPAL_VIDEO
00850
00852 bool WriteVideo(
00853 const OpalBaseMixer::Key_T & key,
00854 const RTP_DataFrame & input
00855 ) { return m_videoMixer.WriteStream(key, input); }
00856 #endif // OPAL_VIDEO
00857
00858
00863 const PGloballyUniqueID & GetGUID() const { return m_guid; }
00864
00867 const PStringList & GetNames() const { return m_names; }
00868
00871 void AddName(
00872 const PString & name
00873 );
00874
00877 void RemoveName(
00878 const PString & name
00879 );
00880
00886 PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
00887
00890 PSafePtr<OpalMixerConnection> GetFirstConnection(
00891 PSafetyMode mode = PSafeReference
00892 ) const { return PSafePtr<OpalMixerConnection>(m_connections, mode); }
00893
00896 const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
00897
00900 const PTime & GetCreationTime() const { return m_creationTime; }
00902
00903 protected:
00904 OpalMixerEndPoint & m_endpoint;
00905 PGloballyUniqueID m_guid;
00906 PStringList m_names;
00907 OpalMixerNodeInfo * m_info;
00908 PTime m_creationTime;
00909
00910 PSafeList<OpalMixerConnection> m_connections;
00911
00912 struct MediaMixer
00913 {
00914 MediaMixer();
00915
00916 PSafeList<OpalMixerMediaStream> m_outputStreams;
00917 };
00918
00919 struct AudioMixer : public OpalAudioMixer, public MediaMixer
00920 {
00921 AudioMixer(const OpalMixerNodeInfo & info);
00922 ~AudioMixer();
00923
00924 virtual bool OnPush();
00925
00926 struct CachedAudio {
00927 CachedAudio();
00928 ~CachedAudio();
00929 enum { Collecting, Collected, Completed } m_state;
00930 RTP_DataFrame m_raw;
00931 RTP_DataFrame m_encoded;
00932 OpalTranscoder * m_transcoder;
00933 };
00934 std::map<PString, CachedAudio> m_cache;
00935
00936 void PushOne(
00937 OpalMixerMediaStream & stream,
00938 CachedAudio & cache,
00939 const short * audioToSubtract
00940 );
00941 #ifdef OPAL_MIXER_AUDIO_DEBUG
00942 class PAudioMixerDebug * m_audioDebug;
00943 #endif
00944 };
00945 AudioMixer m_audioMixer;
00946
00947 #if OPAL_VIDEO
00948 struct VideoMixer : public OpalVideoMixer, public MediaMixer
00949 {
00950 VideoMixer(const OpalMixerNodeInfo & info);
00951 ~VideoMixer();
00952
00953 virtual bool OnMixed(RTP_DataFrame * & output);
00954
00955 PDictionary<OpalMediaFormat, OpalTranscoder> m_transcoders;
00956 };
00957 VideoMixer m_videoMixer;
00958 #endif // OPAL_VIDEO
00959 };
00960
00961
00962 #endif // OPAL_OPAL_OPAL_MIXER
00963
00964