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