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