OPAL Version 3.10.4

- Main Page
- Related Pages
- Namespaces
- Data Structures
- Files

- File List
- Globals

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: 26969 $
29 * $Author: rjongbloed $
30 * $Date: 2012-02-10 18:45:16 -0600 (Fri, 10 Feb 2012) $
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 #include <queue>
44
45 #include <opal/localep.h>
46 #include <codec/vidcodec.h>
47
48
49 class RTP_DataFrame;
50 class OpalJitterBuffer;
51
52
53 //#define OPAL_MIXER_AUDIO_DEBUG 1
54
55
56 #define OPAL_OPT_LISTEN_ONLY "Listen-Only"
57
58
60
70 class OpalBaseMixer
71 {
72 public:
73 OpalBaseMixer(
74 bool pushThread,
75 unsigned periodMS,
76 unsigned periodTS
77 );
78
79 virtual ~OpalBaseMixer();
80
81 typedef PString Key_T;
82
85 virtual bool AddStream(
86 const Key_T & key
87 );
88
91 virtual void RemoveStream(
92 const Key_T & key
93 );
94
97 virtual void RemoveAllStreams();
98
104 virtual bool WriteStream(
105 const Key_T & key,
106 const RTP_DataFrame & input
107 );
108
118 virtual RTP_DataFrame * ReadMixed();
119 virtual bool ReadMixed(RTP_DataFrame & mixed);
120
131 virtual bool OnMixed(
132 RTP_DataFrame * & mixed
133 );
134
138 void StartPushThread();
139
144 void StopPushThread(bool lock = true);
145
148 unsigned GetPeriodTS() const { return m_periodTS; }
149
150 protected:
151 struct Stream {
152 virtual ~Stream() { }
153 virtual void QueuePacket(const RTP_DataFrame & rtp) = 0;
154 queue<RTP_DataFrame> m_queue;
155 };
156 typedef std::map<Key_T, Stream *> StreamMap_T;
157
158 virtual Stream * CreateStream() = 0;
159 virtual bool MixStreams(RTP_DataFrame & frame) = 0;
160 virtual size_t GetOutputSize() const = 0;
161
162 virtual bool OnPush();
163 void PushThreadMain();
164
165 bool m_pushThread; // true if to use a thread to push data out
166 unsigned m_periodMS; // Mixing interval in milliseconds
167 unsigned m_periodTS; // Mixing interval in timestamp units
168
169 StreamMap_T m_inputStreams; // Map of key to stream for input RTP frame queues
170 unsigned m_outputTimestamp; // RTP timestamp for output data
171 RTP_DataFrame * m_pushFrame; // Cached frame for pushing RTP
172 PThread * m_workerThread; // reader thread handle
173 bool m_threadRunning; // used to stop reader thread
174 PMutex m_mutex; // mutex for list of streams and thread handle
175 };
176
178
187 class OpalAudioMixer : public OpalBaseMixer
188 {
189 public:
190 OpalAudioMixer(
191 bool stereo = false,
192 unsigned sampleRate = OpalMediaFormat::AudioClockRate,
193 bool pushThread = true,
194 unsigned period = 10
195 );
196
197 ~OpalAudioMixer() { StopPushThread(); }
198
201 virtual void RemoveStream(
202 const Key_T & key
203 );
204
207 virtual void RemoveAllStreams();
208
211 bool IsStereo() const { return m_stereo; }
212
215 unsigned GetSampleRate() const { return m_sampleRate; }
216
223 bool SetSampleRate(
224 unsigned rate
225 );
226
233 bool SetJitterBufferSize(
234 const Key_T & key,
235 unsigned minJitterDelay,
236 unsigned maxJitterDelay
237 );
238
239 protected:
240 struct AudioStream : public Stream
241 {
242 AudioStream(OpalAudioMixer & mixer);
243 ~AudioStream();
244
245 virtual void QueuePacket(const RTP_DataFrame & rtp);
246 const short * GetAudioDataPtr();
247
248 OpalAudioMixer & m_mixer;
249 OpalJitterBuffer * m_jitter;
250 unsigned m_nextTimestamp;
251 PShortArray m_cacheSamples;
252 size_t m_samplesUsed;
253 };
254
255 virtual Stream * CreateStream();
256 virtual bool MixStreams(RTP_DataFrame & frame);
257 virtual size_t GetOutputSize() const;
258
259 void PreMixStreams();
260 void MixStereo(RTP_DataFrame & frame);
261 void MixAdditive(RTP_DataFrame & frame, const short * audioToSubtract);
262
263 protected:
264 bool m_stereo;
265 unsigned m_sampleRate;
266
267 AudioStream * m_left;
268 AudioStream * m_right;
269 std::vector<int> m_mixedAudio;
270 };
271
272
274
275 #if OPAL_VIDEO
276
283 class OpalVideoMixer : public OpalBaseMixer
284 {
285 public:
286 enum Styles {
287 eSideBySideLetterbox,
291 eSideBySideScaled,
295 eStackedPillarbox,
299 eStackedScaled,
303 eGrid,
305 };
306
307 OpalVideoMixer(
308 Styles style,
309 unsigned width,
310 unsigned height,
311 unsigned rate = 15,
312 bool pushThread = true
313 );
314
315 ~OpalVideoMixer() { StopPushThread(); }
316
319 unsigned GetFrameWidth() const { return m_width; }
320
323 unsigned GetFrameHeight() const { return m_height; }
324
327 unsigned GetFrameRate() const { return 1000/m_periodMS; }
328
332 bool SetFrameRate(
333 unsigned rate // New frames per second.
334 );
335
339 bool SetFrameSize(
340 unsigned width,
341 unsigned height
342 );
343
344 protected:
345 struct VideoStream : public Stream
346 {
347 VideoStream(OpalVideoMixer & mixer);
348 virtual void QueuePacket(const RTP_DataFrame & rtp);
349 void InsertVideoFrame(unsigned x, unsigned y, unsigned w, unsigned h);
350
351 OpalVideoMixer & m_mixer;
352 };
353
354 friend struct VideoStream;
355
356 virtual Stream * CreateStream();
357 virtual bool MixStreams(RTP_DataFrame & frame);
358 virtual size_t GetOutputSize() const;
359
360 protected:
361 Styles m_style;
362 unsigned m_width, m_height;
363 BYTE m_bgFillRed,m_bgFillGreen,m_bgFillBlue;
364
365 PBYTEArray m_frameStore;
366 size_t m_lastStreamCount;
367 };
368
369 #endif // OPAL_VIDEO
370
371
373
374
379 struct OpalMixerNodeInfo
380 {
381 OpalMixerNodeInfo(const char * name = NULL)
382 : m_name(name)
383 , m_listenOnly(false)
384 , m_sampleRate(OpalMediaFormat::AudioClockRate)
385 #if OPAL_VIDEO
386 , m_audioOnly(false)
387 , m_style(OpalVideoMixer::eGrid)
388 , m_width(PVideoFrameInfo::CIFWidth)
389 , m_height(PVideoFrameInfo::CIFHeight)
390 , m_rate(15)
391 #endif
392 , m_mediaPassThru(false)
393 { }
394
395 virtual ~OpalMixerNodeInfo() { }
396
397 virtual OpalMixerNodeInfo * Clone() const { return new OpalMixerNodeInfo(*this); }
398
399 PString m_name;
400 bool m_listenOnly;
401 unsigned m_sampleRate;
402 #if OPAL_VIDEO
403 bool m_audioOnly;
404 OpalVideoMixer::Styles m_style;
405 unsigned m_width;
406 unsigned m_height;
407 unsigned m_rate;
408 #endif
409 bool m_mediaPassThru;
411 };
412
413
415
416 class OpalMixerNode;
417
418
423 class OpalMixerNodeManager : public PObject
424 {
425 PCLASSINFO(OpalMixerNodeManager, PObject);
426 public:
431 OpalMixerNodeManager();
432
436 virtual ~OpalMixerNodeManager();
437
440 virtual void ShutDown();
441
447 virtual PBoolean GarbageCollection();
449
458 virtual OpalMixerNode * CreateNode(
459 OpalMixerNodeInfo * info
460 );
461
467 virtual PSafePtr<OpalMixerNode> AddNode(
468 OpalMixerNodeInfo * info
469 );
470
473 void AddNode(OpalMixerNode * node);
474
478 PSafePtr<OpalMixerNode> GetFirstNode(
479 PSafetyMode mode = PSafeReference
480 ) const { return PSafePtr<OpalMixerNode>(m_nodesByUID, mode); }
481
485 virtual PSafePtr<OpalMixerNode> FindNode(
486 const PString & name,
487 PSafetyMode mode = PSafeReference
488 );
489
494 virtual void RemoveNode(
495 OpalMixerNode & node
496 );
497
500 void AddNodeName(
501 PString name,
502 OpalMixerNode * node
503 );
504
507 void RemoveNodeName(
508 PString name
509 );
510
514 void RemoveNodeNames(
515 PStringList names
516 );
518
519 protected:
520 PSafeDictionary<PGloballyUniqueID, OpalMixerNode> m_nodesByUID;
521 PDictionary<PString, OpalMixerNode> m_nodesByName;
522 };
523
524
526
527 class OpalMixerConnection;
528
533 class OpalMixerEndPoint : public OpalLocalEndPoint
534 {
535 PCLASSINFO(OpalMixerEndPoint, OpalLocalEndPoint);
536 public:
541 OpalMixerEndPoint(
542 OpalManager & manager,
543 const char * prefix
544 );
545
548 ~OpalMixerEndPoint();
549
554 virtual void ShutDown();
556
569 virtual OpalMediaFormatList GetMediaFormats() const;
570
600 virtual PSafePtr<OpalConnection> MakeConnection(
601 OpalCall & call,
602 const PString & party,
603 void * userData = NULL,
604 unsigned options = 0,
605 OpalConnection::StringOptions * stringOptions = NULL
606 );
607
612 virtual PBoolean GarbageCollection();
614
623 PSafePtr<OpalMixerConnection> GetMixerConnectionWithLock(
624 const PString & token,
625 PSafetyMode mode = PSafeReadWrite
626 ) { return GetConnectionWithLockAs<OpalMixerConnection>(token, mode); }
627
631 virtual OpalMixerConnection * CreateConnection(
632 PSafePtr<OpalMixerNode> node,
633 OpalCall & call,
634 void * userData,
635 unsigned options,
636 OpalConnection::StringOptions * stringOptions
637 );
639
647 PSafePtr<OpalMixerNode> AddNode(
648 OpalMixerNodeInfo * info
649 );
650
657 virtual OpalMixerNode * CreateNode(
658 OpalMixerNodeInfo * info
659 );
660
664 PSafePtr<OpalMixerNode> GetFirstNode(
665 PSafetyMode mode = PSafeReference
666 ) const { return m_nodeManager.GetFirstNode(mode); }
667
671 PSafePtr<OpalMixerNode> FindNode(
672 const PString & name,
673 PSafetyMode mode = PSafeReference
674 ) { return m_nodeManager.FindNode(name, mode); }
675
680 void RemoveNode(
681 OpalMixerNode & node
682 ) { m_nodeManager.RemoveNode(node); }
684
699 void SetAdHocNodeInfo(
700 const OpalMixerNodeInfo & info
701 );
702 void SetAdHocNodeInfo(
703 OpalMixerNodeInfo * info
704 );
705
717 OpalMixerNodeInfo * GetAdHocNodeInfo() { return m_adHocNodeInfo; }
718
721 const OpalMixerNodeManager & GetNodeManager() const { return m_nodeManager; }
722 OpalMixerNodeManager & GetNodeManager() { return m_nodeManager; }
724
725 protected:
726 OpalMixerNodeInfo * m_adHocNodeInfo;
727 OpalMixerNodeManager m_nodeManager;
728 };
729
730
732
735 class OpalMixerConnection : public OpalLocalConnection
736 {
737 PCLASSINFO(OpalMixerConnection, OpalLocalConnection);
738 public:
743 OpalMixerConnection(
744 PSafePtr<OpalMixerNode> node,
745 OpalCall & call,
746 OpalMixerEndPoint & endpoint,
747 void * userData,
748 unsigned options = 0,
749 OpalConnection::StringOptions * stringOptions = NULL
750 );
751
754 ~OpalMixerConnection();
756
776 virtual void OnReleased();
777
784 virtual OpalMediaFormatList GetMediaFormats() const;
785
800 virtual OpalMediaStream * CreateMediaStream(
801 const OpalMediaFormat & mediaFormat,
802 unsigned sessionID,
803 PBoolean isSource
804 );
805
808 virtual void OnStartMediaPatch(
809 OpalMediaPatch & patch
810 );
811
813 virtual void OnApplyStringOptions();
814
821 virtual PBoolean SendUserInputString(
822 const PString & value
823 );
824
841 virtual PBoolean SendUserInputTone(
842 char tone,
843 unsigned duration = 0
844 );
846
851 void SetListenOnly(
852 bool listenOnly
853 );
854
857 bool GetListenOnly() const { return m_listenOnly; }
858
861 PSafePtr<OpalMixerNode> GetNode() const { return m_node; }
863
864 protected:
865 OpalMixerEndPoint & m_endpoint;
866 PSafePtr<OpalMixerNode> m_node;
867 bool m_listenOnly;
868 };
869
870
874 class OpalMixerMediaStream : public OpalMediaStream
875 {
876 PCLASSINFO(OpalMixerMediaStream, OpalMediaStream);
877 public:
882 OpalMixerMediaStream(
883 OpalConnection & conn,
884 const OpalMediaFormat & mediaFormat,
885 unsigned sessionID,
886 bool isSource,
887 PSafePtr<OpalMixerNode> node,
888 bool listenOnly
889 );
890
893 ~OpalMixerMediaStream();
895
900 virtual PBoolean Open();
901
904 virtual PBoolean Close();
905
911 virtual PBoolean WritePacket(
912 RTP_DataFrame & packet
913 );
914
918 virtual PBoolean IsSynchronous() const;
919
930 virtual PBoolean RequiresPatchThread() const;
931
939 virtual bool EnableJitterBuffer(bool enab = true) const;
941
946 PSafePtr<OpalMixerNode> GetNode() { return m_node; }
948
949 protected:
950 PSafePtr<OpalMixerNode> m_node;
951 bool m_listenOnly;
952 #if OPAL_VIDEO
953 bool m_video;
954 #endif
955 };
956
957
961 class OpalMixerNode : public PSafeObject
962 {
963 PCLASSINFO(OpalMixerNode, PSafeObject);
964 public:
969 OpalMixerNode(
970 OpalMixerNodeManager & manager,
971 OpalMixerNodeInfo * info
972 );
973 OpalMixerNode(
974 OpalMixerEndPoint & endpoint,
975 OpalMixerNodeInfo * info
976 );
977
980 ~OpalMixerNode();
981
986 void ShutDown();
988
995 void PrintOn(
996 ostream & strm
997 ) const;
999
1004 void AttachConnection(
1005 OpalConnection * connection
1006 );
1007
1010 void DetachConnection(
1011 OpalConnection * connection
1012 );
1013
1016 bool AttachStream(
1017 OpalMixerMediaStream * stream
1018 );
1019
1022 void DetachStream(
1023 OpalMixerMediaStream * stream
1024 );
1025
1028 void UseMediaPassThrough(
1029 unsigned sessionID,
1030 OpalConnection * connection = NULL
1031 );
1032
1039 bool SetJitterBufferSize(
1040 const OpalBaseMixer::Key_T & key,
1041 unsigned minJitterDelay,
1042 unsigned maxJitterDelay
1043 ) { return m_audioMixer.SetJitterBufferSize(key, minJitterDelay, maxJitterDelay); }
1044
1047 bool WriteAudio(
1048 const OpalBaseMixer::Key_T & key,
1049 const RTP_DataFrame & input
1050 ) { return m_audioMixer.WriteStream(key, input); }
1051
1052 #if OPAL_VIDEO
1053
1055 bool WriteVideo(
1056 const OpalBaseMixer::Key_T & key,
1057 const RTP_DataFrame & input
1058 ) { return m_videoMixer.WriteStream(key, input); }
1059 #endif // OPAL_VIDEO
1060
1063 virtual void BroadcastUserInput(
1064 const OpalConnection * connection,
1065 const PString & value
1066 );
1068
1073 const PGloballyUniqueID & GetGUID() const { return m_guid; }
1074
1077 const PStringList & GetNames() const { return m_names; }
1078
1081 void AddName(
1082 const PString & name
1083 );
1084
1087 void RemoveName(
1088 const PString & name
1089 );
1090
1096 PINDEX GetConnectionCount() const { return m_connections.GetSize(); }
1097
1100 template <class Subclass>
1101 PSafePtr<Subclass> GetFirstConnectionAs(
1102 PSafetyMode mode = PSafeReference
1103 ) const { return PSafePtr<Subclass>(m_connections, mode); }
1104
1107 PSafePtr<OpalConnection> GetFirstConnection(
1108 PSafetyMode mode = PSafeReference
1109 ) const { return GetFirstConnectionAs<OpalConnection>(mode); }
1110
1113 const OpalMixerNodeInfo & GetNodeInfo() { return *m_info; }
1114
1117 const PTime & GetCreationTime() const { return m_creationTime; }
1119
1120 protected:
1121 void Construct();
1122
1123 OpalMixerNodeManager & m_manager;
1124 PGloballyUniqueID m_guid;
1125 PStringList m_names;
1126 OpalMixerNodeInfo * m_info;
1127 PTime m_creationTime;
1128
1129 PSafeList<OpalConnection> m_connections;
1130
1131 struct MediaMixer
1132 {
1133 MediaMixer();
1134
1135 PSafeList<OpalMixerMediaStream> m_outputStreams;
1136 };
1137
1138 struct AudioMixer : public OpalAudioMixer, public MediaMixer
1139 {
1140 AudioMixer(const OpalMixerNodeInfo & info);
1141 ~AudioMixer();
1142
1143 virtual bool OnPush();
1144
1145 struct CachedAudio {
1146 CachedAudio();
1147 ~CachedAudio();
1148 enum { Collecting, Collected, Completed } m_state;
1149 RTP_DataFrame m_raw;
1150 RTP_DataFrame m_encoded;
1151 OpalTranscoder * m_transcoder;
1152 };
1153 std::map<PString, CachedAudio> m_cache;
1154
1155 void PushOne(
1156 PSafePtr<OpalMixerMediaStream> & stream,
1157 CachedAudio & cache,
1158 const short * audioToSubtract
1159 );
1160 #ifdef OPAL_MIXER_AUDIO_DEBUG
1161 class PAudioMixerDebug * m_audioDebug;
1162 #endif
1163 };
1164 AudioMixer m_audioMixer;
1165
1166 #if OPAL_VIDEO
1167 struct VideoMixer : public OpalVideoMixer, public MediaMixer
1168 {
1169 VideoMixer(const OpalMixerNodeInfo & info);
1170 ~VideoMixer();
1171
1172 virtual bool OnMixed(RTP_DataFrame * & output);
1173
1174 PDictionary<PString, OpalTranscoder> m_transcoders;
1175 };
1176 VideoMixer m_videoMixer;
1177 #endif // OPAL_VIDEO
1178 };
1179
1180
1181 #endif // OPAL_OPAL_OPAL_MIXER
1182
1183