1 /*
2  * localep.cxx
3  *
4  * Local EndPoint/Connection.
5  *
6  * Open Phone Abstraction Library (OPAL)
7  *
8  * Copyright (c) 2008 Vox Lucida Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open Phone Abstraction Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 28040 $
27  * $Author: rjongbloed $
28  * $Date: 2012-07-16 01:23:59 -0500 (Mon, 16 Jul 2012) $
29  */
30 
31 #include <ptlib.h>
32 
33 #ifdef __GNUC__
34 #pragma implementation "localep.h"
35 #endif
36 
37 #include <opal/buildopts.h>
38 
39 #include <opal/localep.h>
40 #include <opal/call.h>
41 
42 
43 #define new PNEW
44 
45 
46 /////////////////////////////////////////////////////////////////////////////
47 
OpalLocalEndPoint(OpalManager & mgr,const char * prefix)48 OpalLocalEndPoint::OpalLocalEndPoint(OpalManager & mgr, const char * prefix)
49   : OpalEndPoint(mgr, prefix, CanTerminateCall)
50   , m_deferredAlerting(false)
51   , m_deferredAnswer(false)
52   , m_defaultAudioSynchronicity(e_Synchronous)
53   , m_defaultVideoSourceSynchronicity(e_Synchronous)
54 {
55   PTRACE(3, "LocalEP\tCreated endpoint.");
56 }
57 
58 
~OpalLocalEndPoint()59 OpalLocalEndPoint::~OpalLocalEndPoint()
60 {
61   PTRACE(4, "LocalEP\tDeleted endpoint.");
62 }
63 
64 
GetMediaFormats() const65 OpalMediaFormatList OpalLocalEndPoint::GetMediaFormats() const
66 {
67   return manager.GetCommonMediaFormats(false, true);
68 }
69 
70 
MakeConnection(OpalCall & call,const PString &,void * userData,unsigned int options,OpalConnection::StringOptions * stringOptions)71 PSafePtr<OpalConnection> OpalLocalEndPoint::MakeConnection(OpalCall & call,
72                                                       const PString & /*remoteParty*/,
73                                                                void * userData,
74                                                          unsigned int options,
75                                       OpalConnection::StringOptions * stringOptions)
76 {
77   return AddConnection(CreateConnection(call, userData, options, stringOptions));
78 }
79 
80 
CreateConnection(OpalCall & call,void * userData,unsigned options,OpalConnection::StringOptions * stringOptions)81 OpalLocalConnection * OpalLocalEndPoint::CreateConnection(OpalCall & call,
82                                                               void * userData,
83                                                             unsigned options,
84                                      OpalConnection::StringOptions * stringOptions)
85 {
86   return new OpalLocalConnection(call, *this, userData, options, stringOptions);
87 }
88 
89 
OnOutgoingSetUp(const OpalLocalConnection &)90 bool OpalLocalEndPoint::OnOutgoingSetUp(const OpalLocalConnection & /*connection*/)
91 {
92   return true;
93 }
94 
95 
OnOutgoingCall(const OpalLocalConnection &)96 bool OpalLocalEndPoint::OnOutgoingCall(const OpalLocalConnection & /*connection*/)
97 {
98   return true;
99 }
100 
101 
OnIncomingCall(OpalLocalConnection & connection)102 bool OpalLocalEndPoint::OnIncomingCall(OpalLocalConnection & connection)
103 {
104   if (!m_deferredAnswer)
105     connection.AcceptIncoming();
106   return true;
107 }
108 
109 
AlertingIncomingCall(const PString & token,OpalConnection::StringOptions * options)110 bool OpalLocalEndPoint::AlertingIncomingCall(const PString & token, OpalConnection::StringOptions * options)
111 {
112   PSafePtr<OpalLocalConnection> connection = GetLocalConnectionWithLock(token, PSafeReadOnly);
113   if (connection == NULL) {
114     PTRACE(2, "LocalEP\tCould not find connection using token \"" << token << '"');
115     return false;
116   }
117 
118   if (options != NULL)
119     connection->SetStringOptions(*options, false);
120 
121   connection->AlertingIncoming();
122   return true;
123 }
124 
125 
AcceptIncomingCall(const PString & token,OpalConnection::StringOptions * options)126 bool OpalLocalEndPoint::AcceptIncomingCall(const PString & token, OpalConnection::StringOptions * options)
127 {
128   PSafePtr<OpalLocalConnection> connection = GetLocalConnectionWithLock(token, PSafeReadOnly);
129   if (connection == NULL) {
130     PTRACE(2, "LocalEP\tCould not find connection using token \"" << token << '"');
131     return false;
132   }
133 
134   if (options != NULL)
135     connection->SetStringOptions(*options, false);
136 
137   connection->AcceptIncoming();
138   return true;
139 }
140 
141 
RejectIncomingCall(const PString & token,const OpalConnection::CallEndReason & reason)142 bool OpalLocalEndPoint::RejectIncomingCall(const PString & token, const OpalConnection::CallEndReason & reason)
143 {
144   PSafePtr<OpalLocalConnection> connection = GetLocalConnectionWithLock(token, PSafeReadOnly);
145   if (connection == NULL)
146     return false;
147 
148   connection->Release(reason);
149   return true;
150 }
151 
152 
OnUserInput(const OpalLocalConnection &,const PString &)153 bool OpalLocalEndPoint::OnUserInput(const OpalLocalConnection &, const PString &)
154 {
155   return true;
156 }
157 
158 
OnReadMediaFrame(const OpalLocalConnection &,const OpalMediaStream &,RTP_DataFrame &)159 bool OpalLocalEndPoint::OnReadMediaFrame(const OpalLocalConnection & /*connection*/,
160                                          const OpalMediaStream & /*mediaStream*/,
161                                          RTP_DataFrame & /*frame*/)
162 {
163   return false;
164 }
165 
166 
OnWriteMediaFrame(const OpalLocalConnection &,const OpalMediaStream &,RTP_DataFrame &)167 bool OpalLocalEndPoint::OnWriteMediaFrame(const OpalLocalConnection & /*connection*/,
168                                           const OpalMediaStream & /*mediaStream*/,
169                                           RTP_DataFrame & /*frame*/)
170 {
171   return false;
172 }
173 
174 
OnReadMediaData(const OpalLocalConnection &,const OpalMediaStream &,void * data,PINDEX size,PINDEX & length)175 bool OpalLocalEndPoint::OnReadMediaData(const OpalLocalConnection & /*connection*/,
176                                         const OpalMediaStream & /*mediaStream*/,
177                                         void * data,
178                                         PINDEX size,
179                                         PINDEX & length)
180 {
181   memset(data, 0, size);
182   length = size;
183   return true;
184 }
185 
186 
OnWriteMediaData(const OpalLocalConnection &,const OpalMediaStream &,const void *,PINDEX length,PINDEX & written)187 bool OpalLocalEndPoint::OnWriteMediaData(const OpalLocalConnection & /*connection*/,
188                                          const OpalMediaStream & /*mediaStream*/,
189                                          const void * /*data*/,
190                                          PINDEX length,
191                                          PINDEX & written)
192 {
193   written = length;
194   return true;
195 }
196 
197 
198 OpalLocalEndPoint::Synchronicity
GetSynchronicity(const OpalMediaFormat & mediaFormat,bool isSource) const199 OpalLocalEndPoint::GetSynchronicity(const OpalMediaFormat & mediaFormat,
200                                     bool isSource) const
201 {
202   if (mediaFormat.GetMediaType() == OpalMediaType::Audio())
203     return m_defaultAudioSynchronicity;
204 
205   if (isSource && mediaFormat.GetMediaType() == OpalMediaType::Audio())
206     return m_defaultVideoSourceSynchronicity;
207 
208   return e_Asynchronous;
209 }
210 
211 
212 /////////////////////////////////////////////////////////////////////////////
213 
OpalLocalConnection(OpalCall & call,OpalLocalEndPoint & ep,void * userData,unsigned options,OpalConnection::StringOptions * stringOptions,char tokenPrefix)214 OpalLocalConnection::OpalLocalConnection(OpalCall & call,
215                                 OpalLocalEndPoint & ep,
216                                              void * userData,
217                                            unsigned options,
218                     OpalConnection::StringOptions * stringOptions,
219                                                char tokenPrefix)
220   : OpalConnection(call, ep, ep.GetManager().GetNextToken(tokenPrefix), options, stringOptions)
221   , endpoint(ep)
222   , m_userData(userData)
223 {
224 #if OPAL_PTLIB_DTMF
225   m_sendInBandDTMF = m_detectInBandDTMF = false;
226 #endif
227   PTRACE(4, "LocalCon\tCreated connection with token \"" << callToken << '"');
228 }
229 
230 
~OpalLocalConnection()231 OpalLocalConnection::~OpalLocalConnection()
232 {
233   PTRACE(4, "LocalCon\tDeleted connection.");
234 }
235 
236 
OnApplyStringOptions()237 void OpalLocalConnection::OnApplyStringOptions()
238 {
239   OpalConnection::OnApplyStringOptions();
240 
241   PSafePtr<OpalConnection> otherConnection = GetOtherPartyConnection();
242   if (otherConnection != NULL && dynamic_cast<OpalLocalConnection*>(&*otherConnection) == NULL) {
243     PTRACE(4, "LocalCon\tPassing string options to " << *otherConnection);
244     otherConnection->SetStringOptions(m_stringOptions, false);
245   }
246 }
247 
248 
SetUpConnection()249 PBoolean OpalLocalConnection::SetUpConnection()
250 {
251   originating = true;
252 
253   // Check if we are A-Party in thsi call, so need to do things differently
254   if (ownerCall.GetConnection(0) == this) {
255     SetPhase(SetUpPhase);
256     if (!OnIncomingConnection(0, NULL)) {
257       Release(EndedByCallerAbort);
258       return false;
259     }
260 
261     PTRACE(3, "LocalCon\tOutgoing call routed to " << ownerCall.GetPartyB() << " for " << *this);
262     if (!OnOutgoingSetUp() || !ownerCall.OnSetUp(*this)) {
263       Release(EndedByNoAccept);
264       return false;
265     }
266   }
267   else if (ownerCall.IsEstablished()) {
268     PTRACE(3, "LocalCon\tTransfer of connection in call " << ownerCall);
269     OnApplyStringOptions();
270     AutoStartMediaStreams();
271     OnConnectedInternal();
272   }
273   else {
274     PTRACE(3, "LocalCon\tIncoming call from " << remotePartyName);
275 
276     OnApplyStringOptions();
277 
278     if (!OnIncoming()) {
279       Release(EndedByLocalBusy);
280       return false;
281     }
282 
283     if (!endpoint.IsDeferredAlerting())
284       AlertingIncoming();
285   }
286 
287   return true;
288 }
289 
290 
SetAlerting(const PString & calleeName,PBoolean)291 PBoolean OpalLocalConnection::SetAlerting(const PString & calleeName, PBoolean)
292 {
293   PTRACE(3, "LocalCon\tSetAlerting(" << calleeName << ')');
294   SetPhase(AlertingPhase);
295   remotePartyName = calleeName;
296   return endpoint.OnOutgoingCall(*this);
297 }
298 
299 
SetConnected()300 PBoolean OpalLocalConnection::SetConnected()
301 {
302   PTRACE(3, "LocalCon\tSetConnected()");
303 
304   if (GetMediaStream(PString::Empty(), true) == NULL)
305     AutoStartMediaStreams(); // if no media streams, try and start them
306 
307   return OpalConnection::SetConnected();
308 }
309 
CreateMediaStream(const OpalMediaFormat & mediaFormat,unsigned sessionID,PBoolean isSource)310 OpalMediaStream * OpalLocalConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat,
311                                                          unsigned sessionID,
312                                                          PBoolean isSource)
313 {
314   return new OpalLocalMediaStream(*this, mediaFormat, sessionID, isSource,
315                                   endpoint.GetSynchronicity(mediaFormat, isSource));
316 }
317 
318 
OpenMediaStream(const OpalMediaFormat & mediaFormat,unsigned sessionID,bool isSource)319 OpalMediaStreamPtr OpalLocalConnection::OpenMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)
320 {
321 #if OPAL_VIDEO
322   if ( isSource &&
323        mediaFormat.GetMediaType() == OpalMediaType::Video() &&
324       !ownerCall.IsEstablished() &&
325       !endpoint.GetManager().CanAutoStartTransmitVideo()) {
326     PTRACE(3, "LocalCon\tOpenMediaStream auto start disabled, refusing video open");
327     return NULL;
328   }
329 #endif
330 
331   return OpalConnection::OpenMediaStream(mediaFormat, sessionID, isSource);
332 }
333 
334 
SendUserInputString(const PString & value)335 bool OpalLocalConnection::SendUserInputString(const PString & value)
336 {
337   PTRACE(3, "LocalCon\tSendUserInputString(" << value << ')');
338   return endpoint.OnUserInput(*this, value);
339 }
340 
341 
OnOutgoingSetUp()342 bool OpalLocalConnection::OnOutgoingSetUp()
343 {
344   return endpoint.OnOutgoingSetUp(*this);
345 }
346 
347 
OnOutgoing()348 bool OpalLocalConnection::OnOutgoing()
349 {
350   return endpoint.OnOutgoingCall(*this);
351 }
352 
353 
OnIncoming()354 bool OpalLocalConnection::OnIncoming()
355 {
356   return endpoint.OnIncomingCall(*this);
357 }
358 
359 
AlertingIncoming()360 void OpalLocalConnection::AlertingIncoming()
361 {
362   if (LockReadWrite()) {
363     if (GetPhase() < AlertingPhase) {
364       SetPhase(AlertingPhase);
365       OnAlerting();
366     }
367     UnlockReadWrite();
368   }
369 }
370 
371 
AcceptIncoming()372 void OpalLocalConnection::AcceptIncoming()
373 {
374   if (LockReadWrite()) {
375     AlertingIncoming();
376     AutoStartMediaStreams();
377     OnConnectedInternal();
378     UnlockReadWrite();
379   }
380 }
381 
382 
383 /////////////////////////////////////////////////////////////////////////////
384 
OpalLocalMediaStream(OpalLocalConnection & connection,const OpalMediaFormat & mediaFormat,unsigned sessionID,bool isSource,OpalLocalEndPoint::Synchronicity synchronicity)385 OpalLocalMediaStream::OpalLocalMediaStream(OpalLocalConnection & connection,
386                                            const OpalMediaFormat & mediaFormat,
387                                            unsigned sessionID,
388                                            bool isSource,
389                                            OpalLocalEndPoint::Synchronicity synchronicity)
390   : OpalMediaStream(connection, mediaFormat, sessionID, isSource)
391   , OpalMediaStreamPacing(mediaFormat)
392   , m_synchronicity(synchronicity)
393 {
394 }
395 
396 
ReadPacket(RTP_DataFrame & frame)397 PBoolean OpalLocalMediaStream::ReadPacket(RTP_DataFrame & frame)
398 {
399   if (!isOpen)
400     return false;
401 
402   OpalLocalEndPoint & ep = dynamic_cast<OpalLocalEndPoint &>(connection.GetEndPoint());
403   OpalLocalConnection & conn = dynamic_cast<OpalLocalConnection &>(connection);
404   if (ep.OnReadMediaFrame(conn, *this, frame))
405     return true;
406 
407   return OpalMediaStream::ReadPacket(frame);
408 }
409 
410 
WritePacket(RTP_DataFrame & frame)411 PBoolean OpalLocalMediaStream::WritePacket(RTP_DataFrame & frame)
412 {
413   if (!isOpen)
414     return false;
415 
416   OpalLocalEndPoint & ep = dynamic_cast<OpalLocalEndPoint &>(connection.GetEndPoint());
417   OpalLocalConnection & conn = dynamic_cast<OpalLocalConnection &>(connection);
418   if (ep.OnWriteMediaFrame(conn, *this, frame))
419     return true;
420 
421   return OpalMediaStream::WritePacket(frame);
422 }
423 
424 
ReadData(BYTE * data,PINDEX size,PINDEX & length)425 PBoolean OpalLocalMediaStream::ReadData(BYTE * data, PINDEX size, PINDEX & length)
426 {
427   OpalLocalEndPoint & ep = dynamic_cast<OpalLocalEndPoint &>(connection.GetEndPoint());
428   OpalLocalConnection & conn = dynamic_cast<OpalLocalConnection &>(connection);
429   if (!ep.OnReadMediaData(conn, *this, data, size, length))
430     return false;
431 
432   if (m_synchronicity == OpalLocalEndPoint::e_SimulateSyncronous)
433     Pace(true, size, marker);
434   return true;
435 }
436 
437 
WriteData(const BYTE * data,PINDEX length,PINDEX & written)438 PBoolean OpalLocalMediaStream::WriteData(const BYTE * data, PINDEX length, PINDEX & written)
439 {
440   OpalLocalEndPoint & ep = dynamic_cast<OpalLocalEndPoint &>(connection.GetEndPoint());
441   OpalLocalConnection & conn = dynamic_cast<OpalLocalConnection &>(connection);
442   if (!ep.OnWriteMediaData(conn, *this, data, length, written))
443     return false;
444 
445   if (m_synchronicity == OpalLocalEndPoint::e_SimulateSyncronous)
446     Pace(false, written, marker);
447   return true;
448 }
449 
450 
IsSynchronous() const451 PBoolean OpalLocalMediaStream::IsSynchronous() const
452 {
453   return m_synchronicity != OpalLocalEndPoint::e_Asynchronous;
454 }
455 
456 
457 /////////////////////////////////////////////////////////////////////////////
458