1 /*
2 * main.cxx
3 *
4 * OPAL call generator
5 *
6 * Copyright (c) 2007 Equivalence Pty. Ltd.
7 *
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
16 * under the License.
17 *
18 * The Original Code is CallGen.
19 *
20 * Contributor(s): Equivalence Pty. Ltd.
21 *
22 * $Revision: 28052 $
23 * $Author: rjongbloed $
24 * $Date: 2012-07-18 02:24:41 -0500 (Wed, 18 Jul 2012) $
25 */
26
27 #include "precompile.h"
28 #include "main.h"
29 #include "version.h"
30
31 #include <opal/transcoders.h>
32
33
34 PCREATE_PROCESS(CallGen);
35
36
37 ///////////////////////////////////////////////////////////////////////////////
38
CallGen()39 CallGen::CallGen()
40 : PProcess("Equivalence", "CallGen", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER),
41 console(PConsoleChannel::StandardInput)
42 {
43 totalAttempts = 0;
44 totalEstablished = 0;
45 }
46
47
Main()48 void CallGen::Main()
49 {
50 PArgList & args = GetArguments();
51 args.Parse("a-access-token-oid:"
52 "c-cdr:"
53 "C-cycle."
54 "D-disable:"
55 "f-fast-disable."
56 "g-gatekeeper:"
57 "G-discover-gatekeeper."
58 "I-in-dir:"
59 "-h323-interface:"
60 "-sip-interface:"
61 "l-listen."
62 "m-max:"
63 "O-out-msg:"
64 #if PTRACING
65 "o-output:"
66 #endif
67 "P-prefer:"
68 "p-password:"
69 "q-quiet."
70 "r-repeat:"
71 "-require-gatekeeper."
72 "S-stun:"
73 "T-h245tunneldisable."
74 #if PTRACING
75 "t-trace."
76 #endif
77 "-tmaxest:"
78 "-tmincall:"
79 "-tmaxcall:"
80 "-tminwait:"
81 "-tmaxwait:"
82 "-tcp-base:"
83 "-tcp-max:"
84 "-udp-base:"
85 "-udp-max:"
86 "-rtp-base:"
87 "-rtp-max:"
88 "u-user:"
89 , FALSE);
90
91 if (args.GetCount() == 0 && !args.HasOption('l')) {
92 cout << "Usage:\n"
93 " " << GetFile().GetTitle() << " [options] -l\n"
94 " " << GetFile().GetTitle() << " [options] destination [ destination ... ]\n"
95 "where options:\n"
96 " -l Passive/listening mode.\n"
97 " -q --quiet Do not display call progress output.\n"
98 " -m --max num Maximum number of simultaneous calls\n"
99 " -r --repeat num Repeat calls n times\n"
100 " -C --cycle Each simultaneous call cycles through destination list\n"
101 " -S --stun server Specify Host/addres of STUN server\n"
102 " --sip-interface addr Specify IP address and port listen on for SIP [*:5060]\n"
103 " --h323-interface addr Specify IP address and port listen on for H.323 [*:1720]\n"
104 " -g --gatekeeper host Specify gatekeeper host [no gatekeeper]\n"
105 " -G --discover-gk Discover gatekeeper automatically [false]\n"
106 " --require-gatekeeper Exit if gatekeeper discovery fails [false]\n"
107 " -u --user username Specify local username [login name]\n"
108 " -p --password pwd Specify gatekeeper H.235 password [none]\n"
109 " -P --prefer codec Set codec preference (use multiple times) [none]\n"
110 " -D --disable codec Disable codec (use multiple times) [none]\n"
111 " -f --fast-disable Disable fast start\n"
112 " -T --h245tunneldisable Disable H245 tunnelling.\n"
113 " -O --out-msg file Specify PCM16 WAV file for outgoing message [ogm.wav]\n"
114 " -I --in-dir dir Specify directory for incoming WAV files [disabled]\n"
115 " -c --cdr file Specify Call Detail Record file [none]\n"
116 " --tcp-base port Specific the base TCP port to use.\n"
117 " --tcp-max port Specific the maximum TCP port to use.\n"
118 " --udp-base port Specific the base UDP port to use.\n"
119 " --udp-max port Specific the maximum UDP port to use.\n"
120 " --rtp-base port Specific the base RTP/RTCP pair of UDP port to use.\n"
121 " --rtp-max port Specific the maximum RTP/RTCP pair of UDP port to use.\n"
122 " --tmaxest secs Maximum time to wait for \"Established\" [0]\n"
123 " --tmincall secs Minimum call duration in seconds [10]\n"
124 " --tmaxcall secs Maximum call duration in seconds [60]\n"
125 " --tminwait secs Minimum interval between calls in seconds [10]\n"
126 " --tmaxwait secs Maximum interval between calls in seconds [30]\n"
127 #if PTRACING
128 " -t --trace Trace enable (use multiple times for more detail)\n"
129 " -o --output file Specify filename for trace output [stdout]\n"
130 #endif
131 "\n"
132 "Notes:\n"
133 " If --tmaxest is set a non-zero value then --tmincall is the time to leave\n"
134 " the call running once established. If zero (the default) then --tmincall\n"
135 " is the length of the call from initiation. The call may or may not be\n"
136 " \"answered\" within that time.\n"
137 "\n";
138 return;
139 }
140
141 #if PTRACING
142 PTrace::Initialise(args.GetOptionCount('t'),
143 args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL,
144 PTrace::Blocks | PTrace::DateAndTime | PTrace::Thread | PTrace::FileAndLine);
145 #endif
146
147 #if OPAL_H323
148 H323EndPoint * h323 = new H323EndPoint(manager);
149 #endif
150
151 quietMode = args.HasOption('q');
152
153 m_outgoingMessageFile.Parse(args.GetOptionString('O', "ogm.wav"), "file");
154 if (m_outgoingMessageFile.IsEmpty())
155 cout << "Not using outgoing message file." << endl;
156 else if (PFile::Exists(m_outgoingMessageFile.AsFilePath()))
157 cout << "Using outgoing message file: " << m_outgoingMessageFile << endl;
158 else {
159 cout << "Outgoing message file \"" << m_outgoingMessageFile << "\" does not exist!" << endl;
160 PTRACE(1, "CallGen\tOutgoing message file \"" << m_outgoingMessageFile << "\" does not exist");
161 m_outgoingMessageFile = PString::Empty();
162 }
163
164 incomingAudioDirectory = args.GetOptionString('I');
165 if (incomingAudioDirectory.IsEmpty())
166 cout << "Not saving incoming audio data." << endl;
167 else if (PDirectory::Exists(incomingAudioDirectory) ||
168 PDirectory::Create(incomingAudioDirectory)) {
169 incomingAudioDirectory = PDirectory(incomingAudioDirectory);
170 cout << "Using incoming audio directory: " << incomingAudioDirectory << endl;
171 }
172 else {
173 cout << "Could not create incoming audio directory \"" << incomingAudioDirectory << "\"!" << endl;
174 PTRACE(1, "CallGen\tCould not create incoming audio directory \"" << incomingAudioDirectory << '"');
175 incomingAudioDirectory = PString::Empty();
176 }
177
178 if (args.HasOption('S'))
179 manager.SetSTUNServer(args.GetOptionString('S'));
180
181 #if OPAL_H323
182 {
183 PStringArray interfaces = args.GetOptionString("h323-interface").Lines();
184 if (!h323->StartListeners(interfaces)) {
185 cout << "Couldn't start any listeners on interfaces/ports:\n"
186 << setfill('\n') << interfaces << setfill(' ') << endl;
187 return;
188 }
189 cout << "H.323 listening on: " << setfill(',') << h323->GetListeners() << setfill(' ') << endl;
190 }
191 #endif // OPAL_H323
192
193 #if OPAL_SIP
194 {
195 PStringArray interfaces = args.GetOptionString("sip-interface").Lines();
196 SIPEndPoint * sip = new SIPEndPoint(manager);
197 if (!sip->StartListeners(interfaces)) {
198 cout << "Couldn't start any listeners on interfaces/ports:\n"
199 << setfill('\n') << interfaces << setfill(' ') << endl;
200 return;
201 }
202 cout << "SIP listening on: " << setfill(',') << sip->GetListeners() << setfill(' ') << endl;
203 }
204 #endif // OPAL_SIP
205
206 #if OPAL_IVR
207 OpalIVREndPoint * ivr = new OpalIVREndPoint(manager);
208 ivr->SetDefaultVXML("repeat=1000;"+m_outgoingMessageFile.AsString());
209 #endif // OPAL_IVR
210
211
212 unsigned simultaneous = args.GetOptionString('m').AsUnsigned();
213 if (simultaneous == 0)
214 simultaneous = 1;
215
216 if (args.HasOption('c')) {
217 if (cdrFile.Open(args.GetOptionString('c'), PFile::WriteOnly, PFile::Create)) {
218 cdrFile.SetPosition(0, PFile::End);
219 PTRACE(1, "CallGen\tSetting CDR to \"" << cdrFile.GetFilePath() << '"');
220 cout << "Sending Call Detail Records to \"" << cdrFile.GetFilePath() << '"' << endl;
221 }
222 else {
223 cout << "Could not open \"" << cdrFile.GetFilePath() << "\"!" << endl;
224 }
225 }
226
227 if (args.HasOption("tcp-base"))
228 manager.SetTCPPorts(args.GetOptionString("tcp-base").AsUnsigned(),
229 args.GetOptionString("tcp-max").AsUnsigned());
230 if (args.HasOption("udp-base"))
231 manager.SetUDPPorts(args.GetOptionString("udp-base").AsUnsigned(),
232 args.GetOptionString("udp-max").AsUnsigned());
233 if (args.HasOption("rtp-base"))
234 manager.SetRtpIpPorts(args.GetOptionString("rtp-base").AsUnsigned(),
235 args.GetOptionString("rtp-max").AsUnsigned());
236 else {
237 // Make sure that there are enough RTP ports for the simultaneous calls
238 unsigned availablePorts = manager.GetRtpIpPortMax() - manager.GetRtpIpPortBase();
239 if (availablePorts < simultaneous*4) {
240 manager.SetRtpIpPorts(manager.GetRtpIpPortBase(), manager.GetRtpIpPortBase()+simultaneous*5);
241 cout << "Increasing RTP ports available from " << availablePorts << " to " << simultaneous*5 << endl;
242 }
243 }
244
245 if (args.HasOption('D'))
246 manager.SetMediaFormatMask(args.GetOptionString('D').Lines());
247 if (args.HasOption('P'))
248 manager.SetMediaFormatOrder(args.GetOptionString('P').Lines());
249
250 OpalMediaFormatList allMediaFormats;
251 #if OPAL_IVR
252 allMediaFormats = OpalTranscoder::GetPossibleFormats(ivr->GetMediaFormats()); // Get transcoders
253 #endif
254
255 #if PTRACING
256 ostream & traceStream = PTrace::Begin(3, __FILE__, __LINE__);
257 traceStream << "Simple\tAvailable media formats:\n";
258 for (PINDEX i = 0; i < allMediaFormats.GetSize(); i++)
259 allMediaFormats[i].PrintOptions(traceStream);
260 traceStream << PTrace::End;
261 #endif
262
263 for (PINDEX i = 0; i < allMediaFormats.GetSize(); i++) {
264 if (!allMediaFormats[i].IsTransportable())
265 allMediaFormats.RemoveAt(i--); // Don't show media formats that are not used over the wire
266 }
267 allMediaFormats.Remove(manager.GetMediaFormatMask());
268 allMediaFormats.Reorder(manager.GetMediaFormatOrder());
269
270 cout << "Codecs removed : " << setfill(',') << manager.GetMediaFormatMask() << "\n"
271 "Codec order : " << setfill(',') << manager.GetMediaFormatOrder() << "\n"
272 "Available codecs: " << allMediaFormats << setfill(' ') << endl;
273
274 // set local username, is necessary
275 if (args.HasOption('u')) {
276 PStringArray aliases = args.GetOptionString('u').Lines();
277 manager.SetDefaultUserName(aliases[0]);
278 #if OPAL_H323
279 for (PINDEX i = 1; i < aliases.GetSize(); ++i)
280 h323->AddAliasName(aliases[i]);
281 #endif // OPAL_H323
282 }
283 cout << "Local username: \"" << manager.GetDefaultUserName() << '"' << endl;
284
285 #if OPAL_H323
286 if (args.HasOption('p')) {
287 h323->SetGatekeeperPassword(args.GetOptionString('p'));
288 cout << "Using H.235 security." << endl;
289 }
290
291 if (args.HasOption('a')) {
292 h323->SetGkAccessTokenOID(args.GetOptionString('a'));
293 cout << "Set Access Token OID to \"" << h323->GetGkAccessTokenOID() << '"' << endl;
294 }
295
296 // process gatekeeper registration options
297 if (args.HasOption('g')) {
298 PString gkAddr = args.GetOptionString('g');
299 cout << "Registering with gatekeeper \"" << gkAddr << "\" ..." << flush;
300 if (!h323->UseGatekeeper(gkAddr)) {
301 cout << "\nError registering with gatekeeper at \"" << gkAddr << '"' << endl;
302 return;
303 }
304 }
305 else if (args.HasOption('G')) {
306 cout << "Searching for gatekeeper ..." << flush;
307 if (!h323->UseGatekeeper()) {
308 cout << "\nNo gatekeeper found." << endl;
309 if (args.HasOption("require-gatekeeper"))
310 return;
311 }
312 }
313
314 H323Gatekeeper * gk = h323->GetGatekeeper();
315 if (gk != NULL) {
316 H323Gatekeeper::RegistrationFailReasons reason;
317 while ((reason = gk->GetRegistrationFailReason()) == H323Gatekeeper::UnregisteredLocally)
318 PThread::Sleep(500);
319 switch (reason) {
320 case H323Gatekeeper::RegistrationSuccessful :
321 cout << "\nGatekeeper set to \"" << *gk << '"' << endl;
322 break;
323 case H323Gatekeeper::DuplicateAlias :
324 cout << "\nGatekeeper registration failed: duplicate alias" << endl;
325 break;
326 case H323Gatekeeper::SecurityDenied :
327 cout << "\nGatekeeper registration failed: security denied" << endl;
328 break;
329 case H323Gatekeeper::TransportError :
330 cout << "\nGatekeeper registration failed: transport error" << endl;
331 break;
332 default :
333 cout << "\nGatekeeper registration failed: code=" << reason << endl;
334 }
335 }
336
337 if (args.HasOption('f'))
338 h323->DisableFastStart(TRUE);
339 if (args.HasOption('T'))
340 h323->DisableH245Tunneling(TRUE);
341 #endif // OPAL_H323
342
343 if (args.HasOption('l')) {
344 manager.AddRouteEntry(".*\t.* = ivr:"); // Everything goes to IVR
345 cout << "Endpoint is listening for incoming calls, press ENTER to exit.\n";
346 console.ReadChar();
347 manager.ClearAllCalls();
348 }
349 else {
350 CallParams params(*this);
351 params.tmax_est .SetInterval(0, args.GetOptionString("tmaxest", "0" ).AsUnsigned());
352 params.tmin_call.SetInterval(0, args.GetOptionString("tmincall", "10").AsUnsigned());
353 params.tmax_call.SetInterval(0, args.GetOptionString("tmaxcall", "60").AsUnsigned());
354 params.tmin_wait.SetInterval(0, args.GetOptionString("tminwait", "10").AsUnsigned());
355 params.tmax_wait.SetInterval(0, args.GetOptionString("tmaxwait", "30").AsUnsigned());
356
357 if (params.tmin_call == 0 ||
358 params.tmin_wait == 0 ||
359 params.tmin_call > params.tmax_call ||
360 params.tmin_wait > params.tmax_wait) {
361 cerr << "Invalid times entered!\n";
362 return;
363 }
364
365 cout << "Maximum time between calls: " << params.tmin_wait << '-' << params.tmax_wait << "\n"
366 "Maximum total call duration: " << params.tmin_call << '-' << params.tmax_call << "\n"
367 "Maximum wait for establish: " << params.tmax_est << "\n"
368 "Endpoint starting " << simultaneous << " simultaneous call";
369 if (simultaneous > 1)
370 cout << 's';
371 cout << ' ';
372
373 params.repeat = args.GetOptionString('r', "1").AsUnsigned();
374 if (params.repeat != 0)
375 cout << params.repeat;
376 else
377 cout << "infinite";
378 cout << " time";
379 if (params.repeat != 1)
380 cout << 's';
381 if (params.repeat != 0)
382 cout << ", grand total of " << simultaneous*params.repeat << " calls";
383 cout << ".\n\n"
384 "Press ENTER at any time to quit.\n\n"
385 << endl;
386
387 // create some threads to do calls, but start them randomly
388 for (unsigned idx = 0; idx < simultaneous; idx++) {
389 if (args.HasOption('C'))
390 threadList.Append(new CallThread(idx+1, args.GetParameters(), params));
391 else {
392 PINDEX arg = idx%args.GetCount();
393 threadList.Append(new CallThread(idx+1, args.GetParameters(arg, arg), params));
394 }
395 }
396
397 PThread::Create(PCREATE_NOTIFIER(Cancel), 0);
398
399 for (;;) {
400 threadEnded.Wait();
401 PThread::Sleep(100);
402
403 bool finished = TRUE;
404 for (PINDEX i = 0; i < threadList.GetSize(); i++) {
405 if (!threadList[i].IsTerminated()) {
406 finished = FALSE;
407 break;
408 }
409 }
410
411 if (finished) {
412 cout << "\nAll call sets completed." << endl;
413 console.Close();
414 break;
415 }
416 }
417 }
418
419 if (totalAttempts > 0)
420 cout << "Total calls: " << totalAttempts
421 << " attempted, " << totalEstablished << " established\n";
422
423 manager.ShutDownEndpoints();
424 }
425
426
Cancel(PThread &,INT)427 void CallGen::Cancel(PThread &, INT)
428 {
429 PTRACE(3, "CallGen\tCancel thread started.");
430
431 // wait for a keypress
432 while (console.ReadChar() != '\n') {
433 if (!console.IsOpen()) {
434 PTRACE(3, "CallGen\tCancel thread ended.");
435 return;
436 }
437 }
438
439 PTRACE(2, "CallGen\tCancelling calls.");
440
441 coutMutex.Wait();
442 cout << "\nAborting all calls ..." << endl;
443 coutMutex.Signal();
444
445 // stop threads
446 for (PINDEX i = 0; i < threadList.GetSize(); i++)
447 threadList[i].Stop();
448
449 // stop all calls
450 CallGen::Current().manager.ClearAllCalls();
451
452 PTRACE(1, "CallGen\tCancelled calls.");
453 }
454
455
456 ///////////////////////////////////////////////////////////////////////////////
457
CallThread(unsigned _index,const PStringArray & _destinations,const CallParams & _params)458 CallThread::CallThread(unsigned _index,
459 const PStringArray & _destinations,
460 const CallParams & _params)
461 : PThread(1000, NoAutoDeleteThread, NormalPriority, psprintf("CallGen %u", _index)),
462 destinations(_destinations),
463 index(_index),
464 params(_params)
465 {
466 Resume();
467 }
468
469
RandomRange(PRandom & rand,const PTimeInterval & tmin,const PTimeInterval & tmax)470 static unsigned RandomRange(PRandom & rand,
471 const PTimeInterval & tmin,
472 const PTimeInterval & tmax)
473 {
474 unsigned umax = tmax.GetInterval();
475 unsigned umin = tmin.GetInterval();
476 return rand.Generate() % (umax - umin + 1) + umin;
477 }
478
479
480 #define START_OUTPUT(index, token) \
481 if (CallGen::Current().quietMode) ; else { \
482 CallGen::Current().coutMutex.Wait(); \
483 cout << setw(3) << index << ": " << setw(10) << token.Left(10) << ": "
484
485 #define END_OUTPUT() \
486 cout << endl; \
487 CallGen::Current().coutMutex.Signal(); \
488 }
489
490 static bool generateOutput = false;
491
492 #define OUTPUT(index, token, info) START_OUTPUT(index, token) << info; END_OUTPUT()
493
494
Main()495 void CallThread::Main()
496 {
497 PTRACE(2, "CallGen\tStarted thread " << index);
498
499 CallGen & callgen = CallGen::Current();
500 PRandom rand(PRandom::Number());
501
502 PTimeInterval delay = RandomRange(rand, (index-1)*500, (index+1)*500);
503 OUTPUT(index, PString::Empty(), "Initial delay of " << delay << " seconds");
504
505 if (exit.Wait(delay)) {
506 PTRACE(2, "CallGen\tAborted thread " << index);
507 callgen.threadEnded.Signal();
508 return;
509 }
510
511 // Loop "repeat" times for (repeat > 0), or loop forever for (repeat == 0)
512 unsigned count = 1;
513 do {
514 PString destination = destinations[(index-1 + count-1)%destinations.GetSize()];
515
516 // trigger a call
517 PString token;
518 PTRACE(1, "CallGen\tMaking call to " << destination);
519 unsigned totalAttempts = ++callgen.totalAttempts;
520 if (!callgen.manager.SetUpCall("ivr:*", destination, token, this))
521 OUTPUT(index, token, "Failed to start call to " << destination)
522 else {
523 bool stopping = FALSE;
524
525 delay = RandomRange(rand, params.tmin_call, params.tmax_call);
526 PTRACE(1, "CallGen\tMax call time " << delay);
527
528 START_OUTPUT(index, token) << "Making call " << count;
529 if (params.repeat)
530 cout << " of " << params.repeat;
531 cout << " (total=" << totalAttempts
532 << ") for " << delay << " seconds to "
533 << destination;
534 END_OUTPUT();
535
536 if (params.tmax_est > 0) {
537 OUTPUT(index, token, "Waiting " << params.tmax_est << " seconds for establishment");
538 PTRACE(1, "CallGen\tWaiting " << params.tmax_est << " seconds for establishment");
539
540 PTimer timeout = params.tmax_est;
541 while (!callgen.manager.IsCallEstablished(token)) {
542 stopping = exit.Wait(100);
543 if (stopping || !timeout.IsRunning() || !callgen.manager.HasCall(token)) {
544 PTRACE(1, "CallGen\tTimeout/Stopped on establishment");
545 delay = 0;
546 break;
547 }
548 }
549 }
550
551 if (delay > 0) {
552 // wait for a random time
553 PTRACE(1, "CallGen\tWaiting for " << delay);
554 stopping = exit.Wait(delay);
555 }
556
557 // end the call
558 OUTPUT(index, token, "Clearing call");
559 PTRACE(1, "CallGen\tClearing call");
560
561 callgen.manager.ClearCallSynchronous(token);
562
563 if (stopping)
564 break;
565 }
566
567 count++;
568 if (params.repeat > 0 && count > params.repeat)
569 break;
570
571 // wait for a random delay
572 delay = RandomRange(rand, params.tmin_wait, params.tmax_wait);
573 OUTPUT(index, PString::Empty(), "Delaying for " << delay << " seconds");
574
575 PTRACE(1, "CallGen\tDelaying for " << delay);
576 // wait for a random time
577 } while (!exit.Wait(delay));
578
579 OUTPUT(index, PString::Empty(), "Completed call set.");
580 PTRACE(2, "CallGen\tFinished thread " << index);
581
582 callgen.threadEnded.Signal();
583 }
584
585
Stop()586 void CallThread::Stop()
587 {
588 if (!IsTerminated()) {
589 OUTPUT(index, PString::Empty(), "Stopping.");
590 }
591
592 exit.Signal();
593 }
594
595
596 ///////////////////////////////////////////////////////////////////////////////
597
CreateCall(void * userData)598 OpalCall * MyManager:: CreateCall(void * userData)
599 {
600 return new MyCall(*this, (CallThread *)userData);
601 }
602
603
OnOpenMediaStream(OpalConnection & connection,OpalMediaStream & stream)604 PBoolean MyManager::OnOpenMediaStream(OpalConnection & connection, OpalMediaStream & stream)
605 {
606 dynamic_cast<MyCall &>(connection.GetCall()).OnOpenMediaStream(connection, stream);
607 return OpalManager::OnOpenMediaStream(connection, stream);
608 }
609
610
611 ///////////////////////////////////////////////////////////////////////////////
612
MyCall(MyManager & mgr,CallThread * caller)613 MyCall::MyCall(MyManager & mgr, CallThread * caller)
614 : OpalCall(mgr)
615 , manager(mgr)
616 , index(caller != NULL ? caller->index : 0)
617 , openedTransmitMedia(0)
618 , openedReceiveMedia(0)
619 , receivedMedia(0)
620 {
621 }
622
623
OnEstablishedCall()624 void MyCall::OnEstablishedCall()
625 {
626 PSafePtr<OpalConnection> connection = GetConnection(GetPartyA().NumCompare("IVR/") == EqualTo ? 1 : 0, PSafeReadOnly);
627
628 OUTPUT(index, GetToken(), "Established \"" << connection->GetRemotePartyName() << "\""
629 " " << connection->GetRemotePartyAddress() <<
630 " active=" << manager.GetActiveCalls() <<
631 " total=" << ++CallGen::Current().totalEstablished);
632 OpalCall::OnEstablishedCall();
633 }
634
635
OnReleased(OpalConnection & connection)636 void MyCall::OnReleased(OpalConnection & connection)
637 {
638 if (connection.GetRemotePartyName().NumCompare("IVR/") != EqualTo) {
639
640 OUTPUT(index, GetToken(), "Cleared \"" << connection.GetRemotePartyName() << "\""
641 " " << connection.GetRemotePartyAddress() <<
642 " reason=" << connection.GetCallEndReason());
643
644 PTextFile & cdrFile = CallGen::Current().cdrFile;
645
646 if (cdrFile.IsOpen()) {
647 static PMutex cdrMutex;
648 cdrMutex.Wait();
649
650 if (cdrFile.GetLength() == 0)
651 cdrFile << "Call Start Time,"
652 "Total duration,"
653 "Media open transmit time,"
654 "Media open received time,"
655 "Media received time,"
656 "ALERTING time,"
657 "CONNECT time,"
658 "Call End Reason,"
659 "Remote party,"
660 "Signalling gateway,"
661 "Media gateway,"
662 "Call Id,"
663 "Call Token\n";
664
665 PTime setupTime = connection.GetSetupUpTime();
666
667 cdrFile << setupTime.AsString("yyyy/M/d hh:mm:ss") << ','
668 << setprecision(1) << (connection.GetConnectionEndTime() - setupTime) << ',';
669
670 if (openedTransmitMedia.IsValid())
671 cdrFile << (openedTransmitMedia - setupTime);
672 cdrFile << ',';
673
674 if (openedReceiveMedia.IsValid())
675 cdrFile << (openedReceiveMedia - setupTime);
676 cdrFile << ',';
677
678 if (receivedMedia.IsValid())
679 cdrFile << (receivedMedia - setupTime);
680 cdrFile << ',';
681
682 if (connection.GetAlertingTime().IsValid())
683 cdrFile << (connection.GetAlertingTime() - setupTime);
684 cdrFile << ',';
685
686 if (connection.GetConnectionStartTime().IsValid())
687 cdrFile << (connection.GetConnectionStartTime() - setupTime);
688 cdrFile << ',';
689
690 cdrFile << connection.GetCallEndReason() << ','
691 << connection.GetRemotePartyName() << ','
692 << connection.GetRemotePartyAddress() << ','
693 << mediaGateway << ','
694 << connection.GetIdentifier() << ','
695 << connection.GetToken()
696 << endl;
697
698 cdrMutex.Signal();
699 }
700 }
701
702 OpalCall::OnReleased(connection);
703 }
704
705
OnOpenMediaStream(OpalConnection & connection,OpalMediaStream & stream)706 PBoolean MyCall::OnOpenMediaStream(OpalConnection & connection, OpalMediaStream & stream)
707 {
708 (stream.IsSink() ? openedTransmitMedia : openedReceiveMedia) = PTime();
709
710 OUTPUT(index, connection.GetCall().GetToken(),
711 "Opened " << (stream.IsSink() ? "transmitter" : "receiver")
712 << " for " << stream.GetMediaFormat());
713
714 return TRUE;
715 }
716
717
OnRTPStatistics(const OpalConnection & connection,const RTP_Session & session)718 void MyCall::OnRTPStatistics(const OpalConnection & connection, const RTP_Session & session)
719 {
720 if (receivedMedia.GetTimeInSeconds() == 0 && session.GetPacketsReceived() > 0) {
721 receivedMedia = PTime();
722 OUTPUT(index, connection.GetCall().GetToken(), "Received media");
723
724 const RTP_UDP * udpSess = dynamic_cast<const RTP_UDP *>(&session);
725 if (udpSess != NULL)
726 mediaGateway = OpalTransportAddress(udpSess->GetRemoteAddress(), udpSess->GetRemoteDataPort());
727 }
728 }
729
730
731 // End of file ////////////////////////////////////////////////////////////////
732