1 /*
2 * connection.cxx
3 *
4 * Connection abstraction
5 *
6 * Open Phone Abstraction Library (OPAL)
7 * Formally known as the Open H323 project.
8 *
9 * Copyright (c) 2001 Equivalence Pty. Ltd.
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 Equivalence Pty. Ltd.
24 *
25 * Contributor(s): ______________________________________.
26 *
27 * $Revision: 28445 $
28 * $Author: rjongbloed $
29 * $Date: 2012-10-02 20:11:02 -0500 (Tue, 02 Oct 2012) $
30 */
31
32 #include <ptlib.h>
33
34 #ifdef __GNUC__
35 #pragma implementation "connection.h"
36 #endif
37
38 #include <opal/buildopts.h>
39
40 #include <opal/connection.h>
41
42 #include <opal/manager.h>
43 #include <opal/endpoint.h>
44 #include <opal/call.h>
45 #include <opal/transcoders.h>
46 #include <opal/patch.h>
47 #include <codec/silencedetect.h>
48 #include <codec/echocancel.h>
49 #include <codec/rfc2833.h>
50 #include <codec/g711codec.h>
51 #include <codec/vidcodec.h>
52 #include <rtp/rtp.h>
53 #include <t120/t120proto.h>
54 #include <t38/t38proto.h>
55 #include <ptclib/url.h>
56
57 #if OPAL_HAS_IM
58 #include <im/rfc4103.h>
59 #if OPAL_SIP
60 #include <im/sipim.h>
61 #include <sip/sipcon.h>
62 #endif
63 #endif
64
65 #define new PNEW
66
67
68 #if PTRACING
operator <<(ostream & out,OpalConnection::Phases phase)69 ostream & operator<<(ostream & out, OpalConnection::Phases phase)
70 {
71 static const char * const names[OpalConnection::NumPhases] = {
72 "UninitialisedPhase",
73 "SetUpPhase",
74 "ProceedingPhase",
75 "AlertingPhase",
76 "ConnectedPhase",
77 "EstablishedPhase",
78 "ForwardingPhase",
79 "ReleasingPhase",
80 "ReleasedPhase"
81 };
82 return out << names[phase];
83 }
84
85
operator <<(ostream & out,OpalConnection::CallEndReason reason)86 ostream & operator<<(ostream & out, OpalConnection::CallEndReason reason)
87 {
88 const char * const names[OpalConnection::NumCallEndReasons] = {
89 "EndedByLocalUser", /// Local endpoint application cleared call
90 "EndedByNoAccept", /// Local endpoint did not accept call OnIncomingCall()=PFalse
91 "EndedByAnswerDenied", /// Local endpoint declined to answer call
92 "EndedByRemoteUser", /// Remote endpoint application cleared call
93 "EndedByRefusal", /// Remote endpoint refused call
94 "EndedByNoAnswer", /// Remote endpoint did not answer in required time
95 "EndedByCallerAbort", /// Remote endpoint stopped calling
96 "EndedByTransportFail", /// Transport error cleared call
97 "EndedByConnectFail", /// Transport connection failed to establish call
98 "EndedByGatekeeper", /// Gatekeeper has cleared call
99 "EndedByNoUser", /// Call failed as could not find user (in GK)
100 "EndedByNoBandwidth", /// Call failed as could not get enough bandwidth
101 "EndedByCapabilityExchange",/// Could not find common capabilities
102 "EndedByCallForwarded", /// Call was forwarded using FACILITY message
103 "EndedBySecurityDenial", /// Call failed a security check and was ended
104 "EndedByLocalBusy", /// Local endpoint busy
105 "EndedByLocalCongestion", /// Local endpoint congested
106 "EndedByRemoteBusy", /// Remote endpoint busy
107 "EndedByRemoteCongestion", /// Remote endpoint congested
108 "EndedByUnreachable", /// Could not reach the remote party
109 "EndedByNoEndPoint", /// The remote party is not running an endpoint
110 "EndedByOffline", /// The remote party is off line
111 "EndedByTemporaryFailure", /// The remote failed temporarily app may retry
112 "EndedByQ931Cause", /// The remote ended the call with unmapped Q.931 cause code
113 "EndedByDurationLimit", /// Call cleared due to an enforced duration limit
114 "EndedByInvalidConferenceID",/// Call cleared due to invalid conference ID
115 "EndedByNoDialTone", /// Call cleared due to missing dial tone
116 "EndedByNoRingBackTone", /// Call cleared due to missing ringback tone
117 "EndedByOutOfService", /// Call cleared because the line is out of service,
118 "EndedByAcceptingCallWaiting", /// Call cleared because another call is answered
119 "EndedByGkAdmissionFailed", /// Call cleared because gatekeeper admission request failed.
120 };
121 PAssert((PINDEX)(reason & 0xff) < PARRAYSIZE(names), "Invalid reason");
122 return out << names[reason & 0xff];
123 }
124
operator <<(ostream & o,OpalConnection::AnswerCallResponse s)125 ostream & operator<<(ostream & o, OpalConnection::AnswerCallResponse s)
126 {
127 static const char * const AnswerCallResponseNames[OpalConnection::NumAnswerCallResponses] = {
128 "AnswerCallNow",
129 "AnswerCallDenied",
130 "AnswerCallPending",
131 "AnswerCallDeferred",
132 "AnswerCallAlertWithMedia",
133 "AnswerCallDeferredWithMedia",
134 "AnswerCallProgress",
135 "AnswerCallNowAndReleaseCurrent"
136 };
137 if ((PINDEX)s >= PARRAYSIZE(AnswerCallResponseNames))
138 o << "InvalidAnswerCallResponse<" << (unsigned)s << '>';
139 else if (AnswerCallResponseNames[s] == NULL)
140 o << "AnswerCallResponse<" << (unsigned)s << '>';
141 else
142 o << AnswerCallResponseNames[s];
143 return o;
144 }
145
operator <<(ostream & o,OpalConnection::SendUserInputModes m)146 ostream & operator<<(ostream & o, OpalConnection::SendUserInputModes m)
147 {
148 static const char * const SendUserInputModeNames[OpalConnection::NumSendUserInputModes] = {
149 "SendUserInputAsQ931",
150 "SendUserInputAsString",
151 "SendUserInputAsTone",
152 "SendUserInputAsRFC2833",
153 "SendUserInputInBand",
154 "SendUserInputAsProtocolDefault"
155 };
156
157 if ((PINDEX)m >= PARRAYSIZE(SendUserInputModeNames))
158 o << "InvalidSendUserInputMode<" << (unsigned)m << '>';
159 else if (SendUserInputModeNames[m] == NULL)
160 o << "SendUserInputMode<" << (unsigned)m << '>';
161 else
162 o << SendUserInputModeNames[m];
163 return o;
164 }
165
166 #endif
167
168
169 static POrdinalToString::Initialiser const CallEndReasonStringsInitialiser[] = {
170 { OpalConnection::EndedByLocalUser, "Local party cleared call" },
171 { OpalConnection::EndedByNoAccept, "Local party did not accept call" },
172 { OpalConnection::EndedByAnswerDenied, "Local party declined to answer call" },
173 { OpalConnection::EndedByRemoteUser, "Remote party cleared call" },
174 { OpalConnection::EndedByRefusal, "Remote party refused call" },
175 { OpalConnection::EndedByNoAnswer, "Remote party did not answer in required time" },
176 { OpalConnection::EndedByCallerAbort, "Remote party stopped calling" },
177 { OpalConnection::EndedByTransportFail, "Call failed due to a transport error" },
178 { OpalConnection::EndedByConnectFail, "Connection to remote failed" },
179 { OpalConnection::EndedByGatekeeper, "Gatekeeper has cleared call" },
180 { OpalConnection::EndedByNoUser, "Call failed as could not find user" },
181 { OpalConnection::EndedByNoBandwidth, "Call failed due to insufficient bandwidth" },
182 { OpalConnection::EndedByCapabilityExchange, "Call failed as could not find common media capabilities" },
183 { OpalConnection::EndedByCallForwarded, "Call was forwarded" },
184 { OpalConnection::EndedBySecurityDenial, "Call failed security check" },
185 { OpalConnection::EndedByLocalBusy, "Local party busy" },
186 { OpalConnection::EndedByLocalCongestion, "Local party congested" },
187 { OpalConnection::EndedByRemoteBusy, "Remote party busy" },
188 { OpalConnection::EndedByRemoteCongestion, "Remote switch congested" },
189 { OpalConnection::EndedByUnreachable, "Remote party could not be reached" },
190 { OpalConnection::EndedByNoEndPoint, "Remote party application is not running" },
191 { OpalConnection::EndedByHostOffline, "Remote party host is off line" },
192 { OpalConnection::EndedByTemporaryFailure, "Remote system failed temporarily" },
193 { OpalConnection::EndedByQ931Cause, "Call cleared with Q.931 cause code %u" },
194 { OpalConnection::EndedByDurationLimit, "Call cleared due to an enforced duration limit" },
195 { OpalConnection::EndedByInvalidConferenceID, "Call cleared due to invalid conference ID" },
196 { OpalConnection::EndedByNoDialTone, "Call cleared due to missing dial tone" },
197 { OpalConnection::EndedByNoRingBackTone, "Call cleared due to missing ringback tone" },
198 { OpalConnection::EndedByOutOfService, "Call cleared because the line is out of service" },
199 { OpalConnection::EndedByAcceptingCallWaiting, "Call cleared because another call is answered" },
200 { OpalConnection::EndedByGkAdmissionFailed, "Call cleared because gatekeeper admission request failed." },
201 };
202
203 static POrdinalToString CallEndReasonStrings(PARRAYSIZE(CallEndReasonStringsInitialiser), CallEndReasonStringsInitialiser);
204
205
206 /////////////////////////////////////////////////////////////////////////////
207
OpalConnection(OpalCall & call,OpalEndPoint & ep,const PString & token,unsigned int options,OpalConnection::StringOptions * stringOptions)208 OpalConnection::OpalConnection(OpalCall & call,
209 OpalEndPoint & ep,
210 const PString & token,
211 unsigned int options,
212 OpalConnection::StringOptions * stringOptions)
213 : PSafeObject(&call) // Share the lock flag from the call
214 , ownerCall(call)
215 , endpoint(ep)
216 , m_phase(UninitialisedPhase)
217 , callToken(token)
218 , originating(PFalse)
219 , productInfo(ep.GetProductInfo())
220 , localPartyName(ep.GetDefaultLocalPartyName())
221 , displayName(ep.GetDefaultDisplayName())
222 , remotePartyName(token)
223 , callEndReason(NumCallEndReasons)
224 , silenceDetector(NULL)
225 #if OPAL_AEC
226 , echoCanceler(NULL)
227 #endif
228 #if OPAL_PTLIB_DTMF
229 , m_dtmfScaleMultiplier(1)
230 , m_dtmfScaleDivisor(1)
231 #ifdef _MSC_VER
232 #pragma warning(disable:4355)
233 #endif
234 , m_dtmfDetectNotifier(PCREATE_NOTIFIER(OnDetectInBandDTMF))
235 , m_sendInBandDTMF(true)
236 , m_emittedInBandDTMF(0)
237 #endif
238 , m_dtmfSendNotifier(PCREATE_NOTIFIER(OnSendInBandDTMF))
239 #if OPAL_HAS_MIXER
240 , m_recordAudioNotifier(PCREATE_NOTIFIER(OnRecordAudio))
241 #if OPAL_VIDEO
242 , m_recordVideoNotifier(PCREATE_NOTIFIER(OnRecordVideo))
243 #endif
244 #endif
245 #ifdef _MSC_VER
246 #pragma warning(default:4355)
247 #endif
248 #if OPAL_STATISTICS
249 , m_VideoUpdateRequestsSent(0)
250 #endif
251 #if OPAL_FAX
252 , m_faxMediaStreamsSwitchState(e_NotSwitchingFaxMediaStreams)
253 #endif
254 #if P_LUA
255 , m_luaScriptLoaded(false)
256 #endif
257 {
258 PTRACE(3, "OpalCon\tCreated connection " << *this);
259
260 PAssert(ownerCall.SafeReference(), PLogicError);
261
262 ownerCall.connectionsActive.Append(this);
263
264 if (stringOptions != NULL)
265 m_stringOptions = *stringOptions;
266
267 minAudioJitterDelay = endpoint.GetManager().GetMinAudioJitterDelay();
268 maxAudioJitterDelay = endpoint.GetManager().GetMaxAudioJitterDelay();
269 bandwidthAvailable = endpoint.GetInitialBandwidth();
270
271 #if OPAL_PTLIB_DTMF
272 switch (options&DetectInBandDTMFOptionMask) {
273 case DetectInBandDTMFOptionDisable :
274 m_detectInBandDTMF = false;
275 break;
276
277 case DetectInBandDTMFOptionEnable :
278 m_detectInBandDTMF = true;
279 break;
280
281 default :
282 m_detectInBandDTMF = !endpoint.GetManager().DetectInBandDTMFDisabled();
283 break;
284 }
285 #endif
286
287 switch (options & SendDTMFMask) {
288 case SendDTMFAsString:
289 sendUserInputMode = SendUserInputAsString;
290 break;
291 case SendDTMFAsTone:
292 sendUserInputMode = SendUserInputAsTone;
293 break;
294 case SendDTMFAsRFC2833:
295 sendUserInputMode = SendUserInputAsRFC2833;
296 break;
297 case SendDTMFAsDefault:
298 default:
299 sendUserInputMode = ep.GetSendUserInputMode();
300 break;
301 }
302
303 #if 0 // OPAL_HAS_IM
304 m_rfc4103Context[0].SetMediaFormat(OpalT140);
305 m_rfc4103Context[1].SetMediaFormat(OpalT140);
306 #endif
307
308 #if P_LUA
309 BindToInstance(m_lua, "connection");
310 m_lua.SetFunction("PTRACE", PLua::TraceFunction);
311 m_lua.SetValue("token", callToken);
312
313 PString str = GetPrefixName();
314 m_lua.SetValue("prefix", str);
315 m_lua.CallLuaFunction("OnConstruct");
316 #endif
317
318 m_phaseTime[UninitialisedPhase].SetCurrentTime();
319 }
320
~OpalConnection()321 OpalConnection::~OpalConnection()
322 {
323 mediaStreams.RemoveAll();
324
325 delete silenceDetector;
326 #if OPAL_AEC
327 delete echoCanceler;
328 #endif
329 #if OPAL_T120DATA
330 delete t120handler;
331 #endif
332
333 #if P_LUA
334 m_lua.CallLuaFunction("OnDestroy");
335 UnbindFromInstance(m_lua, "call");
336 #endif
337
338 ownerCall.connectionsActive.Remove(this);
339 ownerCall.SafeDereference();
340
341 PTRACE(3, "OpalCon\tConnection " << *this << " destroyed.");
342 }
343
344
PrintOn(ostream & strm) const345 void OpalConnection::PrintOn(ostream & strm) const
346 {
347 strm << ownerCall << '-'<< endpoint << '[' << callToken << ']';
348 }
349
350
GarbageCollection()351 bool OpalConnection::GarbageCollection()
352 {
353 return mediaStreams.DeleteObjectsToBeRemoved();
354 }
355
356
OnSetUpConnection()357 PBoolean OpalConnection::OnSetUpConnection()
358 {
359 PTRACE(3, "OpalCon\tOnSetUpConnection" << *this);
360 return endpoint.OnSetUpConnection(*this);
361 }
362
363
Hold(bool,bool)364 bool OpalConnection::Hold(bool /*fromRemote*/, bool /*placeOnHold*/)
365 {
366 return false;
367 }
368
369
IsOnHold(bool)370 PBoolean OpalConnection::IsOnHold(bool /*fromRemote*/)
371 {
372 return false;
373 }
374
375
OnHold(bool fromRemote,bool onHold)376 void OpalConnection::OnHold(bool fromRemote, bool onHold)
377 {
378 PTRACE(4, "OpalCon\tOnHold " << *this);
379 endpoint.OnHold(*this, fromRemote, onHold);
380 }
381
382
GetCallEndReasonText(CallEndReason reason)383 PString OpalConnection::GetCallEndReasonText(CallEndReason reason)
384 {
385 return psprintf(CallEndReasonStrings(reason.code), reason.q931);
386 }
387
388
SetCallEndReasonText(CallEndReasonCodes reasonCode,const PString & newText)389 void OpalConnection::SetCallEndReasonText(CallEndReasonCodes reasonCode, const PString & newText)
390 {
391 CallEndReasonStrings.SetAt(reasonCode, newText);
392 }
393
394
SetCallEndReason(CallEndReason reason)395 void OpalConnection::SetCallEndReason(CallEndReason reason)
396 {
397 PWaitAndSignal mutex(m_phaseMutex);
398
399 // Only set reason if not already set to something
400 if (callEndReason == NumCallEndReasons) {
401 PTRACE(3, "OpalCon\tCall end reason for " << *this << " set to " << reason);
402 callEndReason = reason;
403 ownerCall.SetCallEndReason(reason);
404 }
405 }
406
407
ClearCall(CallEndReason reason,PSyncPoint * sync)408 void OpalConnection::ClearCall(CallEndReason reason, PSyncPoint * sync)
409 {
410 SetCallEndReason(reason);
411 ownerCall.Clear(reason, sync);
412 }
413
414
ClearCallSynchronous(PSyncPoint * sync,CallEndReason reason)415 void OpalConnection::ClearCallSynchronous(PSyncPoint * sync, CallEndReason reason)
416 {
417 SetCallEndReason(reason);
418
419 PSyncPoint syncPoint;
420 if (sync == NULL)
421 sync = &syncPoint;
422
423 ClearCall(reason, sync);
424
425 PTRACE(5, "OpalCon\tSynchronous wait for " << *this);
426 sync->Wait();
427 }
428
429
TransferConnection(const PString & PTRACE_PARAM (remoteParty))430 bool OpalConnection::TransferConnection(const PString & PTRACE_PARAM(remoteParty))
431 {
432 PTRACE(2, "OpalCon\tCan not transfer connection to " << remoteParty);
433 return false;
434 }
435
436
Release(CallEndReason reason,bool synchronous)437 void OpalConnection::Release(CallEndReason reason, bool synchronous)
438 {
439 {
440 PWaitAndSignal mutex(m_phaseMutex);
441 if (IsReleased()) {
442 PTRACE(3, "OpalCon\tAlready released " << *this);
443 return;
444 }
445 SetPhase(ReleasingPhase);
446 SetCallEndReason(reason);
447 }
448
449 if (synchronous) {
450 PTRACE(3, "OpalCon\tReleasing synchronously " << *this);
451 OnReleased();
452 return;
453 }
454
455 PTRACE(3, "OpalCon\tReleasing asynchronously " << *this);
456
457 // Add a reference for the thread we are about to start
458 SafeReference();
459 PThread::Create(PCREATE_NOTIFIER(OnReleaseThreadMain), 0,
460 PThread::AutoDeleteThread,
461 PThread::NormalPriority,
462 "OnRelease");
463 }
464
465
OnReleaseThreadMain(PThread &,INT)466 void OpalConnection::OnReleaseThreadMain(PThread &, INT)
467 {
468 OnReleased();
469
470 PTRACE(4, "OpalCon\tOnRelease thread completed for " << *this);
471
472 // Dereference on the way out of the thread
473 SafeDereference();
474 }
475
476
OnReleased()477 void OpalConnection::OnReleased()
478 {
479 PTRACE(4, "OpalCon\tOnReleased " << *this);
480
481 CloseMediaStreams();
482
483 endpoint.OnReleased(*this);
484
485 SetPhase(ReleasedPhase);
486
487 #if PTRACING
488 static const int Level = 3;
489 if (PTrace::CanTrace(Level)) {
490 ostream & trace = PTrace::Begin(Level, __FILE__, __LINE__);
491 trace << "OpalCon\tConnection " << *this << " released\n"
492 " Initial Time: " << m_phaseTime[UninitialisedPhase] << '\n';
493 for (Phases ph = SetUpPhase; ph < NumPhases; ph = (Phases)(ph+1)) {
494 trace << setw(20) << ph << ": ";
495 if (m_phaseTime[ph].IsValid())
496 trace << (m_phaseTime[ph]-m_phaseTime[UninitialisedPhase]);
497 else
498 trace << "N/A";
499 trace << '\n';
500 }
501 trace << " Call end reason: " << callEndReason << '\n'
502 << PTrace::End;
503 }
504 #endif
505 }
506
507
OnIncomingConnection(unsigned options,OpalConnection::StringOptions * stringOptions)508 PBoolean OpalConnection::OnIncomingConnection(unsigned options, OpalConnection::StringOptions * stringOptions)
509 {
510 return endpoint.OnIncomingConnection(*this, options, stringOptions);
511 }
512
513
GetDestinationAddress()514 PString OpalConnection::GetDestinationAddress()
515 {
516 return '*';
517 }
518
519
ForwardCall(const PString &)520 PBoolean OpalConnection::ForwardCall(const PString & /*forwardParty*/)
521 {
522 return PFalse;
523 }
524
525
GetOtherPartyConnection() const526 PSafePtr<OpalConnection> OpalConnection::GetOtherPartyConnection() const
527 {
528 return GetCall().GetOtherPartyConnection(*this);
529 }
530
531
OnProceeding()532 void OpalConnection::OnProceeding()
533 {
534 endpoint.OnProceeding(*this);
535 }
536
OnAlerting()537 void OpalConnection::OnAlerting()
538 {
539 endpoint.OnAlerting(*this);
540 }
541
OnAnswerCall(const PString & callerName)542 OpalConnection::AnswerCallResponse OpalConnection::OnAnswerCall(const PString & callerName)
543 {
544 return endpoint.OnAnswerCall(*this, callerName);
545 }
546
547
AnsweringCall(AnswerCallResponse response)548 void OpalConnection::AnsweringCall(AnswerCallResponse response)
549 {
550 PTRACE(3, "OpalCon\tAnswering call: " << response);
551
552 PSafeLockReadWrite safeLock(*this);
553 // Can only answer call in UninitialisedPhase, SetUpPhase and AlertingPhase phases
554 if (!safeLock.IsLocked() || GetPhase() > AlertingPhase)
555 return;
556
557 switch (response) {
558 case AnswerCallDenied :
559 // If response is denied, abort the call
560 Release(EndedByAnswerDenied);
561 break;
562
563 case AnswerCallAlertWithMedia :
564 SetAlerting(GetLocalPartyName(), PTrue);
565 break;
566
567 case AnswerCallPending :
568 SetAlerting(GetLocalPartyName(), PFalse);
569 break;
570
571 case AnswerCallNow:
572 PTRACE(3, "OpalCon\tApplication has answered incoming call");
573 GetOtherPartyConnection()->OnConnectedInternal();
574 break;
575
576 default : // AnswerCallDeferred etc
577 break;
578 }
579 }
580
581
SetConnected()582 PBoolean OpalConnection::SetConnected()
583 {
584 PTRACE(3, "OpalCon\tSetConnected for " << *this);
585
586 if (GetPhase() < ConnectedPhase)
587 SetPhase(ConnectedPhase);
588
589 if (!mediaStreams.IsEmpty() && GetPhase() < EstablishedPhase) {
590 SetPhase(EstablishedPhase);
591 OnEstablished();
592 }
593
594 return true;
595 }
596
597
OnConnectedInternal()598 void OpalConnection::OnConnectedInternal()
599 {
600 if (GetPhase() < ConnectedPhase) {
601 SetPhase(ConnectedPhase);
602 OnConnected();
603 }
604
605 if (!mediaStreams.IsEmpty() && GetPhase() < EstablishedPhase) {
606 SetPhase(EstablishedPhase);
607 OnEstablished();
608 }
609 }
610
611
OnConnected()612 void OpalConnection::OnConnected()
613 {
614 PTRACE(3, "OpalCon\tOnConnected for " << *this);
615 endpoint.OnConnected(*this);
616 }
617
618
OnEstablished()619 void OpalConnection::OnEstablished()
620 {
621 PTRACE(3, "OpalCon\tOnEstablished " << *this);
622 ownerCall.StartMediaStreams();
623 endpoint.OnEstablished(*this);
624 }
625
626
OnTransferNotify(const PStringToString & info,const OpalConnection * transferringConnection)627 bool OpalConnection::OnTransferNotify(const PStringToString & info,
628 const OpalConnection * transferringConnection)
629 {
630 if (transferringConnection == this) {
631 PSafePtr<OpalConnection> otherConnection = GetOtherPartyConnection();
632 if (otherConnection != NULL)
633 otherConnection->OnTransferNotify(info, transferringConnection);
634 }
635
636 return endpoint.OnTransferNotify(*this, info);
637
638 }
639
640
AdjustMediaFormats(bool local,const OpalConnection * otherConnection,OpalMediaFormatList & mediaFormats) const641 void OpalConnection::AdjustMediaFormats(bool local,
642 const OpalConnection * otherConnection,
643 OpalMediaFormatList & mediaFormats) const
644 {
645 if (otherConnection != NULL)
646 return;
647
648 mediaFormats.Remove(m_stringOptions(OPAL_OPT_REMOVE_CODEC).Lines());
649
650 if (local) {
651 for (PINDEX i = 0; i < m_stringOptions.GetSize(); ++i) {
652 PString key = m_stringOptions.GetKeyAt(i);
653 PINDEX colon = key.Find(':');
654 if (colon != P_MAX_INDEX) {
655 PString fmtName = key.Left(colon);
656 PString optName = key.Mid(colon+1);
657 if (!fmtName.IsEmpty() && !optName.IsEmpty()) {
658 PString optValue = m_stringOptions.GetDataAt(i);
659 OpalMediaFormatList::const_iterator iterFormat;
660 while ((iterFormat = mediaFormats.FindFormat(fmtName, iterFormat)) != mediaFormats.end()) {
661 OpalMediaFormat & format = const_cast<OpalMediaFormat &>(*iterFormat);
662 if (format.SetOptionValue(optName, optValue)) {
663 PTRACE(4, "OpalCon\tSet media format " << format
664 << " option " << optName << " to \"" << optValue << '"');
665 }
666 else {
667 PTRACE(2, "OpalCon\tFailed to set media format " << format
668 << " option " << optName << " to \"" << optValue << '"');
669 }
670 }
671 }
672 }
673 }
674 }
675
676 endpoint.AdjustMediaFormats(local, *this, mediaFormats);
677 }
678
679
GetNextSessionID(const OpalMediaType &,bool)680 unsigned OpalConnection::GetNextSessionID(const OpalMediaType & /*mediaType*/, bool /*isSource*/)
681 {
682 return 0;
683 }
684
685
AutoStartMediaStreams(bool force)686 void OpalConnection::AutoStartMediaStreams(bool force)
687 {
688 OpalMediaTypeFactory::KeyList_T mediaTypes = OpalMediaType::GetList();
689 for (OpalMediaTypeFactory::KeyList_T::iterator iter = mediaTypes.begin(); iter != mediaTypes.end(); ++iter) {
690 OpalMediaType mediaType = *iter;
691 if ((GetAutoStart(mediaType)&OpalMediaType::Transmit) != 0 && (force || GetMediaStream(mediaType, true) == NULL))
692 ownerCall.OpenSourceMediaStreams(*this, mediaType, mediaType.GetDefinition()->GetDefaultSessionId());
693 }
694 StartMediaStreams();
695 }
696
697
698 #if OPAL_FAX
SwitchFaxMediaStreams(bool toT38)699 bool OpalConnection::SwitchFaxMediaStreams(bool toT38)
700 {
701 if (m_faxMediaStreamsSwitchState != e_NotSwitchingFaxMediaStreams) {
702 PTRACE(2, "OpalCon\tNested call to SwitchFaxMediaStreams on " << *this);
703 return false;
704 }
705
706 PTRACE(3, "OpalCon\tSwitchFaxMediaStreams to " << (toT38 ? "T.38" : "audio") << " on " << *this);
707 OpalMediaFormat format = toT38 ? OpalT38 : OpalG711uLaw;
708 if (!ownerCall.OpenSourceMediaStreams(*this, format.GetMediaType(), 1, format))
709 return false;
710
711 m_faxMediaStreamsSwitchState = toT38 ? e_SwitchingToFaxMediaStreams : e_SwitchingFromFaxMediaStreams;
712 return true;
713 }
714
715
OnSwitchedFaxMediaStreams(bool toT38,bool success)716 void OpalConnection::OnSwitchedFaxMediaStreams(bool toT38, bool success)
717 {
718 if (m_faxMediaStreamsSwitchState != e_NotSwitchingFaxMediaStreams) {
719 PTRACE(3, "OpalCon\tSwitch of media streams to "
720 << (toT38 ? "T.38" : "audio") << ' '
721 << (success ? "succeeded" : "failed")
722 << " on " << *this);
723
724 m_faxMediaStreamsSwitchState = e_NotSwitchingFaxMediaStreams;
725
726 PSafePtr<OpalConnection> other = GetOtherPartyConnection();
727 if (other != NULL)
728 other->OnSwitchedFaxMediaStreams(toT38, success);
729 }
730 }
731
732
OnSwitchingFaxMediaStreams(bool toT38)733 bool OpalConnection::OnSwitchingFaxMediaStreams(bool toT38)
734 {
735 PTRACE(3, "OpalCon\tRemote switch of media streams to " << (toT38 ? "T.38" : "audio") << " on " << *this);
736 return !toT38;
737 }
738 #endif
739
740
OpenMediaStream(const OpalMediaFormat & mediaFormat,unsigned sessionID,bool isSource)741 OpalMediaStreamPtr OpalConnection::OpenMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)
742 {
743 PSafeLockReadWrite safeLock(*this);
744 if (!safeLock.IsLocked())
745 return NULL;
746
747 // See if already opened
748 OpalMediaStreamPtr stream = GetMediaStream(sessionID, isSource);
749 if (stream != NULL && stream->IsOpen()) {
750 if (stream->GetMediaFormat() == mediaFormat) {
751 PTRACE(3, "OpalCon\tOpenMediaStream (already opened) for session " << sessionID << " on " << *this);
752 return stream;
753 }
754 // Changing the media format, needs to close and re-open the stream
755 stream->Close();
756 stream.SetNULL();
757 }
758
759 if (stream == NULL) {
760 stream = CreateMediaStream(mediaFormat, sessionID, isSource);
761 if (stream == NULL) {
762 PTRACE(1, "OpalCon\tCreateMediaStream returned NULL for session " << sessionID << " on " << *this);
763 return NULL;
764 }
765 mediaStreams.Append(stream);
766 }
767
768 if (stream->Open()) {
769 if (OnOpenMediaStream(*stream)) {
770 PTRACE(3, "OpalCon\tOpened " << (isSource ? "source" : "sink") << " stream " << stream->GetID() << " with format " << mediaFormat);
771 return stream;
772 }
773 PTRACE(2, "OpalCon\tOnOpenMediaStream failed for " << mediaFormat << ", closing " << *stream);
774 stream->Close();
775 }
776 else {
777 PTRACE(2, "OpalCon\tSource media stream open failed for " << *stream << " (" << mediaFormat << ')');
778 }
779
780 mediaStreams.Remove(stream);
781
782 return NULL;
783 }
784
785
CloseMediaStream(unsigned sessionId,bool source)786 bool OpalConnection::CloseMediaStream(unsigned sessionId, bool source)
787 {
788 OpalMediaStreamPtr stream = GetMediaStream(sessionId, source);
789 return stream != NULL && stream->IsOpen() && CloseMediaStream(*stream);
790 }
791
792
CloseMediaStream(OpalMediaStream & stream)793 bool OpalConnection::CloseMediaStream(OpalMediaStream & stream)
794 {
795 return stream.Close();
796 }
797
798
RemoveMediaStream(OpalMediaStream & stream)799 PBoolean OpalConnection::RemoveMediaStream(OpalMediaStream & stream)
800 {
801 stream.Close();
802 PTRACE(3, "OpalCon\tRemoved media stream " << stream);
803 return mediaStreams.Remove(&stream);
804 }
805
806
StartMediaStreams()807 void OpalConnection::StartMediaStreams()
808 {
809 for (OpalMediaStreamPtr mediaStream = mediaStreams; mediaStream != NULL; ++mediaStream)
810 mediaStream->Start();
811
812 PTRACE(3, "OpalCon\tMedia stream threads started for " << *this);
813 }
814
815
CloseMediaStreams()816 void OpalConnection::CloseMediaStreams()
817 {
818 // Do this double loop as while closing streams, the instance may disappear from the
819 // mediaStreams list, prematurely stopping the for loop.
820 bool someOpen = true;
821 while (someOpen) {
822 someOpen = false;
823 for (OpalMediaStreamPtr mediaStream(mediaStreams, PSafeReference); mediaStream != NULL; ++mediaStream) {
824 if (mediaStream->IsOpen()) {
825 someOpen = true;
826 CloseMediaStream(*mediaStream);
827 }
828 }
829 }
830
831 PTRACE(3, "OpalCon\tMedia streams closed.");
832 }
833
834
PauseMediaStreams(bool paused)835 void OpalConnection::PauseMediaStreams(bool paused)
836 {
837 for (OpalMediaStreamPtr mediaStream = mediaStreams; mediaStream != NULL; ++mediaStream)
838 mediaStream->SetPaused(paused);
839 }
840
841
OnPauseMediaStream(OpalMediaStream &,bool)842 void OpalConnection::OnPauseMediaStream(OpalMediaStream & /*strm*/, bool /*paused*/)
843 {
844 }
845
846
847 #if OPAL_VIDEO
CreateMediaStream(const OpalMediaFormat & mediaFormat,unsigned sessionID,PBoolean isSource)848 OpalMediaStream * OpalConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat,
849 unsigned sessionID,
850 PBoolean isSource)
851 {
852 if (mediaFormat.GetMediaType() == OpalMediaType::Video()) {
853 if (isSource) {
854 PVideoInputDevice * videoDevice;
855 PBoolean autoDeleteGrabber;
856 if (CreateVideoInputDevice(mediaFormat, videoDevice, autoDeleteGrabber)) {
857 PTRACE(4, "OpalCon\tCreated capture device \"" << videoDevice->GetDeviceName() << '"');
858
859 PVideoOutputDevice * previewDevice;
860 PBoolean autoDeletePreview;
861 if (CreateVideoOutputDevice(mediaFormat, PTrue, previewDevice, autoDeletePreview))
862 PTRACE(4, "OpalCon\tCreated preview device \"" << previewDevice->GetDeviceName() << '"');
863 else
864 previewDevice = NULL;
865
866 return new OpalVideoMediaStream(*this, mediaFormat, sessionID, videoDevice, previewDevice, autoDeleteGrabber, autoDeletePreview);
867 }
868 }
869 else {
870 PVideoOutputDevice * videoDevice;
871 PBoolean autoDelete;
872 if (CreateVideoOutputDevice(mediaFormat, PFalse, videoDevice, autoDelete)) {
873 PTRACE(4, "OpalCon\tCreated display device \"" << videoDevice->GetDeviceName() << '"');
874 return new OpalVideoMediaStream(*this, mediaFormat, sessionID, NULL, videoDevice, false, autoDelete);
875 }
876 }
877 }
878
879 return NULL;
880 }
881 #else
CreateMediaStream(const OpalMediaFormat &,unsigned,PBoolean)882 OpalMediaStream * OpalConnection::CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean)
883 {
884 return NULL;
885 }
886 #endif
887
888
OnOpenMediaStream(OpalMediaStream & stream)889 PBoolean OpalConnection::OnOpenMediaStream(OpalMediaStream & stream)
890 {
891 if (!endpoint.OnOpenMediaStream(*this, stream))
892 return PFalse;
893
894 if (!LockReadWrite())
895 return PFalse;
896
897 if (GetPhase() == ConnectedPhase) {
898 SetPhase(EstablishedPhase);
899 OnEstablished();
900 }
901
902 UnlockReadWrite();
903
904 return PTrue;
905 }
906
907
OnClosedMediaStream(const OpalMediaStream & stream)908 void OpalConnection::OnClosedMediaStream(const OpalMediaStream & stream)
909 {
910 OpalMediaPatch * patch = stream.GetPatch();
911 if (patch != NULL) {
912 #if OPAL_HAS_MIXER
913 OnStopRecording(patch);
914 #endif
915
916 if (silenceDetector != NULL && patch->RemoveFilter(silenceDetector->GetReceiveHandler(), m_filterMediaFormat)) {
917 PTRACE(4, "OpalCon\tRemoved silence detect filter on connection " << *this << ", patch " << patch);
918 }
919
920 #if OPAL_AEC
921 if (echoCanceler != NULL && patch->RemoveFilter(stream.IsSource() ? echoCanceler->GetReceiveHandler()
922 : echoCanceler->GetSendHandler(), m_filterMediaFormat)) {
923 PTRACE(4, "OpalCon\tRemoved echo canceler filter on connection " << *this << ", patch " << patch);
924 }
925 #endif
926
927 #if OPAL_PTLIB_DTMF
928 if (patch->RemoveFilter(m_dtmfDetectNotifier, OpalPCM16)) {
929 PTRACE(4, "OpalCon\tRemoved detect DTMF filter on connection " << *this << ", patch " << patch);
930 }
931 if (!m_dtmfSendFormat.IsEmpty() && patch->RemoveFilter(m_dtmfSendNotifier, m_dtmfSendFormat)) {
932 PTRACE(4, "OpalCon\tRemoved DTMF send filter on connection " << *this << ", patch " << patch);
933 }
934 #endif
935 }
936
937 endpoint.OnClosedMediaStream(stream);
938 }
939
940 #if OPAL_HAS_MIXER
941
MakeRecordingKey(const OpalMediaPatch & patch)942 static PString MakeRecordingKey(const OpalMediaPatch & patch)
943 {
944 return psprintf("%08x", &patch);
945 }
946
947
OnStartRecording(OpalMediaPatch * patch)948 void OpalConnection::OnStartRecording(OpalMediaPatch * patch)
949 {
950 if (patch == NULL)
951 return;
952
953 if (!ownerCall.OnStartRecording(MakeRecordingKey(*patch), patch->GetSource().GetMediaFormat())) {
954 PTRACE(4, "OpalCon\tNo record filter added on connection " << *this << ", patch " << *patch);
955 return;
956 }
957
958 patch->AddFilter(m_recordAudioNotifier, OpalPCM16);
959 #if OPAL_VIDEO
960 patch->AddFilter(m_recordVideoNotifier, OPAL_YUV420P);
961 #endif
962
963 PTRACE(4, "OpalCon\tAdded record filter on connection " << *this << ", patch " << *patch);
964 }
965
966
OnStopRecording(OpalMediaPatch * patch)967 void OpalConnection::OnStopRecording(OpalMediaPatch * patch)
968 {
969 if (patch == NULL)
970 return;
971
972 ownerCall.OnStopRecording(MakeRecordingKey(*patch));
973
974 patch->RemoveFilter(m_recordAudioNotifier, OpalPCM16);
975 #if OPAL_VIDEO
976 patch->RemoveFilter(m_recordVideoNotifier, OPAL_YUV420P);
977 #endif
978
979 PTRACE(4, "OpalCon\tRemoved record filter on " << *patch);
980 }
981
982 #endif
983
OnPatchMediaStream(PBoolean isSource,OpalMediaPatch & patch)984 void OpalConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)
985 {
986 OpalMediaFormat mediaFormat = isSource ? patch.GetSource().GetMediaFormat() : patch.GetSink()->GetMediaFormat();
987
988 if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) {
989 if (!mediaFormat.IsTransportable()) {
990 m_filterMediaFormat = mediaFormat;
991
992 if (isSource && silenceDetector != NULL) {
993 silenceDetector->SetParameters(endpoint.GetManager().GetSilenceDetectParams(), mediaFormat.GetClockRate());
994 patch.AddFilter(silenceDetector->GetReceiveHandler(), mediaFormat);
995 PTRACE(4, "OpalCon\tAdded silence detect filter on connection " << *this << ", patch " << patch);
996 }
997
998 #if OPAL_AEC
999 if (echoCanceler) {
1000 echoCanceler->SetParameters(endpoint.GetManager().GetEchoCancelParams());
1001 echoCanceler->SetClockRate(mediaFormat.GetClockRate());
1002 patch.AddFilter(isSource ? echoCanceler->GetReceiveHandler()
1003 : echoCanceler->GetSendHandler(), mediaFormat);
1004 PTRACE(4, "OpalCon\tAdded echo canceler filter on connection " << *this << ", patch " << patch);
1005 }
1006 #endif
1007 }
1008
1009 #if OPAL_PTLIB_DTMF
1010 if (m_detectInBandDTMF && isSource) {
1011 patch.AddFilter(m_dtmfDetectNotifier, OpalPCM16);
1012 PTRACE(4, "OpalCon\tAdded detect DTMF filter on connection " << *this << ", patch " << patch);
1013 }
1014
1015 if (m_sendInBandDTMF && !isSource) {
1016 if (mediaFormat == OpalG711_ULAW_64K || mediaFormat == OpalG711_ALAW_64K)
1017 m_dtmfSendFormat = mediaFormat;
1018 else
1019 m_dtmfSendFormat = OpalPCM16;
1020 patch.AddFilter(m_dtmfSendNotifier, mediaFormat);
1021 PTRACE(4, "OpalCon\tAdded send DTMF filter on connection " << *this << ", patch " << patch);
1022 }
1023 #endif
1024 }
1025
1026 #if OPAL_HAS_MIXER
1027 if (!m_recordingFilename.IsEmpty())
1028 ownerCall.StartRecording(m_recordingFilename);
1029 else if (ownerCall.IsRecording())
1030 OnStartRecording(&patch);
1031 #endif
1032
1033 PTRACE(3, "OpalCon\t" << (isSource ? "Source" : "Sink") << " stream of connection " << *this << " uses patch " << patch);
1034 }
1035
1036
1037 #if OPAL_HAS_MIXER
EnableRecording()1038 void OpalConnection::EnableRecording()
1039 {
1040 OpalMediaStreamPtr stream = GetMediaStream(OpalMediaType::Audio(), true);
1041 if (stream != NULL)
1042 OnStartRecording(stream->GetPatch());
1043
1044 stream = GetMediaStream(OpalMediaType::Video(), true);
1045 if (stream != NULL)
1046 OnStartRecording(stream->GetPatch());
1047 }
1048
1049
DisableRecording()1050 void OpalConnection::DisableRecording()
1051 {
1052 OpalMediaStreamPtr stream = GetMediaStream(OpalMediaType::Audio(), true);
1053 if (stream != NULL)
1054 OnStopRecording(stream->GetPatch());
1055
1056 stream = GetMediaStream(OpalMediaType::Video(), true);
1057 if (stream != NULL)
1058 OnStopRecording(stream->GetPatch());
1059 }
1060
1061
OnRecordAudio(RTP_DataFrame & frame,INT param)1062 void OpalConnection::OnRecordAudio(RTP_DataFrame & frame, INT param)
1063 {
1064 const OpalMediaPatch * patch = (const OpalMediaPatch *)param;
1065 ownerCall.OnRecordAudio(MakeRecordingKey(*patch), frame);
1066 }
1067
1068
1069 #if OPAL_VIDEO
1070
OnRecordVideo(RTP_DataFrame & frame,INT param)1071 void OpalConnection::OnRecordVideo(RTP_DataFrame & frame, INT param)
1072 {
1073 const OpalMediaPatch * patch = (const OpalMediaPatch *)param;
1074 ownerCall.OnRecordVideo(MakeRecordingKey(*patch), frame);
1075 }
1076
1077 #endif
1078
1079 #endif
1080
1081
AttachRFC2833HandlerToPatch(PBoolean,OpalMediaPatch &)1082 void OpalConnection::AttachRFC2833HandlerToPatch(PBoolean /*isSource*/, OpalMediaPatch & /*patch*/)
1083 {
1084 }
1085
1086
GetMediaStream(const PString & streamID,bool source) const1087 OpalMediaStreamPtr OpalConnection::GetMediaStream(const PString & streamID, bool source) const
1088 {
1089 for (PSafePtr<OpalMediaStream> mediaStream(mediaStreams, PSafeReference); mediaStream != NULL; ++mediaStream) {
1090 if ((streamID.IsEmpty() || mediaStream->GetID() == streamID) && mediaStream->IsSource() == source)
1091 return mediaStream;
1092 }
1093
1094 return NULL;
1095 }
1096
1097
GetMediaStream(unsigned sessionId,bool source) const1098 OpalMediaStreamPtr OpalConnection::GetMediaStream(unsigned sessionId, bool source) const
1099 {
1100 for (OpalMediaStreamPtr mediaStream(mediaStreams, PSafeReference); mediaStream != NULL; ++mediaStream) {
1101 if (mediaStream->GetSessionID() == sessionId && mediaStream->IsSource() == source)
1102 return mediaStream;
1103 }
1104
1105 return NULL;
1106 }
1107
1108
GetMediaStream(const OpalMediaType & mediaType,bool source,OpalMediaStreamPtr mediaStream) const1109 OpalMediaStreamPtr OpalConnection::GetMediaStream(const OpalMediaType & mediaType,
1110 bool source,
1111 OpalMediaStreamPtr mediaStream) const
1112 {
1113 if (mediaStream == NULL)
1114 mediaStream = OpalMediaStreamPtr(mediaStreams, PSafeReference);
1115 else
1116 ++mediaStream;
1117
1118 while (mediaStream != NULL) {
1119 if ((mediaType.empty() || mediaStream->GetMediaFormat().GetMediaType() == mediaType) && mediaStream->IsSource() == source)
1120 return mediaStream;
1121 ++mediaStream;
1122 }
1123
1124 return NULL;
1125 }
1126
1127
IsMediaBypassPossible(unsigned) const1128 PBoolean OpalConnection::IsMediaBypassPossible(unsigned /*sessionID*/) const
1129 {
1130 PTRACE(4, "OpalCon\tIsMediaBypassPossible: default returns false");
1131 return false;
1132 }
1133
1134
1135 #if OPAL_VIDEO
1136
CreateVideoInputDevice(const OpalMediaFormat & mediaFormat,PVideoInputDevice * & device,PBoolean & autoDelete)1137 PBoolean OpalConnection::CreateVideoInputDevice(const OpalMediaFormat & mediaFormat,
1138 PVideoInputDevice * & device,
1139 PBoolean & autoDelete)
1140 {
1141 return endpoint.CreateVideoInputDevice(*this, mediaFormat, device, autoDelete);
1142 }
1143
1144
CreateVideoOutputDevice(const OpalMediaFormat & mediaFormat,PBoolean preview,PVideoOutputDevice * & device,PBoolean & autoDelete)1145 PBoolean OpalConnection::CreateVideoOutputDevice(const OpalMediaFormat & mediaFormat,
1146 PBoolean preview,
1147 PVideoOutputDevice * & device,
1148 PBoolean & autoDelete)
1149 {
1150 return endpoint.CreateVideoOutputDevice(*this, mediaFormat, preview, device, autoDelete);
1151 }
1152
1153
SendVideoUpdatePicture(unsigned sessionID,bool force) const1154 bool OpalConnection::SendVideoUpdatePicture(unsigned sessionID, bool force) const
1155 {
1156 if (IsReleased())
1157 return false;
1158
1159 OpalMediaStreamPtr stream = sessionID != 0 ? GetMediaStream(sessionID, false)
1160 : GetMediaStream(OpalMediaType::Video(), false);
1161 if (stream == NULL) {
1162 PTRACE(3, "OpalCon\tNo video stream do video update picture in connection " << *this);
1163 return false;
1164 }
1165
1166 PTRACE(3, "OpalCon\tVideo update picture (I-Frame) requested in video stream " << *stream << " on " << *this);
1167 if (force)
1168 stream->ExecuteCommand(OpalVideoUpdatePicture());
1169 else
1170 stream->ExecuteCommand(OpalVideoPictureLoss());
1171
1172 return true;
1173 }
1174
1175 #endif // OPAL_VIDEO
1176
1177
SetAudioVolume(PBoolean,unsigned)1178 PBoolean OpalConnection::SetAudioVolume(PBoolean /*source*/, unsigned /*percentage*/)
1179 {
1180 return PFalse;
1181 }
1182
1183
GetAudioVolume(PBoolean,unsigned &)1184 PBoolean OpalConnection::GetAudioVolume(PBoolean /*source*/, unsigned & /*percentage*/)
1185 {
1186 return PFalse;
1187 }
1188
1189
SetAudioMute(bool,bool)1190 bool OpalConnection::SetAudioMute(bool /*source*/, bool /*mute*/)
1191 {
1192 return false;
1193 }
1194
1195
GetAudioMute(bool,bool &)1196 bool OpalConnection::GetAudioMute(bool /*source*/, bool & /*mute*/)
1197 {
1198 return false;
1199 }
1200
1201
GetAudioSignalLevel(PBoolean)1202 unsigned OpalConnection::GetAudioSignalLevel(PBoolean /*source*/)
1203 {
1204 return UINT_MAX;
1205 }
1206
SetBandwidthAvailable(unsigned newBandwidth,PBoolean force)1207 PBoolean OpalConnection::SetBandwidthAvailable(unsigned newBandwidth, PBoolean force)
1208 {
1209 PTRACE(3, "OpalCon\tSetting bandwidth to " << newBandwidth << "00b/s on connection " << *this);
1210
1211 unsigned used = GetBandwidthUsed();
1212 if (used > newBandwidth) {
1213 if (!force)
1214 return PFalse;
1215
1216 #if 0
1217 // Go through media channels and close down some.
1218 PINDEX chanIdx = GetmediaStreams->GetSize();
1219 while (used > newBandwidth && chanIdx-- > 0) {
1220 OpalChannel * channel = logicalChannels->GetChannelAt(chanIdx);
1221 if (channel != NULL) {
1222 used -= channel->GetBandwidthUsed();
1223 const H323ChannelNumber & number = channel->GetNumber();
1224 CloseLogicalChannel(number, number.IsFromRemote());
1225 }
1226 }
1227 #endif
1228 }
1229
1230 bandwidthAvailable = newBandwidth - used;
1231 return PTrue;
1232 }
1233
1234
GetBandwidthUsed() const1235 unsigned OpalConnection::GetBandwidthUsed() const
1236 {
1237 unsigned used = 0;
1238
1239 #if 0
1240 for (PINDEX i = 0; i < logicalChannels->GetSize(); i++) {
1241 OpalChannel * channel = logicalChannels->GetChannelAt(i);
1242 if (channel != NULL)
1243 used += channel->GetBandwidthUsed();
1244 }
1245 #endif
1246
1247 PTRACE(3, "OpalCon\tBandwidth used is "
1248 << used << "00b/s for " << *this);
1249
1250 return used;
1251 }
1252
1253
SetBandwidthUsed(unsigned releasedBandwidth,unsigned requiredBandwidth)1254 PBoolean OpalConnection::SetBandwidthUsed(unsigned releasedBandwidth,
1255 unsigned requiredBandwidth)
1256 {
1257 PTRACE_IF(3, releasedBandwidth > 0, "OpalCon\tBandwidth release of "
1258 << releasedBandwidth/10 << '.' << releasedBandwidth%10 << "kb/s");
1259
1260 bandwidthAvailable += releasedBandwidth;
1261
1262 PTRACE_IF(3, requiredBandwidth > 0, "OpalCon\tBandwidth request of "
1263 << requiredBandwidth/10 << '.' << requiredBandwidth%10
1264 << "kb/s, available: "
1265 << bandwidthAvailable/10 << '.' << bandwidthAvailable%10
1266 << "kb/s");
1267
1268 if (requiredBandwidth > bandwidthAvailable) {
1269 PTRACE(2, "OpalCon\tAvailable bandwidth exceeded on " << *this);
1270 return PFalse;
1271 }
1272
1273 bandwidthAvailable -= requiredBandwidth;
1274
1275 return PTrue;
1276 }
1277
SetSendUserInputMode(SendUserInputModes mode)1278 void OpalConnection::SetSendUserInputMode(SendUserInputModes mode)
1279 {
1280 PTRACE(3, "OPAL\tSetting default User Input send mode to " << mode);
1281 sendUserInputMode = mode;
1282 }
1283
SendUserInputString(const PString & value)1284 PBoolean OpalConnection::SendUserInputString(const PString & value)
1285 {
1286 for (const char * c = value; *c != '\0'; c++) {
1287 if (!SendUserInputTone(*c, 0))
1288 return PFalse;
1289 }
1290 return PTrue;
1291 }
1292
1293
1294 #if OPAL_PTLIB_DTMF
SendUserInputTone(char tone,unsigned duration)1295 PBoolean OpalConnection::SendUserInputTone(char tone, unsigned duration)
1296 {
1297 if (m_dtmfSendFormat.IsEmpty())
1298 return false;
1299
1300 if (duration <= 0)
1301 duration = PDTMFEncoder::DefaultToneLen;
1302
1303 PTRACE(3, "OPAL\tSending in-band DTMF tone '" << tone << "', duration=" << duration);
1304
1305 PDTMFEncoder dtmfSamples;
1306 dtmfSamples.AddTone(tone, duration);
1307 PINDEX size = dtmfSamples.GetSize();
1308
1309 m_inBandMutex.Wait();
1310
1311 switch (m_dtmfSendFormat.GetPayloadType()) {
1312 case RTP_DataFrame::PCMU :
1313 if (m_inBandDTMF.SetSize(size)) {
1314 for (PINDEX i = 0; i < size; ++i)
1315 m_inBandDTMF[i] = (BYTE)Opal_PCM_G711_uLaw::ConvertSample(dtmfSamples[i]);
1316 }
1317 break;
1318
1319 case RTP_DataFrame::PCMA :
1320 if (m_inBandDTMF.SetSize(size)) {
1321 for (PINDEX i = 0; i < size; ++i)
1322 m_inBandDTMF[i] = (BYTE)Opal_PCM_G711_ALaw::ConvertSample(dtmfSamples[i]);
1323 }
1324 break;
1325
1326 default :
1327 size *= sizeof(short);
1328 if (m_inBandDTMF.SetSize(size))
1329 memcpy(m_inBandDTMF.GetPointer(), dtmfSamples, size);
1330 }
1331
1332 m_inBandMutex.Signal();
1333
1334 return true;
1335 }
1336 #else
SendUserInputTone(char,unsigned)1337 PBoolean OpalConnection::SendUserInputTone(char /*tone*/, unsigned /*duration*/)
1338 {
1339 return false;
1340 }
1341 #endif
1342
1343
OnUserInputString(const PString & value)1344 void OpalConnection::OnUserInputString(const PString & value)
1345 {
1346 endpoint.OnUserInputString(*this, value);
1347 }
1348
1349
OnUserInputTone(char tone,unsigned duration)1350 void OpalConnection::OnUserInputTone(char tone, unsigned duration)
1351 {
1352 endpoint.OnUserInputTone(*this, tone, duration);
1353 }
1354
1355
GetUserInput(unsigned timeout)1356 PString OpalConnection::GetUserInput(unsigned timeout)
1357 {
1358 PString reply;
1359 if (userInputAvailable.Wait(PTimeInterval(0, timeout)) && !IsReleased() && LockReadWrite()) {
1360 reply = userInputString;
1361 userInputString = PString();
1362 UnlockReadWrite();
1363 }
1364 return reply;
1365 }
1366
1367
SetUserInput(const PString & input)1368 void OpalConnection::SetUserInput(const PString & input)
1369 {
1370 if (LockReadWrite()) {
1371 userInputString += input;
1372 userInputAvailable.Signal();
1373 UnlockReadWrite();
1374 }
1375 }
1376
1377
ReadUserInput(const char * terminators,unsigned lastDigitTimeout,unsigned firstDigitTimeout)1378 PString OpalConnection::ReadUserInput(const char * terminators,
1379 unsigned lastDigitTimeout,
1380 unsigned firstDigitTimeout)
1381 {
1382 return endpoint.ReadUserInput(*this, terminators, lastDigitTimeout, firstDigitTimeout);
1383 }
1384
1385
PromptUserInput(PBoolean)1386 PBoolean OpalConnection::PromptUserInput(PBoolean /*play*/)
1387 {
1388 return PTrue;
1389 }
1390
1391
1392 #if OPAL_PTLIB_DTMF
OnDetectInBandDTMF(RTP_DataFrame & frame,INT)1393 void OpalConnection::OnDetectInBandDTMF(RTP_DataFrame & frame, INT)
1394 {
1395 // This function is set up as an 'audio filter'.
1396 // This allows us to access the 16 bit PCM audio (at 8Khz sample rate)
1397 // before the audio is passed on to the sound card (or other output device)
1398
1399 // Pass the 16 bit PCM audio through the DTMF decoder
1400 PString tones = m_dtmfDecoder.Decode((const short *)frame.GetPayloadPtr(),
1401 frame.GetPayloadSize()/sizeof(short),
1402 m_dtmfScaleMultiplier,
1403 m_dtmfScaleDivisor);
1404 if (!tones.IsEmpty()) {
1405 PTRACE(3, "OPAL\tDTMF detected: \"" << tones << '"');
1406 for (PINDEX i = 0; i < tones.GetLength(); i++)
1407 OnUserInputTone(tones[i], PDTMFDecoder::DetectTime);
1408 }
1409 }
1410
OnSendInBandDTMF(RTP_DataFrame & frame,INT)1411 void OpalConnection::OnSendInBandDTMF(RTP_DataFrame & frame, INT)
1412 {
1413 if (m_inBandDTMF.IsEmpty())
1414 return;
1415
1416 // Can't do the usual LockReadWrite() as deadlocks
1417 m_inBandMutex.Wait();
1418
1419 PINDEX bytes = m_inBandDTMF.GetSize() - m_emittedInBandDTMF;
1420 if (bytes > frame.GetPayloadSize())
1421 bytes = frame.GetPayloadSize();
1422 memcpy(frame.GetPayloadPtr(), &m_inBandDTMF[m_emittedInBandDTMF], bytes);
1423
1424 m_emittedInBandDTMF += bytes;
1425
1426 if (m_emittedInBandDTMF >= m_inBandDTMF.GetSize()) {
1427 PTRACE(4, "OPAL\tSent in-band DTMF tone, " << m_inBandDTMF.GetSize() << " bytes");
1428 m_inBandDTMF.SetSize(0);
1429 m_emittedInBandDTMF = 0;
1430 }
1431
1432 m_inBandMutex.Signal();
1433 }
1434 #endif
1435
1436
GetPrefixName() const1437 PString OpalConnection::GetPrefixName() const
1438 {
1439 return endpoint.GetPrefixName();
1440 }
1441
1442
SetLocalPartyName(const PString & name)1443 void OpalConnection::SetLocalPartyName(const PString & name)
1444 {
1445 localPartyName = name;
1446 }
1447
1448
GetLocalPartyURL() const1449 PString OpalConnection::GetLocalPartyURL() const
1450 {
1451 return GetPrefixName() + ':' + PURL::TranslateString(GetLocalPartyName(), PURL::LoginTranslation);
1452 }
1453
1454
IsPresentationBlocked() const1455 bool OpalConnection::IsPresentationBlocked() const
1456 {
1457 return m_stringOptions.GetBoolean(OPAL_OPT_PRESENTATION_BLOCK);
1458 }
1459
1460
MakeURL(const PString & prefix,const PString & partyName)1461 static PString MakeURL(const PString & prefix, const PString & partyName)
1462 {
1463 if (partyName.IsEmpty())
1464 return PString::Empty();
1465
1466 PINDEX colon = partyName.Find(':');
1467 if (colon != P_MAX_INDEX && partyName.FindSpan("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") == colon)
1468 return partyName;
1469
1470 PStringStream url;
1471 url << prefix << ':' << partyName;
1472 return url;
1473 }
1474
1475
GetRemotePartyURL() const1476 PString OpalConnection::GetRemotePartyURL() const
1477 {
1478 if (!remotePartyURL.IsEmpty())
1479 return remotePartyURL;
1480
1481 return MakeURL(GetPrefixName(), GetRemotePartyAddress());
1482 }
1483
1484
GetCalledPartyURL()1485 PString OpalConnection::GetCalledPartyURL()
1486 {
1487 return MakeURL(GetPrefixName(), GetDestinationAddress());
1488 }
1489
1490
CopyPartyNames(const OpalConnection & other)1491 void OpalConnection::CopyPartyNames(const OpalConnection & other)
1492 {
1493 remotePartyName = other.remotePartyName;
1494 remotePartyNumber = other.remotePartyNumber;
1495 remotePartyAddress = other.remotePartyAddress;
1496 remotePartyURL = other.remotePartyURL;
1497 m_calledPartyName = other.m_calledPartyName;
1498 m_calledPartyNumber = other.m_calledPartyNumber;
1499 remoteProductInfo = other.remoteProductInfo;
1500 }
1501
1502
GetAlertingType() const1503 PString OpalConnection::GetAlertingType() const
1504 {
1505 return PString::Empty();
1506 }
1507
1508
SetAlertingType(const PString &)1509 bool OpalConnection::SetAlertingType(const PString & /*info*/)
1510 {
1511 return false;
1512 }
1513
1514
GetCallInfo() const1515 PString OpalConnection::GetCallInfo() const
1516 {
1517 return PString::Empty();
1518 }
1519
1520
SetAudioJitterDelay(unsigned minDelay,unsigned maxDelay)1521 void OpalConnection::SetAudioJitterDelay(unsigned minDelay, unsigned maxDelay)
1522 {
1523 maxDelay = PMAX(10, PMIN(maxDelay, 999));
1524 minDelay = PMAX(10, PMIN(minDelay, 999));
1525
1526 if (maxDelay < minDelay)
1527 maxDelay = minDelay;
1528
1529 minAudioJitterDelay = minDelay;
1530 maxAudioJitterDelay = maxDelay;
1531 }
1532
1533
GetIdentifier() const1534 PString OpalConnection::GetIdentifier() const
1535 {
1536 return GetToken();
1537 }
1538
1539
GetMaxRtpPayloadSize() const1540 PINDEX OpalConnection::GetMaxRtpPayloadSize() const
1541 {
1542 return endpoint.GetManager().GetMaxRtpPayloadSize();
1543 }
1544
1545
SetPhase(Phases phaseToSet)1546 void OpalConnection::SetPhase(Phases phaseToSet)
1547 {
1548 PTRACE(3, "OpalCon\tSetPhase from " << m_phase << " to " << phaseToSet << " for " << *this);
1549
1550 PWaitAndSignal mutex(m_phaseMutex);
1551
1552 // With next few lines we will prevent phase to ever go down when it
1553 // reaches ReleasingPhase - end result - once you call Release you never
1554 // go back.
1555 if (m_phase < ReleasingPhase || (m_phase == ReleasingPhase && phaseToSet == ReleasedPhase)) {
1556 m_phase = phaseToSet;
1557 if (!m_phaseTime[m_phase].IsValid())
1558 m_phaseTime[m_phase].SetCurrentTime();
1559 }
1560 }
1561
1562
SetStringOptions(const StringOptions & options,bool overwrite)1563 void OpalConnection::SetStringOptions(const StringOptions & options, bool overwrite)
1564 {
1565 if (overwrite)
1566 m_stringOptions = options;
1567 else {
1568 for (PINDEX i = 0; i < options.GetSize(); ++i)
1569 m_stringOptions.SetAt(options.GetKeyAt(i), options.GetDataAt(i));
1570 }
1571
1572 OnApplyStringOptions();
1573 }
1574
OnApplyStringOptions()1575 void OpalConnection::OnApplyStringOptions()
1576 {
1577 endpoint.GetManager().OnApplyStringOptions(*this, m_stringOptions);
1578
1579 PTRACE_IF(4, !m_stringOptions.IsEmpty(), "OpalCon\tApplying string options:\n" << m_stringOptions);
1580
1581 if (LockReadWrite()) {
1582 PCaselessString str;
1583
1584 str = m_stringOptions(IsOriginating() ? OPAL_OPT_CALLING_PARTY_NAME : OPAL_OPT_CALLED_PARTY_NAME);
1585 if (!str.IsEmpty())
1586 SetLocalPartyName(str);
1587
1588 // Allow for explicitly haveing empty string for display name
1589 str = IsOriginating() ? OPAL_OPT_CALLING_DISPLAY_NAME : OPAL_OPT_CALLED_DISPLAY_NAME;
1590 if (m_stringOptions.Contains(str))
1591 SetDisplayName(m_stringOptions[str]);
1592
1593 str = m_stringOptions(OPAL_OPT_USER_INPUT_MODE);
1594 if (str == "RFC2833")
1595 SetSendUserInputMode(SendUserInputAsRFC2833);
1596 else if (str == "String")
1597 SetSendUserInputMode(SendUserInputAsString);
1598 else if (str == "Tone")
1599 SetSendUserInputMode(SendUserInputAsTone);
1600 else if (str == "Q.931")
1601 SetSendUserInputMode(SendUserInputAsQ931);
1602 else if (str == "InBand") {
1603 SetSendUserInputMode(SendUserInputInBand);
1604 m_sendInBandDTMF = true;
1605 }
1606
1607 #if OPAL_PTLIB_DTMF
1608 m_sendInBandDTMF = m_stringOptions.GetBoolean(OPAL_OPT_ENABLE_INBAND_DTMF, m_sendInBandDTMF);
1609 m_detectInBandDTMF = m_stringOptions.GetBoolean(OPAL_OPT_DETECT_INBAND_DTMF, m_detectInBandDTMF);
1610 m_sendInBandDTMF = m_stringOptions.GetBoolean(OPAL_OPT_SEND_INBAND_DTMF, m_sendInBandDTMF);
1611
1612 m_dtmfScaleMultiplier = m_stringOptions.GetInteger(OPAL_OPT_DTMF_MULT, m_dtmfScaleMultiplier);
1613 m_dtmfScaleDivisor = m_stringOptions.GetInteger(OPAL_OPT_DTMF_DIV, m_dtmfScaleDivisor);
1614 #endif
1615
1616 m_autoStartInfo.Initialise(m_stringOptions);
1617
1618 if (m_stringOptions.GetBoolean(OPAL_OPT_DISABLE_JITTER))
1619 maxAudioJitterDelay = minAudioJitterDelay = 0;
1620 else {
1621 maxAudioJitterDelay = m_stringOptions.GetInteger(OPAL_OPT_MAX_JITTER, maxAudioJitterDelay);
1622 minAudioJitterDelay = m_stringOptions.GetInteger(OPAL_OPT_MIN_JITTER, minAudioJitterDelay);
1623 }
1624
1625 #if OPAL_HAS_MIXER
1626 if (m_stringOptions.Contains(OPAL_OPT_RECORD_AUDIO))
1627 m_recordingFilename = m_stringOptions(OPAL_OPT_RECORD_AUDIO);
1628 #endif
1629
1630 str = m_stringOptions(OPAL_OPT_ALERTING_TYPE);
1631 if (!str.IsEmpty())
1632 SetAlertingType(str);
1633
1634 #if P_LUA
1635 if (!m_luaScriptLoaded) {
1636 str = m_stringOptions("script");
1637 bool ok = true;
1638 if (!str.IsEmpty())
1639 ok = m_lua.LoadString(str);
1640 else {
1641 str = m_stringOptions("scriptfile");
1642 if (!str.IsEmpty())
1643 m_lua.LoadFile(str);
1644 }
1645 }
1646 m_lua.CallLuaFunction("OnSetOptions");
1647 #endif
1648
1649 UnlockReadWrite();
1650 }
1651 }
1652
1653
GetMediaFormats() const1654 OpalMediaFormatList OpalConnection::GetMediaFormats() const
1655 {
1656 return endpoint.GetMediaFormats();
1657 }
1658
1659
GetLocalMediaFormats()1660 OpalMediaFormatList OpalConnection::GetLocalMediaFormats()
1661 {
1662 if (m_localMediaFormats.IsEmpty()) {
1663 m_localMediaFormats = ownerCall.GetMediaFormats(*this);
1664 PTRACE(4, "SIP\tLocal media formats set:\n " << setfill(',') << m_localMediaFormats << setfill(' '));
1665 }
1666 return m_localMediaFormats;
1667 }
1668
1669
OnStartMediaPatch(OpalMediaPatch & patch)1670 void OpalConnection::OnStartMediaPatch(OpalMediaPatch & patch)
1671 {
1672 #if P_LUA
1673 m_lua.CallLuaFunction("OnStartMediaPatch");
1674 #endif
1675 GetEndPoint().GetManager().OnStartMediaPatch(*this, patch);
1676 }
1677
1678
OnStopMediaPatch(OpalMediaPatch & patch)1679 void OpalConnection::OnStopMediaPatch(OpalMediaPatch & patch)
1680 {
1681 #if P_LUA
1682 m_lua.CallLuaFunction("OnStopMediaPatch");
1683 #endif
1684 GetEndPoint().GetManager().OnStopMediaPatch(*this, patch);
1685 }
1686
1687
OnMediaCommand(OpalMediaStream & stream,const OpalMediaCommand & command)1688 bool OpalConnection::OnMediaCommand(OpalMediaStream & stream, const OpalMediaCommand & command)
1689 {
1690 PTRACE(3, "OpalCon\tOnMediaCommand \"" << command << "\" on " << stream << " for " << *this);
1691 if (&stream.GetConnection() != this)
1692 return false;
1693
1694 PSafePtr<OpalConnection> other = GetOtherPartyConnection();
1695 return other != NULL && other->OnMediaCommand(stream, command);
1696 }
1697
1698
GetAutoStart(const OpalMediaType & mediaType) const1699 OpalMediaType::AutoStartMode OpalConnection::GetAutoStart(const OpalMediaType & mediaType) const
1700 {
1701 return m_autoStartInfo.GetAutoStart(mediaType);
1702 }
1703
1704
AutoStartMap()1705 OpalConnection::AutoStartMap::AutoStartMap()
1706 {
1707 m_initialised = false;
1708 }
1709
1710
Initialise(const OpalConnection::StringOptions & stringOptions)1711 void OpalConnection::AutoStartMap::Initialise(const OpalConnection::StringOptions & stringOptions)
1712 {
1713 PWaitAndSignal m(m_mutex);
1714
1715 // make function idempotent
1716 if (m_initialised)
1717 return;
1718
1719 m_initialised = true;
1720
1721 // get autostart option as lines
1722 PStringArray lines = stringOptions(OPAL_OPT_AUTO_START).Lines();
1723 for (PINDEX i = 0; i < lines.GetSize(); ++i) {
1724 PString line = lines[i];
1725 PINDEX colon = line.Find(':');
1726 OpalMediaType mediaType = line.Left(colon);
1727
1728 // see if media type is known, and if it is, enable it
1729 OpalMediaTypeDefinition * def = mediaType.GetDefinition();
1730 if (def != NULL) {
1731 if (colon == P_MAX_INDEX)
1732 SetAutoStart(mediaType, OpalMediaType::ReceiveTransmit);
1733 else {
1734 PStringArray tokens = line.Mid(colon+1).Tokenise(";", FALSE);
1735 PINDEX j;
1736 for (j = 0; j < tokens.GetSize(); ++j) {
1737 if ((tokens[i] *= "no") || (tokens[i] *= "false") || (tokens[i] *= "0"))
1738 SetAutoStart(mediaType, OpalMediaType::DontOffer);
1739 else if ((tokens[i] *= "yes") || (tokens[i] *= "true") || (tokens[i] *= "1") || (tokens[i] *= "sendrecv"))
1740 SetAutoStart(mediaType, OpalMediaType::ReceiveTransmit);
1741 else if (tokens[i] *= "recvonly")
1742 SetAutoStart(mediaType, OpalMediaType::Receive);
1743 else if (tokens[i] *= "sendonly")
1744 SetAutoStart(mediaType, OpalMediaType::Transmit);
1745 else if ((tokens[i] *= "offer") || (tokens[i] *= "inactive"))
1746 SetAutoStart(mediaType, OpalMediaType::OfferInactive);
1747 else if (tokens[i] *= "exclusive") {
1748 OpalMediaTypeFactory::KeyList_T types = OpalMediaType::GetList();
1749 for (OpalMediaTypeFactory::KeyList_T::iterator r = types.begin(); r != types.end(); ++r)
1750 SetAutoStart(*r, *r == mediaType ? OpalMediaType::ReceiveTransmit : OpalMediaType::DontOffer);
1751 }
1752 }
1753 }
1754 }
1755 }
1756 }
1757
1758
SetAutoStart(const OpalMediaType & mediaType,OpalMediaType::AutoStartMode autoStart)1759 void OpalConnection::AutoStartMap::SetAutoStart(const OpalMediaType & mediaType, OpalMediaType::AutoStartMode autoStart)
1760 {
1761 PWaitAndSignal m(m_mutex);
1762 m_initialised = true;
1763
1764 // deconflict session ID
1765 unsigned sessionID = mediaType.GetDefinition()->GetDefaultSessionId();
1766 if (size() == 0) {
1767 if (sessionID == 0)
1768 sessionID = 1;
1769 }
1770 else {
1771 iterator r = begin();
1772 while (r != end()) {
1773 if (r->second.preferredSessionId != sessionID)
1774 ++r;
1775 else {
1776 ++sessionID;
1777 r = begin();
1778 }
1779 }
1780 }
1781
1782 AutoStartInfo info;
1783 info.autoStart = autoStart;
1784 info.preferredSessionId = sessionID;
1785
1786 insert(value_type(mediaType, info));
1787 }
1788
1789
GetAutoStart(const OpalMediaType & mediaType) const1790 OpalMediaType::AutoStartMode OpalConnection::AutoStartMap::GetAutoStart(const OpalMediaType & mediaType) const
1791 {
1792 PWaitAndSignal m(m_mutex);
1793 const_iterator r = find(mediaType);
1794 return r == end() ? mediaType.GetAutoStart() : r->second.autoStart;
1795 }
1796
1797
1798 #if 0 //OPAL_HAS_IM
1799
1800 bool OpalConnection::TransmitInternalIM(const OpalMediaFormat & format, RTP_IMFrame & frame)
1801 {
1802 // if the call contains any compatible IM stream, use it
1803 for (OpalMediaStreamPtr mediaStream(mediaStreams, PSafeReference); mediaStream != NULL; ++mediaStream) {
1804 if (mediaStream->IsSource() && (mediaStream->GetMediaFormat() == format)) {
1805 PTRACE(3, "OpalCon\tSending " << format << " IM message within call");
1806 return mediaStream->GetPatch()->PushFrame(frame);
1807 }
1808 }
1809
1810 // otherwise push the frame directly to the other connection
1811 PSafePtr<OpalConnection> other = GetOtherPartyConnection();
1812 if (other != NULL) {
1813 PTRACE(3, "OpalCon\tSending " << format << " IM message directly to other connection");
1814 other->OnReceiveInternalIM(format, frame);
1815 }
1816 return true;
1817 }
1818
1819
1820 void OpalConnection::OnReceiveInternalIM(const OpalMediaFormat & format, RTP_IMFrame & rtp)
1821 {
1822 TransmitExternalIM(format, rtp);
1823 }
1824
1825
1826 bool OpalConnection::TransmitExternalIM(const OpalMediaFormat & /*format*/, RTP_IMFrame & frame)
1827 {
1828 PURL remotePartyURL, localPartyURL;
1829 PString remotePartyName;
1830
1831 // get information about sender
1832 PSafePtr<OpalConnection> otherParty = GetOtherPartyConnectionAs<OpalConnection>();
1833 if (otherParty != NULL) {
1834 remotePartyURL = otherParty->GetRemotePartyCallbackURL();
1835 remotePartyName = otherParty->GetRemotePartyName();
1836 localPartyURL = otherParty->GetLocalPartyURL();
1837 }
1838
1839 // pass information up the chain
1840
1841 OpalIM message;
1842 message.m_conversationId = GetToken();
1843 message.m_to = localPartyURL;
1844 message.m_from = remotePartyURL;
1845 message.m_fromName = remotePartyName;
1846 message.m_mimeType = frame.GetContentType();
1847
1848 T140String t140;
1849 frame.GetContent(t140);
1850 t140.AsString(message.m_body);
1851
1852 endpoint.OnMessageReceived(message);
1853
1854 return true;
1855 }
1856
1857 bool OpalConnection::OnReceiveExternalIM(const OpalMediaFormat & format, RTP_IMFrame & body)
1858 {
1859 return TransmitInternalIM(format, body);
1860 }
1861
1862 #endif
1863
1864
ExtractFromURL(PURL & url)1865 void OpalConnection::StringOptions::ExtractFromURL(PURL & url)
1866 {
1867 PStringToString params = url.GetParamVars();
1868 params.MakeUnique();
1869 for (PINDEX i = 0; i < params.GetSize(); ++i) {
1870 PCaselessString key = params.GetKeyAt(i);
1871 if (key.NumCompare(OPAL_URL_PARAM_PREFIX) == EqualTo) {
1872 SetAt(key.Mid(5), params.GetDataAt(i));
1873 url.SetParamVar(key, PString::Empty());
1874 }
1875 }
1876 }
1877
1878 #if P_LUA
1879
LuaSetOption(lua_State *)1880 int OpalConnection::LuaSetOption(lua_State * /*lua*/)
1881 {
1882 return 0;
1883 }
1884
1885 #endif
1886
1887 /////////////////////////////////////////////////////////////////////////////
1888