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