OPAL  Version 3.14.3
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: 32284 $
29  * $Author: rjongbloed $
30  * $Date: 2014-07-02 17:39:14 +1000 (Wed, 02 Jul 2014) $
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_config.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 PSmartObject
77 {
78  PCLASSINFO(OpalBaseMixer, PSmartObject);
79  public:
81  bool pushThread,
82  unsigned periodMS,
83  unsigned periodTS
84  );
85 
86  virtual ~OpalBaseMixer();
87 
88  typedef PString Key_T;
89 
92  virtual bool AddStream(
93  const Key_T & key
94  );
95 
98  virtual void RemoveStream(
99  const Key_T & key
100  );
101 
104  virtual void RemoveAllStreams();
105 
111  virtual bool WriteStream(
112  const Key_T & key,
113  const RTP_DataFrame & input
114  );
115 
125  virtual RTP_DataFrame * ReadMixed();
126  virtual bool ReadMixed(RTP_DataFrame & mixed);
127 
138  virtual bool OnMixed(
139  RTP_DataFrame * & mixed
140  );
141 
145  void StartPushThread();
146 
151  void StopPushThread(bool lock = true);
152 
155  unsigned GetPeriodTS() const { return m_periodTS; }
156 
157  protected:
158  struct Stream : public PObject {
159  virtual ~Stream() { }
160  virtual void QueuePacket(const RTP_DataFrame & rtp) = 0;
161  std::queue<RTP_DataFrame> m_queue;
162  };
163  typedef std::map<Key_T, Stream *> StreamMap_T;
164 
165  virtual Stream * CreateStream() = 0;
166  virtual bool MixStreams(RTP_DataFrame & frame) = 0;
167  virtual size_t GetOutputSize() const = 0;
168 
169  virtual bool OnPush();
170  void PushThreadMain();
171 
172  bool m_pushThread; // true if to use a thread to push data out
173  unsigned m_periodMS; // Mixing interval in milliseconds
174  unsigned m_periodTS; // Mixing interval in timestamp units
175 
176  StreamMap_T m_inputStreams; // Map of key to stream for input RTP frame queues
177  unsigned m_outputTimestamp; // RTP timestamp for output data
178  RTP_DataFrame * m_pushFrame; // Cached frame for pushing RTP
179  PThread * m_workerThread; // reader thread handle
180  bool m_threadRunning; // used to stop reader thread
181  PMutex m_mutex; // mutex for list of streams and thread handle
182 };
183 
185 
195 {
196  PCLASSINFO(OpalAudioMixer, OpalBaseMixer);
197  public:
199  bool stereo = false,
200  unsigned sampleRate = OpalMediaFormat::AudioClockRate,
201  bool pushThread = true,
202  unsigned period = 10
203  );
204 
206 
209  virtual void RemoveStream(
210  const Key_T & key
211  );
212 
215  virtual void RemoveAllStreams();
216 
219  bool IsStereo() const { return m_stereo; }
220 
223  unsigned GetSampleRate() const { return m_sampleRate; }
224 
231  bool SetSampleRate(
232  unsigned rate
233  );
234 
241  bool SetJitterBufferSize(
242  const Key_T & key,
243  const OpalJitterBuffer::Init & init
244  );
245 
246  protected:
247  struct AudioStream : public Stream
248  {
249  AudioStream(OpalAudioMixer & mixer);
250  ~AudioStream();
251 
252  virtual void QueuePacket(const RTP_DataFrame & rtp);
253  const short * GetAudioDataPtr();
254 
257  unsigned m_nextTimestamp;
258  PShortArray m_cacheSamples;
260  };
261 
262  virtual Stream * CreateStream();
263  virtual bool MixStreams(RTP_DataFrame & frame);
264  virtual size_t GetOutputSize() const;
265 
266  void PreMixStreams();
267  void MixStereo(RTP_DataFrame & frame);
268  void MixAdditive(RTP_DataFrame & frame, const short * audioToSubtract);
269 
270  protected:
271  bool m_stereo;
272  unsigned m_sampleRate;
273 
276  std::vector<int> m_mixedAudio;
277 };
278 
279 
281 
282 #if OPAL_VIDEO
283 
291 {
292  PCLASSINFO(OpalVideoMixer, OpalBaseMixer);
293  public:
294  enum Styles {
314  };
315 
317  Styles style,
318  unsigned width,
319  unsigned height,
320  unsigned rate = 15,
321  bool pushThread = true
322  );
323 
325 
328  unsigned GetFrameWidth() const { return m_width; }
329 
332  unsigned GetFrameHeight() const { return m_height; }
333 
336  unsigned GetFrameRate() const { return 1000/m_periodMS; }
337 
341  bool SetFrameRate(
342  unsigned rate // New frames per second.
343  );
344 
348  bool SetFrameSize(
349  unsigned width,
350  unsigned height
351  );
352 
353  protected:
354  struct VideoStream : public Stream
355  {
356  VideoStream(OpalVideoMixer & mixer);
357  virtual void QueuePacket(const RTP_DataFrame & rtp);
358  void InsertVideoFrame(unsigned x, unsigned y, unsigned w, unsigned h);
359 
361  };
362 
363  friend struct VideoStream;
364 
365  virtual Stream * CreateStream();
366  virtual bool MixStreams(RTP_DataFrame & frame);
367  virtual size_t GetOutputSize() const;
368 
369  virtual bool MixVideo();
370  virtual bool StartMix(unsigned & x, unsigned & y, unsigned & w, unsigned & h, unsigned & left);
371  virtual bool NextMix(unsigned & x, unsigned & y, unsigned & w, unsigned & h, unsigned & left);
372  void InsertVideoFrame(const StreamMap_T::iterator & it, unsigned x, unsigned y, unsigned w, unsigned h);
373 
374  protected:
376  unsigned m_width, m_height;
378 
379  PBYTEArray m_frameStore;
381 };
382 
383 #endif // OPAL_VIDEO
384 
385 
387 
388 
394 {
395  OpalMixerNodeInfo(const char * name = NULL)
396  : m_name(name)
397  , m_closeOnEmpty(false)
398  , m_listenOnly(false)
399  , m_sampleRate(OpalMediaFormat::AudioClockRate)
400 #if OPAL_VIDEO
401  , m_audioOnly(false)
402  , m_style(OpalVideoMixer::eGrid)
403  , m_width(PVideoFrameInfo::CIFWidth)
404  , m_height(PVideoFrameInfo::CIFHeight)
405  , m_rate(15)
406 #endif
407  , m_mediaPassThru(false)
408  { }
409 
410  virtual ~OpalMixerNodeInfo() { }
411 
412  virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
413 
414  PString m_name;
417  unsigned m_sampleRate;
418 #if OPAL_VIDEO
419  bool m_audioOnly;
421  unsigned m_width;
422  unsigned m_height;
423  unsigned m_rate;
424 #endif
428  PString m_displayText;
429  PString m_subject;
430  PString m_notes;
431  PString m_keywords;
432 };
433 
434 
436 
437 class OpalMixerNode;
440 
441 
447 {
448  public:
454 
458  virtual ~OpalMixerNodeManager();
459 
462  virtual void ShutDown();
463 
469  virtual PBoolean GarbageCollection();
471 
480  virtual OpalMixerNode * CreateNode(
481  OpalMixerNodeInfo * info
482  );
483 
489  virtual PSafePtr<OpalMixerNode> AddNode(
490  OpalMixerNodeInfo * info
491  );
492 
495  void AddNode(OpalMixerNode * node);
496 
500  PSafePtr<OpalMixerNode> GetFirstNode(
501  PSafetyMode mode = PSafeReference
502  ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
503 
507  virtual PSafePtr<OpalMixerNode> FindNode(
508  const PString & name,
509  PSafetyMode mode = PSafeReference
510  );
511 
516  virtual void RemoveNode(
517  OpalMixerNode & node
518  );
519 
522  void AddNodeName(
523  PString name,
524  OpalMixerNode * node
525  );
526 
529  void RemoveNodeName(
530  PString name
531  );
532 
536  void RemoveNodeNames(
537  const PStringSet & names
538  );
539 
542  virtual PString CreateInternalURI(
543  const PGloballyUniqueID & guid
544  );
545 
550  virtual void OnNodeStatusChanged(
551  const OpalMixerNode & node,
553  );
554 
557 
558 #if OPAL_VIDEO
559 
561 #endif
562 
564  OpalManager & GetManager() const { return m_manager; }
566 
567  protected:
569 
570  PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
571  PSafeDictionary<PString, OpalMixerNode> m_nodesByName;
572 };
573 
574 
576 
582 {
584  public:
590  OpalManager & manager,
591  const char * prefix = OPAL_MIXER_PREFIX
592  );
593 
597 
602  virtual void ShutDown();
604 
617  virtual OpalMediaFormatList GetMediaFormats() const;
618 
646  virtual PSafePtr<OpalConnection> MakeConnection(
647  OpalCall & call,
648  const PString & party,
649  void * userData = NULL,
650  unsigned options = 0,
651  OpalConnection::StringOptions * stringOptions = NULL
652  );
653 
669  virtual bool GetConferenceStates(
670  OpalConferenceStates & states,
671  const PString & name = PString::Empty()
672  ) const;
673 
678  virtual PBoolean GarbageCollection();
680 
689  PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
690  const PString & token,
691  PSafetyMode mode = PSafeReadWrite
692  ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
693 
698  PSafePtr<OpalMixerNode> node,
699  OpalCall & call,
700  void * userData,
701  unsigned options,
702  OpalConnection::StringOptions * stringOptions
703  );
704 
709  const PString & name
710  );
712 
727  void SetAdHocNodeInfo(
728  const OpalMixerNodeInfo & info
729  );
730  void SetAdHocNodeInfo(
731  OpalMixerNodeInfo * info
732  );
733 
746 
764  void SetFactoryNodeInfo(
765  const OpalMixerNodeInfo & info
766  );
767  void SetFactoryNodeInfo(
768  OpalMixerNodeInfo * info
769  );
770 
775  virtual PString GetNewFactoryName();
776 
780 
781  protected:
782  virtual PString CreateInternalURI(const PGloballyUniqueID & guid);
783  virtual void OnNodeStatusChanged(const OpalMixerNode & node, OpalConferenceState::ChangeType change);
784 
787  PMutex m_infoMutex; // For above two fields
788  PAtomicInteger m_factoryIndex;
789 };
790 
791 
793 
797 {
799  public:
805  PSafePtr<OpalMixerNode> node,
806  OpalCall & call,
808  void * userData,
809  unsigned options = 0,
810  OpalConnection::StringOptions * stringOptions = NULL
811  );
812 
817 
837  virtual void OnReleased();
838 
845  virtual OpalMediaFormatList GetMediaFormats() const;
846 
862  const OpalMediaFormat & mediaFormat,
863  unsigned sessionID,
864  PBoolean isSource
865  );
866 
869  virtual void OnStartMediaPatch(
870  OpalMediaPatch & patch
871  );
872 
874  virtual void OnApplyStringOptions();
875 
882  virtual PBoolean SendUserInputString(
883  const PString & value
884  );
885 
902  virtual PBoolean SendUserInputTone(
903  char tone,
904  unsigned duration = 0
905  );
906 
919  virtual bool GetConferenceState(
920  OpalConferenceState * state
921  ) const;
923 
928  void SetListenOnly(
929  bool listenOnly
930  );
931 
934  bool GetListenOnly() const { return m_listenOnly; }
935 
938  PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
940 
941  protected:
943  PSafePtr<OpalMixerNode> m_node;
945 };
946 
947 
952 {
954  public:
960  OpalConnection & conn,
961  const OpalMediaFormat & mediaFormat,
962  unsigned sessionID,
963  bool isSource,
964  PSafePtr<OpalMixerNode> node,
965  bool listenOnly
966  );
967 
972 
977  virtual PBoolean Open();
978 
984  virtual PBoolean Start();
985 
991  virtual PBoolean WritePacket(
992  RTP_DataFrame & packet
993  );
994 
998  virtual PBoolean IsSynchronous() const;
999 
1010  virtual PBoolean RequiresPatchThread() const;
1012 
1017  PSafePtr<OpalMixerNode> GetNode() { return m_node; }
1019 
1020 #if OPAL_VIDEO
1021  bool CheckMixedVideoSize(unsigned width, unsigned height);
1022 #endif
1023 
1024  protected:
1025  virtual void InternalClose();
1026  virtual bool InternalSetJitterBuffer(const OpalJitterBuffer::Init & init) const;
1027 
1028  PSafePtr<OpalMixerNode> m_node;
1030 #if OPAL_VIDEO
1033 #endif
1034 };
1035 
1036 
1038 {
1039  public:
1041  void Append(const PSafePtr<OpalMixerMediaStream> & stream) { m_outputStreams.Append(stream); }
1042  void Remove(const PSafePtr<OpalMixerMediaStream> & stream) { m_outputStreams.Remove(stream); }
1043  void CloseOne(const PSafePtr<OpalMixerMediaStream> & stream);
1044 
1045  protected:
1046  PSafeList<OpalMixerMediaStream> m_outputStreams;
1047 };
1048 
1053 {
1054  PCLASSINFO(OpalAudioStreamMixer, OpalAudioMixer);
1055  public:
1058 
1059  virtual bool OnPush();
1060 
1061  protected:
1063  {
1064  CachedAudio();
1065  ~CachedAudio();
1066  enum
1067  {
1069  } m_state;
1073  };
1074  std::map<PString, CachedAudio> m_cache;
1075 
1076  void PushOne(
1077  PSafePtr<OpalMixerMediaStream> & stream,
1078  CachedAudio & cache,
1079  const short * audioToSubtract
1080  );
1081 
1082 #ifdef OPAL_MIXER_AUDIO_DEBUG
1083  class PAudioMixerDebug * m_audioDebug;
1084 #endif
1085 };
1086 
1087 
1088 #if OPAL_VIDEO
1089 
1097 {
1098  PCLASSINFO(OpalVideoStreamMixer, OpalVideoMixer);
1099  public:
1102 
1103  virtual bool OnMixed(RTP_DataFrame * & output);
1104 
1105  protected:
1106  PDictionary<PString, OpalTranscoder> m_transcoders;
1107 };
1108 #endif // OPAL_VIDEO
1109 
1110 
1114 class OpalMixerNode : public PSafeObject
1115 {
1116  PCLASSINFO(OpalMixerNode, PSafeObject);
1117  public:
1122  OpalMixerNode(
1123  OpalMixerNodeManager & manager,
1124  OpalMixerNodeInfo * info
1125  );
1126 
1129  ~OpalMixerNode();
1130 
1135  void ShutDown();
1137 
1144  void PrintOn(
1145  ostream & strm
1146  ) const;
1148 
1153  virtual void AttachConnection(
1154  OpalConnection * connection
1155  );
1156 
1159  virtual void DetachConnection(
1160  OpalConnection * connection
1161  );
1162 
1165  virtual bool AttachStream(
1166  OpalMixerMediaStream * stream
1167  );
1168 
1171  virtual void DetachStream(
1172  OpalMixerMediaStream * stream
1173  );
1174 
1177  virtual void UseMediaPassThrough(
1178  unsigned sessionID,
1179  OpalConnection * connection = NULL
1180  );
1181 
1188  bool SetJitterBufferSize(
1189  const OpalBaseMixer::Key_T & key,
1190  const OpalJitterBuffer::Init & init
1191  );
1192 
1195  bool WritePacket(
1196  const OpalMixerMediaStream & stream,
1197  const RTP_DataFrame & input
1198  );
1199 
1202  virtual void BroadcastUserInput(
1203  const OpalConnection * connection,
1204  const PString & value
1205  );
1206 
1219  virtual void GetConferenceState(
1220  OpalConferenceState & state
1221  ) const;
1223 
1228  const PGloballyUniqueID & GetGUID() const { return m_guid; }
1229 
1232  const PStringSet & GetNames() const { return m_names; }
1233 
1236  void AddName(
1237  const PString & name
1238  );
1239 
1242  void RemoveName(
1243  const PString & name
1244  );
1245 
1251  PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
1252 
1255  template <class Subclass>
1256  PSafePtr<Subclass> GetFirstConnectionAs(
1257  PSafetyMode mode = PSafeReference
1258  ) const { return PSafePtr<Subclass>(m_connections, mode); }
1259 
1262  PSafePtr<OpalConnection> GetFirstConnection(
1263  PSafetyMode mode = PSafeReference
1264  ) const { return GetFirstConnectionAs<OpalConnection>(mode); }
1265 
1268  const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
1269 
1272  const PTime & GetCreationTime() const { return m_creationTime; }
1273 
1281  const PString & connectionIdentifier
1282  ) { m_ownerConnection = connectionIdentifier; }
1284 
1285  protected:
1287  PGloballyUniqueID m_guid;
1288  PStringSet m_names;
1291  PAtomicBoolean m_shuttingDown;
1292 
1293  PSafeList<OpalConnection> m_connections;
1295 
1297 #if OPAL_VIDEO
1298  typedef std::map<OpalVideoFormat::ContentRole, OpalVideoStreamMixer *> VideoMixerMap;
1300 #endif // OPAL_VIDEO
1301 
1302  typedef std::map<PString, OpalBaseMixer *> MixerByIdMap;
1304 };
1305 
1306 
1307 #endif // OPAL_HAS_MIXER
1308 
1309 #endif // OPAL_OPAL_OPAL_MIXER
1310 
1311