Reviewed and adjusted PTRACE log levels
[opal.git] / src / lids / lidep.cxx
blob0a1903ed556f4d5721068ec4dc6b34356e884e74
1 /*
2 * lidep.cxx
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
18 * under the License.
20 * The Original Code is Open H323 Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
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
76 * Added LID plug ins
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)
195 #include <ptlib.h>
197 #ifdef __GNUC__
198 #pragma implementation "lidep.cxx"
199 #endif
201 #include <lids/lidep.h>
203 #include <opal/manager.h>
204 #include <opal/call.h>
205 #include <opal/patch.h>
208 #define new PNEW
210 /////////////////////////////////////////////////////////////////////////////
212 OpalLIDEndPoint::OpalLIDEndPoint(OpalManager & mgr,
213 const PString & prefix,
214 unsigned attributes)
215 : OpalEndPoint(mgr, prefix, attributes),
216 defaultLine("*")
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());
231 exitFlag.Signal();
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
241 RemoveAllLines();
243 PTRACE(4, "LID EP\tOpalLIDEndPoint " << GetPrefixName() << " destroyed");
247 BOOL OpalLIDEndPoint::MakeConnection(OpalCall & call,
248 const PString & remoteParty,
249 void * userData,
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);
269 else {
270 if (HasAttribute(CanTerminateCall))
271 lineName = remoteParty.Mid(prefixLength + 1);
272 else
273 number = remoteParty.Mid(prefixLength + 1);
276 if (lineName.IsEmpty())
277 lineName = '*';
280 PTRACE(3,"LID EP\tMakeConnection line = \"" << lineName << "\", number = \"" << number << '"');
282 // Locate a line
283 OpalLine * line = GetLine(lineName, TRUE);
284 if (line == NULL){
285 PTRACE(1,"LID EP\tMakeConnection cannot find the line \"" << lineName << '"');
286 line = GetLine(defaultLine, TRUE);
288 if (line == NULL){
289 PTRACE(1,"LID EP\tMakeConnection cannot find the default line " << defaultLine);
290 return FALSE;
294 return AddConnection(CreateConnection(call, *line, userData, number));
298 OpalMediaFormatList OpalLIDEndPoint::GetMediaFormats() const
300 OpalMediaFormatList mediaFormats;
301 #if OPAL_VIDEO
302 AddVideoMediaFormats(mediaFormats);
303 #endif
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];
313 return mediaFormats;
317 OpalLineConnection * OpalLIDEndPoint::CreateConnection(OpalCall & call,
318 OpalLine & line,
319 void * /*userData*/,
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);
330 line->Ring(0, NULL);
331 line->StopTone();
332 line->StopReading();
333 line->StopWriting();
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)
346 return FALSE;
348 if (!line->GetDevice().IsOpen())
349 return FALSE;
351 if (line->IsTerminal() != HasAttribute(CanTerminateCall))
352 return FALSE;
354 InitialiseLine(line);
356 linesMutex.Wait();
357 lines.Append(line);
358 linesMutex.Signal();
360 return TRUE;
364 void OpalLIDEndPoint::RemoveLine(OpalLine * line)
366 linesMutex.Wait();
367 lines.Remove(line);
368 linesMutex.Signal();
372 void OpalLIDEndPoint::RemoveLine(const PString & token)
374 linesMutex.Wait();
375 for (PINDEX i = 0; i < lines.GetSize(); i++) {
376 if (lines[i].GetToken() *= token)
377 lines.RemoveAt(i--);
379 linesMutex.Signal();
383 void OpalLIDEndPoint::RemoveAllLines()
385 linesMutex.Wait();
386 lines.RemoveAll();
387 devices.RemoveAll();
388 linesMutex.Signal();
391 BOOL OpalLIDEndPoint::AddLinesFromDevice(OpalLineInterfaceDevice & device)
393 if (!device.IsOpen()){
394 PTRACE(1, "LID EP\tAddLinesFromDevice device " << device.GetDeviceName() << "is not opened");
395 return FALSE;
398 BOOL atLeastOne = FALSE;
400 linesMutex.Wait();
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);
411 atLeastOne = TRUE;
415 linesMutex.Signal();
417 return atLeastOne;
421 void OpalLIDEndPoint::RemoveLinesFromDevice(OpalLineInterfaceDevice & device)
423 linesMutex.Wait();
424 for (PINDEX i = 0; i < lines.GetSize(); i++) {
425 if (lines[i].GetToken().Find(device.GetDeviceName()) == 0)
426 lines.RemoveAt(i--);
428 linesMutex.Signal();
432 BOOL OpalLIDEndPoint::AddDeviceNames(const PStringArray & descriptors)
434 BOOL ok = FALSE;
436 for (PINDEX i = 0; i < descriptors.GetSize(); i++) {
437 if (AddDeviceName(descriptors[i]))
438 ok = TRUE;
441 return ok;
445 BOOL OpalLIDEndPoint::AddDeviceName(const PString & descriptor)
447 PINDEX colon = descriptor.Find(':');
448 if (colon == P_MAX_INDEX)
449 return FALSE;
451 PString deviceType = descriptor.Left(colon).Trim();
452 PString deviceName = descriptor.Mid(colon+1).Trim();
454 // Make sure not already there.
455 linesMutex.Wait();
456 for (PINDEX i = 0; i < devices.GetSize(); i++) {
457 if (devices[i].GetDeviceType() == deviceType && devices[i].GetDeviceName() == deviceName) {
458 linesMutex.Signal();
459 return TRUE;
462 linesMutex.Signal();
464 // Not there so add it.
465 OpalLineInterfaceDevice * device = OpalLineInterfaceDevice::Create(deviceType);
466 if (device == NULL)
467 return FALSE;
469 if (device->Open(deviceName))
470 return AddDevice(device);
472 delete device;
473 return FALSE;
477 BOOL OpalLIDEndPoint::AddDevice(OpalLineInterfaceDevice * device)
479 if (PAssertNULL(device) == NULL)
480 return FALSE;
482 linesMutex.Wait();
483 devices.Append(device);
484 linesMutex.Signal();
485 return AddLinesFromDevice(*device);
489 void OpalLIDEndPoint::RemoveDevice(OpalLineInterfaceDevice * device)
491 if (PAssertNULL(device) == NULL)
492 return;
494 RemoveLinesFromDevice(*device);
495 linesMutex.Wait();
496 devices.Remove(device);
497 linesMutex.Signal();
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 << '"');
517 return &lines[i];
521 return NULL;
525 void OpalLIDEndPoint::SetDefaultLine(const PString & lineName)
527 PTRACE(3, "LID EP\tSetDefaultLine " << lineName);
528 linesMutex.Wait();
529 defaultLine = lineName;
530 linesMutex.Signal();
534 void OpalLIDEndPoint::MonitorLines(PThread &, INT)
536 PTRACE(4, "LID EP\tMonitor thread started for " << GetPrefixName());
538 while (!exitFlag.Wait(100)) {
539 linesMutex.Wait();
540 for (PINDEX i = 0; i < lines.GetSize(); i++)
541 MonitorLine(lines[i]);
542 linesMutex.Signal();
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());
555 return;
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.");
562 line.StopTone();
563 line.DisableAudio();
565 return;
568 if (line.IsTerminal()) {
569 // Not off hook, so nothing happening, just return
570 if (!line.IsOffHook())
571 return;
572 PTRACE(3, "LID EP\tLine " << line << " has gone off hook.");
574 else {
575 // Not ringing, so nothing happening, just return
576 if (!line.IsRinging())
577 return;
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())
585 return;
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();
594 #if 1
595 BOOL OpalLIDEndPoint::OnSetUpConnection(OpalLineConnection & PTRACE_PARAM(connection))
597 PTRACE(3, "LID EP\tOnSetUpConnection" << connection);
598 return TRUE;
600 #endif
603 /////////////////////////////////////////////////////////////////////////////
605 OpalLineConnection::OpalLineConnection(OpalCall & call,
606 OpalLIDEndPoint & ep,
607 OpalLine & ln,
608 const PString & number)
609 : OpalConnection(call, ep, ln.GetToken()),
610 endpoint(ep),
611 line(ln)
613 remotePartyNumber = number.Right(number.Find(':'));
614 silenceDetector = new OpalLineSilenceDetector(line);
616 answerRingCount = 3;
617 requireTonesForDial = TRUE;
618 wasOffHook = FALSE;
619 handlerThread = NULL;
621 m_uiDialDelay = 0;
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);
643 line.Ring(0, NULL);
644 line.SetOnHook();
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.");
667 return TRUE;
670 PTRACE(3, "LID Con\tSetAlerting " << *this);
672 if (GetPhase() != SetUpPhase)
673 return FALSE;
675 // switch phase
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.");
688 return TRUE;
691 PTRACE(3, "LID Con\tSetConnected " << *this);
693 if (GetPhase() < ConnectedPhase) {
694 // switch phase
695 phase = ConnectedPhase;
696 connectedTime = PTime();
698 return line.StopTone();
700 return FALSE;
704 OpalMediaFormatList OpalLineConnection::GetMediaFormats() const
706 OpalMediaFormatList formats = line.GetDevice().GetMediaFormats();
707 #if OPAL_VIDEO
708 AddVideoMediaFormats(formats);
709 #endif
710 return formats;
714 OpalMediaStream * OpalLineConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat,
715 unsigned sessionID,
716 BOOL isSource)
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))
728 return FALSE;
730 if (mediaStream.IsSource()) {
731 OpalMediaPatch * patch = mediaStream.GetPatch();
732 if (patch != NULL) {
733 silenceDetector->SetParameters(endpoint.GetManager().GetSilenceDetectParams());
734 patch->AddFilter(silenceDetector->GetReceiveHandler(), line.GetReadFormat());
738 return TRUE;
742 BOOL OpalLineConnection::SetAudioVolume(BOOL source, unsigned percentage)
744 OpalLineMediaStream * stream = PDownCast(OpalLineMediaStream, GetMediaStream(OpalMediaFormat::DefaultAudioSessionID, source));
745 if (stream == NULL)
746 return FALSE;
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));
756 if (stream == NULL)
757 return UINT_MAX;
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)
772 if (duration <= 0)
773 return line.PlayDTMF(&tone);
774 else
775 return line.PlayDTMF(&tone, duration);
779 BOOL OpalLineConnection::PromptUserInput(BOOL play)
781 PTRACE(3, "LID Con\tConnection " << callToken << " dial tone " << (play ? "on" : "off"));
783 if (play)
784 return line.PlayTone(OpalLineInterfaceDevice::DialTone);
785 else
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);
806 if (!offHook) {
807 Release(EndedByRemoteUser);
808 return;
811 if (IsOriginating()) {
812 // Ok, they went off hook, stop ringing
813 line.Ring(0, NULL);
815 // If we are in alerting state then we are B-Party
816 if (phase == AlertingPhase) {
817 phase = ConnectedPhase;
818 OnConnected();
820 else {
821 // Otherwise we are A-Party
822 if (!OnIncomingConnection(0, NULL)) {
823 Release(EndedByCallerAbort);
824 return;
827 PTRACE(3, "LID Con\tOutgoing connection " << *this << " routed to \"" << ownerCall.GetPartyB() << '"');
828 if (!ownerCall.OnSetUp(*this)) {
829 Release(EndedByNoAccept);
830 return;
836 // If we are off hook, we continually suck out DTMF tones and pass them up
837 if (offHook) {
838 char tone;
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);
849 phase = SetUpPhase;
851 if (line.IsTerminal())
852 remotePartyName = line.GetDescription();
853 else {
854 // Count incoming rings
855 unsigned count;
856 do {
857 count = line.GetRingCount();
858 if (count == 0) {
859 PTRACE(3, "LID Con\tIncoming PSTN call stopped.");
860 Release(EndedByCallerAbort);
861 return;
863 PThread::Sleep(100);
864 if (phase >= ReleasingPhase)
865 return;
866 } while (count < answerRingCount);
868 // Get caller ID
869 PString callerId;
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 << '"');
875 else {
876 remotePartyNumber = words[0].Trim();
877 remotePartyName = words[1].Trim();
878 if (remotePartyName.IsEmpty())
879 remotePartyName = remotePartyNumber;
883 line.SetOffHook();
886 wasOffHook = TRUE;
888 if (!OnIncomingConnection(0, NULL)) {
889 Release(EndedByCallerAbort);
890 return;
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 << '"');
902 phase = SetUpPhase;
903 originating = TRUE;
905 if (line.IsTerminal()) {
906 line.SetCallerID(remotePartyNumber);
907 line.Ring(1, NULL);
908 if (ownerCall.GetConnection(0) != this) {
909 // Are B-Party, so move to alerting state
910 phase = AlertingPhase;
911 OnAlerting();
914 else {
915 switch (line.DialOut(remotePartyNumber, requireTonesForDial, getDialDelay())) {
916 case OpalLineInterfaceDevice::DialTone :
917 PTRACE(3, "LID Con\tdial tone on " << line);
918 return FALSE;
920 case OpalLineInterfaceDevice::RingTone :
921 PTRACE(3, "LID Con\tGot ringback on " << line);
922 phase = AlertingPhase;
923 OnAlerting();
924 break;
926 default :
927 PTRACE(1, "LID Con\tError dialling " << remotePartyNumber << " on " << line);
928 return FALSE;
932 return TRUE;
936 ///////////////////////////////////////////////////////////////////////////////
938 OpalLineMediaStream::OpalLineMediaStream(OpalLineConnection & conn,
939 const OpalMediaFormat & mediaFormat,
940 unsigned sessionID,
941 BOOL isSource,
942 OpalLine & ln)
943 : OpalMediaStream(conn, mediaFormat, sessionID, isSource),
944 line(ln)
946 useDeblocking = FALSE;
947 missedCount = 0;
948 lastSID[0] = 2;
949 lastFrameWasSignal = TRUE;
953 BOOL OpalLineMediaStream::Open()
955 if (isOpen)
956 return TRUE;
958 if (IsSource()) {
959 if (!line.SetReadFormat(mediaFormat))
960 return FALSE;
961 useDeblocking = !line.SetReadFrameSize(GetDataSize()) || line.GetReadFrameSize() != GetDataSize();
963 else {
964 if (!line.SetWriteFormat(mediaFormat))
965 return FALSE;
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()
980 if (IsSource())
981 line.StopReading();
982 else
983 line.StopWriting();
985 return OpalMediaStream::Close();
989 BOOL OpalLineMediaStream::ReadData(BYTE * buffer, PINDEX size, PINDEX & length)
991 length = 0;
993 if (IsSink()) {
994 PTRACE(1, "Media\tTried to read from sink media stream");
995 return FALSE;
998 if (useDeblocking) {
999 line.SetReadFrameSize(size);
1000 if (line.ReadBlock(buffer, size)) {
1001 length = size;
1002 return TRUE;
1005 else {
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) {
1010 switch (length) {
1011 case 1 : // CNG frame
1012 memcpy(buffer, lastSID, 4);
1013 length = 4;
1014 lastFrameWasSignal = FALSE;
1015 break;
1016 case 4 :
1017 if ((*(BYTE *)buffer&3) == 2)
1018 memcpy(lastSID, buffer, 4);
1019 lastFrameWasSignal = FALSE;
1020 break;
1021 default :
1022 lastFrameWasSignal = TRUE;
1025 return TRUE;
1029 PTRACE_IF(1, line.GetDevice().GetErrorNumber() != 0,
1030 "Media\tDevice read frame error: " << line.GetDevice().GetErrorText());
1032 return FALSE;
1036 BOOL OpalLineMediaStream::WriteData(const BYTE * buffer, PINDEX length, PINDEX & written)
1038 written = 0;
1040 if (IsSource()) {
1041 PTRACE(1, "Media\tTried to write to source media stream");
1042 return FALSE;
1045 // Check for writing silence
1046 PBYTEArray silenceBuffer;
1047 if (length != 0)
1048 missedCount = 0;
1049 else {
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;
1055 length = 24;
1057 else {
1058 static const BYTE g723_cng_frame[4] = { 3 };
1059 buffer = g723_cng_frame;
1060 length = 1;
1062 break;
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);
1069 break;
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;
1075 length = 2;
1076 break;
1078 // Else fall into default case
1080 default :
1081 buffer = silenceBuffer.GetPointer(line.GetWriteFrameSize()); // Fills with zeros
1082 length = silenceBuffer.GetSize();
1083 break;
1087 if (useDeblocking) {
1088 line.SetWriteFrameSize(length);
1089 if (line.WriteBlock(buffer, length)) {
1090 written = length;
1091 return TRUE;
1094 else {
1095 if (line.WriteFrame(buffer, length, written))
1096 return TRUE;
1099 PTRACE_IF(1, line.GetDevice().GetErrorNumber() != 0,
1100 "Media\tLID write frame error: " << line.GetDevice().GetErrorText());
1102 return FALSE;
1106 BOOL OpalLineMediaStream::SetDataSize(PINDEX dataSize)
1108 if (IsSource())
1109 useDeblocking = !line.SetReadFrameSize(dataSize) || line.GetReadFrameSize() != dataSize;
1110 else
1111 useDeblocking = !line.SetWriteFrameSize(dataSize) || line.GetWriteFrameSize() != dataSize;
1112 return OpalMediaStream::SetDataSize(dataSize);
1116 BOOL OpalLineMediaStream::IsSynchronous() const
1118 return TRUE;
1122 /////////////////////////////////////////////////////////////////////////////
1124 OpalLineSilenceDetector::OpalLineSilenceDetector(OpalLine & l)
1125 : line(l)
1130 unsigned OpalLineSilenceDetector::GetAverageSignalLevel(const BYTE *, PINDEX)
1132 return line.GetAverageSignalLevel(TRUE);
1136 /////////////////////////////////////////////////////////////////////////////