/*
* main.cxx
*
* Main source for for OpenAM
*
* A H.323 answering machine application.
*
* Copyright (c) 1993-2001 Equivalence Pty. Ltd.
*
* 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 Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: main.cxx,v $
* Revision 1.93 2005/02/03 01:21:42 csoutheren
* Added called party to call log and command parameters
*
* Revision 1.92 2004/05/26 04:01:57 csoutheren
* Changed to work completely with new plugin codecs
*
* Revision 1.91 2004/05/10 13:07:19 rjongbloed
* Changed G.726 and MS-ADPCM to plug in codecs.
*
* Revision 1.90 2004/05/04 12:21:16 rjongbloed
* Converted LPC-10 codec to plug in.
*
* Revision 1.89 2004/05/03 13:25:46 rjongbloed
* Converted everything to be codec plug in freindly
* Removed GSM and G.729 as now plug ins are "the way"!
*
* Revision 1.88 2004/04/06 11:27:49 rjongbloed
* Changes to support native C++ Run Time Type Information
* Changes for codec plug ins
*
* Revision 1.87 2004/01/31 07:38:21 rjongbloed
* Changed HAS_IXJ usage to if defined not if non-zero
*
* Revision 1.86 2004/01/02 02:52:10 csoutheren
* Thanks to Michal Zygmuntowicz for these changes
* Added support for iLBC codec
* Added ability to specify a gatekeeper password,
* Fixed small bug with Speex capabilities being accidentally included.
*
* Revision 1.85 2003/12/02 09:46:59 csoutheren
* Added --loop option thanks to Jan Willamowius
*
* Revision 1.84 2002/11/13 10:23:12 rogerh
* Enable Speex support by default.
*
* Revision 1.83 2002/11/10 08:12:42 robertj
* Moved constants for "well known" ports to better place (OPAL change).
*
* Revision 1.82 2002/08/21 08:48:09 rogerh
* Fix some problems caused by the user specif OGM changes
*
* Revision 1.81 2002/08/21 06:55:09 rogerh
* Support all 5 Speex bitrates.
*
* Revision 1.80 2002/08/16 02:04:30 craigs
* Ensure compilation without Speex installed
*
* Revision 1.79 2002/08/15 18:52:49 rogerh
* Add support for one of the Speex codecs
*
* Revision 1.78 2002/08/05 10:06:06 robertj
* Changed to use the version of G.7231. file capability/codec as used in
* opalvxml module. Prevents duplicate symbol link errors.
*
* Revision 1.77 2002/06/28 00:30:41 robertj
* Fixed Clone() for G.723 file capability, must clone contents!
*
* Revision 1.76 2002/05/08 03:15:35 robertj
* Removed unecessary initialisation of frameBuffer PBYTEArray
*
* Revision 1.75 2002/04/01 14:45:43 craigs
* Fixed flag to end call after OGM
*
* Revision 1.74 2002/04/01 13:16:09 craigs
* Added ability to kill calls at end of playing OGM
* Added ability to play different OGMs depending on target DN
*
* Revision 1.73 2002/02/21 07:21:52 rogerh
* Change the outgoing audio's FrameDelay() code to use the PAdaptiveDelay
* class which should give clearer audio for the OGMs.
*
* Revision 1.72 2002/02/04 13:23:24 rogerh
* Change the silence frame back to a 4 byte SID. It caused Quicnet cards
* to play a loud noise instead of silence.
*
* Revision 1.71 2002/01/31 16:12:26 rogerh
* Clear the end of the frame buffer if we cannot read a full frame.
*
* Revision 1.70 2002/01/26 09:40:03 rogerh
* Make OpenAM send out G.723.1 OGM audio files correctly.
*
* Revision 1.69 2002/01/25 11:50:10 rogerh
* dd --port option to play and record commands so you can use a
* telephone on the POTS port or use speakers and microphone.
*
* Revision 1.68 2002/01/25 08:58:46 rogerh
* Send out silence using a standard 24 byte frame instead of a 4 byte SID.
* This is a workaround to support an end point which does not understand SID.
*
* Revision 1.67 2002/01/22 04:15:04 craigs
* Updated for move of PWavFile to PTCLib
* Replaced AudioDelay with PAdaptiveDelay
*
* Revision 1.66 2002/01/11 16:07:15 rogerh
* Make PCMExt non static as it is used externally in cmds.cxx
*
* Revision 1.65 2001/11/18 23:07:36 craigs
* Fixed problem whereby recording could start when queue of OGMs was not empty
* Thanks to Frank Derks
*
* Revision 1.64 2001/10/24 10:45:33 rogerh
* replace 4 byte SID frames with 24 byte frames of silence. Windows Media
* Player's codec cannot handle SID frames.
*
* Revision 1.63 2001/10/24 10:28:59 rogerh
* Make -m option find g7231 wav files.
* Fix bug where record file format was set by the outgoing codec type.
* (fixes bug when NetMeeting decides to receive G.723.1 and send MS-GSM)
*
* Revision 1.62 2001/10/24 09:01:07 rogerh
* Fix a mistake in the 24 byte frames used for silence.
*
* Revision 1.61 2001/10/16 12:28:43 rogerh
* Add support for saving g.723.1 audio data into a g.723.1 WAV File
*
* Revision 1.60 2001/10/15 14:55:03 rogerh
* Add error message
*
* Revision 1.59 2001/10/15 14:20:10 rogerh
* Add support for reading G.723.1 WAV files for the G.723.1 codec.
* Recorded messages are still saved out in raw .g723 files.
*
* Revision 1.58 2001/10/15 07:09:38 rogerh
* New sox homepage
*
* Revision 1.57 2001/10/14 08:22:54 rogerh
* Record the silence in G.723.1 connections to the record file.
* This also gives a proper fix for the 100% CPU utilisation bug.
*
* Revision 1.56 2001/10/13 07:13:18 rogerh
* Add a 5ms sleep if the length of the G.723.1 audio data is zero.
* This hack stops the 100% CPU usage seen when recording G.723.1 files.
* Submitted by Maurizio Beni <m.beni@deimositalia.com>
*
* Revision 1.55 2001/10/04 23:55:00 craigs
* Added deletion of file in PCM_RecordFile destructor thanks to Patrick Koorevaar
*
* Revision 1.54 2001/10/02 11:02:31 rogerh
* Use the audio delay class when writing G723.1 audio files.
* Add a quick hack.Set the frameBuffer to 1024 so OpenAM tworks on Unix boxes
*
* Revision 1.53 2001/09/29 07:11:48 rogerh
* Delete ogmChanel in destructor - Patrick Koorevaar <pkoorevaar@hotmail.com>
* Only call conn.ClearCall() once after we pass the record time limit.
* Reported by Patrick.
*
* Revision 1.52 2001/09/28 00:14:30 robertj
* Changed BYTE* to PBYTEArray so get automatic memory management.
* Fixed redundant entries in argument parsing, thanks Patrick Koorevaar
*
* Revision 1.51 2001/09/24 22:39:42 craigs
* Added commands to play and record data files, esp G.723.1
*
* Revision 1.50 2001/08/24 14:04:29 rogerh
* Delete the listener if StartListener() fails.
*
* Revision 1.49 2001/08/13 00:01:15 robertj
* Fixed #ifdef for memory check code to use PMEMORY_CHECK and not _DEBUG
*
* Revision 1.48 2001/07/23 09:17:36 rogerh
* Add the LPC10 codec, a low quality and low bitrate codec.
*
* Revision 1.47 2001/07/23 04:01:10 rogerh
* remove debugging info
*
* Revision 1.46 2001/07/20 03:36:58 robertj
* Minor cosmetic changes to new PWAVFile class.
*
* Revision 1.45 2001/07/19 10:05:27 rogerh
* PWAVFile is now part of the standard PWLib.
*
* Revision 1.44 2001/07/17 14:33:01 rogerh
* Support writing of .wav audio files from PCM codecs (eg G711 and GSM).
* .wav files are written out by default now. If you still want to record
* to raw audio files with a .sw extension, use the --recordraw option.
*
* Revision 1.43 2001/07/17 12:02:37 rogerh
* Change title, OpenAm -> OpenAM
*
* Revision 1.42 2001/07/14 07:44:29 rogerh
* Add .wav file support to the OGM.
* The -m option now looks for .wav and then .sw file extensions.
* .wav file must be 16 bit mono at 8000 Hz
*
* Revision 1.41 2001/07/11 15:47:55 rogerh
* Add G711 A-Law codec, reported by Niels Svennekjær <linux@post.tele.dk>
*
* Revision 1.40 2001/07/01 07:38:57 rogerh
* Add Microsoft GSM codec. Also allocate memory for frameBuffer dynamically
* as different codecs can have different frame sizes.
*
* Revision 1.39 2001/06/29 11:13:15 rogerh
* Add AudioDelay class which removes the jitter in recorded files.
*
* Revision 1.38 2001/06/29 09:01:17 rogerh
* Put back a line accidentally deleted in the last commit
*
* Revision 1.37 2001/06/29 06:34:57 rogerh
* Add mutex locks in PCM_Recordfile. This solves the race condition where
* Close() was called while Write() was still running.
*
* Revision 1.36 2001/04/27 07:08:46 robertj
* Fixed 100% Cip problem, thanks APinaev@microtest.ru
*
* Revision 1.35 2001/03/20 23:42:55 robertj
* Used the new PTrace::Initialise function for starting trace code.
*
* Revision 1.34 2001/01/25 07:27:14 robertj
* Major changes to add more flexible OpalMediaFormat class to normalise
* all information about media types, especially codecs.
*
* Revision 1.33 2000/10/20 23:11:29 robertj
* Fixed incorrect parameter parsing string that stopped -l from working, thanks Bruno BOSQUED
*
* Revision 1.32 2000/10/19 06:55:41 robertj
* Fixed compiler crash by rearranging loop.
*
* Revision 1.31 2000/08/29 23:11:41 robertj
* Fixed MSVC warnings.
*
* Revision 1.30 2000/08/29 12:32:07 craigs
* Fixed problems with recording messages
*
* Revision 1.29 2000/08/28 16:42:59 craigs
* Finally fixed problems with G.723.1. All codecs now working
*
* Revision 1.28 2000/08/28 09:13:54 robertj
* Fixed MSVC compiler warnings.
*
* Revision 1.27 2000/08/28 07:49:26 craigs
* New code to maybe get G.723.1 replaying working
*
* Revision 1.26 2000/08/28 00:38:37 craigs
* Added support for setting listening port number
*
* Revision 1.25 2000/08/27 23:42:24 craigs
* Fixed problem with playback of messages
* Fixed problem with recording messages
*
* Revision 1.24 2000/06/20 02:38:27 robertj
* Changed H323TransportAddress to default to IP.
*
* Revision 1.23 2000/06/17 09:14:52 robertj
* Added setting of closed flag when closing OGM.
*
* Revision 1.22 2000/05/25 13:25:47 robertj
* Fixed incorrect "save" parameter specification.
*
* Revision 1.21 2000/05/25 12:06:17 robertj
* Added PConfigArgs class so can save program arguments to config files.
*
* Revision 1.20 2000/05/11 11:47:11 robertj
* Fixed alpha linux GNU compiler problems.
*
* Revision 1.19 2000/05/10 05:14:25 robertj
* Changed capabilities so has a function to get name of codec, instead of relying on PrintOn.
*
* Revision 1.18 2000/05/09 11:22:15 craigs
* Fixed problems caused by new jitter buffer code
* and fixed OGM problems
*
* Revision 1.17 2000/05/09 02:41:32 craigs
* Added extra debugging, and fixed problems with OGM in non-IVR mode
*
* Revision 1.16 2000/04/26 03:18:38 craigs
* Fixed problem when GSM specified as preferred codec
*
* Revision 1.15 2000/04/25 23:34:22 craigs
* Added lots of new code, including outgoing and incoming
* multiplexors, and the start of an IVR system
*
* Revision 1.14 2000/01/13 04:03:45 robertj
* Added video transmission
*
* Revision 1.13 2000/01/07 08:28:09 robertj
* Additions and changes to line interface device base class.
*
* Revision 1.12 1999/12/10 01:44:46 craigs
* Added ability to set interface
*
* Revision 1.11 1999/12/01 04:38:25 robertj
* Added gatekeeper support to OpenAM
*
* Revision 1.10 1999/11/11 00:27:49 robertj
* Changed OnAnswerCall() call back function to allow for asyncronous response.
*
* Revision 1.9 1999/11/06 13:27:48 craigs
* Added extra output and changed for new library changes
*
* Revision 1.8 1999/10/29 10:57:04 robertj
* Added answering machine project.
*
* Revision 1.7 1999/10/24 12:50:37 craigs
* Fixed G723.1 capability, and added ability for discrete OGMs
*
* Revision 1.6 1999/10/24 08:24:56 craigs
* Added GSM capability back in
*
* Revision 1.5 1999/10/24 08:19:58 craigs
* Fixed problem that caused crash when unknown codecs used
*
* Revision 1.4 1999/10/24 03:29:07 craigs
* Fixed problem with -h parsing
*
* Revision 1.3 1999/10/24 03:08:49 craigs
* Fixed problem with recording zero length messages, and added autodelete of files
*
* Revision 1.2 1999/10/22 09:56:24 craigs
* Fixed various compile warnings
*
* Revision 1.1 1999/10/11 00:15:18 craigs
* Initial version
*
*/
#include <ptlib.h>
#include <ptlib/pipechan.h>
#include "version.h"
#include "opalvxml.h"
#include "main.h"
PCREATE_PROCESS(OpenAm);
#define new PNEW
#define DEFAULT_MSG_LIMIT 30
#define DEFAULT_CALL_LOG "call_log.txt"
#define G7231_SAMPLES_PER_BLOCK 240
#define CHECK_PCM 1
#define CHECK_G7231 2
#define MENU_PREFIX "UserMenu-"
static PMutex logMutex;
static PTextFile logFile;
static PFilePath logFilename = DEFAULT_CALL_LOG;
PString G7231Ext = ".g723";
PString WAVExt = ".wav";
PString PCMExt = ".sw";
static void LogMessage(const PString & str)
{
PTime now;
PString msg = now.AsString("hh:mm:ss dd/MM/yyyy") & str;
logMutex.Wait();
if (!logFile.IsOpen()) {
logFile.Open(logFilename, PFile::ReadWrite);
logFile.SetPosition(0, PFile::End);
}
logFile.WriteLine(msg);
logFile.Close();
logMutex.Signal();
}
static void LogCall(const PFilePath & fn,
const PString & from,
const PString & user,
unsigned len,
const PString & codec,
const PString & product,
const PString & to)
{
PString addr = from;
LogMessage(addr & "\"" + user + "\"" & PString(PString::Unsigned, len) & codec & "\"" + product + "\"" & "\"" + fn + "\"" & "\"" + to + "\"");
}
///////////////////////////////////////////////////////////////
OpenAm::OpenAm()
: PProcess("OpenH323 Project", "OpenAM",
MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER)
{
}
OpenAm::~OpenAm()
{
}
void OpenAm::Main()
{
cout << GetName()
<< " Version " << GetVersion(TRUE)
<< " by " << GetManufacturer()
<< " on " << GetOSClass() << ' ' << GetOSName()
<< " (" << GetOSVersion() << '-' << GetOSHardware() << ")\n\n";
PConfigArgs args(GetArguments());
args.Parse(
"D-disable:"
"d-directory:"
"g-gatekeeper:" "n-no-gatekeeper."
"-g711-ulaw." "-no-g711-ulaw."
"-g711-alaw." "-no-g711-alaw."
"-g711message:" "-no-g711message."
"-g7231." "-no-g7231."
"-g7231message:" "-no-g7231message."
"-ilbc." "-no-ilbc."
"-ilbcmessage:" "-no-ilbcmessage."
"-gsm." "-no-gsm."
"-gsmmessage:" "-no-gsmmessage."
"h-help."
"H-hangup." "-no-hangup."
"i-interface:" "-no-interface."
"k-kill." "-no-kill."
"l-limit:" "-no-limit."
"-listenport:" "-no-listenport."
"-lpc10message:" "-no-lpc10message."
"-speexmessage:" "-no-speexmessage."
"m-message:" "-no-message."
"-no-recordg7231."
"-loop."
#if PTRACING
"o-output:"
#endif
"P-prefer:"
"-pcm." "-no-pcm."
"-pcmmessage:" "-no-pcmmessage."
"-port:"
"q-quicknet:" "-no-quicknet:"
"r-run:" "-no-run."
"-recordraw."
"-require-gatekeeper." "-no-require-gatekeeper."
"-save."
#if PMEMORY_CHECK
"-setallocationbreakpoint:"
#endif
#if PTRACING
"t-trace."
#endif
"u-username:" "-no-username."
"p-password:"
, FALSE);
#if PMEMORY_CHECK
if (args.HasOption("setallocationbreakpoint"))
PMemoryHeap::SetAllocationBreakpoint(args.GetOptionString("setallocationbreakpoint").AsInteger());
#endif
#if PTRACING
PTrace::Initialise(args.GetOptionCount('t'),
args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL);
#endif
if (args.HasOption('h')) {
cout << "Usage : " << GetName() << " [options]\n"
"Options:\n"
" -d --directory dir : Put recorded mesages into dir\n"
" -l --limit secs : Limit recorded messages to secs duration (default " << DEFAULT_MSG_LIMIT << ")\n"
" -m --pcmmessage fn : Set outgoing message for PCM derived codecs (G.711/GSM) to fn\n"
" --g7231message fn : Set outgoing message for G723.1 codec to fn\n"
" --g711message fn : Set outgoing message for G711 codec to fn\n"
" --gsmmessage fn : Set outgoing message for GSM codec to fn\n"
" --lpc10message fn : Set outgoing message for LPC10 codec to fn\n"
" --speexmessage fn : Set outgoing message for Speex codec to fn\n"
" --ilbcmessage fn : Set outgoing message for iLBC codec to fn\n"
" --loop : loop message, no recording\n"
" --recordraw : Record PCM audo in raw files (.sw) instead of .wav\n"
" -r --run cmd : Run this command after each recorded message\n"
" -k --kill : Kill recorded files after user command\n"
" -H --hangup : hangup after playing message\n"
" -u --username str : Set the local endpoint name to str\n"
" -p --password str : Set the gatekeeper password to str\n"
" -i --interface ip : Bind to a specific interface\n"
" --listenport port : Listen on a specific port\n"
" -g --gatekeeper host: Specify gatekeeper host.\n"
" -n --no-gatekeeper : Disable gatekeeper discovery.\n"
" --require-gatekeeper: Exit if gatekeeper discovery fails.\n"
" -D --disable codec : Disable the specified codec (may be used multiple times)\n"
" -P --prefer codec : Prefer the specified codec (may be used multiple times)\n"
#if PTRACING
" -t --trace : Enable trace, use multiple times for more detail\n"
" -o --output : File for trace output, default is stderr\n"
#endif
" --save : Save arguments in configuration file\n"
" -h --help : Display this help message\n";
return;
}
args.Save("save");
#ifdef HAS_IXJ
if (args.GetCount() > 0) {
if (args[0] *= "record")
RecordFile(args);
else if (args[0] *= "play")
PlayFile(args);
else
cerr << "unknown command \"" << args[0] << "\"" << endl;
return;
}
#endif
unsigned callLimit = DEFAULT_MSG_LIMIT;
if (args.HasOption('l')) {
callLimit = args.GetOptionString('l').AsInteger();
if (callLimit > 3600) {
cout << "warning: maximum call length " << callLimit << " is out of range. Using " << DEFAULT_MSG_LIMIT << " instead\n";
callLimit = DEFAULT_MSG_LIMIT;
} else if (callLimit == 0)
cout << "warning: recorded message call limit disabled\n";
}
cout << "Recorded messages limited to " << callLimit << " seconds\n";
PString runCmd;
if (args.HasOption('r')) {
runCmd = args.GetOptionString('r');
cout << "Executing \"" << runCmd << "\" after each message" << endl;
}
PDirectory dir;
if (args.HasOption('d'))
dir = args.GetOptionString('d');
int flags = 0;
if (args.HasOption("no-recordg7231")) {
cout << "Supressing recording of G723.1 messages" << endl;
flags |= MyH323EndPoint::NoRecordG7231;
}
if (args.HasOption('k')) {
cout << "Deleting recorded files after processing" << endl;
if (runCmd.IsEmpty())
cout << "WARNING: recorded files will be deleted even though no run command is present" << endl;
flags |= MyH323EndPoint::DeleteAfterRecord;
}
if (args.HasOption('H'))
flags |= MyH323EndPoint::HangupAfterPlay;
MyH323EndPoint endpoint(callLimit, runCmd, dir, flags);
PString userName = "OpenH323 Answering Machine v" + GetVersion();
if (args.HasOption('u'))
userName = args.GetOptionString('u');
endpoint.SetLocalUserName(userName);
if (args.HasOption('p')) {
const PString password = args.GetOptionString('p');
endpoint.SetGatekeeperPassword(password);
}
if (!endpoint.Initialise(args))
return;
// start the H.323 listener
H323ListenerTCP * listener;
PIPSocket::Address interfaceAddress(INADDR_ANY);
WORD listenPort = H323EndPoint::DefaultTcpPort;
if (args.HasOption("listenport"))
listenPort = (WORD)args.GetOptionString("listenport").AsInteger();
if (args.HasOption('i'))
interfaceAddress = PIPSocket::Address(args.GetOptionString('i'));
listener = new H323ListenerTCP(endpoint, interfaceAddress, listenPort);
if (!endpoint.StartListener(listener)) {
cout << "Could not open H.323 listener port on "
<< listener->GetListenerPort() << endl;
delete listener;
return;
}
if (args.HasOption('g')) {
PString gkName = args.GetOptionString('g');
if (endpoint.SetGatekeeper(gkName, new H323TransportUDP(endpoint)))
cout << "Gatekeeper set: " << *endpoint.GetGatekeeper() << endl;
else {
cout << "Error registering with gatekeeper at \"" << gkName << '"' << endl;
return;
}
}
else if (!args.HasOption('n')) {
cout << "Searching for gatekeeper..." << flush;
if (endpoint.DiscoverGatekeeper(new H323TransportUDP(endpoint)))
cout << "\nGatekeeper found: " << *endpoint.GetGatekeeper() << endl;
else {
cout << "\nNo gatekeeper found." << endl;
if (args.HasOption("require-gatekeeper"))
return;
}
}
cout << "Waiting for incoming calls for \"" << endpoint.GetLocalUserName() << '"' << endl;
for (;;)
PThread::Current()->Sleep(5000);
}
///////////////////////////////////////////////////////////////
MyH323EndPoint::MyH323EndPoint(unsigned _callLimit,
const PString & _runCmd,
const PDirectory & _dir,
int _flags)
: callLimit(_callLimit), runCmd(_runCmd), dir(_dir), flags(_flags)
{
}
BOOL MyH323EndPoint::OnIncomingCall(H323Connection & _conn,
const H323SignalPDU & setupPDU,
H323SignalPDU &)
{
MyH323Connection & conn = (MyH323Connection &)_conn;
// see if incoming call is to a getway address
PString number;
if (setupPDU.GetDestinationE164(number))
conn.SetE164Number(number);
return TRUE;
}
H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference)
{
return new MyH323Connection(*this, callReference);
}
BOOL MyH323EndPoint::Initialise(PConfigArgs & args)
{
// format for record files, raw or wav
if (args.HasOption("recordraw"))
SetRecordWav(FALSE);
else
SetRecordWav(TRUE);
// get G723.1 OGM
if (args.HasOption("g7231message"))
g7231Ogm = args.GetOptionString("g7231message");
else if (args.HasOption('m')) {
if (PFile::Exists(args.GetOptionString('m') + "_g7231" + WAVExt)) {
g7231Ogm = args.GetOptionString('m') + "_g7231" + WAVExt;
}
else if (PFile::Exists(args.GetOptionString('m') + PCMExt)) {
g7231Ogm = args.GetOptionString('m') + G7231Ext;
}
}
if (!g7231Ogm.IsEmpty()) {
// check if the file exists. (do not check if filename contains %s)
if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(g7231Ogm)) {
cout << "warning: cannot open G723.1 OGM file \"" << g7231Ogm << "\"" << endl;
g7231Ogm = "";
}
}
if (g7231Ogm.IsEmpty())
cout << "No G.723.1 outgoing message set\n";
else {
cout << "Using \"" << g7231Ogm << "\" as G.723.1 outgoing message\n";
}
// Get the OGM message for the 'PCM' codecs
// Check if the file specified exists. If it does, use it.
// If it does not exist, try with .wav and .sw extensions.
if (args.HasOption("pcmmessage")) {
pcmOgm = args.GetOptionString("pcmmessage");
}
else if (args.HasOption('m')) {
if (PFile::Exists(args.GetOptionString('m'))) {
pcmOgm = args.GetOptionString('m');
}
else if (PFile::Exists(args.GetOptionString('m') + WAVExt)) {
pcmOgm = args.GetOptionString('m') + WAVExt;
}
else if (PFile::Exists(args.GetOptionString('m') + PCMExt)) {
pcmOgm = args.GetOptionString('m') + PCMExt;
}
}
if (args.HasOption("loop"))
SetLoopMessage(TRUE);
else
SetLoopMessage(FALSE);
// By default, use the pcmOgm for all the PCM codecs, but allow the user
// to override them.
gsmOgm = pcmOgm;
g711Ogm = pcmOgm;
lpc10Ogm = pcmOgm;
speexOgm = pcmOgm;
ilbcOgm = pcmOgm;
// We can set the filename for specific codecs.
if (args.HasOption("gsmmessage"))
gsmOgm = args.GetOptionString("gsmmessage");
if (args.HasOption("g711message"))
g711Ogm = args.GetOptionString("g711message");
if (args.HasOption("lpc10message"))
lpc10Ogm = args.GetOptionString("lpc10message");
if (args.HasOption("speexmessage"))
speexOgm = args.GetOptionString("speexmessage");
if (args.HasOption("ilbcmessage"))
ilbcOgm = args.GetOptionString("ilbcmessage");
// Check GSM OGM message
if (!gsmOgm.IsEmpty()) {
if ((gsmOgm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(gsmOgm)) {
cout << "warning: cannot open GSM OGM file \"" << gsmOgm << "\"" << endl;
gsmOgm = "";
}
}
if (gsmOgm.IsEmpty())
cout << "No GSM outgoing message set\n";
else {
cout << "Using \"" << gsmOgm << "\" as GSM outgoing message\n";
}
// Check G.711 OGM message
if (!g711Ogm.IsEmpty()) {
if ((g711Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(g711Ogm)) {
cout << "warning: cannot open G711 OGM file \"" << g711Ogm << "\"" << endl;
g711Ogm = "";
}
}
if (g711Ogm.IsEmpty())
cout << "No G711 outgoing message set\n";
else {
cout << "Using \"" << g711Ogm << "\" as G.711 outgoing message\n";
}
// Check LPC10 OGM message
if (!lpc10Ogm.IsEmpty()) {
if ((lpc10Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(lpc10Ogm)) {
cout << "warning: cannot open LPC10 OGM file \"" << lpc10Ogm << "\"" << endl;
lpc10Ogm = "";
}
}
if (lpc10Ogm.IsEmpty())
cout << "No LPC10 outgoing message set\n";
else {
cout << "Using \"" << lpc10Ogm << "\" as LPC10 outgoing message\n";
}
// Check Speex OGM message
if (!speexOgm.IsEmpty()) {
// check if the file exists. (do not check if filename contains %s)
if ((speexOgm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(speexOgm)) {
cout << "warning: cannot open Speex OGM file \"" << speexOgm << "\"" << endl;
speexOgm = "";
}
}
if (speexOgm.IsEmpty())
cout << "No Speex outgoing message set\n";
else {
cout << "Using \"" << speexOgm << "\" as Speex outgoing message\n";
}
// Check iLBC OGM message
if (!ilbcOgm.IsEmpty()) {
// check if the file exists. (do not check if filename contains %s)
if ((ilbcOgm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(ilbcOgm)) {
cout << "warning: cannot open iLBC OGM file \"" << ilbcOgm << "\"" << endl;
ilbcOgm = "";
}
}
if (ilbcOgm.IsEmpty())
cout << "No iLBC outgoing message set\n";
else {
cout << "Using \"" << ilbcOgm << "\" as iLBC outgoing message\n";
}
if (g7231Ogm.IsEmpty() && gsmOgm.IsEmpty() && g711Ogm.IsEmpty()
&& lpc10Ogm.IsEmpty()
&& speexOgm.IsEmpty()
) {
cerr << "Must specify at least one outgoing message" << endl;
return FALSE;
}
AddAllCapabilities(0, 0, "*");
PString removeString;
if (gsmOgm.IsEmpty())
removeString = removeString & OpalGSM0610 & "MS-GSM";
if (speexOgm.IsEmpty())
removeString = removeString & "Speex";
if (g711Ogm.IsEmpty())
removeString = removeString & "711";
if (lpc10Ogm.IsEmpty())
removeString = removeString & "LPC-10";
if (ilbcOgm.IsEmpty())
removeString = removeString & "iLBC";
if (!g7231Ogm.IsEmpty())
SetCapability(0, 0, new G7231_File_Capability);
capabilities.Remove(args.GetOptionString('D').Lines());
if (!removeString.IsEmpty())
capabilities.Remove(removeString.Tokenise(' '));
capabilities.Reorder(args.GetOptionString('P').Lines());
cout << "Codecs (in preference order):\n" << setprecision(2) << capabilities << endl;
return TRUE;
}
///////////////////////////////////////////////////////////////
PCM_RecordFile::PCM_RecordFile(MyH323Connection & _conn, const PFilePath & _fn, unsigned _callLimit)
: conn(_conn), fn(_fn), callLimit(_callLimit)
{
recordStarted = FALSE;
timeLimitExceeded = FALSE;
closed = FALSE;
dataWritten = FALSE;
// If the file name ends in .wav then open the output as a WAV file.
// Otherwise open it as a raw file.
if ((_fn.Right(4)).ToLower() == ".wav")
fileclass = new PWAVFile(_fn, PFile::WriteOnly,
PFile::ModeDefault,PWAVFile::PCM_WavFile);
else
fileclass = new PFile(_fn, PFile::WriteOnly);
}
void PCM_RecordFile::StartRecording()
{
PWaitAndSignal mutex(pcmrecordMutex);
if (recordStarted)
return;
PTRACE(1, "Starting recording to " << fn);
PTime now;
recordStarted = TRUE;
finishTime = now + (callLimit * 1000);
}
BOOL PCM_RecordFile::Close()
{
PWaitAndSignal mutex(pcmrecordMutex);
closed = TRUE;
return fileclass->Close();
}
BOOL PCM_RecordFile::Write(const void * buf, PINDEX len)
{
// Wait for the mutex, and Signal it at the end of this function
PWaitAndSignal mutex(pcmrecordMutex);
// If the record file has been closed, or if the time limit has
// been exceeded, then return immediatly.
if (closed || timeLimitExceeded)
return FALSE;
if (!recordStarted) {
DelayFrame(len);
return TRUE;
}
PTime now;
if ((callLimit != 0) && (now >= finishTime)) {
PTRACE(1, "Terminating call due to timeout");
conn.ClearCall();
timeLimitExceeded = TRUE;
return TRUE;
}
DelayFrame(len);
dataWritten = TRUE;
return WriteFrame(buf, len);
}
BOOL PCM_RecordFile::WriteFrame(const void * buf, PINDEX len)
{
//cerr << "Writing PCM " << len << endl;
return fileclass->Write(buf, len);
}
void PCM_RecordFile::DelayFrame(PINDEX len)
{
delay.Delay(len/16);
}
PCM_RecordFile::~PCM_RecordFile()
{
PWaitAndSignal mutex(pcmrecordMutex);
if (!dataWritten) {
PTRACE(1, "Deleting " << fn << " as no data recorded");
fileclass->Remove(fn);
}
delete fileclass;
}
///////////////////////////////////////////////////////////////
// Override some of the PCM_RecordFile functions to write
// G723.1 data instead of PCM data.
G7231_RecordFile::G7231_RecordFile(MyH323Connection & _conn, const PFilePath & _fn, unsigned _callLimit)
: PCM_RecordFile(_conn, _fn, _callLimit)
{
// If the record file is a .wav file, we need to close the file
// that PCM_RecordFile will have opened, and reopen it as a G.723.1 Wav file.
if ((_fn.Right(4)).ToLower() == ".wav") {
fileclass->Remove(_fn);
delete fileclass;
fileclass = new PWAVFile(_fn, PFile::WriteOnly,
PFile::ModeDefault,PWAVFile::G7231_WavFile);
}
}
BOOL G7231_RecordFile::WriteFrame(const void * buf, PINDEX /*len*/)
{
int frameLen = G7231_File_Codec::GetFrameLen(*(BYTE *)buf);
// cerr << "Writing G7231 " << frameLen << endl;
return fileclass->Write(buf, frameLen);
}
void G7231_RecordFile::DelayFrame(PINDEX /*len*/)
{
// Ignore the len parameter as that is the compressed size.
// We must delay by the actual sample time.
delay.Delay((G7231_SAMPLES_PER_BLOCK*2)/16);
}
///////////////////////////////////////////////////////////////
static BOOL MatchString(const PString & str1, const PString str2)
{
if (str1.GetLength() != str2.GetLength())
return FALSE;
PINDEX len = str1.GetLength();
PINDEX i;
for (i = 0; i < len; i++)
if ((str1[i] != '?') && (str2[i] != '?') && (str1[i] != str2[i]))
return FALSE;
return TRUE;
}
static PINDEX FindMatch(const PStringList & list, const PString & key)
{
PINDEX maxKeyLen = 0;
PINDEX i;
PINDEX keyLen = key.GetLength();
PINDEX listLen = list.GetSize();
for (i = 0; i < listLen; i++)
maxKeyLen = PMAX(maxKeyLen, list[i].GetLength());
if (keyLen == 0 || maxKeyLen == 0)
return P_MAX_INDEX;
if (keyLen > maxKeyLen)
return P_MAX_INDEX;
PINDEX len = 1;
while (len <= keyLen) {
PString subStr = key.Left(len);
PINDEX matches = 0;
PINDEX lastMatch = P_MAX_INDEX;
PINDEX i;
// look for a match to the substring
for (i = 0; i < list.GetSize(); i++) {
if ((list[i].GetLength() >= keyLen) && MatchString(list[i].Left(len), subStr)) {
matches++;
lastMatch = i;
}
}
// if we got ONE match, we have a winner
if (matches == 1)
return lastMatch+1;
// if we have no matches, then there is no point continuing
if (matches == 0)
return P_MAX_INDEX;
// if we have more than one match, try the next char
len++;
}
// too many matches
return 0;
}
MyH323Connection::MyH323Connection(MyH323EndPoint & _ep, unsigned callReference)
: H323Connection(_ep, callReference), ep(_ep)
{
basename = psprintf("%04i%02i%02i_%02i%02i%02i", callStartTime.GetYear(), callStartTime.GetMonth(), callStartTime.GetDay(),
callStartTime.GetHour(), callStartTime.GetMinute(), callStartTime.GetSecond());
recordFile = NULL;
ogmChannel = NULL;
receiveCodecName = transmitCodecName = "none";
cout << "Opening connection" << endl;
currentMenu = 0;
digits = "";
PConfig config;
PStringList sections = config.GetSections();
PINDEX i;
for (i = 0; i < sections.GetSize(); i++) {
if (sections[i].Find(MENU_PREFIX) == 0)
menuNames.AppendString(sections[i]);
}
}
BOOL MyH323Connection::OnReceivedSignalSetup(const H323SignalPDU & setupPDU)
{
if (!H323Connection::OnReceivedSignalSetup(setupPDU))
return FALSE;
// extract the called number
const H225_Setup_UUIE & setup = setupPDU.m_h323_uu_pdu.m_h323_message_body;
if (setup.HasOptionalField(H225_Setup_UUIE::e_destinationAddress) && setup.m_destinationAddress.GetSize() > 0) {
calledParty = H323GetAliasAddressString(setup.m_destinationAddress[0]);
}
return TRUE;
}
MyH323Connection::~MyH323Connection()
{
cout << "Closing connection" << endl;
PTime now;
PTimeInterval interval = now - recordStartTime;
PString addr = GetControlChannel().GetRemoteAddress();
PString codecStr = receiveCodecName + "/" + transmitCodecName;
unsigned duration = (unsigned)((interval.GetMilliSeconds()+999)/1000);
LogCall(recordFn, addr, GetRemotePartyName(), duration, codecStr, product, calledParty);
if ((recordFile!= NULL) && (recordFile->WasRecordStarted()) && !ep.GetRunCmd().IsEmpty()) {
PString cmdStr = ep.GetRunCmd() &
recordFn &
"'" + addr + "'" &
"\"" + GetRemotePartyName() + "\"" &
PString(PString::Unsigned, duration) &
"\"" + codecStr + "\"" &
"\"" + product + "\"" &
"\"" + calledParty + "\"";
PTRACE(1, "Executing : " << cmdStr);
system((const char *)cmdStr);
} else {
PTRACE(1, "No action to perform at end of record");
}
if (ogmChannel != NULL)
delete ogmChannel;
if (recordFile != NULL)
delete recordFile;
if (ep.GetDeleteAfterRecord()) {
PTRACE(1, "Removing " << recordFn << " as requested by option");
PFile::Remove(recordFn);
}
}
H323Connection::AnswerCallResponse
MyH323Connection::OnAnswerCall(const PString & caller,
const H323SignalPDU & setupPDU,
H323SignalPDU & /*connectPDU*/)
{
product = "Unknown";
const H225_Setup_UUIE & setup = setupPDU.m_h323_uu_pdu.m_h323_message_body;
const H225_EndpointType & epInfo = setup.m_sourceInfo;
if (epInfo.HasOptionalField(H225_EndpointType::e_vendor)) {
const H225_VendorIdentifier & vendorInfo = epInfo.m_vendor;
if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_productId))
product = vendorInfo.m_productId.AsString();
if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_versionId))
product = product + "/" + vendorInfo.m_versionId.AsString();
}
cout << "Accepting call from " << caller << " using " << product << endl;
return AnswerCallNow;
}
BOOL MyH323Connection::OpenAudioChannel(BOOL isEncoding,
unsigned /* bufferSize */,
H323AudioCodec & codec)
{
codec.SetSilenceDetectionMode(H323AudioCodec::NoSilenceDetection);
PString codecName = codec.GetMediaFormat();
PString ogm;
BOOL isPCM = FALSE;
if (codecName == OPAL_G7231_6k3) {
ogm = ep.GetG7231OGM();
isPCM = FALSE;
} else {
static OpalMediaFormat::List list = H323PluginCodecManager::GetMediaFormats();
if (list.GetValuesIndex(codecName) == P_MAX_INDEX) {
cerr << "Unknown codec \"" << codecName << endl;
return FALSE;
}
isPCM = TRUE;
if (codecName == OPAL_G711_ULAW_64K || codecName == OPAL_G711_ALAW_64K)
ogm = ep.GetG711OGM();
else if (codecName == OPAL_GSM0610)
ogm = ep.GetGSMOGM();
else if (codecName.Find("iLBC") != P_MAX_INDEX)
ogm = ep.GetiLBCOGM();
else if (codecName.Find("Speex") != P_MAX_INDEX)
ogm = ep.GetSPEEXOGM();
else
ogm = ep.GetG711OGM();
}
PWaitAndSignal mutex(connMutex);
if ((recordFile == NULL) && (isEncoding == FALSE)) {
if (isPCM) {
if (ep.GetRecordWav() == TRUE)
recordFn = ep.GetDirectory() + (basename + ".wav");
else
recordFn = ep.GetDirectory() + (basename + ".sw");
recordFile = new PCM_RecordFile (*this, recordFn, ep.GetCallLimit());
} else {
if (ep.GetRecordWav() == TRUE)
recordFn = ep.GetDirectory() + (basename + ".wav");
else
recordFn = ep.GetDirectory() + (basename + ".g723");
recordFile = new G7231_RecordFile(*this, recordFn, ep.GetCallLimit());
}
}
if ((ogmChannel == NULL) && (isEncoding == TRUE)) {
if (isPCM)
ogmChannel = new PCM_OGMChannel(*this);
else
ogmChannel = new G7231_OGMChannel(*this);
}
if (isEncoding) {
if (ep.GetHangupAfterPlay())
ogmChannel->SetPlayOnce();
if (ep.GetLoopMessage())
ogmChannel->SetLoopMessage();
if (ogm.Find("%s"))
ogm.Replace("%s", e164Number);
transmitCodecName = codecName;
if (!StartMenu(0)) {
if (!PFile::Exists(ogm)) {
cerr << "error: cannot find OGM \"" << ogm << "\"" << endl;
return FALSE;
}
else
ogmChannel->QueueFile(ogm);
if (!ep.GetNoRecordG7231())
ogmChannel->SetRecordTrigger();
}
codec.AttachChannel(ogmChannel, FALSE);
} else {
receiveCodecName = codecName;
codec.AttachChannel(recordFile, FALSE);
}
return TRUE;
}
BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel)
{
if (!H323Connection::OnStartLogicalChannel(channel))
return FALSE;
cout << "Started logical channel: ";
switch (channel.GetDirection()) {
case H323Channel::IsTransmitter :
cout << "sending ";
break;
case H323Channel::IsReceiver :
cout << "receiving ";
break;
default :
break;
}
cout << channel.GetCapability() << endl;
return TRUE;
}
void MyH323Connection::StartRecording()
{
recordFile->StartRecording();
}
void MyH323Connection::OnUserInputString(const PString & value)
{
PINDEX i;
for (i = 0; i < value.GetLength(); i++) {
OnUserInputChar(value[i]);
}
}
BOOL MyH323Connection::StartMenu(int menuNumber)
{
digits = "";
currentMenu = menuNumber;
PString menuName = psprintf("%s%i", MENU_PREFIX, menuNumber);
if (menuNames.GetStringsIndex(menuName) == P_MAX_INDEX)
return FALSE;
PTRACE(1, "Starting menu " << menuNumber);
PConfig menu(menuName);
PString startCmd = menu.GetString("start");
if (!startCmd.IsEmpty())
ProcessMenuCmd(startCmd);
return TRUE;
}
BOOL MyH323Connection::ProcessMenuCmd(const PString & cmdStr)
{
PTRACE(1, "Processing menu cmd " << cmdStr);
PStringArray tokens = cmdStr.Tokenise(" ", FALSE);
int len = tokens.GetSize();
if (len == 0)
return TRUE;
PString cmd = tokens[0];
if ((len >= 2) && (cmd *= "play")) {
ogmChannel->QueueFile(tokens[1]);
if (len > 2) {
cmd = "menu";
tokens[1] = tokens[2];
}
}
if ((len >= 2) && (cmd *= "menu")) {
int newMenu = tokens[1].AsInteger();
if (newMenu != currentMenu)
StartMenu(newMenu);
}
else if (cmd *= "hangup")
ogmChannel->SetHangupTrigger();
else if (cmd *= "record")
ogmChannel->SetRecordTrigger();
return TRUE;
}
void MyH323Connection::OnUserInputChar(char ch)
{
if (ch == '#')
digits += '$';
else
digits += ch;
PTRACE(1, "Processing digit string " << digits);
ogmChannel->FlushQueue();
PString menuName = psprintf("%s%i", MENU_PREFIX, currentMenu);
if (menuNames.GetStringsIndex(menuName) == P_MAX_INDEX) {
PTRACE(1, "Cannot find menu " << menuName);
StartMenu(0);
return;
}
PConfig menu(menuName);
PStringList keys = menu.GetKeys();
PINDEX keyMatch = FindMatch(keys, digits);
// if key is still ambiguous, then keep collecting
if (keyMatch == 0)
return;
PString cmd;
if (keyMatch != P_MAX_INDEX) {
PString key = keys[keyMatch-1];
PTRACE(1, "Executing cmd for key " << key);
cmd = menu.GetString(key);
} else {
PTRACE(1, "Cannot match cmd " << digits << " in menu " << menuName);
cmd = menu.GetString("error", "menu 0");
}
if (!cmd.IsEmpty()) {
ProcessMenuCmd(cmd);
digits = "";
}
}
///////////////////////////////////////////////////////////////
BOOL CheckWAVFileValid(PWAVFile *chan, int type) {
// Check the wave file header
if (!chan->IsValid()) {
PTRACE(1, chan->GetName() << " wav file header invalid");
return FALSE;
}
// Check the wave file format
if ( (type == CHECK_PCM) && (chan->GetFormat() != 0x01) ){
PTRACE(1, chan->GetName() << " is not a PCM format wav file");
PTRACE(1, "It is format " << chan->GetFormat() );
return FALSE;
}
if ( (type == CHECK_G7231) &&
((chan->GetFormat() != 0x42) && (chan->GetFormat() != 0x111)) ){
PTRACE(1, chan->GetName() << " is not a G.723.1 format wav file");
PTRACE(1, "It is format " << chan->GetFormat() );
return FALSE;
}
// Check the sample rate for PCM wave files
if ( (type == CHECK_PCM) &&
( (chan->GetSampleRate() != 8000)
||(chan->GetChannels() != 1)
||(chan->GetSampleSize() != 16) )
) {
PTRACE(1, chan->GetName() << " is not a 16 Bit, Mono, 8000 Hz (8Khz) PCM wav file");
PTRACE(1, "It is " << chan->GetSampleSize() << " bits, "
<< (chan->GetChannels()==1 ? "mono " : "stereo ")
<< chan->GetSampleRate() << " Hz");
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////////////////
PCM_OGMChannel::PCM_OGMChannel(MyH323Connection & _conn)
: conn(_conn)
{
silentCount = 20; // wait 20 frames before playing the OGM
recordTrigger = FALSE;
hangupTrigger = FALSE;
closed = FALSE;
playOnce = FALSE;
loopMessage = FALSE;
frameLen = frameOffs = 0;
}
void PCM_OGMChannel::PlayFile(PFile * chan)
{
PWaitAndSignal mutex(chanMutex);
// if (IsOpen())
// Close();
if (!chan->Open(PFile::ReadOnly)) {
PTRACE(1, "Cannot open file \"" << chan->GetName() << "\"");
return;
}
PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
totalData = 0;
SetReadChannel(chan, TRUE);
}
BOOL PCM_OGMChannel::IsWAVFileValid(PWAVFile *chan) {
// Check that this is a PCM wave file
return CheckWAVFileValid(chan, CHECK_PCM);
}
BOOL PCM_OGMChannel::Read(void * buffer, PINDEX amount)
{
PWaitAndSignal mutex(chanMutex);
// if the channel is closed, then return error
if (closed)
return FALSE;
// Create the frame buffer using the amount of bytes the codec wants to
// read. Different codecs use different read sizes.
frameBuffer.SetMinSize(1024);//amount);
// assume we are returning silence
BOOL doSilence = TRUE;
BOOL frameBoundary = FALSE;
// if still outputting a frame from last time, then keep doing it
if (frameOffs < frameLen) {
frameBoundary = AdjustFrame(buffer, amount);
doSilence = FALSE;
} else {
// if we are returning silence frames, then
if (silentCount > 0)
silentCount--;
// if a channel is already open, don't do silence
else if (GetBaseReadChannel() != NULL)
doSilence = FALSE;
// If not in silence and no existing channel, open a new file.
else {
PString * str = playQueue.Dequeue();
if (str != NULL) {
// check the file extension and open a .wav or a raw (.sw or .g723) file
if (((*str).Right(4)).ToLower() == ".wav") {
PWAVFile *chan;
chan = new PWAVFile(*str, PFile::ReadOnly);
if (!chan->IsOpen()) {
PTRACE(1, "Cannot open file \"" << chan->GetName() << "\"");
delete chan;
} else {
if (!IsWAVFileValid(chan) ){
PTRACE(1, chan->GetName() << " is not a valid wav file");
delete chan;
cerr << "wave file is invalid" << endl;
} else {
PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
totalData = 0;
SetReadChannel(chan, TRUE);
doSilence = FALSE;
}
if (loopMessage) {
PTRACE(1, "Looping file \"" << *str << "\"");
playQueue.Enqueue(new PString(*str));
}
}
} else { // raw file (eg .sw)
PFile *chan;
chan = new PFile(*str);
if (!chan->Open(PFile::ReadOnly)) {
PTRACE(1, "Cannot open file \"" << chan->GetName() << "\"");
delete chan;
} else {
PTRACE(1, "Playing file \"" << chan->GetName() << "\"");
totalData = 0;
SetReadChannel(chan, TRUE);
doSilence = FALSE;
}
}
delete str;
}
}
// if not doing silence, try and read from the file
if (!doSilence) {
if (ReadFrame(amount)) {
frameBoundary = AdjustFrame(buffer, amount);
totalData += amount;
} else {
PTRACE(1, "Finished playing " << totalData << " bytes");
//closed = TRUE;
PIndirectChannel::Close();
silentCount = 5; // always do 5 frames of silence after every file
// hangup if required
if (hangupTrigger || playOnce)
conn.ClearCall();
// trigger record if required
else if (recordTrigger) {
if ((playQueue.GetSize() == 0) && (GetBaseReadChannel() == NULL))
conn.StartRecording();
}
// no silence
doSilence = TRUE;
}
}
}
// start silence frame if required
if (doSilence) {
CreateSilenceFrame(amount);
frameBoundary = AdjustFrame(buffer, amount);
}
// delay to synchronise to frame boundary
if (frameBoundary)
Synchronise(amount);
return TRUE;
}
BOOL PCM_OGMChannel::Close()
{
PWaitAndSignal mutex(chanMutex);
closed = TRUE;
PIndirectChannel::Close();
return TRUE;
}
void PCM_OGMChannel::SetRecordTrigger()
{
PWaitAndSignal mutex(chanMutex);
recordTrigger = TRUE;
if ((playQueue.GetSize() == 0) && (GetBaseReadChannel() == NULL))
conn.StartRecording();
}
void PCM_OGMChannel::SetHangupTrigger()
{
PWaitAndSignal mutex(chanMutex);
hangupTrigger = TRUE;
if (GetBaseReadChannel() == NULL)
conn.ClearCall();
}
void PCM_OGMChannel::QueueFile(const PString & fn)
{
PWaitAndSignal mutex(chanMutex);
PTRACE(1, "Enqueueing file " << fn << " for playing");
playQueue.Enqueue(new PString(fn));
}
void PCM_OGMChannel::FlushQueue()
{
PWaitAndSignal mutex(chanMutex);
if (GetBaseReadChannel() != NULL) {
PIndirectChannel::Close();
if (hangupTrigger)
conn.ClearCall();
else if (recordTrigger)
conn.StartRecording();
}
PString * str;
while ((str = playQueue.Dequeue()) != NULL)
delete str;
}
BOOL PCM_OGMChannel::AdjustFrame(void * buffer, PINDEX amount)
{
if ((frameOffs + amount) > frameLen) {
cerr << "Reading past end of frame:offs=" << frameOffs << ",amt=" << amount << ",len=" << frameLen << endl;
return TRUE;
}
//PAssert((frameOffs + amount) <= frameLen, "Reading past end of frame");
memcpy(buffer, frameBuffer.GetPointer()+frameOffs, amount);
frameOffs += amount;
lastReadCount = amount;
return frameOffs == frameLen;
}
void PCM_OGMChannel::Synchronise(PINDEX amount)
{
ogm_delay.Delay(amount / 16);
}
BOOL PCM_OGMChannel::ReadFrame(PINDEX amount)
{
frameOffs = 0;
frameLen = amount;
BOOL result = PIndirectChannel::Read(frameBuffer.GetPointer(), frameLen);
// if we did not read a full frame of audio, fill the end of the
// frame with zeros.
PINDEX count = GetLastReadCount();
if (count < frameLen)
memset(frameBuffer.GetPointer()+count, 0, frameLen-count);
return result;
}
void PCM_OGMChannel::CreateSilenceFrame(PINDEX amount)
{
frameOffs = 0;
frameLen = amount;
memset(frameBuffer.GetPointer(), 0, frameLen);
}
///////////////////////////////////////////////////////////////
G7231_OGMChannel::G7231_OGMChannel(MyH323Connection & conn)
: PCM_OGMChannel(conn)
{
}
void G7231_OGMChannel::Synchronise(PINDEX /*amount*/)
{
ogm_delay.Delay(30);
}
BOOL G7231_OGMChannel::ReadFrame(PINDEX /*amount*/)
{
if (!PIndirectChannel::Read(frameBuffer.GetPointer(), 1))
return FALSE;
frameOffs = 0;
frameLen = G7231_File_Codec::GetFrameLen(frameBuffer[0]);
return PIndirectChannel::Read(frameBuffer.GetPointer()+1, frameLen-1);
}
void G7231_OGMChannel::CreateSilenceFrame(PINDEX /*amount*/)
{
frameOffs = 0;
frameLen = 4;
frameBuffer[0] = 2;
memset(frameBuffer.GetPointer()+1, 0, 3);
}
BOOL G7231_OGMChannel::IsWAVFileValid(PWAVFile *chan) {
// Check that this is a G.723.1 wave file
return CheckWAVFileValid(chan, CHECK_G7231);
}
///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1