OPAL  Version 3.12.9
opalmixer.h
Go to the documentation of this file.
1 /*
2  * opalmixer.h
3  *
4  * OPAL media mixers
5  *
6  * Open Phone Abstraction Library (OPAL)
7  * Formally known as the Open H323 project.
8  *
9  * Copyright (C) 2007 Post Increment
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.0 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18  * the License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * The Original Code is Open Phone Abstraction Library.
22  *
23  * The Initial Developer of the Original Code is Post Increment
24  *
25  * Contributor(s): Craig Southeren (craigs@postincrement.com)
26  * Robert Jongbloed (robertj@voxlucida.com.au)
27  *
28  * $Revision: 31065 $
29  * $Author: rjongbloed $
30  * $Date: 2013-12-11 17:08:05 +1100 (Wed, 11 Dec 2013) $
31  */
32 
33 
34 #ifndef OPAL_OPAL_OPALMIXER_H
35 #define OPAL_OPAL_OPALMIXER_H
36 
37 #ifndef _PTLIB_H
38 #include <ptlib.h>
39 #endif
40 
41 #include <opal/buildopts.h>
42 
43 #if OPAL_HAS_MIXER
44 
45 #include <queue>
46 
47 #include <ep/localep.h>
48 #include <codec/vidcodec.h>
49 
50 
51 class RTP_DataFrame;
52 class OpalJitterBuffer;
54 
55 
56 //#define OPAL_MIXER_AUDIO_DEBUG 1
57 
58 
59 #define OPAL_MIXER_PREFIX "mcu"
60 
61 #define OPAL_OPT_LISTEN_ONLY "Listen-Only"
62 #define OPAL_OPT_CONF_OWNER "Conference-Owner"
63 
64 
66 
76 class OpalBaseMixer : public PObject
77 {
78  public:
80  bool pushThread,
81  unsigned periodMS,
82  unsigned periodTS
83  );
84 
85  virtual ~OpalBaseMixer();
86 
87  typedef PString Key_T;
88 
91  virtual bool AddStream(
92  const Key_T & key
93  );
94 
97  virtual void RemoveStream(
98  const Key_T & key
99  );
100 
103  virtual void RemoveAllStreams();
104 
110  virtual bool WriteStream(
111  const Key_T & key,
112  const RTP_DataFrame & input
113  );
114 
124  virtual RTP_DataFrame * ReadMixed();
125  virtual bool ReadMixed(RTP_DataFrame & mixed);
126 
137  virtual bool OnMixed(
138  RTP_DataFrame * & mixed
139  );
140 
144  void StartPushThread();
145 
150  void StopPushThread(bool lock = true);
151 
154  unsigned GetPeriodTS() const { return m_periodTS; }
155 
156  protected:
157  struct Stream : public PObject {
158  virtual ~Stream() { }
159  virtual void QueuePacket(const RTP_DataFrame & rtp) = 0;
160  std::queue<RTP_DataFrame> m_queue;
161  };
162  typedef std::map<Key_T, Stream *> StreamMap_T;
163 
164  virtual Stream * CreateStream() = 0;
165  virtual bool MixStreams(RTP_DataFrame & frame) = 0;
166  virtual size_t GetOutputSize() const = 0;
167 
168  virtual bool OnPush();
169  void PushThreadMain();
170 
171  bool m_pushThread; // true if to use a thread to push data out
172  unsigned m_periodMS; // Mixing interval in milliseconds
173  unsigned m_periodTS; // Mixing interval in timestamp units
174 
175  StreamMap_T m_inputStreams; // Map of key to stream for input RTP frame queues
176  unsigned m_outputTimestamp; // RTP timestamp for output data
177  RTP_DataFrame * m_pushFrame; // Cached frame for pushing RTP
178  PThread * m_workerThread; // reader thread handle
179  bool m_threadRunning; // used to stop reader thread
180  PMutex m_mutex; // mutex for list of streams and thread handle
181 };
182 
184 
194 {
195  public:
197  bool stereo = false,
198  unsigned sampleRate = OpalMediaFormat::AudioClockRate,
199  bool pushThread = true,
200  unsigned period = 10
201  );
202 
204 
207  virtual void RemoveStream(
208  const Key_T & key
209  );
210 
213  virtual void RemoveAllStreams();
214 
217  bool IsStereo() const { return m_stereo; }
218 
221  unsigned GetSampleRate() const { return m_sampleRate; }
222 
229  bool SetSampleRate(
230  unsigned rate
231  );
232 
239  bool SetJitterBufferSize(
240  const Key_T & key,
241  const OpalJitterBuffer::Init & init
242  );
243 
244  protected:
245  struct AudioStream : public Stream
246  {
247  AudioStream(OpalAudioMixer & mixer);
248  ~AudioStream();
249 
250  virtual void QueuePacket(const RTP_DataFrame & rtp);
251  const short * GetAudioDataPtr();
252 
255  unsigned m_nextTimestamp;
256  PShortArray m_cacheSamples;
258  };
259 
260  virtual Stream * CreateStream();
261  virtual bool MixStreams(RTP_DataFrame & frame);
262  virtual size_t GetOutputSize() const;
263 
264  void PreMixStreams();
265  void MixStereo(RTP_DataFrame & frame);
266  void MixAdditive(RTP_DataFrame & frame, const short * audioToSubtract);
267 
268  protected:
269  bool m_stereo;
270  unsigned m_sampleRate;
271 
274  std::vector<int> m_mixedAudio;
275 };
276 
277 
279 
280 #if OPAL_VIDEO
281 
289 {
290  public:
291  enum Styles {
310  };
311 
313  Styles style,
314  unsigned width,
315  unsigned height,
316  unsigned rate = 15,
317  bool pushThread = true
318  );
319 
321 
324  unsigned GetFrameWidth() const { return m_width; }
325 
328  unsigned GetFrameHeight() const { return m_height; }
329 
332  unsigned GetFrameRate() const { return 1000/m_periodMS; }
333 
337  bool SetFrameRate(
338  unsigned rate // New frames per second.
339  );
340 
344  bool SetFrameSize(
345  unsigned width,
346  unsigned height
347  );
348 
349  protected:
350  struct VideoStream : public Stream
351  {
352  VideoStream(OpalVideoMixer & mixer);
353  virtual void QueuePacket(const RTP_DataFrame & rtp);
354  void InsertVideoFrame(unsigned x, unsigned y, unsigned w, unsigned h);
355 
357  };
358 
359  friend struct VideoStream;
360 
361  virtual Stream * CreateStream();
362  virtual bool MixStreams(RTP_DataFrame & frame);
363  virtual size_t GetOutputSize() const;
364 
365  protected:
367  unsigned m_width, m_height;
369 
370  PBYTEArray m_frameStore;
372 };
373 
374 #endif // OPAL_VIDEO
375 
376 
378 
379 
385 {
386  OpalMixerNodeInfo(const char * name = NULL)
387  : m_name(name)
388  , m_closeOnEmpty(false)
389  , m_listenOnly(false)
390  , m_sampleRate(OpalMediaFormat::AudioClockRate)
391 #if OPAL_VIDEO
392  , m_audioOnly(false)
393  , m_style(OpalVideoMixer::eGrid)
394  , m_width(PVideoFrameInfo::CIFWidth)
395  , m_height(PVideoFrameInfo::CIFHeight)
396  , m_rate(15)
397 #endif
398  , m_mediaPassThru(false)
399  { }
400 
401  virtual ~OpalMixerNodeInfo() { }
402 
403  virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
404 
405  PString m_name;
408  unsigned m_sampleRate;
409 #if OPAL_VIDEO
410  bool m_audioOnly;
412  unsigned m_width;
413  unsigned m_height;
414  unsigned m_rate;
415 #endif
419  PString m_displayText;
420  PString m_subject;
421  PString m_notes;
422  PString m_keywords;
423 };
424 
425 
427 
428 class OpalMixerNode;
429 
430 
436 {
437  public:
443 
447  virtual ~OpalMixerNodeManager();
448 
451  virtual void ShutDown();
452 
458  virtual PBoolean GarbageCollection();
460 
469  virtual OpalMixerNode * CreateNode(
470  OpalMixerNodeInfo * info
471  );
472 
478  virtual PSafePtr<OpalMixerNode> AddNode(
479  OpalMixerNodeInfo * info
480  );
481 
484  void AddNode(OpalMixerNode * node);
485 
489  PSafePtr<OpalMixerNode> GetFirstNode(
490  PSafetyMode mode = PSafeReference
491  ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
492 
496  virtual PSafePtr<OpalMixerNode> FindNode(
497  const PString & name,
498  PSafetyMode mode = PSafeReference
499  );
500 
505  virtual void RemoveNode(
506  OpalMixerNode & node
507  );
508 
511  void AddNodeName(
512  PString name,
513  OpalMixerNode * node
514  );
515 
518  void RemoveNodeName(
519  PString name
520  );
521 
525  void RemoveNodeNames(
526  const PStringSet & names
527  );
528 
531  virtual PString CreateInternalURI(
532  const PGloballyUniqueID & guid
533  );
534 
539  virtual void OnNodeStatusChanged(
540  const OpalMixerNode & node,
542  );
543 
545  OpalManager & GetManager() const { return m_manager; }
547 
548  protected:
550 
551  PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
552  PSafeDictionary<PString, OpalMixerNode> m_nodesByName;
553 };
554 
555 
557 
563 {
565  public:
571  OpalManager & manager,
572  const char * prefix = OPAL_MIXER_PREFIX
573  );
574 
578 
583  virtual void ShutDown();
585 
598  virtual OpalMediaFormatList GetMediaFormats() const;
599 
627  virtual PSafePtr<OpalConnection> MakeConnection(
628  OpalCall & call,
629  const PString & party,
630  void * userData = NULL,
631  unsigned options = 0,
632  OpalConnection::StringOptions * stringOptions = NULL
633  );
634 
650  virtual bool GetConferenceStates(
651  OpalConferenceStates & states,
652  const PString & name = PString::Empty()
653  ) const;
654 
659  virtual PBoolean GarbageCollection();
661 
670  PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
671  const PString & token,
672  PSafetyMode mode = PSafeReadWrite
673  ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
674 
679  PSafePtr<OpalMixerNode> node,
680  OpalCall & call,
681  void * userData,
682  unsigned options,
683  OpalConnection::StringOptions * stringOptions
684  );
685 
690  const PString & name
691  );
693 
708  void SetAdHocNodeInfo(
709  const OpalMixerNodeInfo & info
710  );
711  void SetAdHocNodeInfo(
712  OpalMixerNodeInfo * info
713  );
714 
727 
745  void SetFactoryNodeInfo(
746  const OpalMixerNodeInfo & info
747  );
748  void SetFactoryNodeInfo(
749  OpalMixerNodeInfo * info
750  );
751 
756  virtual PString GetNewFactoryName();
757 
761 
762  protected:
763  virtual PString CreateInternalURI(const PGloballyUniqueID & guid);
764  virtual void OnNodeStatusChanged(const OpalMixerNode & node, OpalConferenceState::ChangeType change);
765 
768  PMutex m_infoMutex; // For above two fields
769  PAtomicInteger m_factoryIndex;
770 };
771 
772 
774 
778 {
780  public:
786  PSafePtr<OpalMixerNode> node,
787  OpalCall & call,
789  void * userData,
790  unsigned options = 0,
791  OpalConnection::StringOptions * stringOptions = NULL
792  );
793 
798 
818  virtual void OnReleased();
819 
826  virtual OpalMediaFormatList GetMediaFormats() const;
827 
843  const OpalMediaFormat & mediaFormat,
844  unsigned sessionID,
845  PBoolean isSource
846  );
847 
850  virtual void OnStartMediaPatch(
851  OpalMediaPatch & patch
852  );
853 
855  virtual void OnApplyStringOptions();
856 
863  virtual PBoolean SendUserInputString(
864  const PString & value
865  );
866 
883  virtual PBoolean SendUserInputTone(
884  char tone,
885  unsigned duration = 0
886  );
887 
900  virtual bool GetConferenceState(
901  OpalConferenceState * state
902  ) const;
904 
909  void SetListenOnly(
910  bool listenOnly
911  );
912 
915  bool GetListenOnly() const { return m_listenOnly; }
916 
919  PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
921 
922  protected:
924  PSafePtr<OpalMixerNode> m_node;
926 };
927 
928 
933 {
935  public:
941  OpalConnection & conn,
942  const OpalMediaFormat & mediaFormat,
943  unsigned sessionID,
944  bool isSource,
945  PSafePtr<OpalMixerNode> node,
946  bool listenOnly
947  );
948 
953 
958  virtual PBoolean Open();
959 
965  virtual PBoolean WritePacket(
966  RTP_DataFrame & packet
967  );
968 
972  virtual PBoolean IsSynchronous() const;
973 
984  virtual PBoolean RequiresPatchThread() const;
986 
991  PSafePtr<OpalMixerNode> GetNode() { return m_node; }
993 
994  protected:
995  virtual void InternalClose();
996  virtual bool InternalSetJitterBuffer(const OpalJitterBuffer::Init & init) const;
997 
998  PSafePtr<OpalMixerNode> m_node;
1000 #if OPAL_VIDEO
1001  bool m_video;
1002 #endif
1003 };
1004 
1005 
1009 class OpalMixerNode : public PSafeObject
1010 {
1011  PCLASSINFO(OpalMixerNode, PSafeObject);
1012  public:
1017  OpalMixerNode(
1018  OpalMixerNodeManager & manager,
1019  OpalMixerNodeInfo * info
1020  );
1021 
1024  ~OpalMixerNode();
1025 
1030  void ShutDown();
1032 
1039  void PrintOn(
1040  ostream & strm
1041  ) const;
1043 
1048  virtual void AttachConnection(
1049  OpalConnection * connection
1050  );
1051 
1054  virtual void DetachConnection(
1055  OpalConnection * connection
1056  );
1057 
1060  virtual bool AttachStream(
1061  OpalMixerMediaStream * stream
1062  );
1063 
1066  virtual void DetachStream(
1067  OpalMixerMediaStream * stream
1068  );
1069 
1072  virtual void UseMediaPassThrough(
1073  unsigned sessionID,
1074  OpalConnection * connection = NULL
1075  );
1076 
1084  const OpalBaseMixer::Key_T & key,
1085  const OpalJitterBuffer::Init & init
1086  ) { return m_audioMixer.SetJitterBufferSize(key, init); }
1087 
1091  const OpalBaseMixer::Key_T & key,
1092  const RTP_DataFrame & input
1093  ) { return m_audioMixer.WriteStream(key, input); }
1094 
1095 #if OPAL_VIDEO
1096 
1099  const OpalBaseMixer::Key_T & key,
1100  const RTP_DataFrame & input
1101  ) { return m_videoMixer.WriteStream(key, input); }
1102 #endif // OPAL_VIDEO
1103 
1106  virtual void BroadcastUserInput(
1107  const OpalConnection * connection,
1108  const PString & value
1109  );
1110 
1123  virtual void GetConferenceState(
1124  OpalConferenceState & state
1125  ) const;
1127 
1132  const PGloballyUniqueID & GetGUID() const { return m_guid; }
1133 
1136  const PStringSet & GetNames() const { return m_names; }
1137 
1140  void AddName(
1141  const PString & name
1142  );
1143 
1146  void RemoveName(
1147  const PString & name
1148  );
1149 
1155  PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
1156 
1159  template <class Subclass>
1160  PSafePtr<Subclass> GetFirstConnectionAs(
1161  PSafetyMode mode = PSafeReference
1162  ) const { return PSafePtr<Subclass>(m_connections, mode); }
1163 
1166  PSafePtr<OpalConnection> GetFirstConnection(
1167  PSafetyMode mode = PSafeReference
1168  ) const { return GetFirstConnectionAs<OpalConnection>(mode); }
1169 
1172  const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
1173 
1176  const PTime & GetCreationTime() const { return m_creationTime; }
1177 
1185  const PString & connectionIdentifier
1186  ) { m_ownerConnection = connectionIdentifier; }
1188 
1189  protected:
1191  PGloballyUniqueID m_guid;
1192  PStringSet m_names;
1195  PAtomicBoolean m_shuttingDown;
1196 
1197  PSafeList<OpalConnection> m_connections;
1199 
1200  struct MediaMixer
1201  {
1202  MediaMixer();
1203  void CloseOne(const PSafePtr<OpalMixerMediaStream> & stream);
1204 
1205  PSafeList<OpalMixerMediaStream> m_outputStreams;
1206  };
1207 
1208  struct AudioMixer : public OpalAudioMixer, public MediaMixer
1209  {
1210  AudioMixer(const OpalMixerNodeInfo & info);
1211  ~AudioMixer();
1212 
1213  virtual bool OnPush();
1214 
1215  struct CachedAudio {
1216  CachedAudio();
1217  ~CachedAudio();
1222  };
1223  std::map<PString, CachedAudio> m_cache;
1224 
1225  void PushOne(
1226  PSafePtr<OpalMixerMediaStream> & stream,
1227  CachedAudio & cache,
1228  const short * audioToSubtract
1229  );
1230 #ifdef OPAL_MIXER_AUDIO_DEBUG
1231  class PAudioMixerDebug * m_audioDebug;
1232 #endif
1233  };
1235 
1236 #if OPAL_VIDEO
1237  struct VideoMixer : public OpalVideoMixer, public MediaMixer
1238  {
1239  VideoMixer(const OpalMixerNodeInfo & info);
1240  ~VideoMixer();
1241 
1242  virtual bool OnMixed(RTP_DataFrame * & output);
1243 
1244  PDictionary<PString, OpalTranscoder> m_transcoders;
1245  };
1247 #endif // OPAL_VIDEO
1248 };
1249 
1250 
1251 #endif // OPAL_HAS_MIXER
1252 
1253 #endif // OPAL_OPAL_OPAL_MIXER
1254 
1255