/*
* main.cxx
*
* OpenH323 call generator
*
* Copyright (c) 2001 Benny L. Prijono <seventhson@theseventhson.freeserve.co.uk>
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is CallGen323.
*
* The Initial Developer of the Original Code is Benny L. Prijono
*
* Contributor(s): Equivalence Pty. Ltd.
*
* $Log: main.cxx,v $
* Revision 1.32 2005/06/02 13:17:08 rjongbloed
* Fixed OPAL compatibility
*
* Revision 1.31 2005/05/04 05:49:32 csoutheren
* Seperated open transmit and open receive media times
*
* Revision 1.30 2005/05/04 02:47:58 csoutheren
* Fixed logging problems
*
* Revision 1.29 2004/08/14 07:57:35 rjongbloed
* Major revision to utilise the PSafeCollection classes for the connections and calls.
*
* Revision 1.28 2004/07/03 02:51:43 rjongbloed
* Fixed usage under openh323
*
* Revision 1.27 2004/04/26 06:34:19 rjongbloed
* Made sure incoming calls are routed to the IVR system.
*
* Revision 1.26 2004/04/26 05:41:10 rjongbloed
* MOre opalisation to be able to make SIP calls
*
* Revision 1.25 2004/04/18 13:57:03 rjongbloed
* Opalisation of call generator
*
* Revision 1.24 2003/05/02 01:43:35 robertj
* Added ability to call a sequence of destinations rather than only in parallel.
*
* Revision 1.23 2003/04/04 01:59:08 robertj
* Improved output to be more consistent
*
* Revision 1.22 2002/12/20 03:41:47 robertj
* Added usage text for port setting command line options.
*
* Revision 1.21 2002/12/10 23:04:49 robertj
* Fixed repeated error message when have no outgoing message file.
*
* Revision 1.20 2002/12/10 06:01:34 robertj
* Fixed conditional around the wrong way
*
* Revision 1.19 2002/12/10 05:54:41 robertj
* Removed delay if call vanishes quickly and are waiting fro establishment.
*
* Revision 1.18 2002/11/25 01:27:00 robertj
* Changed range for initial startup delay.
*
* Revision 1.17 2002/11/16 00:32:03 robertj
* Added wait for establishment timeout.
* Removed delay after last call in run.
*
* Revision 1.16 2002/11/12 22:22:22 robertj
* Fixed incorrect test for outgoing message file, thanks Héctor Leonardo Bolanos Muñoz
*
* Revision 1.15 2002/11/08 03:11:09 robertj
* Changed start up of threads so small inter-call delay does not swamp
* target with large number of calls first up.
*
* Revision 1.14 2002/08/27 05:28:27 robertj
* Changed message name for established to be "established".
*
* Revision 1.13 2002/08/27 03:49:31 robertj
* Added no media transmission if no WAV file supplied, still opens the
* logical channels though. Uses silence suppression.
* Added detection of completion of run and exit of program.
*
* Revision 1.12 2002/07/29 12:03:46 robertj
* Fixed write of G.723.1. file.
* Added port setting.
* Displayed capabilities.
*
* Revision 1.11 2002/07/26 02:14:12 robertj
* Added ability to prefer and delete codecs.
* Added fake G.723.1 codec.
*
* Revision 1.10 2002/07/23 09:33:58 robertj
* Fixed correct calculation of delay times on buffer size.
*
* Revision 1.9 2002/07/23 06:30:58 robertj
* Another set of enhancements, highlights are continually sending ogm WAV
* file and putting received audio into WAV files. As well as more
* statistics including a CDR file drop for every call.
*
* Revision 1.8 2002/05/31 04:32:33 robertj
* Fixed bad conditional, thanks Federico Pinna
*
* Revision 1.7 2002/05/27 00:30:03 robertj
* Added adjustable time ranges for random calls, thanks Federico Pinna
*
* Revision 1.6 2002/05/10 00:25:30 robertj
* Changed to trace out data and time as this is more useful for matching up
* with other endpoints or ethereal traces (provided clocks a synchronised).
*
* Revision 1.5 2002/03/14 05:00:36 robertj
* Added some tracing of call starts.
* Added console output of end of run in each thread.
*
* Revision 1.4 2002/02/19 06:01:06 rogerh
* Added --listenport option. Submitted by andreas.sikkema@philips.com
* Added history log.
*
*
* 25 Jan 2002 Substantial improvement [Equivalence Pty. Ltd.]
* 25 Jan 2000 Update to incorporate openh323 v.01 alpha2 and fix gatekeeper
* related codes [bennylp]
*/
#include "precompile.h"
#include "main.h"
#include "version.h"
#include <ptclib/random.h>
#ifdef OPAL_STATIC_LINK
#define H323_STATIC_LIB
#include <codec/allcodecs.h>
#endif
PCREATE_PROCESS(CallGen);
///////////////////////////////////////////////////////////////////////////////
CallGen::CallGen()
: PProcess("Equivalence", "CallGen", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER),
console(PConsoleChannel::StandardInput)
{
totalAttempts = 0;
totalEstablished = 0;
}
void CallGen::Main()
{
PArgList & args = GetArguments();
args.Parse("a-access-token-oid:"
"c-cdr:"
"C-cycle."
"D-disable:"
"g-gatekeeper:"
"I-in-dir:"
"i-interface:"
"l-listen."
"m-max:"
"n-no-gatekeeper."
"O-out-msg:"
"o-output:"
"P-prefer:"
"p-password:"
"r-repeat:"
"-require-gatekeeper."
"t-trace."
"-tmaxest:"
"-tmincall:"
"-tmaxcall:"
"-tminwait:"
"-tmaxwait:"
"-tcp-base:"
"-tcp-max:"
"-udp-base:"
"-udp-max:"
"-rtp-base:"
"-rtp-max:"
"u-user:"
, FALSE);
if (args.GetCount() == 0 && !args.HasOption('l')) {
cout << "Usage:\n"
" callgen [options] -l\n"
" callgen [options] destination [ destination ... ]\n"
"where options:\n"
" -l Passive/listening mode.\n"
" -m --max num Maximum number of simultaneous calls\n"
" -r --repeat num Repeat calls n times\n"
" -C --cycle Each simultaneous call cycles through destination list\n"
" -t --trace Trace enable (use multiple times for more detail)\n"
" -o --output file Specify filename for trace output [stdout]\n"
" -i --interface addr Specify IP address and port listen on [*:1720]\n"
" -g --gatekeeper host Specify gatekeeper host [auto-discover]\n"
" -n --no-gatekeeper Disable gatekeeper discovery [false]\n"
" --require-gatekeeper Exit if gatekeeper discovery fails [false]\n"
" -u --user username Specify local username [login name]\n"
" -p --password pwd Specify gatekeeper H.235 password [none]\n"
" -P --prefer codec Set codec preference (use multiple times) [none]\n"
" -D --disable codec Disable codec (use multiple times) [none]\n"
" -O --out-msg file Specify PCM16 WAV file for outgoing message [ogm.wav]\n"
" -I --in-dir dir Specify directory for incoming WAV files [disabled]\n"
" -c --cdr file Specify Call Detail Record file [none]\n"
" --tcp-base port Specific the base TCP port to use.\n"
" --tcp-max port Specific the maximum TCP port to use.\n"
" --udp-base port Specific the base UDP port to use.\n"
" --udp-max port Specific the maximum UDP port to use.\n"
" --rtp-base port Specific the base RTP/RTCP pair of UDP port to use.\n"
" --rtp-max port Specific the maximum RTP/RTCP pair of UDP port to use.\n"
" --tmaxest secs Maximum time to wait for \"Established\" [0]\n"
" --tmincall secs Minimum call duration in seconds [10]\n"
" --tmaxcall secs Maximum call duration in seconds [30]\n"
" --tminwait secs Minimum interval between calls in seconds [10]\n"
" --tmaxwait secs Maximum interval between calls in seconds [30]\n"
"\n"
"Notes:\n"
" If --tmaxest is set a non-zero value then --tmincall is the time to leave\n"
" the call running once established. If zero (the default) then --tmincall\n"
" is the length of the call from initiation. The call may or may not be\n"
" \"answered\" within that time.\n"
"\n";
return;
}
#if PTRACING
PTrace::Initialise(args.GetOptionCount('t'),
args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL,
PTrace::Blocks | PTrace::DateAndTime | PTrace::Thread | PTrace::FileAndLine);
#endif
#ifdef USE_OPAL
MyH323EndPoint * h323 = new MyH323EndPoint(manager);
#else
h323 = new MyH323EndPoint();
#endif
outgoingMessageFile = args.GetOptionString('O', "ogm.wav");
if (outgoingMessageFile.IsEmpty())
cout << "Not using outgoing message file." << endl;
else if (PFile::Exists(outgoingMessageFile))
cout << "Using outgoing message file: " << outgoingMessageFile << endl;
else {
cout << "Outgoing message file \"" << outgoingMessageFile << "\" does not exist!" << endl;
PTRACE(1, "CallGen\tOutgoing message file \"" << outgoingMessageFile << "\" does not exist");
outgoingMessageFile = PString::Empty();
}
incomingAudioDirectory = args.GetOptionString('I');
if (incomingAudioDirectory.IsEmpty())
cout << "Not saving incoming audio data." << endl;
else if (PDirectory::Exists(incomingAudioDirectory) ||
PDirectory::Create(incomingAudioDirectory)) {
incomingAudioDirectory = PDirectory(incomingAudioDirectory);
cout << "Using incoming audio directory: " << incomingAudioDirectory << endl;
}
else {
cout << "Could not create incoming audio directory \"" << incomingAudioDirectory << "\"!" << endl;
PTRACE(1, "CallGen\tCould not create incoming audio directory \"" << incomingAudioDirectory << '"');
incomingAudioDirectory = PString::Empty();
}
PStringArray interfaces = args.GetOptionString('i').Lines();
if (!h323->StartListeners(interfaces)) {
cout << "Couldn't start any listeners on interfaces/ports:\n"
<< setfill('\n') << interfaces << setfill(' ') << endl;
return;
}
cout << "H.323 listening on: " << setfill(',') << h323->GetListeners() << setfill(' ') << endl;
#ifdef USE_OPAL
MySIPEndPoint * sip = new MySIPEndPoint(manager);
if (!sip->StartListeners(interfaces)) {
cout << "Couldn't start any listeners on interfaces/ports:\n"
<< setfill('\n') << interfaces << setfill(' ') << endl;
return;
}
cout << "SIP listening on: " << setfill(',') << sip->GetListeners() << setfill(' ') << endl;
OpalIVREndPoint * ivr = new OpalIVREndPoint(manager);
PStringStream vxml;
vxml << "<?xml version=\"1.0\"?>"
"<vxml version=\"1.0\">"
"<form id=\"root\">"
"<break msecs=\"1500\"/>"
"<audio src=\"" + outgoingMessageFile + "\">"
"This is the OPAL call generator";
if (incomingAudioDirectory.IsEmpty())
vxml << "."
"</audio>"
"<break msecs=\"1000\"/>";
else
vxml << ", please speak after the tone."
"</audio>"
"<record name=\"msg\" beep=\"true\" dtmfterm=\"true\" dest=\"" + incomingAudioDirectory + "msg%05u.wav\" maxtime=\"10s\"/>";
vxml << "</form>"
"</vxml>";
ivr->SetDefaultVXML(vxml);
#endif
if (args.HasOption('c')) {
if (cdrFile.Open(args.GetOptionString('c'), PFile::WriteOnly, PFile::Create)) {
cdrFile.SetPosition(0, PFile::End);
PTRACE(1, "CallGen\tSetting CDR to \"" << cdrFile.GetFilePath() << '"');
cout << "Sending Call Detail Records to \"" << cdrFile.GetFilePath() << '"' << endl;
}
else {
cout << "Could not open \"" << cdrFile.GetFilePath() << "\"!" << endl;
}
}
if (args.HasOption("tcp-base"))
h323->SetTCPPorts(args.GetOptionString("tcp-base").AsUnsigned(),
args.GetOptionString("tcp-max").AsUnsigned());
if (args.HasOption("udp-base"))
h323->SetUDPPorts(args.GetOptionString("udp-base").AsUnsigned(),
args.GetOptionString("udp-max").AsUnsigned());
if (args.HasOption("rtp-base"))
h323->SetRtpIpPorts(args.GetOptionString("rtp-base").AsUnsigned(),
args.GetOptionString("rtp-max").AsUnsigned());
#ifdef USE_OPAL
if (args.HasOption('D'))
manager.SetMediaFormatMask(args.GetOptionString('D').Lines());
if (args.HasOption('P'))
manager.SetMediaFormatOrder(args.GetOptionString('P').Lines());
cout << "Codecs removed: " << setfill(',') << manager.GetMediaFormatMask() << "\n"
"Codec order: " << setfill(',') << manager.GetMediaFormatOrder() << setfill(' ') << endl;
#else
h323->RemoveCapabilities(args.GetOptionString('D').Lines());
h323->ReorderCapabilities(args.GetOptionString('P').Lines());
cout << "Local capabilities:\n" << h323->GetCapabilities() << endl;
#endif
// set local username, is necessary
if (args.HasOption('u')) {
PStringArray aliases = args.GetOptionString('u').Lines();
h323->SetLocalUserName(aliases[0]);
for (PINDEX i = 1; i < aliases.GetSize(); ++i)
h323->AddAliasName(aliases[i]);
}
cout << "Local username: \"" << h323->GetLocalUserName() << '"' << endl;
if (args.HasOption('p')) {
h323->SetGatekeeperPassword(args.GetOptionString('p'));
cout << "Using H.235 security." << endl;
}
if (args.HasOption('a')) {
h323->SetGkAccessTokenOID(args.GetOptionString('a'));
cout << "Set Access Token OID to \"" << h323->GetGkAccessTokenOID() << '"' << endl;
}
// process gatekeeper registration options
if (args.HasOption('g')) {
PString gkAddr = args.GetOptionString('g');
cout << "Registering with gatekeeper \"" << gkAddr << "\" ..." << flush;
if (h323->UseGatekeeper(gkAddr))
cout << "\nGatekeeper set to \"" << *h323->GetGatekeeper() << '"' << endl;
else {
cout << "\nError registering with gatekeeper at \"" << gkAddr << '"' << endl;
return;
}
}
else if (!args.HasOption('n')) {
cout << "Searching for gatekeeper ..." << flush;
if (h323->UseGatekeeper())
cout << "\nGatekeeper found: " << *h323->GetGatekeeper() << endl;
else {
cout << "\nNo gatekeeper found." << endl;
if (args.HasOption("require-gatekeeper"))
return;
}
}
if (args.HasOption('l')) {
#ifdef USE_OPAL
manager.AddRouteEntry(".* = ivr:"); // Everything goes to IVR
#endif
cout << "Endpoint is listening for incoming calls, press ENTER to exit.\n";
console.ReadChar();
h323->ClearAllCalls();
}
else {
CallParams params(*this);
params.tmax_est .SetInterval(0, args.GetOptionString("tmaxest", "0" ).AsUnsigned());
params.tmin_call.SetInterval(0, args.GetOptionString("tmincall", "10").AsUnsigned());
params.tmax_call.SetInterval(0, args.GetOptionString("tmaxcall", "60").AsUnsigned());
params.tmin_wait.SetInterval(0, args.GetOptionString("tminwait", "10").AsUnsigned());
params.tmax_wait.SetInterval(0, args.GetOptionString("tmaxwait", "30").AsUnsigned());
if (params.tmin_call == 0 ||
params.tmin_wait == 0 ||
params.tmin_call > params.tmax_call ||
params.tmin_wait > params.tmax_wait) {
cerr << "Invalid times entered!\n";
return;
}
unsigned number = args.GetOptionString('m').AsUnsigned();
if (number == 0)
number = 1;
cout << "Endpoint starting " << number << " simultaneous call";
if (number > 1)
cout << 's';
cout << ' ';
params.repeat = args.GetOptionString('r', "10").AsUnsigned();
if (params.repeat != 0)
cout << params.repeat;
else
cout << "infinite";
cout << " time";
if (params.repeat != 1)
cout << 's';
if (params.repeat != 0)
cout << ", grand total of " << number*params.repeat << " calls";
cout << '.' << endl;
// create some threads to do calls, but start them randomly
for (unsigned idx = 0; idx < number; idx++) {
if (args.HasOption('C'))
threadList.Append(new CallThread(idx+1, args.GetParameters(), params));
else {
PINDEX arg = idx%args.GetCount();
threadList.Append(new CallThread(idx+1, args.GetParameters(arg, arg), params));
}
}
PThread::Create(PCREATE_NOTIFIER(Cancel), 0);
for (;;) {
threadEnded.Wait();
PThread::Sleep(100);
BOOL finished = TRUE;
for (PINDEX i = 0; i < threadList.GetSize(); i++) {
if (!threadList[i].IsTerminated()) {
finished = FALSE;
break;
}
}
if (finished) {
cout << "\nAll call sets completed." << endl;
console.Close();
break;
}
}
}
if (totalAttempts > 0)
cout << "Total calls: " << totalAttempts
<< " attempted, " << totalEstablished << " established\n";
}
void CallGen::Cancel(PThread &, INT)
{
PTRACE(3, "CallGen\tCancel thread started.");
coutMutex.Wait();
cout << "Press ENTER at any time to quit.\n" << endl;
coutMutex.Signal();
// wait for a keypress
while (console.ReadChar() != '\n') {
if (!console.IsOpen()) {
PTRACE(3, "CallGen\tCancel thread ended.");
return;
}
}
PTRACE(2, "CallGen\tCancelling calls.");
coutMutex.Wait();
cout << "\nAborting all calls ..." << endl;
coutMutex.Signal();
// stop threads
for (PINDEX i = 0; i < threadList.GetSize(); i++)
threadList[i].Stop();
// stop all calls
CallGen::Current().ClearAll();
PTRACE(1, "CallGen\tCancelled calls.");
}
///////////////////////////////////////////////////////////////////////////////
CallThread::CallThread(unsigned _index,
const PStringArray & _destinations,
const CallParams & _params)
: PThread(1000, NoAutoDeleteThread, NormalPriority, psprintf("CallGen %u", _index)),
destinations(_destinations),
index(_index),
params(_params)
{
Resume();
}
static unsigned RandomRange(PRandom & rand,
const PTimeInterval & tmin,
const PTimeInterval & tmax)
{
unsigned umax = tmax.GetInterval();
unsigned umin = tmin.GetInterval();
return rand.Generate() % (umax - umin + 1) + umin;
}
#define START_OUTPUT(index, token) \
{ \
CallGen::Current().coutMutex.Wait(); \
cout << setw(3) << index << ": " << setw(20) << token.Left(20) << ": "
#define END_OUTPUT() \
cout << endl; \
CallGen::Current().coutMutex.Signal(); \
}
#define OUTPUT(index, token, info) START_OUTPUT(index, token) << info; END_OUTPUT()
void CallThread::Main()
{
PTRACE(2, "CallGen\tStarted thread " << index);
CallGen & callgen = CallGen::Current();
PRandom rand(PRandom::Number());
PTimeInterval delay = RandomRange(rand, (index-1)*500, (index+1)*500);
OUTPUT(index, PString::Empty(), "Initial delay of " << delay << " seconds");
if (exit.Wait(delay)) {
PTRACE(2, "CallGen\tAborted thread " << index);
callgen.threadEnded.Signal();
return;
}
// Loop "repeat" times for (repeat > 0), or loop forever for (repeat == 0)
unsigned count = 1;
do {
PString destination = destinations[(index-1 + count-1)%destinations.GetSize()];
// trigger a call
PString token;
PTRACE(1, "CallGen\tMaking call to " << destination);
unsigned totalAttempts = ++callgen.totalAttempts;
if (!callgen.Start(destination, token))
PError << setw(3) << index << ": Call creation to " << destination << " failed" << endl;
else {
BOOL stopping = FALSE;
delay = RandomRange(rand, params.tmin_call, params.tmax_call);
START_OUTPUT(index, token) << "Making call " << count;
if (params.repeat)
cout << " of " << params.repeat;
cout << " (total=" << totalAttempts
<< ") for " << delay << " seconds to "
<< destination;
END_OUTPUT();
if (params.tmax_est > 0) {
OUTPUT(index, token, "Waiting " << params.tmax_est << " seconds for establishment");
PTimer timeout = params.tmax_est;
while (!callgen.IsEstablished(token)) {
stopping = exit.Wait(100);
if (stopping || !timeout.IsRunning() || !callgen.Exists(token)) {
delay = 0;
break;
}
}
}
if (delay > 0) {
// wait for a random time
PTRACE(1, "CallGen\tWaiting for " << delay);
stopping = exit.Wait(delay);
}
// end the call
OUTPUT(index, token, "Clearing call");
callgen.Clear(token);
if (stopping)
break;
}
count++;
if (params.repeat > 0 && count > params.repeat)
break;
// wait for a random delay
delay = RandomRange(rand, params.tmin_wait, params.tmax_wait);
OUTPUT(index, PString::Empty(), "Delaying for " << delay << " seconds");
PTRACE(1, "CallGen\tDelaying for " << delay);
// wait for a random time
} while (!exit.Wait(delay));
OUTPUT(index, PString::Empty(), "Completed call set.");
PTRACE(2, "CallGen\tFinished thread " << index);
callgen.threadEnded.Signal();
}
void CallThread::Stop()
{
if (!IsTerminated())
OUTPUT(index, PString::Empty(), "Stopping.");
exit.Signal();
}
///////////////////////////////////////////////////////////////////////////////
void CallDetail::Drop(
#ifdef USE_OPAL
OpalConnection & connection
#else
H323Connection & connection
#endif
)
{
PTextFile & cdrFile = CallGen::Current().cdrFile;
if (!cdrFile.IsOpen())
return;
static PMutex cdrMutex;
cdrMutex.Wait();
if (cdrFile.GetLength() == 0)
cdrFile << "Call Start Time,"
"Total duration,"
"Media open transmit time,"
"Media open received time,"
"Media received time,"
"ALERTING time,"
"CONNECT time,"
"Call End Reason,"
"Remote party,"
"Signalling gateway,"
"Media gateway,"
"Call Id,"
"Call Token\n";
PTime setupTime = connection.GetSetupUpTime();
cdrFile << setupTime.AsString("yyyy/M/d hh:mm:ss") << ','
<< setprecision(1) << (connection.GetConnectionEndTime() - setupTime) << ',';
if (openedTransmitMedia.IsValid())
cdrFile << (openedTransmitMedia - setupTime);
cdrFile << ',';
if (openedReceiveMedia.IsValid())
cdrFile << (openedReceiveMedia - setupTime);
cdrFile << ',';
if (receivedMedia.IsValid())
cdrFile << (receivedMedia - setupTime);
cdrFile << ',';
if (connection.GetAlertingTime().IsValid())
cdrFile << (connection.GetAlertingTime() - setupTime);
cdrFile << ',';
if (connection.GetConnectionStartTime().IsValid())
cdrFile << (connection.GetConnectionStartTime() - setupTime);
cdrFile << ',';
cdrFile << connection.GetCallEndReason() << ','
<< connection.GetRemotePartyName() << ','
<< connection.GetRemotePartyAddress() << ','
<< mediaGateway << ','
#ifdef USE_OPAL
<< connection.GetIdentifier() << ','
<< connection.GetToken()
#else
<< connection.GetCallIdentifier() << ','
<< connection.GetCallToken()
#endif
<< endl;
cdrMutex.Signal();
}
void CallDetail::OnRTPStatistics(const RTP_Session & session, const PString & token)
{
if (receivedMedia.GetTimeInSeconds() == 0 && session.GetPacketsReceived() > 0) {
receivedMedia = PTime();
OUTPUT("", token, "Received media");
const RTP_UDP * udpSess = dynamic_cast<const RTP_UDP *>(&session);
if (udpSess != NULL)
mediaGateway = H323TransportAddress(udpSess->GetRemoteAddress(), udpSess->GetRemoteDataPort());
}
}
///////////////////////////////////////////////////////////////////////////////
#ifdef USE_OPAL
MySIPConnection::MySIPConnection(OpalCall & call,
MySIPEndPoint & ep,
const PString & token,
const SIPURL & address,
OpalTransport * transport)
: SIPConnection(call, ep, token, address, transport),
endpoint(ep)
{
}
void MySIPConnection::OnRTPStatistics(const RTP_Session & session) const
{
((MySIPConnection *)this)->details.OnRTPStatistics(session, GetToken());
}
///////////////////////////////////////////////////////////////////////////////
MySIPEndPoint::MySIPEndPoint(OpalManager & mgr)
: SIPEndPoint(mgr)
{
}
SIPConnection * MySIPEndPoint::CreateConnection(OpalCall & call,
const PString & token,
void * /*userData*/,
const SIPURL & destination,
OpalTransport * transport,
SIP_PDU * /*invite*/)
{
return new MySIPConnection(call, *this, token, destination, transport);
}
void MySIPEndPoint::OnEstablished(OpalConnection & connection)
{
OUTPUT("", connection.GetToken(), "Established \"" << connection.GetRemotePartyName() << "\""
" " << connection.GetRemotePartyAddress() <<
" active=" << connectionsActive.GetSize() <<
" total=" << ++CallGen::Current().totalEstablished);
}
void MySIPEndPoint::OnReleased(OpalConnection & connection)
{
OUTPUT("", connection.GetToken(), "Cleared \"" << connection.GetRemotePartyName() << "\""
" " << connection.GetRemotePartyAddress() <<
" reason=" << connection.GetCallEndReason());
((MySIPConnection&)connection).details.Drop(connection);
SIPEndPoint::OnReleased(connection);
}
BOOL MySIPEndPoint::OnOpenMediaStream(OpalConnection & connection, OpalMediaStream & stream)
{
(stream.IsSink() ? ((MySIPConnection&)connection).details.openedTransmitMedia
: ((MySIPConnection&)connection).details.openedReceiveMedia) = PTime();
OUTPUT("", connection.GetToken(),
"Opened " << (stream.IsSink() ? "transmitter" : "receiver")
<< " for " << stream.GetMediaFormat());
return SIPEndPoint::OnOpenMediaStream(connection, stream);
}
#endif
///////////////////////////////////////////////////////////////////////////////
#ifdef USE_OPAL
MyH323EndPoint::MyH323EndPoint(OpalManager & mgr)
: H323EndPoint(mgr)
{
}
#else
MyH323EndPoint::MyH323EndPoint()
{
// Set capability
AddAllCapabilities(0, 0, "*{sw}");
SetCapability(0, 0, new G7231_File_Capability());
AddAllUserInputCapabilities(0, P_MAX_INDEX);
}
#endif
#ifdef USE_OPAL
H323Connection * MyH323EndPoint::CreateConnection(OpalCall & call,
const PString & token,
void * /*userData*/,
OpalTransport & /*transport*/,
const PString & alias,
const H323TransportAddress & address,
H323SignalPDU * /*setupPDU*/)
{
return new MyH323Connection(call, *this, token, alias, address);
}
#else
H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference)
{
return new MyH323Connection(*this, callReference);
}
#endif
static PString TidyRemotePartyName(const H323Connection & connection)
{
PString name = connection.GetRemotePartyName();
PINDEX bracket = name.FindLast('[');
if (bracket == 0 || bracket == P_MAX_INDEX)
return name;
return name.Left(bracket).Trim();
}
void MyH323EndPoint::OnConnectionEstablished(H323Connection & connection,
const PString & token)
{
OUTPUT("", token, "Established \"" << TidyRemotePartyName(connection) << "\""
" " << connection.GetControlChannel().GetRemoteAddress() <<
" active=" << connectionsActive.GetSize() <<
" total=" << ++CallGen::Current().totalEstablished);
}
void MyH323EndPoint::OnConnectionCleared(H323Connection & connection,
const PString & token)
{
OUTPUT("", token, "Cleared \"" << TidyRemotePartyName(connection) << "\""
" " << connection.GetControlChannel().GetRemoteAddress() <<
" reason=" << connection.GetCallEndReason());
((MyH323Connection&)connection).details.Drop(connection);
}
BOOL MyH323EndPoint::OnStartLogicalChannel(H323Connection & connection,
H323Channel & channel)
{
(channel.GetDirection() == H323Channel::IsTransmitter
? ((MyH323Connection&)connection).details.openedTransmitMedia
: ((MyH323Connection&)connection).details.openedReceiveMedia) = PTime();
OUTPUT("", connection.GetCallToken(),
"Opened " << (channel.GetDirection() == H323Channel::IsTransmitter ? "transmitter" : "receiver")
<< " for " << channel.GetCapability());
return H323EndPoint::OnStartLogicalChannel(connection, channel);
}
///////////////////////////////////////////////////////////////////////////////
MyH323Connection::MyH323Connection(
#ifdef USE_OPAL
OpalCall & call,
MyH323EndPoint & ep,
const PString & token,
const PString & alias,
const H323TransportAddress & address,
unsigned options)
: H323Connection(call, ep, token, alias, address, options),
#else
MyH323EndPoint & ep,
unsigned callRef)
: H323Connection(ep, callRef),
#endif
endpoint(ep)
{
}
void MyH323Connection::OnRTPStatistics(const RTP_Session & session) const
{
((MyH323Connection *)this)->details.OnRTPStatistics(session, GetCallToken());
}
#ifndef USE_OPAL
BOOL MyH323Connection::OpenAudioChannel(BOOL isEncoding,
unsigned bufferSize,
H323AudioCodec & codec)
{
unsigned frameDelay;
if (codec.IsRawDataChannelNative()) {
frameDelay = 30; // Must be G7231_File_Codec, so always have 20ms frames
bufferSize = 0;
}
else
frameDelay = bufferSize/16; // Assume 16 bit PCM
PIndirectChannel * channel;
if (isEncoding)
channel = new PlayMessage(CallGen::Current().outgoingMessageFile, frameDelay, bufferSize);
else {
PString wavFileName;
if (!CallGen::Current().incomingAudioDirectory) {
PString token = GetCallToken();
token.Replace("/", "_", TRUE);
wavFileName = CallGen::Current().incomingAudioDirectory + token;
}
channel = new RecordMessage(wavFileName, frameDelay, bufferSize);
}
codec.AttachChannel(channel);
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
PlayMessage::PlayMessage(const PString & filename,
unsigned frameDelay,
unsigned frameSize)
: PDelayChannel(PDelayChannel::DelayReadsOnly, frameDelay, frameSize)
{
if (filename.IsEmpty())
PTRACE(2, "CallGen\tPlaying silence, no outgoing message file");
else {
if (wavFile.Open(filename, PFile::ReadOnly)) {
Open(wavFile);
PTRACE(2, "CallGen\tPlaying outgoing message file \"" << wavFile.GetFilePath() << '"');
}
else {
PTRACE(2, "CallGen\tCould not open outgoing message file \"" << wavFile.GetFilePath() << '"');
}
}
reallyClose = FALSE;
}
BOOL PlayMessage::Read(void * buf, PINDEX len)
{
if (PDelayChannel::Read(buf, len))
return TRUE;
if (reallyClose)
return FALSE;
// By opening the file as soon as we get a read error, we continually play
// out the outgoing message as the usual error is end of file.
if (wavFile.Open(PFile::ReadOnly)) {
if (PDelayChannel::Read(buf, len))
return TRUE;
}
// Just play out silence
memset(buf, len, 0);
lastReadCount = len;
return TRUE;
}
BOOL PlayMessage::Close()
{
reallyClose = TRUE;
return PDelayChannel::Close();
}
///////////////////////////////////////////////////////////////////////////////
RecordMessage::RecordMessage(const PString & wavFileName,
unsigned frameDelay,
unsigned frameSize)
: PDelayChannel(PDelayChannel::DelayWritesOnly, frameDelay, frameSize)
{
reallyClose = FALSE;
if (wavFileName.IsEmpty())
return;
PWAVFile * wavFile = new PWAVFile(wavFileName, PFile::WriteOnly);
if (wavFile->IsOpen()) {
Open(wavFile, TRUE);
PTRACE(2, "CallGen\tRecording to file \"" << wavFileName << '"');
}
else
delete wavFile;
}
BOOL RecordMessage::Write(const void * buf, PINDEX len)
{
if (PDelayChannel::Write(buf, len))
return TRUE;
lastWriteCount = len;
return !reallyClose;
}
BOOL RecordMessage::Close()
{
reallyClose = TRUE;
return PDelayChannel::Close();
}
#endif
// End of file ////////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1