vxml.h

Go to the documentation of this file.
00001 /*
00002  * vxml.h
00003  *
00004  * VXML engine for pwlib library
00005  *
00006  * Copyright (C) 2002 Equivalence Pty. Ltd.
00007  *
00008  * The contents of this file are subject to the Mozilla Public License
00009  * Version 1.0 (the "License"); you may not use this file except in
00010  * compliance with the License. You may obtain a copy of the License at
00011  * http://www.mozilla.org/MPL/
00012  *
00013  * Software distributed under the License is distributed on an "AS IS"
00014  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00015  * the License for the specific language governing rights and limitations
00016  * under the License.
00017  *
00018  * The Original Code is Portable Windows Library.
00019  *
00020  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
00021  *
00022  * Contributor(s): ______________________________________.
00023  *
00024  * $Revision: 29067 $
00025  * $Author: rjongbloed $
00026  * $Date: 2013-02-11 22:34:39 -0600 (Mon, 11 Feb 2013) $
00027  */
00028 
00029 #ifndef PTLIB_VXML_H
00030 #define PTLIB_VXML_H
00031 
00032 #ifdef P_USE_PRAGMA
00033 #pragma interface
00034 #endif
00035 
00036 
00037 #include <ptclib/pxml.h>
00038 
00039 #if P_VXML
00040 
00041 #include <ptlib/pfactory.h>
00042 #include <ptlib/pipechan.h>
00043 #include <ptclib/delaychan.h>
00044 #include <ptclib/pwavfile.h>
00045 #include <ptclib/ptts.h>
00046 #include <ptclib/url.h>
00047 
00048 #include <queue>
00049 
00050 
00051 class PVXMLSession;
00052 class PVXMLDialog;
00053 class PVXMLSession;
00054 
00055 // these are the same strings as the Opal equivalents, but as this is PWLib, we can't use Opal contants
00056 #define VXML_PCM16         "PCM-16"
00057 #define VXML_G7231         "G.723.1"
00058 #define VXML_G729          "G.729"
00059 
00060 
00062 
00063 class PVXMLGrammar : public PObject
00064 {
00065   PCLASSINFO(PVXMLGrammar, PObject);
00066   public:
00067     PVXMLGrammar(PVXMLSession & session, PXMLElement & field);
00068 
00069     virtual void OnUserInput(const char ch) = 0;
00070     virtual void Start();
00071     virtual bool Process();
00072 
00073     enum GrammarState {
00074       Idle,         
00075       Started,      
00076       Filled,       
00077       NoInput,      
00078       NoMatch,      
00079       Help          
00080     };
00081 
00082     GrammarState GetState() const { return m_state; }
00083 
00084     void SetTimeout(const PTimeInterval & timeout);
00085 
00086   protected:
00087     PDECLARE_NOTIFIER(PTimer, PVXMLGrammar, OnTimeout);
00088 
00089     PVXMLSession & m_session;
00090     PXMLElement  & m_field;
00091     PString        m_value;
00092     GrammarState   m_state;
00093     PTimeInterval  m_timeout;
00094     PTimer         m_timer;
00095     PMutex         m_mutex;
00096 };
00097 
00098 
00100 
00101 class PVXMLMenuGrammar : public PVXMLGrammar
00102 {
00103   PCLASSINFO(PVXMLMenuGrammar, PVXMLGrammar);
00104   public:
00105     PVXMLMenuGrammar(PVXMLSession & session, PXMLElement & field);
00106     virtual void OnUserInput(const char ch);
00107     virtual bool Process();
00108 };
00109 
00110 
00112 
00113 class PVXMLDigitsGrammar : public PVXMLGrammar
00114 {
00115   PCLASSINFO(PVXMLDigitsGrammar, PVXMLGrammar);
00116   public:
00117     PVXMLDigitsGrammar(
00118       PVXMLSession & session,
00119       PXMLElement & field,
00120       PINDEX minDigits,
00121       PINDEX maxDigits,
00122       PString terminators
00123     );
00124 
00125     virtual void OnUserInput(const char ch);
00126 
00127   protected:
00128     PINDEX  m_minDigits;
00129     PINDEX  m_maxDigits;
00130     PString m_terminators;
00131 };
00132 
00133 
00135 
00136 class PVXMLCache : public PMutex
00137 {
00138   public:
00139     PVXMLCache(const PDirectory & directory);
00140 
00141     PFilePath CreateFilename(const PString & prefix, const PString & key, const PString & fileType);
00142 
00143     void Put(const PString & prefix,
00144              const PString & key, 
00145              const PString & fileType, 
00146              const PString & contentType,       
00147            const PFilePath & fn, 
00148                  PFilePath & dataFn);
00149 
00150     PBoolean Get(const PString & prefix,
00151              const PString & key, 
00152              const PString & fileType, 
00153                    PString & contentType,       
00154                  PFilePath & fn);
00155 
00156     PFilePath GetCacheDir() const
00157     { return directory; }
00158 
00159     PFilePath GetRandomFilename(const PString & prefix, const PString & fileType);
00160 
00161     static PVXMLCache & GetResourceCache();
00162 
00163   protected:
00164     PDirectory directory;
00165 };
00166 
00168 
00169 class PVXMLChannel;
00170 
00171 class PVXMLSession : public PIndirectChannel
00172 {
00173   PCLASSINFO(PVXMLSession, PIndirectChannel);
00174   public:
00175     PVXMLSession(PTextToSpeech * tts = NULL, PBoolean autoDelete = false);
00176     virtual ~PVXMLSession();
00177 
00178     // new functions
00179     PTextToSpeech * SetTextToSpeech(PTextToSpeech * tts, PBoolean autoDelete = false);
00180     PTextToSpeech * SetTextToSpeech(const PString & ttsName);
00181     PTextToSpeech * GetTextToSpeech() const { return m_textToSpeech; }
00182 
00183     virtual PBoolean Load(const PString & source);
00184     virtual PBoolean LoadFile(const PFilePath & file, const PString & firstForm = PString::Empty());
00185     virtual PBoolean LoadURL(const PURL & url);
00186     virtual PBoolean LoadVXML(const PString & xml, const PString & firstForm = PString::Empty());
00187     virtual PBoolean IsLoaded() const { return m_xml.IsLoaded(); }
00188 
00189     virtual PBoolean Open(const PString & mediaFormat);
00190     virtual PBoolean Close();
00191 
00192     virtual PBoolean Execute();
00193 
00194     PVXMLChannel * GetAndLockVXMLChannel();
00195     void UnLockVXMLChannel() { m_sessionMutex.Signal(); }
00196     PMutex & GetSessionMutex() { return m_sessionMutex; }
00197 
00198     virtual PBoolean LoadGrammar(PVXMLGrammar * grammar);
00199 
00200     virtual PBoolean PlayText(const PString & text, PTextToSpeech::TextType type = PTextToSpeech::Default, PINDEX repeat = 1, PINDEX delay = 0);
00201     PBoolean ConvertTextToFilenameList(const PString & text, PTextToSpeech::TextType type, PStringArray & list, PBoolean useCacheing);
00202 
00203     virtual PBoolean PlayFile(const PString & fn, PINDEX repeat = 1, PINDEX delay = 0, PBoolean autoDelete = false);
00204     virtual PBoolean PlayData(const PBYTEArray & data, PINDEX repeat = 1, PINDEX delay = 0);
00205     virtual PBoolean PlayCommand(const PString & data, PINDEX repeat = 1, PINDEX delay = 0);
00206     virtual PBoolean PlayResource(const PURL & url, PINDEX repeat = 1, PINDEX delay = 0);
00207     virtual PBoolean PlayTone(const PString & toneSpec, PINDEX repeat = 1, PINDEX delay = 0);
00208     virtual PBoolean PlayElement(PXMLElement & element);
00209 
00210     //virtual PBoolean PlayMedia(const PURL & url, PINDEX repeat = 1, PINDEX delay = 0);
00211     virtual PBoolean PlaySilence(PINDEX msecs = 0);
00212     virtual PBoolean PlaySilence(const PTimeInterval & timeout);
00213 
00214     virtual PBoolean PlayStop();
00215 
00216     virtual void SetPause(PBoolean pause);
00217     virtual void GetBeepData(PBYTEArray & data, unsigned ms);
00218 
00219     virtual PBoolean StartRecording(const PFilePath & fn, PBoolean recordDTMFTerm, const PTimeInterval & recordMaxTime, const PTimeInterval & recordFinalSilence);
00220     virtual PBoolean EndRecording();
00221 
00222     virtual void OnUserInput(const PString & str);
00223 
00224     PString GetXMLError() const;
00225 
00226     virtual void OnEndDialog();
00227     virtual void OnEndSession();
00228 
00229     enum TransferType {
00230       BridgedTransfer,
00231       BlindTransfer,
00232       ConsultationTransfer
00233     };
00234     virtual bool OnTransfer(const PString & /*destination*/, TransferType /*type*/) { return false; }
00235     void SetTransferComplete(bool state);
00236 
00237     const PStringToString & GetVariables() { return m_variables; }
00238     virtual PCaselessString GetVar(const PString & str) const;
00239     virtual void SetVar(const PString & ostr, const PString & val);
00240     virtual PString EvaluateExpr(const PString & oexpr);
00241 
00242     static PTimeInterval StringToTime(const PString & str, int dflt = 0);
00243 
00244     virtual PBoolean RetreiveResource(const PURL & url, PString & contentType, PFilePath & fn, PBoolean useCache = true);
00245 
00246     PDECLARE_NOTIFIER(PThread, PVXMLSession, VXMLExecute);
00247 
00248     bool SetCurrentForm(const PString & id, bool fullURI);
00249     bool GoToEventHandler(PXMLElement & element, const PString & eventName);
00250 
00251     // overrides from VXMLChannelInterface
00252     virtual void OnEndRecording();
00253     virtual void Trigger();
00254 
00255 
00256     virtual PBoolean TraverseAudio(PXMLElement & element);
00257     virtual PBoolean TraverseBreak(PXMLElement & element);
00258     virtual PBoolean TraverseValue(PXMLElement & element);
00259     virtual PBoolean TraverseSayAs(PXMLElement & element);
00260     virtual PBoolean TraverseGoto(PXMLElement & element);
00261     virtual PBoolean TraverseGrammar(PXMLElement & element);
00262     virtual PBoolean TraverseRecord(PXMLElement & element);
00263     virtual PBoolean TraversedRecord(PXMLElement & element);
00264     virtual PBoolean TraverseIf(PXMLElement & element);
00265     virtual PBoolean TraverseExit(PXMLElement & element);
00266     virtual PBoolean TraverseVar(PXMLElement & element);
00267     virtual PBoolean TraverseSubmit(PXMLElement & element);
00268     virtual PBoolean TraverseMenu(PXMLElement & element);
00269     virtual PBoolean TraversedMenu(PXMLElement & element);
00270     virtual PBoolean TraverseChoice(PXMLElement & element);
00271     virtual PBoolean TraverseProperty(PXMLElement & element);
00272     virtual PBoolean TraverseDisconnect(PXMLElement & element);
00273     virtual PBoolean TraverseForm(PXMLElement & element);
00274     virtual PBoolean TraversedForm(PXMLElement & element);
00275     virtual PBoolean TraversePrompt(PXMLElement & element);
00276     virtual PBoolean TraversedPrompt(PXMLElement & element);
00277     virtual PBoolean TraverseField(PXMLElement & element);
00278     virtual PBoolean TraversedField(PXMLElement & element);
00279     virtual PBoolean TraverseTransfer(PXMLElement & element);
00280     virtual PBoolean TraversedTransfer(PXMLElement & element);
00281 
00282     __inline PVXMLChannel * GetVXMLChannel() const { return (PVXMLChannel *)readChannel; }
00283 
00284   protected:
00285     virtual bool InternalLoadVXML(const PString & xml, const PString & firstForm);
00286 
00287     virtual bool ProcessNode();
00288     virtual bool ProcessEvents();
00289     virtual bool ProcessGrammar();
00290     virtual bool NextNode(bool processChildren);
00291 
00292     void SayAs(const PString & className, const PString & text);
00293     void SayAs(const PString & className, const PString & text, const PString & voice);
00294 
00295     PURL NormaliseResourceName(const PString & src);
00296 
00297     PMutex           m_sessionMutex;
00298 
00299     PURL             m_rootURL;
00300     PXML             m_xml;
00301 
00302     PTextToSpeech *  m_textToSpeech;
00303     bool             m_autoDeleteTextToSpeech;
00304 
00305     PThread     *    m_vxmlThread;
00306     bool             m_abortVXML;
00307     PSyncPoint       m_waitForEvent;
00308     PXMLObject  *    m_currentNode;
00309     bool             m_xmlChanged;
00310     bool             m_speakNodeData;
00311     bool             m_bargeIn;
00312     bool             m_bargingIn;
00313 
00314     PVXMLGrammar *   m_grammar;
00315     char             m_defaultMenuDTMF;
00316 
00317     PStringToString  m_variables;
00318     PString          m_variableScope;
00319 
00320     std::queue<char> m_userInputQueue;
00321     PMutex           m_userInputMutex;
00322 
00323     enum {
00324       NotRecording,
00325       RecordingInProgress,
00326       RecordingComplete
00327     }    m_recordingStatus;
00328     bool m_recordStopOnDTMF;
00329 
00330     enum {
00331       NotTransfering,
00332       TransferInProgress,
00333       TransferFailed,
00334       TransferSuccessful
00335     }     m_transferStatus;
00336     PTime m_transferStartTime;
00337 };
00338 
00339 
00341 
00342 class PVXMLRecordable : public PObject
00343 {
00344   PCLASSINFO(PVXMLRecordable, PObject);
00345   public:
00346     PVXMLRecordable();
00347 
00348     virtual PBoolean Open(const PString & arg) = 0;
00349 
00350     virtual bool OnStart(PVXMLChannel & incomingChannel) = 0;
00351     virtual void OnStop() { }
00352 
00353     virtual PBoolean OnFrame(PBoolean /*isSilence*/) { return false; }
00354 
00355     void SetFinalSilence(unsigned v)
00356     { m_finalSilence = v > 0 ? v : 60000; }
00357 
00358     unsigned GetFinalSilence()
00359     { return m_finalSilence; }
00360 
00361     void SetMaxDuration(unsigned v)
00362     { m_maxDuration = v > 0 ? v : 86400000; }
00363 
00364     unsigned GetMaxDuration()
00365     { return m_maxDuration; }
00366 
00367   protected:
00368     PSimpleTimer m_silenceTimer;
00369     PSimpleTimer m_recordTimer;
00370     unsigned     m_finalSilence;
00371     unsigned     m_maxDuration;
00372 };
00373 
00375 
00376 class PVXMLRecordableFilename : public PVXMLRecordable
00377 {
00378   PCLASSINFO(PVXMLRecordableFilename, PVXMLRecordable);
00379   public:
00380     PBoolean Open(const PString & arg);
00381     bool OnStart(PVXMLChannel & incomingChannel);
00382     PBoolean OnFrame(PBoolean isSilence);
00383 
00384   protected:
00385     PFilePath m_fileName;
00386 };
00387 
00389 
00390 class PVXMLPlayable : public PObject
00391 {
00392   PCLASSINFO(PVXMLPlayable, PObject);
00393   public:
00394     PVXMLPlayable();
00395 
00396     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00397 
00398     virtual bool OnStart() = 0;
00399     virtual bool OnRepeat();
00400     virtual bool OnDelay();
00401     virtual void OnStop();
00402 
00403     virtual void SetRepeat(PINDEX v) 
00404     { m_repeat = v; }
00405 
00406     virtual PINDEX GetRepeat() const
00407     { return m_repeat; }
00408 
00409     virtual PINDEX GetDelay() const
00410     { return m_delay; }
00411 
00412     void SetFormat(const PString & fmt)
00413     { m_format = fmt; }
00414 
00415     void SetSampleFrequency(unsigned rate)
00416     { m_sampleFrequency = rate; }
00417 
00418     friend class PVXMLChannel;
00419 
00420   protected:
00421     PVXMLChannel * m_vxmlChannel;
00422     PChannel * m_subChannel;
00423     PINDEX   m_repeat;
00424     PINDEX   m_delay;
00425     PString  m_format;
00426     unsigned m_sampleFrequency;
00427     bool     m_autoDelete;
00428     bool     m_delayDone; // very tacky flag used to indicate when the post-play delay has been done
00429 };
00430 
00432 
00433 class PVXMLPlayableStop : public PVXMLPlayable
00434 {
00435   PCLASSINFO(PVXMLPlayableStop, PVXMLPlayable);
00436   public:
00437     virtual bool OnStart();
00438 };
00439 
00441 
00442 class PVXMLPlayableURL : public PVXMLPlayable
00443 {
00444   PCLASSINFO(PVXMLPlayableURL, PVXMLPlayable);
00445   public:
00446     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00447     virtual bool OnStart();
00448   protected:
00449     PURL m_url;
00450 };
00451 
00453 
00454 class PVXMLPlayableData : public PVXMLPlayable
00455 {
00456   PCLASSINFO(PVXMLPlayableData, PVXMLPlayable);
00457   public:
00458     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00459     void SetData(const PBYTEArray & data);
00460     virtual bool OnStart();
00461     virtual bool OnRepeat();
00462   protected:
00463     PBYTEArray m_data;
00464 };
00465 
00467 
00468 #include <ptclib/dtmf.h>
00469 
00470 class PVXMLPlayableTone : public PVXMLPlayableData
00471 {
00472   PCLASSINFO(PVXMLPlayableTone, PVXMLPlayableData);
00473   public:
00474     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00475   protected:
00476     PTones m_tones;
00477 };
00478 
00480 
00481 class PVXMLPlayableCommand : public PVXMLPlayable
00482 {
00483   PCLASSINFO(PVXMLPlayableCommand, PVXMLPlayable);
00484   public:
00485     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00486     virtual bool OnStart();
00487     virtual void OnStop();
00488 
00489   protected:
00490     PString m_command;
00491 };
00492 
00494 
00495 class PVXMLPlayableFile : public PVXMLPlayable
00496 {
00497   PCLASSINFO(PVXMLPlayableFile, PVXMLPlayable);
00498   public:
00499     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00500     virtual bool OnStart();
00501     virtual bool OnRepeat();
00502     virtual void OnStop();
00503   protected:
00504     PFilePath m_filePath;
00505 };
00506 
00508 
00509 class PVXMLPlayableFileList : public PVXMLPlayableFile
00510 {
00511   PCLASSINFO(PVXMLPlayableFileList, PVXMLPlayableFile);
00512   public:
00513     PVXMLPlayableFileList();
00514     virtual PBoolean Open(PVXMLChannel & chan, const PString & arg, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00515     virtual PBoolean Open(PVXMLChannel & chan, const PStringArray & filenames, PINDEX delay, PINDEX repeat, PBoolean autoDelete);
00516     virtual bool OnStart();
00517     virtual bool OnRepeat();
00518     virtual void OnStop();
00519   protected:
00520     PStringArray m_fileNames;
00521     PINDEX       m_currentIndex;
00522 };
00523 
00525 
00526 PQUEUE(PVXMLQueue, PVXMLPlayable);
00527 
00529 
00530 class PVXMLChannel : public PDelayChannel
00531 {
00532   PCLASSINFO(PVXMLChannel, PDelayChannel);
00533   public:
00534     PVXMLChannel(unsigned frameDelay, PINDEX frameSize);
00535     ~PVXMLChannel();
00536 
00537     virtual PBoolean Open(PVXMLSession * session);
00538 
00539     // overrides from PIndirectChannel
00540     virtual PBoolean IsOpen() const;
00541     virtual PBoolean Close();
00542     virtual PBoolean Read(void * buffer, PINDEX amount);
00543     virtual PBoolean Write(const void * buf, PINDEX len);
00544 
00545     // new functions
00546     virtual PWAVFile * CreateWAVFile(const PFilePath & fn, PBoolean recording = false);
00547 
00548     const PString & GetMediaFormat() const { return mediaFormat; }
00549     PBoolean IsMediaPCM() const { return mediaFormat == "PCM-16"; }
00550     virtual PString AdjustWavFilename(const PString & fn);
00551 
00552     // Incoming channel functions
00553     virtual PBoolean WriteFrame(const void * buf, PINDEX len) = 0;
00554     virtual PBoolean IsSilenceFrame(const void * buf, PINDEX len) const = 0;
00555 
00556     virtual PBoolean QueueRecordable(PVXMLRecordable * newItem);
00557 
00558     PBoolean StartRecording(const PFilePath & fn, unsigned finalSilence = 3000, unsigned maxDuration = 30000);
00559     PBoolean EndRecording();
00560     PBoolean IsRecording() const { return m_recordable != NULL; }
00561 
00562     // Outgoing channel functions
00563     virtual PBoolean ReadFrame(void * buffer, PINDEX amount) = 0;
00564     virtual PINDEX CreateSilenceFrame(void * buffer, PINDEX amount) = 0;
00565     virtual void GetBeepData(PBYTEArray &, unsigned) { }
00566 
00567     virtual PBoolean QueueResource(const PURL & url, PINDEX repeat= 1, PINDEX delay = 0);
00568 
00569     virtual PBoolean QueuePlayable(const PString & type, const PString & str, PINDEX repeat = 1, PINDEX delay = 0, PBoolean autoDelete = false);
00570     virtual PBoolean QueuePlayable(PVXMLPlayable * newItem);
00571     virtual PBoolean QueueData(const PBYTEArray & data, PINDEX repeat = 1, PINDEX delay = 0);
00572 
00573     virtual PBoolean QueueFile(const PString & fn, PINDEX repeat = 1, PINDEX delay = 0, PBoolean autoDelete = false)
00574     { return QueuePlayable("File", fn, repeat, delay, autoDelete); }
00575 
00576     virtual PBoolean QueueCommand(const PString & cmd, PINDEX repeat = 1, PINDEX delay = 0)
00577     { return QueuePlayable("Command", cmd, repeat, delay, true); }
00578 
00579     virtual void FlushQueue();
00580     virtual PBoolean IsPlaying() const { return m_currentPlayItem != NULL || m_playQueue.GetSize() > 0; }
00581 
00582     void SetPause(PBoolean pause) { m_paused = pause; }
00583 
00584     unsigned GetSampleFrequency() const { return m_sampleFrequency; }
00585 
00586     void SetSilence(unsigned msecs);
00587 
00588   protected:
00589     PVXMLSession * m_vxmlSession;
00590 
00591     unsigned m_sampleFrequency;
00592     PString mediaFormat;
00593     PString wavFilePrefix;
00594 
00595     PMutex   m_channelWriteMutex;
00596     PMutex   m_channelReadMutex;
00597     bool     m_closed;
00598     bool     m_paused;
00599     PINDEX   m_totalData;
00600 
00601     // Incoming audio variables
00602     PVXMLRecordable * m_recordable;
00603     unsigned          m_finalSilence;
00604     unsigned          m_silenceRun;
00605 
00606     // Outgoing audio variables
00607     PVXMLQueue      m_playQueue;
00608     PVXMLPlayable * m_currentPlayItem;
00609     PSimpleTimer    m_silenceTimer;
00610 };
00611 
00612 
00614 
00615 class PVXMLNodeHandler : public PObject
00616 {
00617     PCLASSINFO(PVXMLNodeHandler, PObject);
00618   public:
00619     // Return true for process node, false to skip and move to next sibling
00620     virtual bool Start(PVXMLSession & /*session*/, PXMLElement & /*node*/) const { return true; }
00621 
00622     // Return true to move to next sibling, false to stay at this node.
00623     virtual bool Finish(PVXMLSession & /*session*/, PXMLElement & /*node*/) const { return true; }
00624 };
00625 
00626 
00627 typedef PFactory<PVXMLNodeHandler, PCaselessString> PVXMLNodeFactory;
00628 
00629 
00630 #endif // P_VXML
00631 
00632 #endif // PTLIB_VXML_H
00633 
00634 
00635 // End of file ////////////////////////////////////////////////////////////////

Generated on Fri Feb 15 20:58:32 2013 for PTLib by  doxygen 1.4.7