main.cxx
上传用户:wgl386701
上传日期:2007-01-06
资源大小:22k
文件大小:31k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * main.cxx
  3.  *
  4.  * PWLib application source file for Voxilla
  5.  *
  6.  * A H.323 "net telephone" application.
  7.  *
  8.  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Portions of this code were written with the assisance of funding from
  25.  * Vovida Networks, Inc. http://www.vovida.com.
  26.  *
  27.  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
  28.  * All Rights Reserved.
  29.  *
  30.  * Contributor(s): ______________________________________.
  31.  *
  32.  * $Log: main.cxx,v $
  33.  * Revision 1.24  2000/06/20 02:38:27  robertj
  34.  * Changed H323TransportAddress to default to IP.
  35.  *
  36.  * Revision 1.23  2000/06/17 09:14:52  robertj
  37.  * Added setting of closed flag when closing OGM.
  38.  *
  39.  * Revision 1.22  2000/05/25 13:25:47  robertj
  40.  * Fixed incorrect "save" parameter specification.
  41.  *
  42.  * Revision 1.21  2000/05/25 12:06:17  robertj
  43.  * Added PConfigArgs class so can save program arguments to config files.
  44.  *
  45.  * Revision 1.20  2000/05/11 11:47:11  robertj
  46.  * Fixed alpha linux GNU compiler problems.
  47.  *
  48.  * Revision 1.19  2000/05/10 05:14:25  robertj
  49.  * Changed capabilities so has a function to get name of codec, instead of relying on PrintOn.
  50.  *
  51.  * Revision 1.18  2000/05/09 11:22:15  craigs
  52.  * Fixed problems caused by new jitter buffer code
  53.  * and fixed OGM problems
  54.  *
  55.  * Revision 1.17  2000/05/09 02:41:32  craigs
  56.  * Added extra debugging, and fixed problems with OGM in non-IVR mode
  57.  *
  58.  * Revision 1.16  2000/04/26 03:18:38  craigs
  59.  * Fixed problem when GSM specified as preferred codec
  60.  *
  61.  * Revision 1.15  2000/04/25 23:34:22  craigs
  62.  * Added lots of new code, including outgoing and incoming
  63.  * multiplexors, and the start of an IVR system
  64.  *
  65.  * Revision 1.14  2000/01/13 04:03:45  robertj
  66.  * Added video transmission
  67.  *
  68.  * Revision 1.13  2000/01/07 08:28:09  robertj
  69.  * Additions and changes to line interface device base class.
  70.  *
  71.  * Revision 1.12  1999/12/10 01:44:46  craigs
  72.  * Added ability to set interface
  73.  *
  74.  * Revision 1.11  1999/12/01 04:38:25  robertj
  75.  * Added gatekeeper support to OpenAM
  76.  *
  77.  * Revision 1.10  1999/11/11 00:27:49  robertj
  78.  * Changed OnAnswerCall() call back function  to allow for asyncronous response.
  79.  *
  80.  * Revision 1.9  1999/11/06 13:27:48  craigs
  81.  * Added extra output and changed for new library changes
  82.  *
  83.  * Revision 1.8  1999/10/29 10:57:04  robertj
  84.  * Added answering machine project.
  85.  *
  86.  * Revision 1.7  1999/10/24 12:50:37  craigs
  87.  * Fixed G723.1 capability, and added ability for discrete OGMs
  88.  *
  89.  * Revision 1.6  1999/10/24 08:24:56  craigs
  90.  * Added GSM capability back in
  91.  *
  92.  * Revision 1.5  1999/10/24 08:19:58  craigs
  93.  * Fixed problem that caused crash when unknown codecs used
  94.  *
  95.  * Revision 1.4  1999/10/24 03:29:07  craigs
  96.  * Fixed problem with -h parsing
  97.  *
  98.  * Revision 1.3  1999/10/24 03:08:49  craigs
  99.  * Fixed problem with recording zero length messages, and added autodelete of files
  100.  *
  101.  * Revision 1.2  1999/10/22 09:56:24  craigs
  102.  * Fixed various compile warnings
  103.  *
  104.  * Revision 1.1  1999/10/11 00:15:18  craigs
  105.  * Initial version
  106.  *
  107.  */
  108. #include <ptlib.h>
  109. #include <ptlib/pipechan.h>
  110. #include "main.h"
  111. #include "version.h"
  112. PCREATE_PROCESS(OpenAm);
  113. #define new PNEW
  114. #define DEFAULT_MSG_LIMIT 30
  115. #define DEFAULT_CALL_LOG "call_log.txt"
  116. #define G7231_BLOCK_SIZE 24
  117. #define G7231_SAMPLES_PER_BLOCK 240
  118. #define G7231_BANDWIDTH 63
  119. #define MENU_PREFIX "UserMenu-"
  120. static PMutex logMutex;
  121. static PTextFile logFile;
  122. static PFilePath logFilename = DEFAULT_CALL_LOG;
  123. static PString G7231Ext = ".g723";
  124. static PString PCMExt   = ".sw";
  125. static void LogMessage(const PString & str)
  126. {
  127.   PTime now;
  128.   PString msg = now.AsString("hh:mm:ss dd/MM/yyyy") & str;
  129.   logMutex.Wait();
  130.   if (!logFile.IsOpen()) {
  131.     logFile.Open(logFilename, PFile::ReadWrite);
  132.     logFile.SetPosition(0, PFile::End);
  133.   }
  134.   logFile.WriteLine(msg);
  135.   logFile.Close();
  136.   
  137.   logMutex.Signal();
  138. }
  139. static void LogCall(const PFilePath & fn,
  140.                     const PString & from,
  141.                     const PString & user,
  142.                     unsigned len,
  143.                     const PString & codec,
  144.                     const PString & product)
  145. {
  146.   PString addr = from;
  147.   LogMessage(addr & """ + user + """ & PString(PString::Unsigned, len) & codec & """ + product + """ & """ + fn + """);
  148. }
  149. ///////////////////////////////////////////////////////////////
  150. OpenAm::OpenAm()
  151.   : PProcess("OpenH323 Project", "OpenAm",
  152.              MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER)
  153. {
  154. }
  155. OpenAm::~OpenAm()
  156. {
  157. }
  158. void OpenAm::Main()
  159. {
  160.   cout << GetName()
  161.        << " Version " << GetVersion(TRUE)
  162.        << " by " << GetManufacturer()
  163.        << " on " << GetOSClass() << ' ' << GetOSName()
  164.        << " (" << GetOSVersion() << '-' << GetOSHardware() << ")nn";
  165.   //PArgList & args = GetArguments();
  166.   PConfigArgs args(GetArguments());
  167.   args.Parse(
  168.              "d-directory:"
  169.              "g-gatekeeper:"         "n-no-gatekeeper."
  170.              "-require-gatekeeper."  "-no-require-gatekeeper."
  171.              "-g7231message:"        "-no-g7231message."
  172.              "-g711message:"         "-no-g711message."
  173.              "-gsmmessage:"          "-no-gsmmessage."
  174.              "-no-recordg7231."
  175.              "-gsm."                 "-no-gsm."
  176.              "-g711-ulaw."           "-no-g711-ulaw."
  177.              "-g711-alaw."           "-no-g711-alaw."
  178.              "-g7231."               "-no-g7231."
  179.              "h-help."
  180.              "i-interface:"          "-no-interface."
  181.              "k-kill."               "-no-kill."
  182.              "l-limit:"              "-no-limit."
  183.              "m-pcmmessage:"         "-no-message."
  184. #if PTRACING
  185.              "o-output:"
  186. #endif
  187.              "r-run:"                "-no-run."
  188.              "-save."
  189. #if PTRACING
  190.              "t-trace."
  191. #endif
  192.      "u-username:"           "-no-username."
  193.           , FALSE);
  194. #if PTRACING
  195.   PTrace::SetOptions(PTrace::Timestamp | PTrace::Thread);
  196.   PTrace::SetLevel(args.GetOptionCount('t'));
  197.   if (args.HasOption('o')) {
  198.     /* This is a bit tricky. We allocate the file for tracing on the heap, and
  199.        we never actually deallocate it. So it come up as a leak on exit if
  200.        memory checking is on. We cannot deallocate it as during termination
  201.        some PTRACE's may get executed and attempt to trace to a closed (and
  202.        deleted!) file object which crashes. Not good.
  203.      */
  204.     PTextFile * output = new PTextFile;
  205.     if (output->Open(args.GetOptionString('o'), PFile::WriteOnly))
  206.       PTrace::SetStream(output);
  207.     else {
  208.       cout << "Warning: could not open trace output file ""
  209.            << args.GetOptionString('o') << '"' << endl;
  210.       delete output;
  211.     }
  212.   }
  213.   PTRACE(1, GetName()
  214.          << " Version " << GetVersion(TRUE)
  215.          << " by " << GetManufacturer()
  216.          << " on " << GetOSClass() << ' ' << GetOSName()
  217.          << " (" << GetOSVersion() << '-' << GetOSHardware() << ')');
  218. #endif
  219.   if (args.HasOption('h')) {
  220.     cout << "Usage : " << GetName() << " [options]n"
  221.             "Options:n"
  222.             "  -d --directory dir  : Put recorded mesages into dirn"
  223.             "  -l --limit secs     : Limit recorded messages to secs duration (default " << DEFAULT_MSG_LIMIT << ")n"
  224.             "  -m --pcmmessage fn  : Set outgoing message for PCM derived codecs (G.711/GSM) to fnn"
  225.             "  --g7231message fn   : Set outgoing message for G723.1 codec to fnn"
  226.             "  --g711message fn    : Set outgoing message for G711 codec to fnn"
  227.             "  --gsmmessage fn     : Set outgoing message for GSM codec to fnn"
  228.             "  -r --run cmd        : Run this command after each recorded messagen"
  229.             "  -k --kill           : Kill recorded files after user commandn"
  230.             "  -u --username str   : Set the local endpoint name to strn"
  231.             "  -i --interface ip   : Bind to a specific interfacen"
  232.             "  -g --gatekeeper host: Specify gatekeeper host.n"
  233.             "  -n --no-gatekeeper  : Disable gatekeeper discovery.n"
  234.             "  --require-gatekeeper: Exit if gatekeeper discovery fails.n"
  235. #if PTRACING
  236.             "  -t --trace          : Enable trace, use multiple times for more detailn"
  237.             "  -o --output         : File for trace output, default is stderrn"
  238. #endif
  239.             "     --save           : Save arguments in configuration filen"
  240.             "  -h --help           : Display this help messagen";
  241.     return;
  242.   }
  243.   args.Save("save");
  244. #ifdef HAS_CMDS
  245.   if (args.GetCount() > 0) {
  246.     if (args[0] *= "record") 
  247.       RecordFile(args);
  248.     else if (args[0] *= "play") 
  249.       PlayFile(args);
  250.     else
  251.       cerr << "unknown command "" << args[0] << """ << endl;
  252.     return;
  253.   }
  254. #endif
  255.   unsigned callLimit = DEFAULT_MSG_LIMIT;
  256.   if (args.HasOption('l')) {
  257.     callLimit = args.GetOptionString('l').AsInteger();
  258.     if (callLimit > 3600) {
  259.       cout << "warning: maximum call length " << callLimit << " is out of range. Using " << DEFAULT_MSG_LIMIT << " insteadn";
  260.       callLimit = DEFAULT_MSG_LIMIT;
  261.     } else if (callLimit == 0)
  262.       cout << "warning: recorded message call limit disabledn";
  263.   }
  264.   cout << "Recorded messages limited to " << callLimit << " secondsn";
  265.   PString runCmd;
  266.   if (args.HasOption('r')) {
  267.     runCmd = args.GetOptionString('r');
  268.     cout << "Executing "" << runCmd << "" after each message" << endl;
  269.   }
  270.   PDirectory dir;
  271.   if (args.HasOption('d'))
  272.     dir = args.GetOptionString('d');
  273.   int flags = 0;
  274.   if (args.HasOption("no-recordg7231")) {
  275.     cout << "Supressing recording of G723.1 messages" << endl;
  276.     flags |= MyH323EndPoint::NoRecordG7231;
  277.   }
  278.   if (args.HasOption('k')) {
  279.     cout << "Deleting recorded files after processing" << endl;
  280.     if (runCmd.IsEmpty()) 
  281.       cout << "WARNING: recorded files will be deleted even though no run command is present" << endl;
  282.     flags |= MyH323EndPoint::DeleteAfterRecord;
  283.   }
  284.   
  285.   MyH323EndPoint endpoint(callLimit, runCmd, dir, flags);
  286.   PString userName = "OpenH323 Answering Machine v" + GetVersion();
  287.   if (args.HasOption('u'))
  288.     userName = args.GetOptionString('u');
  289.   endpoint.SetLocalUserName(userName);
  290.   BOOL g7231Disable  = args.HasOption("no-g7231");
  291.   BOOL gsmDisable    = args.HasOption("no-gsm");
  292.   BOOL ulaw64Disable = args.HasOption("no-g711-ulaw");
  293.   if (g7231Disable && gsmDisable && ulaw64Disable) {
  294.     cerr << "Cannot disable all codecs - please enable one of GSM, G723.1 or G.711 uLaw64" << endl;
  295.     return;
  296.   }
  297.   H323Capability * g7231  = NULL;
  298.   H323Capability * gsm    = NULL;
  299.   H323Capability * ulaw64 = NULL;
  300.   // get G723.1 OGM
  301.   PString g7231Ogm;
  302.   if (!g7231Disable) {
  303.     if (args.HasOption("g7231message")) {
  304.       PFilePath fn = g7231Ogm = args.GetOptionString("g7231message");
  305.       if (!PFile::Exists(fn + G7231Ext)) {
  306.         cout << "warning: cannot open G723.1 OGM file "" << (fn + G7231Ext) << """ << endl;
  307.         g7231Ogm = "";
  308.       } 
  309.     }
  310.     if (g7231Ogm.IsEmpty()) 
  311.       cout << "No G.723.1 outgoing message setn";
  312.     else {
  313.       cout << "Using "" << (g7231Ogm + G7231Ext) << "" as G.723.1 outgoing messagen";
  314.       endpoint.SetG7231OGM(g7231Ogm);
  315.     }
  316.     g7231  = new G7231_File_Capability;
  317.     if (args.HasOption("g7231"))
  318.       endpoint.AddCapability(g7231);
  319.   }
  320.   // get PCM OGM message which will do for GSM and G711
  321.   PString pcmOgm;
  322.   if (args.HasOption('m')) 
  323.     pcmOgm = args.GetOptionString('m');
  324.   // get GSM OGM message 
  325.   PString gsmOgm = pcmOgm;
  326.   if (!gsmDisable) {
  327.     if (args.HasOption("gsmmessage")) 
  328.       gsmOgm = args.GetOptionString("gsmmessage");
  329.     PFilePath fn = gsmOgm;
  330.     if (!PFile::Exists(fn + PCMExt)) {
  331.       cout << "warning: cannot open GSM OGM file "" << (fn + PCMExt) << """ << endl;
  332.       gsmOgm = "";
  333.     } 
  334.     if (gsmOgm.IsEmpty()) 
  335.       cout << "No GSM outgoing message setn";
  336.     else {
  337.       cout << "Using "" << gsmOgm << "" as GSM outgoing messagen";
  338.       endpoint.SetGSMOGM(gsmOgm);
  339.     }
  340.     gsm    = new H323_GSM0610Capability;
  341.     if (args.HasOption("gsm"))
  342.       endpoint.AddCapability(gsm);
  343.   }
  344.   // get G.711 OGM message 
  345.   PString g711Ogm = pcmOgm;
  346.   if (!ulaw64Disable) {
  347.     if (args.HasOption("g711message")) 
  348.       g711Ogm = args.GetOptionString("g711message");
  349.     PFilePath fn = g711Ogm;
  350.     if (!PFile::Exists(fn + PCMExt)) {
  351.       cout << "warning: cannot open G711 OGM file "" << (fn + PCMExt) << """ << endl;
  352.       g711Ogm = "";
  353.     } 
  354.     if (g711Ogm.IsEmpty()) 
  355.       cout << "No G711 outgoing message setn";
  356.     else {
  357.       cout << "Using "" << g711Ogm << "" as G.711 outgoing messagen";
  358.       endpoint.SetG711OGM(g711Ogm);
  359.     }
  360.     ulaw64 = new H323_G711Capability(H323_G711Capability::muLaw, H323_G711Capability::At64k);
  361.     if (args.HasOption("g711-ulaw"))
  362.       endpoint.AddCapability(ulaw64);
  363.   }
  364.   if ((g7231 == NULL) && (gsm == NULL) && (ulaw64 == NULL)) {
  365.     cerr << "Cannot disable all capabilites - please enable one of GSM, G723.1 or G.711 uLaw64" << endl;
  366.     return;
  367.   }
  368.   if (g7231 != NULL)
  369.     endpoint.SetCapability(0, 0, g7231);
  370.   if (gsm != NULL)
  371.     endpoint.SetCapability(0, 0, gsm);
  372.   if (ulaw64 != NULL)
  373.     endpoint.SetCapability(0, 0, ulaw64);
  374.   // start the H.323 listener
  375.   H323ListenerTCP * listener;
  376.   if (args.GetOptionString('i').IsEmpty())
  377.     listener  = new H323ListenerTCP(endpoint);
  378.   else {
  379.     PIPSocket::Address interfaceAddress(args.GetOptionString('i'));
  380.     listener  = new H323ListenerTCP(endpoint, interfaceAddress);
  381.   }
  382.   if (!endpoint.StartListener(listener)) {
  383.     cout <<  "Could not open H.323 listener port on "
  384.          << listener->GetListenerPort() << endl;
  385.     return;
  386.   }
  387.   if (args.HasOption('g')) {
  388.     PString gkName = args.GetOptionString('g');
  389.     if (endpoint.SetGatekeeper(gkName, new H323TransportUDP(endpoint)))
  390.       cout << "Gatekeeper set: " << *endpoint.GetGatekeeper() << endl;
  391.     else {
  392.       cout << "Error registering with gatekeeper at "" << gkName << '"' << endl;
  393.       return;
  394.     }
  395.   }
  396.   else if (!args.HasOption('n')) {
  397.     cout << "Searching for gatekeeper..." << flush;
  398.     if (endpoint.DiscoverGatekeeper(new H323TransportUDP(endpoint)))
  399.       cout << "nGatekeeper found: " << *endpoint.GetGatekeeper() << endl;
  400.     else {
  401.       cout << "nNo gatekeeper found." << endl;
  402.       if (args.HasOption("require-gatekeeper"))
  403.         return;
  404.     }
  405.   }
  406.   endpoint.ListenForIncomingCalls();
  407.   endpoint.AwaitTermination();
  408. }
  409. ///////////////////////////////////////////////////////////////
  410. G7231_File_Capability::G7231_File_Capability()
  411.  : H323AudioCapability(12*G7231_BLOCK_SIZE, 4*G7231_BLOCK_SIZE)
  412. {
  413. }
  414. PString G7231_File_Capability::GetFormatName() const
  415. {
  416.   return "G723.1";
  417. }
  418. BOOL G7231_File_Capability::OnSendingPDU(H245_AudioCapability & cap, unsigned packetSize) const
  419. {
  420.   // set the choice to the correct type
  421.   cap.SetTag(GetSubType());
  422.   // get choice data
  423.   H245_AudioCapability_g7231 & g7231 = cap;
  424.   // max number of audio frames per PDU we want to send
  425.   g7231.m_maxAl_sduAudioFrames = packetSize; //G7231_BLOCK_SIZE;
  426.   // no silence suppression
  427.   g7231.m_silenceSuppression = FALSE;
  428.   return TRUE;
  429. }
  430. BOOL G7231_File_Capability::OnReceivedPDU(const H245_AudioCapability & cap, unsigned & packetSize)
  431. {
  432.   const H245_AudioCapability_g7231 & g7231 = cap;
  433.   packetSize = g7231.m_maxAl_sduAudioFrames*G7231_BLOCK_SIZE;
  434.   return TRUE;
  435. }
  436. PObject * G7231_File_Capability::Clone() const
  437. {
  438.   return new G7231_File_Capability();
  439. }
  440. H323Codec * G7231_File_Capability::CreateCodec(H323Codec::Direction direction) const
  441. {
  442.   return new G7231_File_Codec(direction);
  443. }
  444. ///////////////////////////////////////////////////////////////
  445. G7231_File_Codec::G7231_File_Codec(Direction dir)
  446. : H323AudioCodec(dir, G7231_SAMPLES_PER_BLOCK)
  447. {
  448.   rtpPayloadType = RTP_DataFrame::G7231;
  449. }
  450. BOOL G7231_File_Codec::Read(BYTE * buffer, unsigned & length, RTP_DataFrame &)
  451. {
  452.   if (rawDataChannel == NULL)
  453.     return FALSE;
  454.     
  455.   BOOL stat = rawDataChannel->Read(buffer, G7231_BLOCK_SIZE);
  456.   if (!stat)
  457.     return FALSE;
  458.   if (rawDataChannel->GetLastReadCount() == 0)
  459.     length = 0;
  460.   else
  461.     length = G7231_BLOCK_SIZE;
  462.   return TRUE;
  463. }
  464. BOOL G7231_File_Codec::Write(const BYTE * buffer, unsigned length, const RTP_DataFrame & /* rtp */, unsigned & writtenLength)
  465. {
  466.   writtenLength = G7231_BLOCK_SIZE;
  467.   if (length == 0)
  468.     return TRUE;
  469.   return rawDataChannel->Write(buffer, writtenLength);
  470. }
  471. unsigned G7231_File_Codec::GetBandwidth() const      { return G7231_BANDWIDTH; }
  472.       
  473. ///////////////////////////////////////////////////////////////
  474. MyH323EndPoint::MyH323EndPoint(unsigned _callLimit,
  475.                                const PString & _runCmd,
  476.                                const PDirectory & _dir,
  477.                                int   _flags)
  478.   : callLimit(_callLimit), runCmd(_runCmd), dir(_dir), flags(_flags)
  479. {
  480. }
  481. H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference)
  482. {
  483.   return new MyH323Connection(*this, callReference);
  484. }
  485. void MyH323EndPoint::ListenForIncomingCalls()
  486. {
  487.   cout << "Waiting for incoming calls for "" << GetLocalUserName() << '"' << endl;
  488. }
  489. void MyH323EndPoint::AwaitTermination()
  490. {
  491.   for (;;) {
  492.     PThread::Current()->Sleep(5000);
  493.   }
  494. }
  495. ///////////////////////////////////////////////////////////////
  496. RecordFile::RecordFile(MyH323Connection & _conn, const PFilePath & _fn, unsigned _callLimit)
  497.   : PFile(_fn, PFile::WriteOnly), conn(_conn), fn(_fn), callLimit(_callLimit)
  498. {
  499.   recordStarted = FALSE;
  500.   closed        = FALSE;
  501. }
  502. void RecordFile::StartRecording()
  503. {
  504.   if (recordStarted)
  505.     return;
  506.   PTRACE(1, "Starting recording to " << fn);
  507.   PTime now;
  508.   recordStarted = TRUE;
  509.   finishTime = now + (callLimit * 1000);
  510. }
  511. BOOL RecordFile::Close()
  512. {
  513.   closed = TRUE;
  514.   return PFile::Close();
  515. }
  516. BOOL RecordFile::Write(const void * buf, PINDEX len)
  517. {
  518.   if (closed)
  519.     return FALSE;
  520.   if (!recordStarted) 
  521.     return TRUE;
  522.   BOOL stat =  PFile::Write(buf, len);
  523.   PTime now;
  524.   if ((callLimit == 0) || (now <= finishTime)) 
  525.     PThread::Current()->Sleep(((int)len / 2) / 8);
  526.   else {
  527.     PTRACE(1, "Terminating call due to timeout");
  528.     conn.ClearCall();
  529.   }
  530.   return stat;
  531. }
  532. RecordFile::~RecordFile()
  533. {
  534.   if (!recordStarted) {
  535.     PTRACE(1, "Deleting " << fn << " as no data recorded");
  536.     PFile::Remove(fn);
  537.   }
  538. }
  539. ///////////////////////////////////////////////////////////////
  540. static BOOL MatchString(const PString & str1, const PString str2)
  541. {
  542.   if (str1.GetLength() != str2.GetLength())
  543.     return FALSE;
  544.   PINDEX len = str1.GetLength();
  545.   PINDEX i;
  546.   for (i = 0; i < len; i++) 
  547.     if ((str1[i] != '?') && (str2[i] != '?') && (str1[i] != str2[i]))
  548.       return FALSE;
  549.   return TRUE;
  550. }
  551. static PINDEX FindMatch(const PStringList & list, const PString & key)
  552. {
  553.   PINDEX maxKeyLen = 0;
  554.   PINDEX i;
  555.   PINDEX keyLen = key.GetLength();
  556.   PINDEX listLen = list.GetSize();
  557.   for (i = 0; i < listLen; i++)
  558.     maxKeyLen = PMAX(maxKeyLen, list[i].GetLength());
  559.   if (keyLen == 0 || maxKeyLen == 0)
  560.     return P_MAX_INDEX;
  561.   if (keyLen > maxKeyLen)
  562.     return P_MAX_INDEX;
  563.   PINDEX len = 1;
  564.   while (len <= keyLen) {
  565.     PString subStr = key.Left(len);
  566.     PINDEX matches = 0;
  567.     PINDEX lastMatch = P_MAX_INDEX;
  568.     PINDEX i;
  569.     // look for a match to the substring
  570.     for (i = 0; i < list.GetSize(); i++) {
  571.       if ((list[i].GetLength() >= keyLen) && MatchString(list[i].Left(len), subStr)) {
  572.         matches++;
  573.         lastMatch = i;
  574.       }
  575.     }
  576.     // if we got ONE match, we have a winner
  577.     if (matches == 1)
  578.       return lastMatch+1;
  579.     // if we have no matches, then there is no point continuing
  580.     if (matches == 0)
  581.       return P_MAX_INDEX;
  582.     // if we have more than one match, try the next char
  583.     len++;
  584.   }
  585.   // too many matches
  586.   return 0;
  587. }
  588. MyH323Connection::MyH323Connection(MyH323EndPoint & _ep, unsigned callReference)
  589.   : H323Connection(_ep, callReference), ep(_ep)
  590. {
  591.   basename = psprintf("%04i%02i%02i_%02i%02i%02i", callStartTime.GetYear(), callStartTime.GetMonth(),  callStartTime.GetDay(),
  592.                                                    callStartTime.GetHour(), callStartTime.GetMinute(), callStartTime.GetSecond());
  593.   recordFile = NULL;
  594.   ogmChannel = NULL;
  595.   receiveCodecName = transmitCodecName = "none";
  596.   cout << "Opening connection" << endl;
  597.   currentMenu = 0;
  598.   digits = "";
  599.   PConfig config;
  600.   PStringList sections = config.GetSections();
  601.   PINDEX i;
  602.   for (i = 0; i < sections.GetSize(); i++) {
  603.     if (sections[i].Find(MENU_PREFIX) == 0) 
  604.       menuNames.AppendString(sections[i]);
  605.   }
  606. }
  607. MyH323Connection::~MyH323Connection()
  608. {
  609.   cout << "Closing connection" << endl;
  610.   PTime now;
  611.   PTimeInterval interval = now - recordStartTime;
  612.   PString addr = GetControlChannel().GetRemoteAddress();
  613.   PString codecStr = receiveCodecName + "/" + transmitCodecName;
  614.   unsigned duration = (unsigned)((interval.GetMilliSeconds()+999)/1000);
  615.   LogCall(recordFn, addr, GetRemotePartyName(), duration, codecStr, product);
  616.   if ((recordFile!= NULL) && (recordFile->WasRecordStarted()) && !ep.GetRunCmd().IsEmpty()) {
  617.     PString cmdStr = ep.GetRunCmd() &
  618.                      recordFn &
  619.                      "'" + addr + "'" &
  620.                      """ + GetRemotePartyName() + """ &
  621.                      PString(PString::Unsigned, duration) &
  622.                      """ + codecStr + """ &
  623.                      """ + product + """;
  624.     PTRACE(1, "Executing : " << cmdStr);
  625.     system((const char *)cmdStr);
  626.   } else {
  627.     PTRACE(1, "No action to perform at end of record");
  628.   }
  629.   if (recordFile != NULL)
  630.     delete recordFile;
  631.   if (ep.GetDeleteAfterRecord()) {
  632.     PTRACE(1, "Removing " << recordFn << " as requested by option");
  633.     PFile::Remove(recordFn);
  634.   }
  635. }
  636. H323Connection::AnswerCallResponse
  637.      MyH323Connection::OnAnswerCall(const PString & caller,
  638.                                     const H323SignalPDU & setupPDU,
  639.                                     H323SignalPDU & /*connectPDU*/)
  640. {
  641.   product = "Unknown";
  642.   const H225_Setup_UUIE & setup = setupPDU.m_h323_uu_pdu.m_h323_message_body;
  643.   const H225_EndpointType & epInfo = setup.m_sourceInfo;
  644.   if (epInfo.HasOptionalField(H225_EndpointType::e_vendor)) {
  645.     const H225_VendorIdentifier & vendorInfo = epInfo.m_vendor;
  646.     if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_productId))
  647.       product = vendorInfo.m_productId.AsString();
  648.     if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_versionId))
  649.       product = product + "/" + vendorInfo.m_versionId.AsString();
  650.   }
  651.   
  652.   cout << "Accepting call from " << caller << " using " << product << endl;
  653.   return AnswerCallNow;
  654. }
  655. BOOL MyH323Connection::OpenAudioChannel(BOOL isEncoding, unsigned /* bufferSize */, H323AudioCodec & codec)
  656. {
  657.   codec.SetSilenceDetectionMode(H323AudioCodec::NoSilenceDetection);
  658.   PStringStream codecName;
  659.   codecName << codec;
  660.   PString ext;
  661.   PString ogm;
  662.   BOOL isPCM = FALSE;
  663.   if (codec.IsDescendant(G7231_File_Codec::Class())) {
  664.     ext   = ".g723";
  665.     ogm   = ep.GetG7231OGM();
  666.     isPCM = FALSE;
  667.   } else if (codec.IsDescendant(H323_GSM0610Codec::Class())) {
  668.     ogm   = ep.GetGSMOGM();
  669.     ext   = PCMExt;
  670.     isPCM = TRUE;
  671.   } else if (codec.IsDescendant(H323_muLawCodec::Class())) {
  672.     ogm   = ep.GetG711OGM();
  673.     ext   = PCMExt;
  674.     isPCM = TRUE;
  675.   } else {
  676.     cerr << "Unknown codec "" << codecName << endl;
  677.     return FALSE;
  678.   }
  679.   PWaitAndSignal mutex(connMutex);
  680.   if (recordFile == NULL) {
  681.     recordFn   = ep.GetDirectory() + (basename + ext);
  682.     recordFile = new RecordFile(*this, recordFn, ep.GetCallLimit());
  683.   }
  684.   if (ogmChannel == NULL) {
  685.     ogmChannel = new OGMChannel(*this);;
  686.   }
  687.   if (isEncoding) {
  688.     transmitCodecName = codecName;
  689.     ogmChannel->SetIsPCM(isPCM);
  690.     ogmChannel->SetExtension(ext);
  691.     if (!StartMenu(0)) {
  692.       if (!PFile::Exists(ogm + ext))
  693.         cerr << "error: cannot find OGM "" << (ogm + ext) << """ << endl;
  694.       else
  695.         ogmChannel->QueueFile(ogm);
  696.       if (!ep.GetNoRecordG7231())
  697.         ogmChannel->SetRecordTrigger();
  698.     }
  699.     codec.AttachChannel(ogmChannel, FALSE);
  700.   } else {
  701.     receiveCodecName = codecName;
  702.     codec.AttachChannel(recordFile, FALSE);
  703.   }
  704.   return TRUE;
  705. }
  706. BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel)
  707. {
  708.   if (!H323Connection::OnStartLogicalChannel(channel))
  709.     return FALSE;
  710.   cout << "Started logical channel: ";
  711.   switch (channel.GetDirection()) {
  712.     case H323Channel::IsTransmitter :
  713.       cout << "sending ";
  714.       break;
  715.     case H323Channel::IsReceiver :
  716.       cout << "receiving ";
  717.       break;
  718.     default :
  719.       break;
  720.   }
  721.   cout << channel.GetCapability() << endl;
  722.   return TRUE;
  723. }
  724. void MyH323Connection::StartRecording()
  725. {
  726.   recordFile->StartRecording();
  727. }
  728. void MyH323Connection::OnUserInputString(const PString & value)
  729. {
  730.   PINDEX i;
  731.   for (i = 0; i < value.GetLength(); i++) {
  732.     OnUserInputChar(value[i]);
  733.   }
  734. }
  735. BOOL MyH323Connection::StartMenu(int menuNumber)
  736. {
  737.   digits = "";
  738.   currentMenu = menuNumber;
  739.   PString menuName = psprintf("%s%i", MENU_PREFIX, menuNumber);
  740.   if (menuNames.GetStringsIndex(menuName) == P_MAX_INDEX) 
  741.     return FALSE;
  742.   PTRACE(1, "Starting menu " << menuNumber);
  743.   PConfig menu(menuName);
  744.   PString startCmd = menu.GetString("start");
  745.   if (!startCmd.IsEmpty())
  746.     ProcessMenuCmd(startCmd);
  747.   return TRUE;
  748. }
  749. BOOL MyH323Connection::ProcessMenuCmd(const PString & cmdStr)
  750. {
  751.   PTRACE(1, "Processing menu cmd " << cmdStr);
  752.   PStringArray tokens = cmdStr.Tokenise(" ", FALSE);
  753.   int len = tokens.GetSize();
  754.   if (len == 0)
  755.     return TRUE;
  756.   PString cmd = tokens[0];
  757.   if ((len >= 2) && (cmd *= "play")) {
  758.     ogmChannel->QueueFile(tokens[1]);
  759.     if (len > 2) {
  760.       cmd = "menu";
  761.       tokens[1] = tokens[2];
  762.     }
  763.   }
  764.   if ((len >= 2) && (cmd *= "menu")) {
  765.     int newMenu = tokens[1].AsInteger();
  766.     if (newMenu != currentMenu)
  767.       StartMenu(newMenu);
  768.   }
  769.   else if (cmd *= "hangup")
  770.     ogmChannel->SetHangupTrigger();
  771.   else if (cmd *= "record")
  772.     ogmChannel->SetRecordTrigger();
  773.   return TRUE;
  774. }
  775. void MyH323Connection::OnUserInputChar(char ch)
  776. {
  777.   if (ch == '#') 
  778.     digits += '$';
  779.   else 
  780.     digits += ch;
  781.   PTRACE(1, "Processing digit string " << digits);
  782.   ogmChannel->FlushQueue();
  783.   PString menuName = psprintf("%s%i", MENU_PREFIX, currentMenu);
  784.   if (menuNames.GetStringsIndex(menuName) == P_MAX_INDEX) {
  785.     PTRACE(1, "Cannot find menu " << menuName);
  786.     StartMenu(0);
  787.     return;
  788.   }
  789.   PConfig menu(menuName);
  790.   PStringList keys = menu.GetKeys();
  791.   PINDEX keyMatch = FindMatch(keys, digits);
  792.   // if key is still ambiguous, then keep collecting
  793.   if (keyMatch == 0)
  794.     return;
  795.   PString cmd;
  796.   if (keyMatch != P_MAX_INDEX) {
  797.     PString key = keys[keyMatch-1];
  798.     PTRACE(1, "Executing cmd for key " << key);
  799.     cmd = menu.GetString(key);
  800.   } else {
  801.     PTRACE(1, "Cannot match cmd " << digits << " in menu " << menuName);
  802.     cmd = menu.GetString("error", "menu 0");
  803.   } 
  804.   if (!cmd.IsEmpty()) {
  805.     ProcessMenuCmd(cmd);
  806.     digits = "";
  807.   }
  808. }
  809. ///////////////////////////////////////////////////////////////
  810. OGMChannel::OGMChannel(MyH323Connection & _conn)
  811.   : conn(_conn)
  812. {
  813.   lastTime    = PTime();
  814.   silentCount = 20;
  815.   recordTrigger = FALSE;
  816.   hangupTrigger = FALSE;
  817.   closed        = FALSE;
  818. }
  819. void OGMChannel::PlayFile(PFile * chan)
  820.   PWaitAndSignal mutex(chanMutex);
  821. //  if (IsOpen())
  822. //    Close();
  823.   if (!chan->Open(PFile::ReadOnly)) {
  824.     PTRACE(1, "Cannot open file "" << chan->GetName() << """);
  825.     return;
  826.   }
  827.   PTRACE(1, "Playing file "" << chan->GetName() << """);
  828.   totalData = 0;
  829.   SetReadChannel(chan, TRUE);
  830. }
  831. void OGMChannel::CreateSilence(void * buffer, PINDEX amount)
  832. {
  833.   if (!isPCM) 
  834.     lastReadCount = 0;
  835.   else {
  836.     memset(buffer, 0, amount);
  837.     lastReadCount = amount;
  838.   }
  839. }
  840. BOOL OGMChannel::Read(void * buffer, PINDEX amount)
  841. {
  842.   PWaitAndSignal mutex(chanMutex);
  843.   if (closed)
  844.     return FALSE;
  845.   BOOL doSilence = TRUE;
  846.   if (silentCount > 0) {
  847.     silentCount--;
  848.   } else {
  849.     if (GetBaseReadChannel() != NULL)
  850.       doSilence = FALSE;
  851.     else {
  852.       PString * str = playQueue.Dequeue();
  853.       if (str != NULL) {
  854.         PFile * chan = new PFile(*str + ext);
  855.         delete str;
  856.         if (!chan->Open(PFile::ReadOnly)) {
  857.           PTRACE(1, "Cannot open file "" << chan->GetName() << """);
  858.           delete chan;
  859.         } else {
  860.           PTRACE(1, "Playing file "" << chan->GetName() << """);
  861.           totalData = 0;
  862.           SetReadChannel(chan, TRUE);
  863.           doSilence = FALSE;
  864.         } 
  865.       }
  866.     }
  867.   }
  868.   if (!doSilence) {
  869.     if (PIndirectChannel::Read(buffer, amount))
  870.       totalData += amount;
  871.     else {
  872.       PTRACE(1, "Finished playing " << totalData << " bytes");
  873.       closed = TRUE;
  874.       PIndirectChannel::Close();
  875.       silentCount = 5;
  876.       if (hangupTrigger) 
  877.         conn.ClearCall();
  878.       else if (recordTrigger) 
  879.         conn.StartRecording();
  880.       doSilence = TRUE;
  881.     }
  882.   }
  883.   if (doSilence)
  884.     CreateSilence(buffer, amount);
  885.   //PThread::Current()->Sleep(amount / 8);
  886.   return TRUE;
  887. }
  888. BOOL OGMChannel::Close()
  889. {
  890.   PWaitAndSignal mutex(chanMutex);
  891.   closed = TRUE;
  892.   PIndirectChannel::Close();
  893.   return TRUE;
  894. }
  895. void OGMChannel::SetRecordTrigger()
  896. {
  897.   PWaitAndSignal mutex(chanMutex);
  898.   recordTrigger = TRUE;
  899.   if ((playQueue.GetSize() == 0) && (GetBaseReadChannel() == NULL))
  900.     conn.StartRecording();
  901. }
  902. void OGMChannel::SetHangupTrigger()
  903. {
  904.   PWaitAndSignal mutex(chanMutex);
  905.   hangupTrigger = TRUE;
  906.   if (GetBaseReadChannel() == NULL)
  907.     conn.ClearCall();
  908. }
  909. void OGMChannel::QueueFile(const PString & fn)
  910. {
  911.   PWaitAndSignal mutex(chanMutex);
  912.   PTRACE(1, "Enqueueing file " << (fn + ext) << " for playing");
  913.   playQueue.Enqueue(new PString(fn));
  914. }
  915. void OGMChannel::FlushQueue()
  916. {
  917.   PWaitAndSignal mutex(chanMutex);
  918.   if (GetBaseReadChannel() != NULL) {
  919.     PIndirectChannel::Close();
  920.     if (hangupTrigger) 
  921.       conn.ClearCall();
  922.     else if (recordTrigger) 
  923.       conn.StartRecording();
  924.   }
  925.   PString * str;
  926.   for (;;) {
  927.     str = playQueue.Dequeue();
  928.     if (str == NULL)
  929.       break;
  930.     delete str;
  931.   }
  932. }
  933. // End of File ///////////////////////////////////////////////////////////////