reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Conversation.cxx
Go to the documentation of this file.
1#include "Conversation.hxx"
2#include "Participant.hxx"
9#include "UserAgent.hxx"
11#include "ReconSubsystem.hxx"
12
15#include <rutil/Log.hxx>
16#include <rutil/Logger.hxx>
18
19using namespace recon;
20using namespace resip;
21
22#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
23
25 ConversationManager& conversationManager,
26 RelatedConversationSet* relatedConversationSet,
27 ConversationHandle sharedMediaInterfaceConvHandle,
29 unsigned int maxParticipants)
30: mHandle(handle),
31 mConversationManager(conversationManager),
32 mDestroying(false),
33 mNumLocalParticipants(0),
34 mNumRemoteParticipants(0),
35 mNumRemoteIMParticipants(0),
36 mNumMediaParticipants(0),
37 mAutoHoldMode(autoHoldMode),
38 mMaxParticipants(maxParticipants),
39 mSharingMediaInterfaceWithAnotherConversation(false)
40{
41 mConversationManager.registerConversation(this);
42
43 if(relatedConversationSet)
44 {
45 mRelatedConversationSet = relatedConversationSet;
46 mRelatedConversationSet->addRelatedConversation(mHandle, this);
47 }
48 else
49 {
50 mRelatedConversationSet = new RelatedConversationSet(mConversationManager, mHandle, this);
51 }
52 InfoLog(<< "Conversation created, handle=" << mHandle);
53
54 if(mConversationManager.getMediaStackAdapter().supportsMultipleMediaInterfaces())
55 {
56 // Check if sharedMediaInterfaceConvHandle was passed in, and if so use the same media interface and bridge mixer that, that
57 // conversation is using
58 if (sharedMediaInterfaceConvHandle != 0)
59 {
60 Conversation* sharedFlowConversation = mConversationManager.getConversation(sharedMediaInterfaceConvHandle);
61 if (sharedFlowConversation)
62 {
63 mBridgeMixer = sharedFlowConversation->getBridgeMixerShared();
64 mSharingMediaInterfaceWithAnotherConversation = true;
65 }
66 }
67 }
68}
69
71{
72 mConversationManager.unregisterConversation(this);
73 if(mRelatedConversationSet)
74 {
75 mRelatedConversationSet->removeConversation(mHandle);
76 }
77 mConversationManager.onConversationDestroyed(mHandle);
78 mBridgeMixer.reset(); // Make sure the mixer is destroyed before the media interface
79 InfoLog(<< "Conversation destroyed, handle=" << mHandle);
80}
81
83Conversation::getParticipant(ParticipantHandle partHandle)
84{
85 ParticipantMap::iterator it = mParticipants.find(partHandle);
86 if(it != mParticipants.end())
87 {
88 return it->second.getParticipant();
89 }
90 else
91 {
92 return 0;
93 }
94}
95
96void
97Conversation::addParticipant(Participant* participant, unsigned int inputGain, unsigned int outputGain)
98{
99 if(mMaxParticipants > 0 && mParticipants.size() >= mMaxParticipants)
100 {
101 WarningLog(<<"Conversation already has " << mMaxParticipants << " participant(s), can't add another");
102 return;
103 }
104
105 // If participant doesn't already exist in this conversation - then add them
106 if(getParticipant(participant->getParticipantHandle()) == 0)
107 {
108 participant->addToConversation(this, inputGain, outputGain);
109 }
110}
111
112void
114{
115 // If participant exists in this conversation - then remove them
116 if(getParticipant(participant->getParticipantHandle()) != 0)
117 {
118 participant->removeFromConversation(this); // Can cause this to be deleted
119 }
120}
121
122void
123Conversation::modifyParticipantContribution(Participant* participant, unsigned int inputGain, unsigned int outputGain)
124{
125 ParticipantMap::iterator it = mParticipants.find(participant->getParticipantHandle());
126 if(it != mParticipants.end())
127 {
128 it->second.setInputGain(inputGain);
129 it->second.setOutputGain(outputGain);
130 participant->applyBridgeMixWeights();
131 }
132}
133
134bool
136{
137 switch (mAutoHoldMode)
138 {
140 return false;
142 return true;
143 default: // Enabled
144 // We should be offering a hold SDP if there are no remote participants at all OR
145 // there is no LocalParticipant in the conversation and there are no other remote or media participants
146 return mNumRemoteParticipants == 0 ||
147 (mNumLocalParticipants == 0 && (mNumRemoteParticipants + mNumMediaParticipants) <= 1);
148 }
149}
150
151bool
156
157void
159{
160 ParticipantMap::iterator i;
161 for(i = mParticipants.begin(); i != mParticipants.end(); i++)
162 {
163 RemoteParticipant* remoteParticipant = dynamic_cast<RemoteParticipant*>(i->second.getParticipant());
164 if(remoteParticipant)
165 {
166 remoteParticipant->checkHoldCondition();
167 }
168 }
169}
170
171void
173{
174 // First check if sourceParticipant exists in thei conversation and has any Output Gain
175 // for this conversation. Only allow relaying if they are allowed output to this conversation.
176 ParticipantMap::iterator it = mParticipants.find(sourceParticipant);
177 if (it == mParticipants.end())
178 {
179 // Source Participant is not part of the conversation, strange, don't continue
180 resip_assert(false); // shouldn't happen
181 return;
182 }
183 if (it->second.getOutputGain() == 0)
184 {
185 // Source participant doesn't have output permission for this conversation, don't relay
186 return;
187 }
188
189 Data prefixText;
190 if (!senderDisplayName.empty())
191 {
192 DataStream ds(prefixText);
193 ds << "[" << senderDisplayName << "] ";
194 }
195 ParticipantMap::iterator i;
196 for (i = mParticipants.begin(); i != mParticipants.end(); i++)
197 {
198 // Only relay if this participant is not the sourceParticipant (so we don't send back to ourself)
199 // Skip participants if they don't have any inputGain from this conversation.
200 if (i->second.getParticipant()->getParticipantHandle() != sourceParticipant &&
201 i->second.getInputGain() > 0)
202 {
203 IMParticipantBase* imParticipant = dynamic_cast<IMParticipantBase*>(i->second.getParticipant());
204 if (imParticipant)
205 {
206 Contents* contentsToSend = msg.getContents()->clone();
207 if (!prefixText.empty() && imParticipant->getPrependSenderInfoToIMs())
208 {
209 PlainContents* plainContents = dynamic_cast<PlainContents*>(contentsToSend);
210 if (plainContents)
211 {
212 // Prepend prefix to Plain text
213 plainContents->text() = prefixText + plainContents->text();
214 }
215 }
216 std::unique_ptr<Contents> contents(contentsToSend);
217 imParticipant->sendInstantMessage(std::move(contents));
218 }
219 }
220 }
221}
222
223void
225{
226 // Create new Related Conversation
227 ConversationHandle relatedConvHandle = mConversationManager.getNewConversationHandle();
228 Conversation* conversation = mConversationManager.getMediaStackAdapter().createConversationInstance(relatedConvHandle, mRelatedConversationSet,
229 // If this conversation is sharing a media interface, then any related
230 // conversations will as well (use our handle as the original handle
231 // passed in contructor could be gone)
232 mSharingMediaInterfaceWithAnotherConversation ? mHandle: 0,
233 mAutoHoldMode);
234
235 // Copy all participants to new Conversation, except origParticipant
236 ParticipantMap::iterator i;
237 for(i = mParticipants.begin(); i != mParticipants.end(); i++)
238 {
239 if(i->second.getParticipant()->getParticipantHandle() != origParticipantHandle)
240 {
241 conversation->addParticipant(i->second.getParticipant(), i->second.getInputGain(), i->second.getOutputGain());
242 }
243 }
244 // add new forked participant to new Conversation
245 conversation->addParticipant(newForkedParticipant);
246
247 // Send onRelatedConversation event
248 mConversationManager.onRelatedConversation(relatedConvHandle, newForkedParticipant->getParticipantHandle(),
249 mHandle, origParticipantHandle);
250}
251
252void
254{
255 // Copy all participants to new Conversation
256 ParticipantMap::iterator i;
257 for(i = mParticipants.begin(); i != mParticipants.end(); i++)
258 {
259 conversation->addParticipant(i->second.getParticipant(), i->second.getInputGain(), i->second.getOutputGain()); // checks for duplicates
260 }
261 destroy(); // destroy this conversation
262}
263
264void
266{
267 if(mParticipants.size() == 0)
268 {
269 delete this;
270 }
271 else
272 {
273 // End each Participant - for local participants just remove them
274 mDestroying = true;
275 ParticipantMap temp = mParticipants; // Need to copy since member list can be changed by terminating participants
276 ParticipantMap::iterator i;
277 for(i = temp.begin(); i != temp.end(); i++)
278 {
279 LocalParticipant* localParticipant = dynamic_cast<LocalParticipant*>(i->second.getParticipant());
280 if(localParticipant)
281 {
282 removeParticipant(localParticipant);
283 }
284 else
285 {
286 if(i->second.getParticipant()->getNumConversations() == 1)
287 {
288 // Destroy participant if it is only in this conversation
289 i->second.getParticipant()->destroyParticipant();
290 }
291 else
292 {
293 removeParticipant(i->second.getParticipant()); // Can cause "this" to get deleted
294 }
295 }
296 }
297 }
298}
299
300void
301Conversation::registerParticipant(Participant *participant, unsigned int inputGain, unsigned int outputGain)
302{
303 bool added = false;
304 // Only increment count if registering new participant
305 if(getParticipant(participant->getParticipantHandle()) == 0)
306 {
307 added = true;
308 bool prevShouldHold = shouldHold();
309 if(dynamic_cast<LocalParticipant*>(participant))
310 {
311 mNumLocalParticipants++;
312 }
313 else if(dynamic_cast<RemoteParticipant*>(participant))
314 {
315 mNumRemoteParticipants++;
316 }
317 else if(dynamic_cast<MediaResourceParticipant*>(participant))
318 {
319 mNumMediaParticipants++;
320 }
321 else if (dynamic_cast<RemoteIMPagerParticipant*>(participant) ||
322 dynamic_cast<RemoteIMSessionParticipant*>(participant))
323 {
324 mNumRemoteIMParticipants++;
325 }
326 else
327 {
328 resip_assert(false);
329 }
330 if(prevShouldHold != shouldHold())
331 {
332 notifyRemoteParticipantsOfHoldChange(); // Note: no need to notify party just added
333 }
334 }
335
336 mParticipants[participant->getParticipantHandle()] = ConversationParticipantAssignment(participant, inputGain, outputGain);
337
338 InfoLog(<< "Participant handle=" << participant->getParticipantHandle() << " added to conversation handle=" << mHandle << " (BridgePort=" << participant->getConnectionPortOnBridge() << ")");
339
340 if(added)
341 {
342 onParticipantAdded(participant);
343 }
344 participant->applyBridgeMixWeights();
345}
346
347void
349{
350 if(getParticipant(participant->getParticipantHandle()) != 0)
351 {
352 onParticipantRemoved(participant);
353 mParticipants.erase(participant->getParticipantHandle()); // No need to notify this party, remove from map first
354
355 bool prevShouldHold = shouldHold();
356 if(dynamic_cast<LocalParticipant*>(participant))
357 {
358 resip_assert(mNumLocalParticipants != 0);
359 mNumLocalParticipants--;
360 }
361 else if(dynamic_cast<RemoteParticipant*>(participant))
362 {
363 resip_assert(mNumRemoteParticipants != 0);
364 mNumRemoteParticipants--;
365 }
366 else if(dynamic_cast<MediaResourceParticipant*>(participant))
367 {
368 resip_assert(mNumMediaParticipants != 0);
369 mNumMediaParticipants--;
370 }
371 else if (dynamic_cast<RemoteIMPagerParticipant*>(participant) ||
372 dynamic_cast<RemoteIMSessionParticipant*>(participant))
373 {
374 resip_assert(mNumRemoteIMParticipants != 0);
375 mNumRemoteIMParticipants--;
376 }
377 else
378 {
379 resip_assert(false);
380 }
381 if(!mDestroying && prevShouldHold != shouldHold())
382 {
384 }
385
386 participant->applyBridgeMixWeights(this);
387 InfoLog(<< "Participant handle=" << participant->getParticipantHandle() << " removed from conversation handle=" << mHandle);
388
389 if(mDestroying && mParticipants.size() == 0)
390 {
391 delete this;
392 }
393 }
394}
395
396
397/* ====================================================================
398
399 Copyright (c) 2021-2023, SIP Spectrum, Inc. http://www.sipspectrum.com
400 Copyright (c) 2021, Daniel Pocock https://danielpocock.com
401 Copyright (c) 2007-2008, Plantronics, Inc.
402 All rights reserved.
403
404 Redistribution and use in source and binary forms, with or without
405 modification, are permitted provided that the following conditions are
406 met:
407
408 1. Redistributions of source code must retain the above copyright
409 notice, this list of conditions and the following disclaimer.
410
411 2. Redistributions in binary form must reproduce the above copyright
412 notice, this list of conditions and the following disclaimer in the
413 documentation and/or other materials provided with the distribution.
414
415 3. Neither the name of Plantronics nor the names of its contributors
416 may be used to endorse or promote products derived from this
417 software without specific prior written permission.
418
419 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
420 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
421 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
422 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
423 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
424 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
425 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
426 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
427 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
428 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
429 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
430
431 ==================================================================== */
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
This class is one of two main classes of concern to an application using the UserAgent library.
virtual void onRelatedConversation(ConversationHandle relatedConvHandle, ParticipantHandle relatedPartHandle, ConversationHandle origConvHandle, ParticipantHandle origPartHandle)=0
Notifies an applications that a outbound remote participant request has forked.
virtual void onConversationDestroyed(ConversationHandle convHandle)=0
Notifies an application when a conversation has been destroyed.
MediaStackAdapter & getMediaStackAdapter()
Conversation * getConversation(ConversationHandle convHandle)
ConversationHandle getNewConversationHandle()
This class is used by a conversation to track assigned participants.
This class is used to manage membership of participants.
Conversation(ConversationHandle handle, ConversationManager &conversationManager, RelatedConversationSet *relatedConversationSet, ConversationHandle sharedMediaInterfaceConvHandle, ConversationManager::AutoHoldMode autoHoldMode, unsigned int maxParticipants=0)
void addParticipant(Participant *participant, unsigned int inputGain=100, unsigned int outputGain=100)
void notifyRemoteParticipantsOfHoldChange()
void removeParticipant(Participant *participant)
void join(Conversation *conversation)
std::shared_ptr< BridgeMixer > getBridgeMixerShared()
virtual void onParticipantAdded(Participant *participant)=0
void relayInstantMessageToRemoteParticipants(ParticipantHandle sourceParticipant, const resip::Data &senderDisplayName, const resip::SipMessage &msg)
void registerParticipant(Participant *, unsigned int inputGain=100, unsigned int outputGain=100)
void unregisterParticipant(Participant *)
std::map< ParticipantHandle, ConversationParticipantAssignment > ParticipantMap
void modifyParticipantContribution(Participant *participant, unsigned int inputGain, unsigned int outputGain)
virtual void onParticipantRemoved(Participant *participant)=0
void createRelatedConversation(RemoteParticipant *newForkedParticipant, ParticipantHandle origParticipantHandle)
This class represents base functionality for a participant that supports IMs.
virtual bool getPrependSenderInfoToIMs()
virtual void sendInstantMessage(std::unique_ptr< resip::Contents > contents)=0
This class represents a local participant.
This class represents a media resource participant.
virtual Conversation * createConversationInstance(ConversationHandle handle, RelatedConversationSet *relatedConversationSet, ConversationHandle sharedMediaInterfaceConvHandle, ConversationManager::AutoHoldMode autoHoldMode)=0
virtual bool supportsMultipleMediaInterfaces()=0
This is the base class for a RemoteParticipant, LocalParticipant and a MediaResourceParticipant.
virtual void removeFromConversation(Conversation *conversation)
virtual void applyBridgeMixWeights()
virtual void addToConversation(Conversation *conversation, unsigned int inputGain=100, unsigned int outputGain=100)
virtual ParticipantHandle getParticipantHandle()
virtual int getConnectionPortOnBridge()=0
This is used to keep a mapping between all related conversations.
void removeConversation(ConversationHandle conversationHandle)
void addRelatedConversation(ConversationHandle relatedConversationHandle, Conversation *relatedConversation)
This class represent an Instant Message remote participant.
This class represents an IM Session remote participant.
This class represent a remote participant.
Base class for all SIP body types.
Definition Contents.hxx:51
virtual Contents * clone() const =0
returns a copy of a Contents object
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
SIP body type for holding plain contents (MIME content-type text/plain).
const Data & text() const
Represents a SIP message.
Contents * getContents() const
Retrieves the body of a SIP message.
unsigned int ConversationHandle
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;