4 * Line Interface Device EndPoint
6 * Open Phone Abstraction Library
8 * Copyright (c) 2001 Equivalence Pty. Ltd.
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/
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
20 * The Original Code is Open H323 Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 2.44 2007/04/04 02:12:00 rjongbloed
28 * Reviewed and adjusted PTRACE log levels
29 * Now follows 1=error,2=warn,3=info,4+=debug
31 * Revision 2.43 2007/04/03 07:40:24 rjongbloed
32 * Fixed CreateCall usage so correct function (with userData) is called on
33 * incoming connections.
35 * Revision 2.42 2007/03/29 23:55:46 rjongbloed
36 * Tidied some code when a new connection is created by an endpoint. Now
37 * if someone needs to derive a connection class they can create it without
38 * needing to remember to do any more than the new.
40 * Revision 2.41 2007/03/29 05:16:49 csoutheren
41 * Pass OpalConnection to OpalMediaSream constructor
42 * Add ID to OpalMediaStreams so that transcoders can match incoming and outgoing codecs
44 * Revision 2.40 2007/03/13 02:17:46 csoutheren
45 * Remove warnings/errors when compiling with various turned off
47 * Revision 2.39 2007/03/13 00:33:10 csoutheren
48 * Simple but messy changes to allow compile time removal of protocol
49 * options such as H.450 and H.460
50 * Fix MakeConnection overrides
52 * Revision 2.38 2007/03/01 05:51:04 rjongbloed
53 * Fixed backward compatibility of OnIncomingConnection() virtual
54 * functions on various classes. If an old override returned FALSE
55 * then it will now abort the call as it used to.
57 * Revision 2.37 2007/01/24 04:00:57 csoutheren
58 * Arrrghh. Changing OnIncomingConnection turned out to have a lot of side-effects
59 * Added some pure viritual functions to prevent old code from breaking silently
60 * New OpalEndpoint and OpalConnection descendants will need to re-implement
61 * OnIncomingConnection. Sorry :)
63 * Revision 2.36 2006/12/18 03:18:42 csoutheren
64 * Messy but simple fixes
65 * - Add access to SIP REGISTER timeout
66 * - Ensure OpalConnection options are correctly progagated
68 * Revision 2.35 2006/10/22 12:05:56 rjongbloed
69 * Fixed correct usage of read/write buffer sizes in LID endpoints.
71 * Revision 2.34 2006/10/15 06:23:35 rjongbloed
72 * Fixed the mechanism where both A-party and B-party are indicated by the application. This now works
73 * for LIDs as well as PC endpoint, wheich is the only one that was used before.
75 * Revision 2.33 2006/10/02 13:30:51 rjongbloed
78 * Revision 2.32 2006/09/28 07:42:18 csoutheren
79 * Merge of useful SRTP implementation
81 * Revision 2.31 2006/08/29 08:47:43 rjongbloed
82 * Added functions to get average audio signal level from audio streams in
83 * suitable connection types.
85 * Revision 2.30 2006/08/17 23:09:04 rjongbloed
86 * Added volume controls
88 * Revision 2.29 2006/07/27 22:34:21 rjongbloed
89 * Fixed compiler warning
91 * Revision 2.28 2006/06/27 13:50:24 csoutheren
92 * Patch 1375137 - Voicetronix patches and lid enhancements
93 * Thanks to Frederich Heem
95 * Revision 2.27 2006/03/08 10:38:01 csoutheren
96 * Applied patch #1441139 - virtualise LID functions and kill monitorlines correctly
97 * Thanks to Martin Yarwood
99 * Revision 2.26 2006/01/14 10:43:06 dsandras
100 * Applied patch from Brian Lu <Brian.Lu _AT_____ sun.com> to allow compilation
101 * with OpenSolaris compiler. Many thanks !!!
103 * Revision 2.25 2004/10/06 13:03:42 rjongbloed
104 * Added "configure" support for known LIDs
105 * Changed LID GetName() function to be normalised against the GetAllNames()
106 * return values and fixed the pre-factory registration system.
107 * Added a GetDescription() function to do what the previous GetName() did.
109 * Revision 2.24 2004/08/14 07:56:31 rjongbloed
110 * Major revision to utilise the PSafeCollection classes for the connections and calls.
112 * Revision 2.23 2004/07/11 12:42:12 rjongbloed
113 * Added function on endpoints to get the list of all media formats any
114 * connection the endpoint may create can support.
116 * Revision 2.22 2004/05/24 13:52:30 rjongbloed
117 * Added a separate structure for the silence suppression paramters to make
118 * it easier to set from global defaults in the manager class.
120 * Revision 2.21 2004/05/17 13:24:18 rjongbloed
121 * Added silence suppression.
123 * Revision 2.20 2004/04/25 08:34:08 rjongbloed
124 * Fixed various GCC 3.4 warnings
126 * Revision 2.19 2004/04/18 13:35:27 rjongbloed
127 * Fixed ability to make calls where both endpoints are specified a priori. In particular
128 * fixing the VXML support for an outgoing sip/h323 call.
130 * Revision 2.18 2003/06/02 02:56:41 rjongbloed
131 * Moved LID specific media stream class to LID source file.
133 * Revision 2.17 2003/03/24 07:18:29 robertj
134 * Added registration system for LIDs so can work with various LID types by
135 * name instead of class instance.
137 * Revision 2.16 2003/03/17 10:26:59 robertj
138 * Added video support.
140 * Revision 2.15 2003/03/06 03:57:47 robertj
141 * IVR support (work in progress) requiring large changes everywhere.
143 * Revision 2.14 2002/09/04 05:28:14 robertj
144 * Added ability to set default line name to be used when the destination
145 * does not match any lines configured.
147 * Revision 2.13 2002/04/09 00:18:39 robertj
148 * Added correct setting of originating flag when originating the connection.
150 * Revision 2.12 2002/01/22 05:16:59 robertj
151 * Revamp of user input API triggered by RFC2833 support
153 * Revision 2.11 2001/11/14 01:31:55 robertj
154 * Corrected placement of adjusting media format list.
156 * Revision 2.10 2001/11/13 06:25:56 robertj
157 * Changed SetUpConnection() so returns BOOL as returning
158 * pointer to connection is not useful.
160 * Revision 2.9 2001/10/15 04:31:56 robertj
161 * Removed answerCall signal and replaced with state based functions.
163 * Revision 2.8 2001/10/04 00:47:02 robertj
164 * Removed redundent parameter.
166 * Revision 2.7 2001/08/23 05:51:17 robertj
167 * Completed implementation of codec reordering.
169 * Revision 2.6 2001/08/23 03:15:51 robertj
170 * Added missing Lock() calls in SetUpConnection
172 * Revision 2.5 2001/08/22 10:20:09 robertj
173 * Changed connection locking to use double mutex to guarantee that
174 * no threads can ever deadlock or access deleted connection.
176 * Revision 2.4 2001/08/17 08:36:48 robertj
177 * Moved call end reasons enum from OpalConnection to global.
178 * Fixed missing mutex on connections structure..
180 * Revision 2.3 2001/08/17 01:11:17 robertj
181 * Added ability to add whole LID's to LID endpoint.
183 * Revision 2.2 2001/08/01 06:23:55 robertj
184 * Changed to use separate mutex for LIDs structure to avoid Unix nested mutex problem.
186 * Revision 2.1 2001/08/01 05:24:01 robertj
187 * Made OpalMediaFormatList class global to help with documentation.
188 * Made the connections media format list to come from LID.
190 * Revision 2.0 2001/07/27 15:48:25 robertj
191 * Conversion of OpenH323 to Open Phone Abstraction Library (OPAL)
198 #pragma implementation "lidep.cxx"
201 #include <lids/lidep.h>
203 #include <opal/manager.h>
204 #include <opal/call.h>
205 #include <opal/patch.h>
210 /////////////////////////////////////////////////////////////////////////////
212 OpalLIDEndPoint::OpalLIDEndPoint(OpalManager
& mgr
,
213 const PString
& prefix
,
215 : OpalEndPoint(mgr
, prefix
, attributes
),
218 PTRACE(4, "LID EP\tOpalLIDEndPoint " << prefix
<< " created");
219 monitorThread
= PThread::Create(PCREATE_NOTIFIER(MonitorLines
), 0,
220 PThread::NoAutoDeleteThread
,
221 PThread::LowPriority
,
222 prefix
.ToUpper() & "Line Monitor");
226 OpalLIDEndPoint::~OpalLIDEndPoint()
228 if(NULL
!= monitorThread
)
230 PTRACE(4, "LID EP\tAwaiting monitor thread termination " << GetPrefixName());
232 monitorThread
->WaitForTermination();
233 delete monitorThread
;
234 monitorThread
= NULL
;
236 /* remove all lines which has been added with AddLine or AddLinesFromDevice
237 RemoveAllLines can be invoked only after the monitorThread has been destroyed,
238 indeed, the monitor thread may called some function such as vpb_get_event_ch_async which may
239 throw an exception if the line handle is no longer valid
243 PTRACE(4, "LID EP\tOpalLIDEndPoint " << GetPrefixName() << " destroyed");
247 BOOL
OpalLIDEndPoint::MakeConnection(OpalCall
& call
,
248 const PString
& remoteParty
,
250 unsigned int /*options*/,
251 OpalConnection::StringOptions
*)
253 PTRACE(3, "LID EP\tMakeConnection remoteParty " << remoteParty
<< ", prefix "<< GetPrefixName());
254 // First strip of the prefix if present
255 PINDEX prefixLength
= 0;
256 PINDEX colonIndex
= remoteParty
.Find(":");
257 if ((remoteParty
.Find(GetPrefixName()) == 0) && (colonIndex
!= P_MAX_INDEX
)){
258 /* the remote party contains the prefix and ':' */
259 prefixLength
= colonIndex
;
262 // Then see if there is a specific line mentioned in the prefix, e.g vpb@1/2:123456
263 PString number
, lineName
;
264 PINDEX at
= remoteParty
.Left(prefixLength
).Find('@');
265 if (at
!= P_MAX_INDEX
) {
266 number
= remoteParty
.Right(remoteParty
.GetLength() - prefixLength
- 1);
267 lineName
= remoteParty(GetPrefixName().GetLength() + 1, prefixLength
- 1);
270 if (HasAttribute(CanTerminateCall
))
271 lineName
= remoteParty
.Mid(prefixLength
+ 1);
273 number
= remoteParty
.Mid(prefixLength
+ 1);
276 if (lineName
.IsEmpty())
280 PTRACE(3,"LID EP\tMakeConnection line = \"" << lineName
<< "\", number = \"" << number
<< '"');
283 OpalLine
* line
= GetLine(lineName
, TRUE
);
285 PTRACE(1,"LID EP\tMakeConnection cannot find the line \"" << lineName
<< '"');
286 line
= GetLine(defaultLine
, TRUE
);
289 PTRACE(1,"LID EP\tMakeConnection cannot find the default line " << defaultLine
);
294 return AddConnection(CreateConnection(call
, *line
, userData
, number
));
298 OpalMediaFormatList
OpalLIDEndPoint::GetMediaFormats() const
300 OpalMediaFormatList mediaFormats
;
302 AddVideoMediaFormats(mediaFormats
);
305 PWaitAndSignal
mutex(linesMutex
);
307 for (PINDEX i
= 0; i
< lines
.GetSize(); i
++) {
308 OpalMediaFormatList devFormats
= lines
[i
].GetDevice().GetMediaFormats();
309 for (PINDEX j
= 0; j
< devFormats
.GetSize(); j
++)
310 mediaFormats
+= devFormats
[j
];
317 OpalLineConnection
* OpalLIDEndPoint::CreateConnection(OpalCall
& call
,
320 const PString
& number
)
322 PTRACE(3, "LID EP\tCreateConnection call = " << call
<< " line = " << line
<< " number = " << number
);
323 return new OpalLineConnection(call
, *this, line
, number
);
327 static void InitialiseLine(OpalLine
* line
)
329 PTRACE(3, "LID EP\tInitialiseLine " << *line
);
334 line
->DisableAudio();
336 for (unsigned lnum
= 0; lnum
< line
->GetDevice().GetLineCount(); lnum
++) {
337 if (lnum
!= line
->GetLineNumber())
338 line
->GetDevice().SetLineToLineDirect(lnum
, line
->GetLineNumber(), FALSE
);
343 BOOL
OpalLIDEndPoint::AddLine(OpalLine
* line
)
345 if (PAssertNULL(line
) == NULL
)
348 if (!line
->GetDevice().IsOpen())
351 if (line
->IsTerminal() != HasAttribute(CanTerminateCall
))
354 InitialiseLine(line
);
364 void OpalLIDEndPoint::RemoveLine(OpalLine
* line
)
372 void OpalLIDEndPoint::RemoveLine(const PString
& token
)
375 for (PINDEX i
= 0; i
< lines
.GetSize(); i
++) {
376 if (lines
[i
].GetToken() *= token
)
383 void OpalLIDEndPoint::RemoveAllLines()
391 BOOL
OpalLIDEndPoint::AddLinesFromDevice(OpalLineInterfaceDevice
& device
)
393 if (!device
.IsOpen()){
394 PTRACE(1, "LID EP\tAddLinesFromDevice device " << device
.GetDeviceName() << "is not opened");
398 BOOL atLeastOne
= FALSE
;
403 PTRACE(3, "LID EP\tAddLinesFromDevice device " << device
.GetDeviceName() <<
404 " has " << device
.GetLineCount() << " lines, CanTerminateCall = " << HasAttribute(CanTerminateCall
));
405 for (unsigned line
= 0; line
< device
.GetLineCount(); line
++) {
406 PTRACE(3, "LID EP\tAddLinesFromDevice line " << line
<< ", terminal = " << device
.IsLineTerminal(line
));
407 if (device
.IsLineTerminal(line
) == HasAttribute(CanTerminateCall
)) {
408 OpalLine
* newLine
= new OpalLine(device
, line
);
409 InitialiseLine(newLine
);
410 lines
.Append(newLine
);
421 void OpalLIDEndPoint::RemoveLinesFromDevice(OpalLineInterfaceDevice
& device
)
424 for (PINDEX i
= 0; i
< lines
.GetSize(); i
++) {
425 if (lines
[i
].GetToken().Find(device
.GetDeviceName()) == 0)
432 BOOL
OpalLIDEndPoint::AddDeviceNames(const PStringArray
& descriptors
)
436 for (PINDEX i
= 0; i
< descriptors
.GetSize(); i
++) {
437 if (AddDeviceName(descriptors
[i
]))
445 BOOL
OpalLIDEndPoint::AddDeviceName(const PString
& descriptor
)
447 PINDEX colon
= descriptor
.Find(':');
448 if (colon
== P_MAX_INDEX
)
451 PString deviceType
= descriptor
.Left(colon
).Trim();
452 PString deviceName
= descriptor
.Mid(colon
+1).Trim();
454 // Make sure not already there.
456 for (PINDEX i
= 0; i
< devices
.GetSize(); i
++) {
457 if (devices
[i
].GetDeviceType() == deviceType
&& devices
[i
].GetDeviceName() == deviceName
) {
464 // Not there so add it.
465 OpalLineInterfaceDevice
* device
= OpalLineInterfaceDevice::Create(deviceType
);
469 if (device
->Open(deviceName
))
470 return AddDevice(device
);
477 BOOL
OpalLIDEndPoint::AddDevice(OpalLineInterfaceDevice
* device
)
479 if (PAssertNULL(device
) == NULL
)
483 devices
.Append(device
);
485 return AddLinesFromDevice(*device
);
489 void OpalLIDEndPoint::RemoveDevice(OpalLineInterfaceDevice
* device
)
491 if (PAssertNULL(device
) == NULL
)
494 RemoveLinesFromDevice(*device
);
496 devices
.Remove(device
);
501 OpalLine
* OpalLIDEndPoint::GetLine(const PString
& lineName
, BOOL enableAudio
) const
503 PWaitAndSignal
mutex(linesMutex
);
505 PTRACE(3, "LID EP\tGetLine " << lineName
<< ", enableAudio = " << enableAudio
);
506 for (PINDEX i
= 0; i
< lines
.GetSize(); i
++) {
507 PTRACE(3, "LID EP\tGetLine line " << i
<< ", description = \"" << lines
[i
].GetDescription() <<
508 "\", enableAudio = " << lines
[i
].EnableAudio());
509 PString lineDescription
= lines
[i
].GetDescription();
510 /* if the line description contains the endpoint prefix, strip it*/
511 if(lineDescription
.Find(GetPrefixName()) == 0){
512 lineDescription
.Delete(0, GetPrefixName().GetLength() + 1);
514 if ((lineName
== defaultLine
|| lineDescription
== lineName
) &&
515 (!enableAudio
|| lines
[i
].EnableAudio())){
516 PTRACE(3, "LID EP\tGetLine found the line \"" << lineName
<< '"');
525 void OpalLIDEndPoint::SetDefaultLine(const PString
& lineName
)
527 PTRACE(3, "LID EP\tSetDefaultLine " << lineName
);
529 defaultLine
= lineName
;
534 void OpalLIDEndPoint::MonitorLines(PThread
&, INT
)
536 PTRACE(4, "LID EP\tMonitor thread started for " << GetPrefixName());
538 while (!exitFlag
.Wait(100)) {
540 for (PINDEX i
= 0; i
< lines
.GetSize(); i
++)
541 MonitorLine(lines
[i
]);
545 PTRACE(4, "LID EP\tMonitor thread stopped for " << GetPrefixName());
549 void OpalLIDEndPoint::MonitorLine(OpalLine
& line
)
551 PSafePtr
<OpalLineConnection
> connection
= GetLIDConnectionWithLock(line
.GetToken());
552 if (connection
!= NULL
) {
553 // Are still in a call, pass hook state it to the connection object for handling
554 connection
->Monitor(!line
.IsDisconnected());
558 if (line
.IsAudioEnabled()) {
559 // Are still in previous call, wait for them to hang up
560 if (line
.IsDisconnected()) {
561 PTRACE(3, "LID EP\tLine " << line
<< " has disconnected.");
568 if (line
.IsTerminal()) {
569 // Not off hook, so nothing happening, just return
570 if (!line
.IsOffHook())
572 PTRACE(3, "LID EP\tLine " << line
<< " has gone off hook.");
575 // Not ringing, so nothing happening, just return
576 if (!line
.IsRinging())
578 PTRACE(3, "LID EP\tLine " << line
<< " is ringing.");
581 // See if we can get exlusive use of the line. With something like a LineJACK
582 // enabling audio on the PSTN line the POTS line will no longer be enable-able
583 // so this will fail and the ringing will be ignored
584 if (!line
.EnableAudio())
587 // Have incoming ring, create a new LID connection and let it handle it
588 connection
= CreateConnection(*manager
.CreateCall(NULL
), line
, NULL
, PString::Empty());
589 if (AddConnection(connection
))
590 connection
->StartIncoming();
595 BOOL
OpalLIDEndPoint::OnSetUpConnection(OpalLineConnection
& PTRACE_PARAM(connection
))
597 PTRACE(3, "LID EP\tOnSetUpConnection" << connection
);
603 /////////////////////////////////////////////////////////////////////////////
605 OpalLineConnection::OpalLineConnection(OpalCall
& call
,
606 OpalLIDEndPoint
& ep
,
608 const PString
& number
)
609 : OpalConnection(call
, ep
, ln
.GetToken()),
613 remotePartyNumber
= number
.Right(number
.Find(':'));
614 silenceDetector
= new OpalLineSilenceDetector(line
);
617 requireTonesForDial
= TRUE
;
619 handlerThread
= NULL
;
622 PTRACE(3, "LID Con\tConnection " << callToken
<< " created to " << number
<<
623 " remotePartyNumber = " << remotePartyNumber
);
628 void OpalLineConnection::OnReleased()
630 PTRACE(3, "LID Con\tOnReleased " << *this);
632 if (handlerThread
!= NULL
) {
633 PTRACE(4, "LID Con\tAwaiting handler thread termination " << *this);
634 // Stop the signalling handler thread
635 SetUserInput(PString()); // Break out of ReadUserInput
636 handlerThread
->WaitForTermination();
637 delete handlerThread
;
638 handlerThread
= NULL
;
641 PTRACE(3, "LID Con\tPlaying clear tone until handset onhook");
642 line
.PlayTone(OpalLineInterfaceDevice::ClearTone
);
646 phase
= ReleasedPhase
;
648 OpalConnection::OnReleased();
652 PString
OpalLineConnection::GetDestinationAddress()
654 return ReadUserInput();
657 BOOL
OpalLineConnection::OnSetUpConnection()
659 PTRACE(3, "LID Con\tOnSetUpConnection");
660 return endpoint
.OnSetUpConnection(*this);
663 BOOL
OpalLineConnection::SetAlerting(const PString
& calleeName
, BOOL
)
665 if (IsOriginating()) {
666 PTRACE(2, "LID Con\tSetAlerting ignored on call we originated.");
670 PTRACE(3, "LID Con\tSetAlerting " << *this);
672 if (GetPhase() != SetUpPhase
)
676 phase
= AlertingPhase
;
677 alertingTime
= PTime();
679 line
.SetCallerID(calleeName
);
680 return line
.PlayTone(OpalLineInterfaceDevice::RingTone
);
684 BOOL
OpalLineConnection::SetConnected()
686 if (IsOriginating()) {
687 PTRACE(2, "LID Con\tSetConnected ignored on call we originated.");
691 PTRACE(3, "LID Con\tSetConnected " << *this);
693 if (GetPhase() < ConnectedPhase
) {
695 phase
= ConnectedPhase
;
696 connectedTime
= PTime();
698 return line
.StopTone();
704 OpalMediaFormatList
OpalLineConnection::GetMediaFormats() const
706 OpalMediaFormatList formats
= line
.GetDevice().GetMediaFormats();
708 AddVideoMediaFormats(formats
);
714 OpalMediaStream
* OpalLineConnection::CreateMediaStream(const OpalMediaFormat
& mediaFormat
,
718 if (sessionID
!= OpalMediaFormat::DefaultAudioSessionID
)
719 return OpalConnection::CreateMediaStream(mediaFormat
, sessionID
, isSource
);
721 return new OpalLineMediaStream(*this, mediaFormat
, sessionID
, isSource
, line
);
725 BOOL
OpalLineConnection::OnOpenMediaStream(OpalMediaStream
& mediaStream
)
727 if (!OpalConnection::OnOpenMediaStream(mediaStream
))
730 if (mediaStream
.IsSource()) {
731 OpalMediaPatch
* patch
= mediaStream
.GetPatch();
733 silenceDetector
->SetParameters(endpoint
.GetManager().GetSilenceDetectParams());
734 patch
->AddFilter(silenceDetector
->GetReceiveHandler(), line
.GetReadFormat());
742 BOOL
OpalLineConnection::SetAudioVolume(BOOL source
, unsigned percentage
)
744 OpalLineMediaStream
* stream
= PDownCast(OpalLineMediaStream
, GetMediaStream(OpalMediaFormat::DefaultAudioSessionID
, source
));
748 OpalLine
& line
= stream
->GetLine();
749 return source
? line
.SetRecordVolume(percentage
) : line
.SetPlayVolume(percentage
);
753 unsigned OpalLineConnection::GetAudioSignalLevel(BOOL source
)
755 OpalLineMediaStream
* stream
= PDownCast(OpalLineMediaStream
, GetMediaStream(OpalMediaFormat::DefaultAudioSessionID
, source
));
759 OpalLine
& line
= stream
->GetLine();
760 return line
.GetAverageSignalLevel(!source
);
764 BOOL
OpalLineConnection::SendUserInputString(const PString
& value
)
766 return line
.PlayDTMF(value
);
770 BOOL
OpalLineConnection::SendUserInputTone(char tone
, int duration
)
773 return line
.PlayDTMF(&tone
);
775 return line
.PlayDTMF(&tone
, duration
);
779 BOOL
OpalLineConnection::PromptUserInput(BOOL play
)
781 PTRACE(3, "LID Con\tConnection " << callToken
<< " dial tone " << (play
? "on" : "off"));
784 return line
.PlayTone(OpalLineInterfaceDevice::DialTone
);
786 return line
.StopTone();
790 void OpalLineConnection::StartIncoming()
792 if (handlerThread
== NULL
)
793 handlerThread
= PThread::Create(PCREATE_NOTIFIER(HandleIncoming
), 0,
794 PThread::NoAutoDeleteThread
,
795 PThread::LowPriority
,
796 "Line Connection:%x");
800 void OpalLineConnection::Monitor(BOOL offHook
)
802 if (wasOffHook
!= offHook
) {
803 wasOffHook
= offHook
;
804 PTRACE(3, "LID Con\tConnection " << callToken
<< " " << (offHook
? "off" : "on") << " hook: phase=" << phase
);
807 Release(EndedByRemoteUser
);
811 if (IsOriginating()) {
812 // Ok, they went off hook, stop ringing
815 // If we are in alerting state then we are B-Party
816 if (phase
== AlertingPhase
) {
817 phase
= ConnectedPhase
;
821 // Otherwise we are A-Party
822 if (!OnIncomingConnection(0, NULL
)) {
823 Release(EndedByCallerAbort
);
827 PTRACE(3, "LID Con\tOutgoing connection " << *this << " routed to \"" << ownerCall
.GetPartyB() << '"');
828 if (!ownerCall
.OnSetUp(*this)) {
829 Release(EndedByNoAccept
);
836 // If we are off hook, we continually suck out DTMF tones and pass them up
839 while ((tone
= line
.ReadDTMF()) != '\0')
840 OnUserInputTone(tone
, 180);
845 void OpalLineConnection::HandleIncoming(PThread
&, INT
)
847 PTRACE(3, "LID Con\tHandling incoming call on " << *this);
851 if (line
.IsTerminal())
852 remotePartyName
= line
.GetDescription();
854 // Count incoming rings
857 count
= line
.GetRingCount();
859 PTRACE(3, "LID Con\tIncoming PSTN call stopped.");
860 Release(EndedByCallerAbort
);
864 if (phase
>= ReleasingPhase
)
866 } while (count
< answerRingCount
);
870 if (line
.GetCallerID(callerId
, TRUE
)) {
871 PStringArray words
= callerId
.Tokenise('\t', TRUE
);
872 if (words
.GetSize() < 3) {
873 PTRACE(2, "LID Con\tMalformed caller ID \"" << callerId
<< '"');
876 remotePartyNumber
= words
[0].Trim();
877 remotePartyName
= words
[1].Trim();
878 if (remotePartyName
.IsEmpty())
879 remotePartyName
= remotePartyNumber
;
888 if (!OnIncomingConnection(0, NULL
)) {
889 Release(EndedByCallerAbort
);
893 PTRACE(3, "LID\tIncoming call routed for " << *this);
894 if (!ownerCall
.OnSetUp(*this))
895 Release(EndedByNoAccept
);
898 BOOL
OpalLineConnection::SetUpConnection()
900 PTRACE(3, "LID Con\tSetUpConnection call on " << *this << " to \"" << remotePartyNumber
<< '"');
905 if (line
.IsTerminal()) {
906 line
.SetCallerID(remotePartyNumber
);
908 if (ownerCall
.GetConnection(0) != this) {
909 // Are B-Party, so move to alerting state
910 phase
= AlertingPhase
;
915 switch (line
.DialOut(remotePartyNumber
, requireTonesForDial
, getDialDelay())) {
916 case OpalLineInterfaceDevice::DialTone
:
917 PTRACE(3, "LID Con\tdial tone on " << line
);
920 case OpalLineInterfaceDevice::RingTone
:
921 PTRACE(3, "LID Con\tGot ringback on " << line
);
922 phase
= AlertingPhase
;
927 PTRACE(1, "LID Con\tError dialling " << remotePartyNumber
<< " on " << line
);
936 ///////////////////////////////////////////////////////////////////////////////
938 OpalLineMediaStream::OpalLineMediaStream(OpalLineConnection
& conn
,
939 const OpalMediaFormat
& mediaFormat
,
943 : OpalMediaStream(conn
, mediaFormat
, sessionID
, isSource
),
946 useDeblocking
= FALSE
;
949 lastFrameWasSignal
= TRUE
;
953 BOOL
OpalLineMediaStream::Open()
959 if (!line
.SetReadFormat(mediaFormat
))
961 useDeblocking
= !line
.SetReadFrameSize(GetDataSize()) || line
.GetReadFrameSize() != GetDataSize();
964 if (!line
.SetWriteFormat(mediaFormat
))
966 useDeblocking
= !line
.SetWriteFrameSize(GetDataSize()) || line
.GetWriteFrameSize() != GetDataSize();
969 PTRACE(3, "Media\tStream set to " << mediaFormat
<< ", frame size: rd="
970 << line
.GetReadFrameSize() << " wr="
971 << line
.GetWriteFrameSize() << ", "
972 << (useDeblocking
? "needs" : "no") << " reblocking.");
974 return OpalMediaStream::Open();
978 BOOL
OpalLineMediaStream::Close()
985 return OpalMediaStream::Close();
989 BOOL
OpalLineMediaStream::ReadData(BYTE
* buffer
, PINDEX size
, PINDEX
& length
)
994 PTRACE(1, "Media\tTried to read from sink media stream");
999 line
.SetReadFrameSize(size
);
1000 if (line
.ReadBlock(buffer
, size
)) {
1006 if (line
.ReadFrame(buffer
, length
)) {
1007 // In the case of G.723.1 remember the last SID frame we sent and send
1008 // it again if the hardware sends us a CNG frame.
1009 if (mediaFormat
.GetPayloadType() == RTP_DataFrame::G7231
) {
1011 case 1 : // CNG frame
1012 memcpy(buffer
, lastSID
, 4);
1014 lastFrameWasSignal
= FALSE
;
1017 if ((*(BYTE
*)buffer
&3) == 2)
1018 memcpy(lastSID
, buffer
, 4);
1019 lastFrameWasSignal
= FALSE
;
1022 lastFrameWasSignal
= TRUE
;
1029 PTRACE_IF(1, line
.GetDevice().GetErrorNumber() != 0,
1030 "Media\tDevice read frame error: " << line
.GetDevice().GetErrorText());
1036 BOOL
OpalLineMediaStream::WriteData(const BYTE
* buffer
, PINDEX length
, PINDEX
& written
)
1041 PTRACE(1, "Media\tTried to write to source media stream");
1045 // Check for writing silence
1046 PBYTEArray silenceBuffer
;
1050 switch (mediaFormat
.GetPayloadType()) {
1051 case RTP_DataFrame::G7231
:
1052 if (missedCount
++ < 4) {
1053 static const BYTE g723_erasure_frame
[24] = { 0xff, 0xff, 0xff, 0xff };
1054 buffer
= g723_erasure_frame
;
1058 static const BYTE g723_cng_frame
[4] = { 3 };
1059 buffer
= g723_cng_frame
;
1064 case RTP_DataFrame::PCMU
:
1065 case RTP_DataFrame::PCMA
:
1066 buffer
= silenceBuffer
.GetPointer(line
.GetWriteFrameSize());
1067 length
= silenceBuffer
.GetSize();
1068 memset((void *)buffer
, 0xff, length
);
1071 case RTP_DataFrame::G729
:
1072 if (mediaFormat
.Find('B') != P_MAX_INDEX
) {
1073 static const BYTE g729_sid_frame
[2] = { 1 };
1074 buffer
= g729_sid_frame
;
1078 // Else fall into default case
1081 buffer
= silenceBuffer
.GetPointer(line
.GetWriteFrameSize()); // Fills with zeros
1082 length
= silenceBuffer
.GetSize();
1087 if (useDeblocking
) {
1088 line
.SetWriteFrameSize(length
);
1089 if (line
.WriteBlock(buffer
, length
)) {
1095 if (line
.WriteFrame(buffer
, length
, written
))
1099 PTRACE_IF(1, line
.GetDevice().GetErrorNumber() != 0,
1100 "Media\tLID write frame error: " << line
.GetDevice().GetErrorText());
1106 BOOL
OpalLineMediaStream::SetDataSize(PINDEX dataSize
)
1109 useDeblocking
= !line
.SetReadFrameSize(dataSize
) || line
.GetReadFrameSize() != dataSize
;
1111 useDeblocking
= !line
.SetWriteFrameSize(dataSize
) || line
.GetWriteFrameSize() != dataSize
;
1112 return OpalMediaStream::SetDataSize(dataSize
);
1116 BOOL
OpalLineMediaStream::IsSynchronous() const
1122 /////////////////////////////////////////////////////////////////////////////
1124 OpalLineSilenceDetector::OpalLineSilenceDetector(OpalLine
& l
)
1130 unsigned OpalLineSilenceDetector::GetAverageSignalLevel(const BYTE
*, PINDEX
)
1132 return line
.GetAverageSignalLevel(TRUE
);
1136 /////////////////////////////////////////////////////////////////////////////