1 /*
2  * call.cxx
3  *
4  * Call management
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: 28690 $
28  * $Author: rjongbloed $
29  * $Date: 2012-12-18 20:47:15 -0600 (Tue, 18 Dec 2012) $
30  */
31 
32 #include <ptlib.h>
33 
34 #ifdef __GNUC__
35 #pragma implementation "call.h"
36 #endif
37 
38 #include <opal/buildopts.h>
39 
40 #include <opal/call.h>
41 
42 #include <opal/manager.h>
43 #include <opal/endpoint.h>
44 #include <opal/patch.h>
45 #include <opal/transcoders.h>
46 
47 
48 #define new PNEW
49 
50 
51 /////////////////////////////////////////////////////////////////////////////
52 
53 #ifdef _MSC_VER
54 #pragma warning(disable:4355)
55 #endif
56 
OpalCall(OpalManager & mgr)57 OpalCall::OpalCall(OpalManager & mgr)
58   : manager(mgr)
59   , myToken(mgr.GetNextToken('C'))
60   , isEstablished(PFalse)
61   , isClearing(PFalse)
62   , callEndReason(OpalConnection::NumCallEndReasons)
63 #if OPAL_HAS_MIXER
64   , m_recordManager(NULL)
65 #endif
66 {
67   manager.activeCalls.SetAt(myToken, this);
68 
69   connectionsActive.DisallowDeleteObjects();
70 
71   PTRACE(3, "Call\tCreated " << *this);
72 }
73 
74 #ifdef _MSC_VER
75 #pragma warning(default:4355)
76 #endif
77 
78 
~OpalCall()79 OpalCall::~OpalCall()
80 {
81 #if OPAL_HAS_MIXER
82   delete m_recordManager;
83 #endif
84 
85   PTRACE(3, "Call\tDestroyed " << *this);
86 }
87 
88 
PrintOn(ostream & strm) const89 void OpalCall::PrintOn(ostream & strm) const
90 {
91   strm << "Call[" << myToken << ']';
92 }
93 
94 
OnEstablishedCall()95 void OpalCall::OnEstablishedCall()
96 {
97   PTRACE(3, "Call\tEstablished " << *this);
98   manager.OnEstablishedCall(*this);
99 }
100 
101 
SetCallEndReason(OpalConnection::CallEndReason reason)102 void OpalCall::SetCallEndReason(OpalConnection::CallEndReason reason)
103 {
104   // Only set reason if not already set to something
105   if (callEndReason == OpalConnection::NumCallEndReasons &&
106      (reason != OpalConnection::EndedByCallForwarded || connectionsActive.GetSize() <= 1))
107     callEndReason = reason;
108 }
109 
110 
Clear(OpalConnection::CallEndReason reason,PSyncPoint * sync)111 void OpalCall::Clear(OpalConnection::CallEndReason reason, PSyncPoint * sync)
112 {
113   PTRACE(3, "Call\tClearing " << (sync != NULL ? "(sync) " : "") << *this << " reason=" << reason);
114 
115   {
116     PSafeLockReadWrite lock(*this);
117     if (!lock.IsLocked() || isClearing) {
118       if (sync != NULL)
119         sync->Signal();
120       return;
121     }
122 
123     isClearing = true;
124 
125     SetCallEndReason(reason);
126 
127     if (sync != NULL)
128       m_endCallSyncPoint.push_back(sync);
129 
130     switch (connectionsActive.GetSize()) {
131       case 0 :
132         break;
133 
134       case 1 :
135         {
136           PSafePtr<OpalConnection> connection = connectionsActive.GetAt(0, PSafeReference);
137           if (connection != NULL)
138             connection->Release(reason);
139         }
140         break;
141 
142       default :
143         // Release all but A-Party, it gets done in the B-Party OnReleased thread.
144         for (PINDEX i = 1; i < connectionsActive.GetSize(); ++i) {
145           PSafePtr<OpalConnection> connection = connectionsActive.GetAt(i, PSafeReference);
146           if (connection != NULL)
147             connection->Release(reason);
148         }
149     }
150   }
151 
152   // Outside of lock
153   InternalOnClear();
154 }
155 
156 
InternalOnClear()157 void OpalCall::InternalOnClear()
158 {
159   if (!connectionsActive.IsEmpty())
160     return;
161 
162   if (m_isCleared.TestAndSet(true))
163     return;
164 
165   OnCleared();
166 
167 #if OPAL_HAS_MIXER
168   StopRecording();
169 #endif
170 
171   if (LockReadWrite()) {
172     while (!m_endCallSyncPoint.empty()) {
173       PTRACE(5, "Call\tSignalling end call.");
174       m_endCallSyncPoint.front()->Signal();
175       m_endCallSyncPoint.pop_front();
176     }
177     UnlockReadWrite();
178   }
179 
180   manager.activeCalls.RemoveAt(GetToken());
181 }
182 
183 
OnCleared()184 void OpalCall::OnCleared()
185 {
186   manager.OnClearedCall(*this);
187 }
188 
189 
OnNewConnection(OpalConnection & connection)190 void OpalCall::OnNewConnection(OpalConnection & connection)
191 {
192   manager.OnNewConnection(connection);
193   SetPartyNames();
194 }
195 
196 
OnSetUp(OpalConnection & connection)197 PBoolean OpalCall::OnSetUp(OpalConnection & connection)
198 {
199   PTRACE(3, "Call\tOnSetUp " << connection);
200 
201   if (isClearing)
202     return false;
203 
204   SetPartyNames();
205 
206   bool ok = false;
207 
208   PSafePtr<OpalConnection> otherConnection;
209   while (EnumerateConnections(otherConnection, PSafeReadWrite, &connection)) {
210     if (otherConnection->SetUpConnection() && otherConnection->OnSetUpConnection())
211       ok = true;
212   }
213 
214   return ok;
215 }
216 
217 
OnProceeding(OpalConnection & PTRACE_PARAM (connection))218 void OpalCall::OnProceeding(OpalConnection & PTRACE_PARAM(connection))
219 {
220   PTRACE(3, "Call\tOnProceeding " << connection);
221 }
222 
223 
OnAlerting(OpalConnection & connection)224 PBoolean OpalCall::OnAlerting(OpalConnection & connection)
225 {
226   PTRACE(3, "Call\tOnAlerting " << connection);
227 
228   if (isClearing)
229     return false;
230 
231   PBoolean hasMedia = connection.GetMediaStream(OpalMediaType::Audio(), true) != NULL;
232 
233   bool ok = false;
234 
235   PSafePtr<OpalConnection> otherConnection;
236   while (EnumerateConnections(otherConnection, PSafeReadWrite, &connection)) {
237     if (otherConnection->SetAlerting(connection.GetRemotePartyName(), hasMedia))
238       ok = true;
239   }
240 
241   SetPartyNames();
242 
243   return ok;
244 }
245 
OnAnswerCall(OpalConnection & PTRACE_PARAM (connection),const PString & PTRACE_PARAM (caller))246 OpalConnection::AnswerCallResponse OpalCall::OnAnswerCall(OpalConnection & PTRACE_PARAM(connection),
247                                                           const PString & PTRACE_PARAM(caller))
248 {
249   PTRACE(3, "Call\tOnAnswerCall " << connection << " caller \"" << caller << '"');
250   return OpalConnection::AnswerCallDeferred;
251 }
252 
253 
OnConnected(OpalConnection & connection)254 PBoolean OpalCall::OnConnected(OpalConnection & connection)
255 {
256   PTRACE(3, "Call\tOnConnected " << connection);
257 
258   if (isClearing || !LockReadOnly())
259     return false;
260 
261   bool havePartyB = connectionsActive.GetSize() == 1 && !m_partyB.IsEmpty();
262 
263   UnlockReadOnly();
264 
265   if (havePartyB) {
266     if (manager.MakeConnection(*this, m_partyB, NULL, 0,
267                                 const_cast<OpalConnection::StringOptions *>(&connection.GetStringOptions())) != NULL)
268       return OnSetUp(connection);
269 
270     connection.Release(OpalConnection::EndedByNoUser);
271     return false;
272   }
273 
274   bool ok = false;
275   PSafePtr<OpalConnection> otherConnection;
276   while (EnumerateConnections(otherConnection, PSafeReadWrite, &connection)) {
277     if (otherConnection->GetPhase() >= OpalConnection::ConnectedPhase ||
278         otherConnection->SetConnected())
279       ok = true;
280   }
281 
282   SetPartyNames();
283 
284   return ok;
285 }
286 
287 
OnEstablished(OpalConnection & connection)288 PBoolean OpalCall::OnEstablished(OpalConnection & connection)
289 {
290   PTRACE(3, "Call\tOnEstablished " << connection);
291 
292   PSafeLockReadWrite lock(*this);
293   if (isClearing || !lock.IsLocked())
294     return false;
295 
296   if (isEstablished)
297     return true;
298 
299   if (connectionsActive.GetSize() < 2)
300     return false;
301 
302   connection.StartMediaStreams();
303 
304   for (PSafePtr<OpalConnection> conn(connectionsActive, PSafeReference); conn != NULL; ++conn) {
305     if (conn->GetPhase() != OpalConnection::EstablishedPhase)
306       return false;
307   }
308 
309   isEstablished = true;
310   OnEstablishedCall();
311 
312   return true;
313 }
314 
315 
GetOtherPartyConnection(const OpalConnection & connection) const316 PSafePtr<OpalConnection> OpalCall::GetOtherPartyConnection(const OpalConnection & connection) const
317 {
318   PTRACE(4, "Call\tGetOtherPartyConnection " << connection);
319 
320   PSafePtr<OpalConnection> otherConnection;
321   EnumerateConnections(otherConnection, PSafeReference, &connection);
322   return otherConnection;
323 }
324 
325 
Hold()326 bool OpalCall::Hold()
327 {
328   PTRACE(3, "Call\tSetting to On Hold");
329 
330   bool ok = false;
331 
332   PSafePtr<OpalConnection> connection;
333   while (EnumerateConnections(connection, PSafeReadWrite)) {
334     if (connection->IsNetworkConnection() && connection->Hold(false, true))
335       ok = true;
336   }
337 
338   return ok;
339 }
340 
341 
Retrieve()342 bool OpalCall::Retrieve()
343 {
344   PTRACE(3, "Call\tRetrieve from On Hold");
345 
346   bool ok = false;
347 
348   PSafePtr<OpalConnection> connection;
349   while (EnumerateConnections(connection, PSafeReadWrite)) {
350     if (connection->IsNetworkConnection() && connection->Hold(false, false))
351       ok = true;
352   }
353 
354   return ok;
355 }
356 
357 
IsOnHold() const358 bool OpalCall::IsOnHold() const
359 {
360   PSafePtr<OpalConnection> connection;
361   while (EnumerateConnections(connection, PSafeReadOnly)) {
362     if (connection->IsNetworkConnection() && connection->IsOnHold(false))
363       return true;
364   }
365 
366   return false;
367 }
368 
369 
Transfer(const PString & newAddress,OpalConnection * connection)370 bool OpalCall::Transfer(const PString & newAddress, OpalConnection * connection)
371 {
372   PCaselessString prefix;
373   PINDEX colon = newAddress.Find(':');
374   if (colon != P_MAX_INDEX)
375     prefix = newAddress.Left(colon);
376 
377   if (connection == NULL) {
378     for (PSafePtr<OpalConnection> conn = GetConnection(0); conn != NULL; ++conn) {
379       if (prefix == conn->GetPrefixName() && !conn->IsReleased())
380         return conn->TransferConnection(newAddress);
381     }
382 
383     PTRACE(2, "Call\tUnable to resolve transfer to \"" << newAddress << '"');
384     return false;
385   }
386 
387   if (connection->IsReleased()) {
388     PTRACE(2, "Call\tCannot transfer to released connection " << *connection);
389     return false;
390   }
391 
392   if (prefix == "*")
393     return connection->TransferConnection(connection->GetPrefixName() + newAddress.Mid(1));
394 
395   if (prefix.IsEmpty() || prefix == connection->GetPrefixName() || manager.HasCall(newAddress))
396     return connection->TransferConnection(newAddress);
397 
398   PTRACE(3, "Call\tTransferring " << *connection << " to \"" << newAddress << '"');
399 
400   PSafePtr<OpalConnection> connectionToKeep = GetOtherPartyConnection(*connection);
401   if (connectionToKeep == NULL)
402     return false;
403 
404   PSafePtr<OpalConnection> newConnection = manager.MakeConnection(*this, newAddress);
405   if (newConnection == NULL)
406     return false;
407 
408   OpalConnection::Phases oldPhase = connection->GetPhase();
409   connection->SetPhase(OpalConnection::ForwardingPhase);
410 
411   // Restart with new connection
412   if (newConnection->SetUpConnection() && newConnection->OnSetUpConnection()) {
413     connectionToKeep->AutoStartMediaStreams(true);
414     connection->Release(OpalConnection::EndedByCallForwarded);
415     newConnection->StartMediaStreams();
416     return true;
417   }
418 
419   newConnection->Release(OpalConnection::EndedByTemporaryFailure);
420   connection->SetPhase(oldPhase);
421   return false;
422 }
423 
424 
GetMediaFormats(const OpalConnection & connection)425 OpalMediaFormatList OpalCall::GetMediaFormats(const OpalConnection & connection)
426 {
427   OpalMediaFormatList commonFormats;
428 
429   bool first = true;
430 
431   PSafePtr<OpalConnection> otherConnection;
432   while (EnumerateConnections(otherConnection, PSafeReadOnly, &connection)) {
433     OpalMediaFormatList possibleFormats = OpalTranscoder::GetPossibleFormats(otherConnection->GetMediaFormats());
434     if (first) {
435       commonFormats = possibleFormats;
436       first = false;
437     }
438     else {
439       // Want intersection of the possible formats for all connections.
440       for (OpalMediaFormatList::iterator format = commonFormats.begin(); format != commonFormats.end(); ) {
441         if (possibleFormats.HasFormat(*format))
442           ++format;
443         else
444           commonFormats.erase(format++);
445       }
446     }
447   }
448 
449   // If nothing comes out of the above, e.g. when this is called very early
450   // in the call sequence and there is no second connection in call yet, then
451   // return all the possible media formats.
452   if (first)
453     commonFormats = OpalTranscoder::GetPossibleFormats(manager.GetCommonMediaFormats(false, true));
454 
455   connection.AdjustMediaFormats(true, NULL, commonFormats);
456 
457   PTRACE(4, "Call\tGetMediaFormats for " << connection << "\n    "
458          << setfill(',') << commonFormats << setfill(' '));
459 
460   return commonFormats;
461 }
462 
463 
AdjustMediaFormats(bool local,const OpalConnection & connection,OpalMediaFormatList & mediaFormats) const464 void OpalCall::AdjustMediaFormats(bool local, const OpalConnection & connection, OpalMediaFormatList & mediaFormats) const
465 {
466   PSafePtr<OpalConnection> otherConnection;
467   while (EnumerateConnections(otherConnection, PSafeReadOnly, &connection))
468     otherConnection->AdjustMediaFormats(local, &connection, mediaFormats);
469 }
470 
471 
OpenSourceMediaStreams(OpalConnection & connection,const OpalMediaType & mediaType,unsigned sessionID,const OpalMediaFormat & preselectedFormat,OpalVideoFormat::ContentRole contentRole)472 PBoolean OpalCall::OpenSourceMediaStreams(OpalConnection & connection,
473                                      const OpalMediaType & mediaType,
474                                                   unsigned sessionID,
475                                    const OpalMediaFormat & preselectedFormat
476 #if OPAL_VIDEO
477                             , OpalVideoFormat::ContentRole contentRole
478 #endif
479                                    )
480 {
481   PSafeLockReadOnly lock(*this);
482   if (isClearing || !lock.IsLocked())
483     return false;
484 
485 #if PTRACING
486   PStringStream traceText;
487   if (PTrace::CanTrace(2)) {
488     traceText << " for " << mediaType << " session " << sessionID;
489     if (preselectedFormat.IsValid())
490       traceText << " (" << preselectedFormat << ')';
491     traceText << " on " << connection;
492   }
493 #endif
494 
495   if (IsOnHold()) {
496     PTRACE(3, "Call\tOpenSourceMediaStreams (call on hold)" << traceText);
497     return false;
498   }
499 
500   // Check if already done
501   OpalMediaStreamPtr sinkStream;
502   OpalMediaStreamPtr sourceStream = connection.GetMediaStream(sessionID, true);
503   if (sourceStream != NULL) {
504     OpalMediaPatch * patch = sourceStream->GetPatch();
505     if (patch != NULL)
506       sinkStream = patch->GetSink();
507     if (sourceStream->GetMediaFormat() == preselectedFormat ||
508         (sinkStream != NULL && sinkStream->GetMediaFormat() == preselectedFormat)) {
509       if (sourceStream->IsPaused()) {
510         sourceStream->SetPaused(false);
511         PTRACE(3, "Call\tOpenSourceMediaStreams (un-pausing)" << traceText);
512       }
513       else {
514         PTRACE(3, "Call\tOpenSourceMediaStreams (already opened)" << traceText);
515       }
516       return true;
517     }
518     if (sinkStream == NULL && sourceStream->IsOpen()) {
519       PTRACE(3, "Call\tOpenSourceMediaStreams (is opening)" << traceText);
520       return true;
521     }
522   }
523 
524   if (sessionID == 0)
525     sessionID = connection.GetNextSessionID(mediaType, true);
526 
527   PTRACE(3, "Call\tOpenSourceMediaStreams " << (sourceStream != NULL ? "replace" : "open") << traceText);
528   sourceStream.SetNULL();
529 
530   // Create the sinks and patch if needed
531   bool startedOne = false;
532   OpalMediaFormat sourceFormat, sinkFormat;
533 
534   // Reorder destinations so we give preference to symmetric codecs
535   if (sinkStream != NULL)
536     sinkFormat = sinkStream->GetMediaFormat();
537 
538   PSafePtr<OpalConnection> otherConnection;
539   while (EnumerateConnections(otherConnection, PSafeReadWrite, &connection)) {
540     PStringArray order;
541     if (sinkFormat.IsValid())
542       order += sinkFormat.GetName(); // Preferential treatment to format already in use
543     order += '@' + mediaType;        // And media of the same type
544 
545     OpalMediaFormatList sinkMediaFormats = otherConnection->GetMediaFormats();
546     if (sinkMediaFormats.IsEmpty()) {
547       PTRACE(2, "Call\tOpenSourceMediaStreams failed with no sink formats" << traceText);
548       return false;
549     }
550 
551     if (preselectedFormat.IsValid() && sinkMediaFormats.HasFormat(preselectedFormat))
552       sinkMediaFormats = preselectedFormat;
553     else
554       sinkMediaFormats.Reorder(order);
555 
556     OpalMediaFormatList sourceMediaFormats;
557     if (sourceFormat.IsValid())
558       sourceMediaFormats = sourceFormat; // Use the source format already established
559     else {
560       sourceMediaFormats = connection.GetMediaFormats();
561       if (sourceMediaFormats.IsEmpty()) {
562         PTRACE(2, "Call\tOpenSourceMediaStreams failed with no source formats" << traceText);
563         return false;
564       }
565 
566       if (preselectedFormat.IsValid() && sourceMediaFormats.HasFormat(preselectedFormat))
567         sourceMediaFormats = preselectedFormat;
568       else
569         sourceMediaFormats.Reorder(order);
570     }
571 
572     // Get other media directions format so we give preference to symmetric codecs
573     OpalMediaStreamPtr otherDirection = sessionID != 0 ? otherConnection->GetMediaStream(sessionID, true)
574                                                        : otherConnection->GetMediaStream(mediaType, true);
575     if (otherDirection != NULL) {
576       PString priorityFormat = otherDirection->GetMediaFormat().GetName();
577       sourceMediaFormats.Reorder(priorityFormat);
578       sinkMediaFormats.Reorder(priorityFormat);
579     }
580 
581 #if OPAL_VIDEO
582     if (contentRole != OpalVideoFormat::eNoRole) {
583       // Remove all media formats no supporting the role
584       OpalMediaFormatList * lists[2] = { &sourceMediaFormats, &sinkMediaFormats };
585       for (PINDEX i = 0; i < 2; i++) {
586         OpalMediaFormatList::iterator format = lists[i]->begin();
587         while (format != lists[i]->end()) {
588           if ( format->GetMediaType() != mediaType ||
589               (format->IsTransportable() &&
590               (format->GetOptionInteger(OpalVideoFormat::ContentRoleMaskOption())&OpalVideoFormat::ContentRoleBit(contentRole)) == 0))
591             *lists[i] -= *format++;
592           else
593             ++format;
594         }
595         if (lists[i]->IsEmpty()) {
596           PTRACE(2, "Call\tUnsupported Content Role " << contentRole << traceText);
597           return false;
598         }
599       }
600     }
601 #endif
602 
603     OpalMediaFormatList localMediaFormats = OpalMediaFormat::GetAllRegisteredMediaFormats();
604     if (!SelectMediaFormats(mediaType,
605                             sourceMediaFormats,
606                             sinkMediaFormats,
607                             localMediaFormats,
608                             sourceFormat,
609                             sinkFormat))
610       return false;
611 
612 #if OPAL_VIDEO
613     sourceFormat.SetOptionEnum(OpalVideoFormat::ContentRoleOption(), contentRole);
614     sinkFormat.SetOptionEnum(OpalVideoFormat::ContentRoleOption(), contentRole);
615 #endif
616 
617     if (sessionID == 0) {
618       sessionID = otherConnection->GetNextSessionID(mediaType, false);
619       PTRACE(3, "Call\tOpenSourceMediaStreams using session " << sessionID << " on " << connection);
620     }
621 
622     // Finally have the negotiated formats, open the streams
623     if (sourceStream == NULL) {
624       sourceStream = connection.OpenMediaStream(sourceFormat, sessionID, true);
625       if (sourceStream == NULL)
626         return false;
627       // If re-opening, need to disconnect from old patch. If new then this sets NULL to NULL.
628       sourceStream->SetPatch(NULL);
629     }
630 
631     sinkStream = otherConnection->OpenMediaStream(sinkFormat, sessionID, false);
632     if (sinkStream != NULL) {
633       if (!sourceStream.SetSafetyMode(PSafeReadOnly))
634         return false;
635 
636       OpalMediaPatch * patch = sourceStream->GetPatch();
637       if (patch == NULL) {
638         patch = manager.CreateMediaPatch(*sourceStream, sinkStream->RequiresPatchThread(sourceStream) &&
639                                                         sourceStream->RequiresPatchThread(sinkStream));
640         if (patch == NULL)
641           return false;
642       }
643 
644       sourceStream.SetSafetyMode(PSafeReference);
645 
646       if (patch->AddSink(sinkStream))
647         startedOne = true;
648       else
649         sinkStream->Close();
650     }
651   }
652 
653   if (!startedOne) {
654     if (sourceStream != NULL)
655       connection.RemoveMediaStream(*sourceStream);
656     return false;
657   }
658 
659   // if a patch was created, make sure the callback is called, just once per
660   // connection. Note must lock connection before stream or we can deadlock.
661   while (EnumerateConnections(otherConnection, PSafeReadWrite)) {
662     if (!sourceStream.SetSafetyMode(PSafeReadOnly))
663       return false;
664 
665     OpalMediaPatch * patch = sourceStream->GetPatch();
666     if (patch == NULL)
667       return false;
668 
669     otherConnection->OnPatchMediaStream(otherConnection == &connection, *patch);
670     sourceStream.SetSafetyMode(PSafeReference);
671   }
672 
673   return true;
674 }
675 
676 
SelectMediaFormats(const OpalMediaType & mediaType,const OpalMediaFormatList & srcFormats,const OpalMediaFormatList & dstFormats,const OpalMediaFormatList & allFormats,OpalMediaFormat & srcFormat,OpalMediaFormat & dstFormat) const677 bool OpalCall::SelectMediaFormats(const OpalMediaType & mediaType,
678                                   const OpalMediaFormatList & srcFormats,
679                                   const OpalMediaFormatList & dstFormats,
680                                   const OpalMediaFormatList & allFormats,
681                                   OpalMediaFormat & srcFormat,
682                                   OpalMediaFormat & dstFormat) const
683 {
684   if (OpalTranscoder::SelectFormats(mediaType, srcFormats, dstFormats, allFormats, srcFormat, dstFormat)) {
685     PTRACE(3, "Call\tSelected media formats " << srcFormat << " -> " << dstFormat);
686     return true;
687   }
688 
689   PTRACE(2, "Call\tSelectMediaFormats could not find compatible " << mediaType << " format:\n"
690             "  source formats=" << setfill(',') << srcFormats << "\n"
691             "   sink  formats=" << dstFormats << setfill(' '));
692   return false;
693 }
694 
695 
StartMediaStreams()696 void OpalCall::StartMediaStreams()
697 {
698   PSafePtr<OpalConnection> connection;
699   while (EnumerateConnections(connection, PSafeReadWrite))
700     connection->StartMediaStreams();
701 }
702 
703 
CloseMediaStreams()704 void OpalCall::CloseMediaStreams()
705 {
706   PSafePtr<OpalConnection> connection;
707   while (EnumerateConnections(connection, PSafeReadWrite))
708     connection->CloseMediaStreams();
709 }
710 
711 
OnRTPStatistics(const OpalConnection &,const RTP_Session &)712 void OpalCall::OnRTPStatistics(const OpalConnection & /*connection*/, const RTP_Session & /*session*/)
713 {
714 }
715 
716 
IsMediaBypassPossible(const OpalConnection & connection,unsigned sessionID) const717 PBoolean OpalCall::IsMediaBypassPossible(const OpalConnection & connection,
718                                      unsigned sessionID) const
719 {
720   PTRACE(3, "Call\tIsMediaBypassPossible " << connection << " session " << sessionID);
721 
722   PSafePtr<OpalConnection> otherConnection;
723   return EnumerateConnections(otherConnection, PSafeReadOnly, &connection) &&
724          manager.IsMediaBypassPossible(connection, *otherConnection, sessionID);
725 }
726 
727 
OnUserInputString(OpalConnection & connection,const PString & value)728 void OpalCall::OnUserInputString(OpalConnection & connection, const PString & value)
729 {
730   PSafePtr<OpalConnection> otherConnection;
731   while (EnumerateConnections(otherConnection, PSafeReadWrite)) {
732     if (otherConnection != &connection)
733       otherConnection->SendUserInputString(value);
734     else
735       connection.SetUserInput(value);
736   }
737 }
738 
739 
OnUserInputTone(OpalConnection & connection,char tone,int duration)740 void OpalCall::OnUserInputTone(OpalConnection & connection,
741                                char tone,
742                                int duration)
743 {
744   bool reprocess = duration > 0 && tone != ' ';
745 
746   PSafePtr<OpalConnection> otherConnection;
747   while (EnumerateConnections(otherConnection, PSafeReadWrite, &connection)) {
748     if (otherConnection->SendUserInputTone(tone, duration))
749       reprocess = false;
750   }
751 
752   if (reprocess)
753     connection.OnUserInputString(tone);
754 }
755 
756 
OnReleased(OpalConnection & connection)757 void OpalCall::OnReleased(OpalConnection & connection)
758 {
759   PTRACE(3, "Call\tOnReleased " << connection);
760 
761   SetCallEndReason(connection.GetCallEndReason());
762 
763   connectionsActive.Remove(&connection);
764 
765   // A call will evaporate when one connection left, at some point this is
766   // to be changes so can have "parked" connections.
767   if(connectionsActive.GetSize() == 1)
768   {
769     PSafePtr<OpalConnection> last = connectionsActive.GetAt(0, PSafeReference);
770     if (last != NULL)
771       last->Release(connection.GetCallEndReason(), true);
772   }
773 
774   InternalOnClear();
775 }
776 
777 
OnHold(OpalConnection &,bool,bool)778 void OpalCall::OnHold(OpalConnection & /*connection*/,
779                       bool /*fromRemote*/,
780                       bool /*onHold*/)
781 {
782 }
783 
784 #if OPAL_HAS_MIXER
785 
StartRecording(const PFilePath & fn,const OpalRecordManager::Options & options)786 bool OpalCall::StartRecording(const PFilePath & fn, const OpalRecordManager::Options & options)
787 {
788   StopRecording();
789 
790   OpalRecordManager * newManager = OpalRecordManager::Factory::CreateInstance(fn.GetType());
791   if (newManager == NULL) {
792     PTRACE(2, "OPAL\tCannot record to file type " << fn);
793     return false;
794   }
795 
796   // create the mixer entry
797   if (!newManager->Open(fn, options)) {
798     delete newManager;
799     return false;
800   }
801 
802   PSafeLockReadWrite lock(*this);
803   if (!lock.IsLocked())
804     return false;
805 
806   m_recordManager = newManager;
807 
808   // tell each connection to start sending data
809   PSafePtr<OpalConnection> connection;
810   while (EnumerateConnections(connection, PSafeReadWrite))
811     connection->EnableRecording();
812 
813   return true;
814 }
815 
IsRecording() const816 bool OpalCall::IsRecording() const
817 {
818   PSafeLockReadOnly lock(*this);
819   return m_recordManager != NULL && m_recordManager->IsOpen();
820 }
821 
822 
StopRecording()823 void OpalCall::StopRecording()
824 {
825   PSafeLockReadWrite lock(*this);
826   if (!lock.IsLocked() || m_recordManager == NULL)
827     return;
828 
829   // tell each connection to stop sending data
830   PSafePtr<OpalConnection> connection;
831   while (EnumerateConnections(connection, PSafeReadWrite))
832     connection->DisableRecording();
833 
834   m_recordManager->Close();
835   delete m_recordManager;
836   m_recordManager = NULL;
837 }
838 
839 
OnStartRecording(const PString & streamId,const OpalMediaFormat & format)840 bool OpalCall::OnStartRecording(const PString & streamId, const OpalMediaFormat & format)
841 {
842   return m_recordManager != NULL && m_recordManager->OpenStream(streamId, format);
843 }
844 
845 
OnStopRecording(const PString & streamId)846 void OpalCall::OnStopRecording(const PString & streamId)
847 {
848   if (m_recordManager != NULL)
849     m_recordManager->CloseStream(streamId);
850 }
851 
852 
OnRecordAudio(const PString & streamId,const RTP_DataFrame & frame)853 void OpalCall::OnRecordAudio(const PString & streamId, const RTP_DataFrame & frame)
854 {
855   if (m_recordManager != NULL && !m_recordManager->WriteAudio(streamId, frame))
856     m_recordManager->CloseStream(streamId);
857 }
858 
859 
860 #if OPAL_VIDEO
861 
OnRecordVideo(const PString & streamId,const RTP_DataFrame & frame)862 void OpalCall::OnRecordVideo(const PString & streamId, const RTP_DataFrame & frame)
863 {
864   if (m_recordManager != NULL && !m_recordManager->WriteVideo(streamId, frame))
865     m_recordManager->CloseStream(streamId);
866 }
867 
868 #endif
869 
870 #endif
871 
872 
IsNetworkOriginated() const873 bool OpalCall::IsNetworkOriginated() const
874 {
875   PSafePtr<OpalConnection> connection = PSafePtr<OpalConnection>(connectionsActive, PSafeReadOnly);
876   return connection == NULL || connection->IsNetworkConnection();
877 }
878 
879 
SetPartyNames()880 void OpalCall::SetPartyNames()
881 {
882   PSafeLockReadWrite lock(*this);
883   if (!lock.IsLocked())
884     return;
885 
886   PSafePtr<OpalConnection> connectionA = connectionsActive.GetAt(0, PSafeReadOnly);
887   if (connectionA == NULL)
888     return;
889 
890   bool networkA = connectionA->IsNetworkConnection();
891   if (networkA)
892     m_partyA = connectionA->GetRemotePartyURL();
893   if (!networkA || m_partyA.IsEmpty())
894     m_partyA = connectionA->GetLocalPartyURL();
895 
896   PSafePtr<OpalConnection> connectionB = connectionsActive.GetAt(1, PSafeReadOnly);
897   if (connectionB == NULL)
898     return;
899 
900   if (connectionB->IsNetworkConnection()) {
901     if (!networkA)
902       connectionA->CopyPartyNames(*connectionB);
903     m_partyB = connectionB->GetRemotePartyURL();
904   }
905   else {
906     if (networkA) {
907       connectionB->CopyPartyNames(*connectionA);
908       m_partyB = connectionA->GetCalledPartyURL();
909     }
910     if (m_partyB.IsEmpty())
911       m_partyB = connectionB->GetLocalPartyURL();
912   }
913 }
914 
915 
EnumerateConnections(PSafePtr<OpalConnection> & connection,PSafetyMode mode,const OpalConnection * skipConnection) const916 bool OpalCall::EnumerateConnections(PSafePtr<OpalConnection> & connection,
917                                     PSafetyMode mode,
918                                     const OpalConnection * skipConnection) const
919 {
920   if (connection == NULL)
921     connection = PSafePtr<OpalConnection>(connectionsActive, PSafeReference);
922   else {
923     connection.SetSafetyMode(PSafeReference);
924     ++connection;
925   }
926 
927   while (connection != NULL) {
928     if (connection != skipConnection &&
929         connection->GetPhase() <= OpalConnection::EstablishedPhase &&
930         connection.SetSafetyMode(mode))
931       return true;
932     ++connection;
933   }
934 
935   return false;
936 }
937 
938 
939 /////////////////////////////////////////////////////////////////////////////
940