/* * main.cxx * * PWLib application source file for OhPhone * * A H.323 "net telephone" application. * * Copyright (c) 1998-2000 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 Open H323 Library. * * The Initial Developer of the Original Code is Equivalence Pty. Ltd. * * Portions of this code were written with the assisance of funding from * Vovida Networks, Inc. http://www.vovida.com. * * Contributor(s): ______________________________________. * Derek J Smithies (derek@indranet.co.nz) * Walter H Whitlock (twohives@nc.rr.com) * * $Log: main.cxx,v $ * Revision 1.345 2005/08/18 02:03:36 dereksmithies * Remove sound card test option.. This will be installed in pwlib/samples/audio * * Revision 1.344 2005/08/12 11:27:21 rjongbloed * Removed SDL picture in picture, sorry. Will fix later! * * Revision 1.343 2005/01/11 07:56:45 csoutheren * Added --number-prefix option * Fixed problem with using proxies * * Revision 1.342 2004/12/16 00:46:52 csoutheren * Added osptoken option * * Revision 1.341 2004/12/15 06:04:51 csoutheren * Added outgoing OSP capability * * Revision 1.340 2004/11/25 07:40:42 csoutheren * Allow port range to be set before STUN is set, allowing STUN to use a specific port range * * Revision 1.339 2004/11/22 02:57:53 csoutheren * Rationalised sound device driver options (again) * * Revision 1.338 2004/11/04 22:20:28 csoutheren * Fixed incorrect handling of sound drivers * * Revision 1.337 2004/11/01 22:21:53 ykiryanov * Removed hack with inclusion of be.inc. BeOS now has gcc 2.95.3 available, * and code built with 2.95.3 has plugins problem gone. Please use gcc 2.95.3 * * Revision 1.336 2004/08/19 22:16:49 dereksmithies * Set payload type for video test when using rfc2190_h263 capability. * Thanks to Srinivas.Kandagatla for your help here..... * * Revision 1.335 2004/07/13 01:57:20 csoutheren * Disabled RFC 2190 H.263 if DLL not available * * Revision 1.334 2004/06/16 06:32:52 ykiryanov * Included be.inc - media registration code. Cannot be anywhere else (sigh) * * Revision 1.333 2004/06/02 00:40:46 csoutheren * Changed g711frames to work in msecs * * Revision 1.332 2004/06/02 00:27:28 csoutheren * Added options to set frames per packet for any codec * * Revision 1.331 2004/05/31 10:53:42 rjongbloed * Fixed missing quote in list of codec names. * * Revision 1.330 2004/05/28 23:38:58 csoutheren * Added --no-h263 and --no-h261 options as shorthand to remove video codecs * * Revision 1.329 2004/05/27 23:33:12 csoutheren * Checked in fixes for new plugin codecs and RFC 2190 H.263 * * Revision 1.328 2004/05/10 13:07:18 rjongbloed * Changed G.726 and MS-ADPCM to plug in codecs. * * Revision 1.327 2004/05/04 12:21:15 rjongbloed * Converted LPC-10 codec to plug in. * * Revision 1.326 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.325 2004/04/22 14:45:26 csoutheren * Added changes for RFC2190 H.263 * * Revision 1.324 2004/04/06 11:27:48 rjongbloed * Changes to support native C++ Run Time Type Information * Changes for codec plug ins * * Revision 1.323 2004/01/18 14:19:03 dereksmithies * Opening of video devices with plugins works now. * * Revision 1.322 2004/01/02 01:34:45 dereksmithies * Attempt to get ohphone working with V4L plugins. * * Revision 1.321 2003/12/18 05:16:35 rjongbloed * Fixed strange error with extra parens! * * Revision 1.320 2003/12/14 11:00:08 rjongbloed * Resolved issue with name space conflict os static and virtual forms of GetDeviceNames() function. * * Revision 1.319 2003/11/19 04:49:48 csoutheren * Changed to support video input and output plugins * * Revision 1.318 2003/11/15 03:51:55 dereksmithies * Add --soundtest option, which records audio, plays back 3 seconds later. Tests if the card is full duplex. * * Revision 1.317 2003/11/09 20:56:23 shawn * added use of stun and ilbc codec * * Revision 1.316 2003/10/31 23:17:29 shawn * use IPv6 as default address family when available; this should not revent IPv4 from working properly * * Revision 1.315 2003/08/04 04:07:54 dereksmithies * Put H261 Capability back in the source * * Revision 1.314 2003/07/24 05:14:52 dereksmithies * Support for vich263 added * * Revision 1.313 2003/06/12 19:39:11 shawn * Added shared memory video input/output devices. Video frames of these two * devices are stored in a named shared memory region and can be accessed by * other applications. * * Revision 1.312 2003/06/04 19:03:58 shawn * realtime scheduling for OSX is replaced by fixed priority scheduling in pwlib * * Revision 1.311 2003/05/23 05:19:03 rjongbloed * Added extra #define for H263 codec * * Revision 1.310 2003/05/15 01:00:14 rjongbloed * Fixed use of correct autoconf variable to include H.263 codec * * Revision 1.309 2003/05/14 13:58:39 rjongbloed * Removed hack of using special payload type for H.263 for a method which * would be less prone to failure in the future. * * Revision 1.308 2003/05/14 02:49:52 dereksmithies * Add videolose option, so X percentage of video packets are dropped when in --videotest mode * * Revision 1.307 2003/05/07 02:45:58 dereks * Alter ohphone to use the PSDLVideoOutputDevice class, which is now part of pwlib. * * Revision 1.306 2003/04/16 04:31:22 dereks * Initial release of h263 video codec, which utilises the ffmpeg library. * Thanks to Guilhem Tardy, and to AliceStreet * * Revision 1.305 2003/04/15 21:16:53 dereks * Patch for firewire video applied - thanks to Goergi Georgiev. * * Revision 1.304 2003/04/04 02:14:34 robertj * Fixed IPv6 support for ports on interfaces, pointed out by Kostas Stamos * * Revision 1.303 2003/03/31 00:22:32 dereks * Can now read 20 digits, instead of 10, from the IXJ card. Thanks Jorge Minassian. * * Revision 1.302 2003/03/28 15:10:52 rogerh * Add 127.0.0.1 to IP addresses which are not translated in NAT mode. * * Revision 1.301 2003/03/24 23:15:25 robertj * Fixed change of variable name * * Revision 1.300 2003/03/21 04:21:30 robertj * Fixed missing set of colour format in video output device. * * history deleted * * Revision 1.1 1998/12/14 09:13:19 robertj * Initial revision * */ #include #include #include "main.h" #include "h261codec.h" #include "h263codec.h" #if H323_AVCODEC #include "ffh263codec.h" #endif #if H323_RFC2190_AVCODEC #include "rfc2190avcodec.h" #endif #include "h323pdu.h" //#include "h323t120.h" //#include "t120proto.h" #ifdef DEPRECATED_CU30 #include "cu30codec.h" #endif #ifdef P_LINUX #include "vidlinux.h" #include #endif #define FICTITOUS_VIDEO "fake" #if defined(P_FREEBSD) || defined(P_OPENBSD) || defined(P_NETBSD) #define DEFAULT_VIDEO "/dev/bktr0" #endif #ifndef DEFAULT_VIDEO #define DEFAULT_VIDEO "/dev/video0" #endif #ifdef HAS_X11 #include "xlibvid.h" #endif #ifdef P_SDL #include #endif #ifdef USE_SHM_VIDEO_DEVICES #include "shmvideo.h" #endif #ifdef HAS_OSS #define DEFAULT_MIXER "/dev/mixer" #ifdef P_LINUX #include #endif #ifdef P_FREEBSD #if P_FREEBSD >= 500000 #include #else #include #endif #endif #if defined(P_OPENBSD) || defined(P_NETBSD) #include #endif #endif // HAS OSS #ifdef HAS_LIDDEVICE static const char * AECLevelNames[] = { "Off", "Low", "Medium", "High", "Auto AEC", "Auto AEC/AGC" }; #endif #define HAS_T38 #ifdef HAS_T38 #include #endif #include "version.h" PCREATE_PROCESS(OhPhone); #define DEFAULT_TIMEOUT 60000 #define LAST_CALL_COUNT 16 #define POTS_LINE 0 class RingThread : public PThread { PCLASSINFO(RingThread, PThread); public: RingThread(MyH323EndPoint & ep) : PThread(1000, NoAutoDeleteThread), endpoint(ep) { Resume(); } void Main() { endpoint.HandleRinging(); } protected: MyH323EndPoint & endpoint; }; #define new PNEW /////////////////////////////////////////////////////////////// OhPhone::OhPhone() : PProcess("Open H323 Project", "OhPhone", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) { } OhPhone::~OhPhone() { } void OhPhone::Main() { //PArgList & args = GetArguments(); PConfigArgs args(GetArguments()); args.Parse( "a-auto-answer." "-no-auto-answer." "b-bandwidth:" "-no-bandwidth." "B-forward-busy:" "-no-forward-busy." #ifdef HAS_LIDDEVICE "c-callerid." "-no-callerid." "C-country:" "-no-country." #endif "d-autodial:" "-no-autodial." "D-disable:" "e-silence." "-no-silence." "f-fast-disable." "-no-fast-disable." "F-forward-always:" "-no-forward-always." "g-gatekeeper:" "G-gatekeeper-id:" "h-help." "I-input-mode:" "i-interface:" "-no-interface." "j-jitter:" "-no-jitter." "l-listen." "n-no-gatekeeper." "N-forward-no-answer:" "-no-forward-no-answer." "-number-prefix:" "-no-number-prefix." "-answer-timeout:" "-no-answer-timeout." #if PTRACING "o-output:" "-no-output." #endif "p-proxy:" "-no-proxy." "-password:" "-no-password." "-listenport:" "-no-listenport." "-connectport:" "-no-connectport." "-connectring:" "-no-connectring." "-port:" "-no-port." "P-prefer:" #ifdef HAS_IXJ "q-quicknet:" "-no-quicknet." #endif #ifdef HAS_VBLASTER "V-voipblaster:" "-no-voipblaster." #endif "r-require-gatekeeper." "-no-require-gatekeeper." "S-disable-h245-in-setup." "-no-disable-h245-in-setup." "-save." "-setup-param:" "-no-setup-param." "s-sound:" "-no-sound." "-sound-in:" "-no-sound-in." "-sound-out:" "-no-sound-out." "-sound-buffers:" "-no-sound-buffers." #ifdef HAS_OSS "-sound-mixer:" "-no-sound-mixer." "-sound-recchan:" "-no-sound-recchan." "-sound-recvol:" "-no-sound-recvol." "-sound-playvol:" "-no-sound-playvol." #endif "-sound-driver:" "-no-sound-driver." "-record-driver:" "-no-record-driver." "-play-driver:" "-no-play-driver." #ifdef PMEMORY_CHECK "-setallocationbreakpoint:" #endif "T-h245tunneldisable." "-no-h245tunneldisable." #if PTRACING "t-trace." "-no-trace." #endif "-tos:" "-no-tos." "-translate:" "-no-translate." "-stun:" "-no-stun." "u-user:" "-no-user." "U-userinputcap:" "-no-user-input-cap." "v-verbose:" "-no-verbose." "-disable-menu." "-no-disable-menu." #ifdef HAS_IXJ "-aec:" "-no-aec." "-dial-after-hangup." "-no-dial-after-hangup." "-callerid." "-no-callerid." "-calleridcw." "-no-calleridcw." "-autohook." "-no-autohook." "-quicknet-recvol:" "-no-quicknet-recvol." "-quicknet-playvol:" "-no-quicknet-playvol." #endif "-g728." "-no-g728." #ifdef G729 "-g729." "-no-g729." #endif "-fpp:-frames-per-packet:" "-gsm." "-no-gsm." "-gsmframes:" "-no-gsmframes." "-g711-ulaw." "-no-g711-ulaw." "-g711-alaw." "-no-g711-alaw." "-g711frames:" "-no-g711frames." "-g7231." "-no-g7231." "-h261:" "-no-h261." "-playvol:" "-no-playvol." "-recvol:" "-no-recvol." "-ringfile:" "-ringdelay:" "-videotransmit." "-no-videotransmit." "-videolocal." "-no-videolocal." "-videosize:" "-no-videosize." "-videoformat:" "-no-videoformat." "-videocolorfmt:" "-no-videocolorfmt." "-videoinput:" "-no-videoinput." "-videodevice:" "-no-videodevice." "-videoreceive:" "-no-videoreceive." "-videoquality:" "-no-videoquality." "-videotxquality:" "-no-videotxquality." "-videotxminquality:" "-no-videotxminquality." "-videoidle:" "-no-videoidle." "-videopip." "-no-videopip." "-videofill:" "-no-videofill." "-videotxfps:" "-no-videotxfps." "-videobitrate:" "-no-videobitrate." "-videotest." "-no-videotest." "-videolose:" "-no-videolose." #ifdef DEPRECATED_CU30 "-videocu30stats:" "-no-videocu30stats." "-videocu30." "-no-videocu30." #endif "-autodisconnect:" "-autorepeat:" "-portbase:" "-portmax:" #ifdef H323_TRANSNEXUS_OSP "-osp:" "-ospdir:" #endif "-osptoken." , FALSE); #if PMEMORY_CHECK if (args.HasOption("setallocationbreakpoint")) PMemoryHeap::SetAllocationBreakpoint(args.GetOptionString("setallocationbreakpoint").AsInteger()); #endif int verbose = 255; if (args.HasOption('v')) verbose = args.GetOptionString('v').AsInteger(); if (verbose >= 3) cout << GetName() << " Version " << GetVersion(TRUE) << " by " << GetManufacturer() << " on " << GetOSClass() << ' ' << GetOSName() << " (" << GetOSVersion() << '-' << GetOSHardware() << ")\n\n"; #if PTRACING PTrace::Initialise(args.GetOptionCount('t'), args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL, PTrace::Blocks | PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine); #endif if (args.HasOption('h') || (!args.HasOption('l') && args.GetCount() == 0)) { cout << "Usage : " << GetName() << " [options] -l\n" " : " << GetName() << " [options] [-p host] hostname/alias\n" "\n where: hostname/alias = Remote host/alias to call\n" "\nOptions:\n" " -a --auto-answer : Automatically answer incoming calls\n" " -d --autodial host : Autodial host if phone off hook\n" " -h --help : Display this help message.\n" " -l --listen : Only listen for incoming calls\n" " -v --verbose n : Set amount of information displayed (0=none)\n" " --disable-menu : Disable internal menu\n" " --ringfile filename : Set sound file for \"ring\" annunciation\n" " --ringdelay seconds : Set delay between playing above file\n" " --save : Save parameters in configuration file.\n" "\nGatekeeper options:\n" " -g --gatekeeper host : Specify gatekeeper host.\n" " -G --gatekeeper-id name : Specify gatekeeper by ID.\n" " -n --no-gatekeeper : Disable gatekeeper discovery.\n" " -r --require-gatekeeper : Exit if gatekeeper discovery fails.\n" " --password pwd : Password for gatekeeper H.235 authentication.\n" " -p --proxy host : Proxy/Gateway hostname/ip address\n" " --osptoken : Copy OSP tokens (if present) from ACF to SETUP\n" "\nDivert options:\n" " -F --forward-always party : Forward to remote party.\n" " -B --forward-busy party : Forward to remote party if busy.\n" " -N --forward-no-answer party : Forward to remote party if no answer.\n" " --answer-timeout time : Time in seconds till forward on no answer.\n" "\nProtocol options:\n" " -i --interface ipaddr : Select interface to bind to for incoming connections (default is all interfaces)\n" " --listenport : Port to listen on for incoming connections (default 1720)\n" " --no-listenport : No listen port\n" " --connectport port : Port to connect to for outgoing connections (default 1720)\n" " --connectring num : Distinctive ring number to send to remote - 0 (default) to 7\n" " -b --bandwidth n : Limit bandwidth usage to (n * 100) bits/second\n" " -f --fast-disable : Disable fast start\n" " -T --h245tunneldisable : Disable H245 tunnelling.\n" " -u --user name : Set local alias name(s) (defaults to login name)\n" " -S --disable-h245-in-setup Disable H245 in setup\n" " --tos n : Set IP Type of Service byte to n\n" " --setup-param string : Arbitrary data to be put into H.225 Setup PDU\n" " --portbase port : Base port for H.245 and RTP data\n" " --portmax port : Maximum port for H.245 and RTP data\n" " --translate ip : Set external IP address to ip if masQueraded\n" " --stun ip : Set STUN server at ip" #ifdef H323_TRANSNEXUS_OSP " --osp server : Use OSP server for number resolution (disable GK if selected).\n" " --ospdir dir : Directory in which OSP certs are stored\n" #endif "\nAudio options:\n" " -e --silence : Disable silence detection for GSM and software G.711\n" " -j --jitter [min-]max : Set minimum (optional) and maximum jitter buffer (in milliseconds).\n" " --recvol n : Set record volume\n" " --playvol n : Set play volume\n" "\nVideo transmit options:\n" " --videodevice dev : Select video capture device (default " DEFAULT_VIDEO ")\n" " --videotransmit : Enable video transmission\n" " --videolocal : Enable local video window\n" " --videosize size : Sets size of transmitted video window\n" " size can be small (default) or large\n" " --videoformat type : Set capture video format\n" " can be auto (default) pal or ntsc\n" " --videocolorfmt format : Set the preferred capture device color format\n" " can be RGB24, RGB32, YUV420P, ...\n" " --videoinput num : Select capture video input (default is 0)\n" " --videotxquality n : Select sent video quality,(def 9). 1(best)<=n<=31\n" " --videotxminquality n : Select video quality lower limit,(def 1). 1(best)<=n<=31\n" " A value of 4 works best for NetMeeting\n" " --videofill n : Select number of updated background blocks per frame 2(def)<=n<=99\n" " --videotxfps n : Maximum number of video frames grabbed per sec 2<10(def)<30\n" " --videosendfps n : Target minimum number of video frames sent per sec 0.001<6(def)<30\n" " --videobitrate n : Enable bitrate control. 16< n <2048 kbit/s (net bw)\n" "\nVideo receive options:\n" " --videoquality n : Set received video quality hint - 0 <= n <= 31\n" " --videoreceive viddev : Receive video to following device\n" " : null do nothing\n" " : ppm create sequence of PPM files\n" #ifdef HAS_VGALIB " : svga256 256 colour VGA (Linux only)\n" " : svga full colour VGA (Linux only)\n" #endif #ifdef P_SDL " : sdl Use Simple DirectMedia Library\n" #endif #ifdef HAS_X11 " : x11 automatically pick best X11 mode\n" " : x1124 X11 using 24 bit colour\n" " : x1116 X11 using 16 bit colour\n" " : x118 X11 using 8 bit grey scale\n" " --videopip : Local video is displayed in corner of received video\n" #endif // HAS_X11 "\nVideo options:\n" " --videotest : Display local video. Exit after 10 seconds. NO h323 call\n" " --videolose : Delete this percentage of the video rtp packets.- For videotest only. Default 0\n" #ifdef DEPRECATED_CU30 " --videocu30 : Enable Cu30 codec\n" " --videocu30stats n : Collect stats for n frames, to optimise subsequent calls. (100-10000)\n" #endif "\nSound card options:\n" " -s --sound device : Select sound card input/output device\n" " --sound-in device : Select sound card input device (overrides --sound)\n" " --sound-out device : Select sound card output device (overrides --sound)\n" " --sound-buffers n : Set sound buffer depth (default=2)\n" #ifdef HAS_OSS " --sound-mixer device : Select sound mixer device (default is " DEFAULT_MIXER ")\n" " --sound-recchan device : Select sound mixer channel (default is mic)\n" " --sound-recvol n : Set record volume for sound card only (overrides --recvol)\n" " --sound-playvol n : Set play volume for sound card only (overrides --playvol)\n" #endif #ifdef HAS_IXJ "\nQuicknet card options:\n" " -q -quicknet dev : Use device (number or full device name)\n" " -C --country name : Set the country code for Quicknet device\n" " --aec n : Set Audio Echo Cancellation level (0..3)\n" " --autohook : Don't use hook switch (for PhoneCard)\n" " -c --callerid : Enable caller id display\n" " --calleridcw : Enable caller id on call waiting display\n" " --dial-after-hangup : Present dial tone after remote hang up\n" " --quicknet-recvol n : Set record volume for Quicknet card only (overrides recvol)\n" " --quicknet-playvol n : Set play volume for Quicknet card only (overrides playvol)\n" #endif #ifdef HAS_VBLASTER "\nVoIPBlaster options:\n" " -V --voipblaster num : Use device number\n" #endif "\nAudio Codec options:\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" " --fpp codec=count : Set frames per packet, e.g. '--fpp GSM06.10=3' (may be used multiple times)\n" " --g711frames count : (deprecated) Set the number G.711 frames in capabilities (default 30)\n" " --gsmframes count : (deprecated) Set the number GSM frames in capabilities (default 4)\n" #if defined(HAS_LIDDEVICE) " --g7231 : Set G.723.1 as preferred codec\n" #endif " --gsm : Set GSM 06.10 as preferred codec (default)\n" " --g711-ulaw : Set G.711 uLaw as preferred codec\n" " --g711-alaw : Set G.711 ALaw as preferred codec\n" " --g728 : Set G.728 as preferred codec\n" #ifdef G729 " --g729 : Set G.729 as preferred codec\n" #endif " --g7231 : Set G.723.1 as preferred codec\n" " -I --input-mode mode : Set the mode for sending User Input Indications (DTMF)\n" " can be string, signal, q931 or rfc2833 (default is string)\n" " -U --user-input-cap mode : Set the mode for User Input Capabilities\n" " can be string, signal, rfc2833 or none (default is all)\n" #if PTRACING || PMEMORY_CHECK "\nDebug options:\n" #endif #if PTRACING " -t --trace : Enable trace, use multiple times for more detail\n" " -o --output : File for trace output, default is stderr\n" #endif #ifdef PMEMORY_CHECK " --setallocationbreakpoint n : Enable breakpoint on memory allocation n\n" #endif << endl; return; } BOOL hasMenu = !args.HasOption("disable-menu"); int autoRepeat; if (!args.HasOption("autorepeat")) autoRepeat = -1; else { autoRepeat = args.GetOptionString("autorepeat").AsInteger(); if (autoRepeat < 1) { cout << "autorepeat must be >= 1" << endl; return; } hasMenu = FALSE; } args.Save("save"); MyH323EndPoint * endpoint = new MyH323EndPoint; if (endpoint->Initialise(args, verbose, hasMenu)) { if (!args.HasOption("videotest")) { if (autoRepeat < 0) { if (args.HasOption('l')) { if (verbose >= 2) cout << "Waiting for incoming calls for \"" << endpoint->GetLocalUserName() << "\"\n"; } else endpoint->MakeOutgoingCall(args[0], args.GetOptionString('p')); endpoint->AwaitTermination(); } else { int i; endpoint->terminateOnHangup = TRUE; for (i = 1; i <= autoRepeat; i++) { if (!args.HasOption('l')) { cout << "Making automatic call " << i << endl; endpoint->MakeOutgoingCall(args[0], args.GetOptionString('p')); } //endpoint->AwaitTermination(); Sleep(20); cout << "Call #" << i; #ifdef P_LINUX struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) == 0) cout << ": memory = " << usage.ru_ixrss << ", " << usage.ru_idrss; #endif cout << endl; if (i == autoRepeat) break; } } } else endpoint->TestVideoGrabber(args); } //initialised OK delete endpoint; if (verbose >= 3) cout << GetName() << " ended." << endl; #if PTRACING if (args.GetOptionCount('t') > 0) { PTrace::ClearOptions(0); PTrace::SetLevel(0); } #endif } /////////////////////////////////////////////////////////////// void MyH323EndPoint::SetCodecFrames(const PString & fmt, unsigned frames) { H323Capability * cap = capabilities.FindCapability(fmt); if (cap == NULL) { cout << "Cannot find codec " << fmt << endl; } else if (frames < 1) cout << "Cannot set frames per packet for code " << *cap << " to 0" << endl; else { cout << "Setting frames per packet for " << *cap << " to " << frames << endl; cap->SetTxFramesInPacket(frames); } } BOOL MyH323EndPoint::Initialise(PConfigArgs & args, int _verbose, BOOL _hasMenu) { PTRACE(3, "H323ep\tInitialise program. Arguments are " << args); PINDEX i; verbose = _verbose; hasMenu = _hasMenu; uiState = uiDialtone; channelsOpenLimit = 2; //Guaranteed to have 2 open audio channels. localVideoChannel = NULL; // get local username if (args.HasOption('u')) { PStringArray aliases = args.GetOptionString('u').Lines(); SetLocalUserName(aliases[0]); for (i = 1; i < aliases.GetSize(); i++) AddAliasName(aliases[i]); } // Let the user set port ranges if (args.HasOption("portbase")) { SetRtpIpPorts(args.GetOptionString("portbase").AsInteger(), args.GetOptionString("portmax").AsInteger()); SetTCPPorts(args.GetOptionString("portbase").AsInteger(), args.GetOptionString("portmax").AsInteger()); SetUDPPorts(args.GetOptionString("portbase").AsInteger(), args.GetOptionString("portmax").AsInteger()); } if (args.HasOption("stun")) { SetSTUNServer(args.GetOptionString("stun")); } if (verbose >= 3) cout << "Incoming channel port ranges " << GetRtpIpPortBase() << " to " << GetRtpIpPortMax()< 99) { cout << "Illegal sound buffers specified." << endl; return FALSE; } } if (verbose >= 3) { cout << "Local username: " << GetLocalUserName() << "\n" << "TerminateOnHangup is " << terminateOnHangup << "\n" << "Auto answer is " << autoAnswer << "\n" << "DialAfterHangup is " << dialAfterHangup << "\n" << defaultCallOptions << endl; } if (args.HasOption("autodial")) { autoDial = args.GetOptionString("autodial"); if (verbose >= 3) cout << "Autodial is set to " << autoDial << "\n"; } if (args.HasOption("h261")) { cout << "warning: --h261 option has been replaced by --videoreceive and --videotransmit" << endl; videoReceiveDevice = args.GetOptionString("h261"); } else if (args.HasOption("videoreceive")) videoReceiveDevice = args.GetOptionString("videoreceive"); if (!videoReceiveDevice.IsEmpty()) { channelsOpenLimit ++; //we expect to have another channel open, for video receive. if ( !(videoReceiveDevice *= "ppm") && !(videoReceiveDevice *= "null") #ifdef HAS_VGALIB && !(videoReceiveDevice *= "svga") && !(videoReceiveDevice *= "svga256") #endif #ifdef P_SDL && !(videoReceiveDevice *= "sdl") #endif #ifdef HAS_X11 && !(videoReceiveDevice *= "x1132") && !(videoReceiveDevice *= "x1124") && !(videoReceiveDevice *= "x1116") && !(videoReceiveDevice *= "x118") && !(videoReceiveDevice *= "x11") && !(videoReceiveDevice *= "x1132s") && !(videoReceiveDevice *= "x1124s") && !(videoReceiveDevice *= "x1116s") && !(videoReceiveDevice *= "x118s") && !(videoReceiveDevice *= "x11s") #endif #ifdef USE_SHM_VIDEO_DEVICES && !(videoReceiveDevice *= "shm") #endif ) { cout << "Unknown video receive device \"" << videoReceiveDevice << "\"" << endl; return FALSE; } if (!args.HasOption("videoquality")) videoQuality = -1; else { videoQuality = args.GetOptionString("videoquality").AsInteger(); videoQuality = PMAX(0, PMIN(31, videoQuality)); } } videoPIP = FALSE; videoSize = 0; //Default is small. autoStartTransmitVideo = args.HasOption("videotransmit") || args.HasOption("videotest"); if (autoStartTransmitVideo) { channelsOpenLimit ++; //we expect to have another channel open, for video transmit. videoDevice = DEFAULT_VIDEO; videoFake = FALSE; if (args.HasOption("videodevice")) { videoDevice = args.GetOptionString("videodevice"); if (videoDevice == FICTITOUS_VIDEO) videoFake = TRUE; } if (args.GetOptionString("videosize") *= "large") videoSize = 1; if (verbose >= 3) cout << "Set video size to be " << (videoSize == 0 ? "small" : "large") << endl; videoInput = 0; if (args.HasOption("videoinput")) { videoInput = args.GetOptionString("videoinput").AsInteger(); if (videoInput<0) { cout << "User interface has changed.\n" << "Select fictitous video device with --videodevice "FICTITOUS_VIDEO"\n" << "Use videoinput argument of 0, 1, 2, ...\n" << "For backwards compatability, negative inputs are currently supported\n"; videoFake = TRUE; videoInput= -1 - videoInput; //-1 ==> 0, -2 ==>1, etc } } videoIsPal = TRUE; if (args.HasOption("videoformat")) videoIsPal = args.GetOptionString("videoformat") *= "pal"; if (args.HasOption("videocolorfmt")) pfdColourFormat = args.GetOptionString("videocolorfmt"); videoLocal = args.HasOption("videolocal"); if (args.HasOption("videopip")) { videoPIP = TRUE; videoLocal = TRUE; } videoTxQuality = 0; // disable setting video quality if (args.HasOption("videotxquality")) videoTxQuality = args.GetOptionString("videotxquality").AsInteger(); videoTxMinQuality = 0; // disable setting minimum video quality if (args.HasOption("videotxminquality")) videoTxMinQuality = args.GetOptionString("videotxminquality").AsInteger(); videoFill = 2; // default video fill value if (args.HasOption("videofill")) { videoFill = args.GetOptionString("videofill").AsInteger(); videoFill = PMAX(2, PMIN(99, videoFill)); } if (args.HasOption("videocu30stats")) { videoCu30Stats = args.GetOptionString("videocu30stats").AsInteger(); videoCu30Stats = PMAX(10, PMIN(10000, videoCu30Stats)); } else videoCu30Stats = 0; // Dont record stats. if (args.HasOption("videotxfps")) { videoFramesPS = args.GetOptionString("videotxfps").AsInteger(); videoFramesPS = PMAX(0, PMIN(30,videoFramesPS)); } else videoFramesPS=0; //default value. frameTimeMs = 0; //disable setting frameTimeMs. if (args.HasOption("videosendfps")) { double videoSendFPS; videoSendFPS = args.GetOptionString("videosendfps").AsReal(); if (0.0 < videoSendFPS) { frameTimeMs = (unsigned)(1000.0/videoSendFPS); frameTimeMs = PMAX(33, PMIN(1000000,frameTimeMs)); // 5 ms to 16.7 minutes } } videoBitRate = 0; //disable setting videoBitRate. if (args.HasOption("videobitrate")) { videoBitRate = args.GetOptionString("videobitrate").AsInteger(); videoBitRate = 1024 * PMAX(16, PMIN(2048, videoBitRate)); } } if (verbose >= 3) { if (videoReceiveDevice.IsEmpty()) cout << "Video receive disabled" << endl << endl; else { cout << "Video receive using device : " << videoReceiveDevice << endl; cout << "Video receive quality hint : " << videoQuality << endl << endl; } if (!autoStartTransmitVideo) cout << "Video transmit disabled" << endl << endl; else { cout << "Video transmit enabled with local video window " << (videoLocal ? "en" : "dis") << "abled" << endl; cout << "Video transmit size is " << ((videoSize == 1) ? "large" : "small") << endl; cout << "Video capture using input " << videoInput << endl; cout << "Video capture using format " << (videoIsPal ? "PAL" : "NTSC") << endl; cout << "Video picture in picture of local video "<< (videoPIP ? "en" : "dis") << "abled" << endl; cout << "Video transmit quality is "<< videoTxQuality<0) cout << "enabled. " << videoCu30Stats << " frames." << endl; else cout << "disabled" << endl; } #endif cout << endl; } #ifdef HAS_LIDDEVICE isXJack = FALSE; lidDevice = NULL; #endif #ifdef HAS_VBLASTER if (args.HasOption('V')) { PString vbDevice = args.GetOptionString('V'); lidDevice = new OpalVoipBlasterDevice; if (lidDevice->Open(vbDevice)) { if (verbose >= 3) cout << "Using VoIPBlaster " << lidDevice->GetName() << '\n'; } autoHook = FALSE; } #endif #ifdef HAS_IXJ if (args.HasOption('q')) { lidDevice = new OpalIxJDevice; PString ixjDevice = args.GetOptionString('q'); if (lidDevice->Open(ixjDevice)) { if (verbose >= 3) cout << "Using Quicknet " << lidDevice->GetName() << '\n'; isXJack = TRUE; lidDevice->SetLineToLineDirect(0, 1, FALSE); lidDevice->EnableAudio(0, TRUE); callerIdEnable = args.HasOption("callerid"); if (verbose >= 3) cout << "Caller ID set to " << callerIdEnable << endl; callerIdCallWaitingEnable = args.HasOption("calleridcw"); if (verbose >= 3) cout << "Caller ID on call waiting set to " << callerIdCallWaitingEnable << endl; if (args.HasOption('C')) lidDevice->SetCountryCodeName(args.GetOptionString('C')); if (verbose >= 3) cout << "Country set to " << lidDevice->GetCountryCodeName() << endl; int aec = 0; if (args.HasOption("aec")) { aec = args.GetOptionString("aec").AsUnsigned(); lidDevice->SetAEC(0, (OpalLineInterfaceDevice::AECLevels)aec); if (verbose >= 3) cout << "AEC set to " << AECLevelNames[aec] << endl; } else { lidDevice->SetAEC(0, OpalLineInterfaceDevice::AECMedium); if (verbose >= 3) cout << "AEC set to default" << endl; } PString volStr; if ((OpalLineInterfaceDevice::AECLevels)aec != OpalLineInterfaceDevice::AECAGC) { if (args.HasOption("quicknet-recvol")) volStr = args.GetOptionString("quicknet-recvol"); else if (args.HasOption("recvol")) volStr = args.GetOptionString("recvol"); unsigned recvol; if (!volStr.IsEmpty()) { recvol = volStr.AsInteger(); lidDevice->SetRecordVolume(0, recvol); } else { lidDevice->GetRecordVolume(0, recvol); } if (verbose >= 3) { cout << "Recording volume set to " << recvol << endl; } } if (args.HasOption("quicknet-playvol")) volStr = args.GetOptionString("quicknet-playvol"); else if (args.HasOption("playvol")) volStr = args.GetOptionString("playvol"); unsigned playvol; if (!volStr.IsEmpty()) { playvol = volStr.AsInteger(); lidDevice->SetPlayVolume(0, playvol); } else { lidDevice->GetPlayVolume(0, playvol); } if (verbose >= 3) { cout << "Playing volume set to " << playvol << endl; } autoHook = args.HasOption("autohook"); if (autoHook) lidDevice->StopTone(0); if (verbose >= 3) cout << "Autohook set to " << autoHook << endl; } else { cout << "Could not open " << ixjDevice << ": "; int code = lidDevice->GetErrorNumber(); if (code == EBADF) cout << "check that the Quicknet driver is installed correctly" << endl; else if (code == EBUSY) cout << "check that the Quicknet driver is not in use" << endl; else { PString errStr = lidDevice->GetErrorText(); if (errStr.IsEmpty()) errStr = psprintf("error code %i", code); cout << errStr << endl; } return FALSE; } } #endif if (args.HasOption("ringfile")) ringFile = args.GetOptionString("ringfile"); if (args.HasOption("ringdelay")) ringDelay = args.GetOptionString("ringdelay").AsInteger(); else ringDelay = 5; #if defined(HAS_LIDDEVICE) if ((lidDevice == NULL) || !lidDevice->IsOpen()) { #endif if ( !SetSoundDevice(args, "sound-in", "record-driver", PSoundChannel::Recorder) && !SetSoundDevice(args, "sound", "record-driver", PSoundChannel::Recorder, true) ) return FALSE; if ( !SetSoundDevice(args, "sound", "play-driver", PSoundChannel::Player) && !SetSoundDevice(args, "sound-out", "play-driver", PSoundChannel::Player, true) ) return FALSE; if (verbose >= 3) cout << "Sound output device: \"" << GetSoundChannelPlayDevice() << "\"\n" "Sound input device: \"" << GetSoundChannelRecordDevice() << "\"\n"; #ifdef HAS_OSS if (!InitialiseMixer(args, verbose)) return FALSE; #endif #if defined(HAS_LIDDEVICE) } // endif #endif // by default, assume we can do PCM codec BOOL canDoPCM = TRUE; // The order in which capabilities are added to the capability table // determines which one is selected by default. #if defined(HAS_LIDDEVICE) if ((lidDevice != NULL) && lidDevice->IsOpen()) { H323_LIDCapability::AddAllCapabilities(*lidDevice, capabilities, 0, 0); canDoPCM = lidDevice->GetMediaFormats().GetValuesIndex(OpalMediaFormat(OPAL_PCM16)) != P_MAX_INDEX; } #endif if (canDoPCM) { AddAllCapabilities(0, 0, "*"); } #ifdef HAS_T38 SetCapability(0, 1, new H323_T38Capability(H323_T38Capability::e_UDP)); #endif // set frames per packet if requested by user if (args.HasOption("fpp")) { PStringArray lines = args.GetOptionString("fpp").Lines(); for (PINDEX i = 0; i < lines.GetSize(); i++) { PStringArray tokens = lines[i].Tokenise('='); if (tokens.GetSize() != 2) { cerr << "error: --fpp option requires argument in the form 'codec=count'" << endl; return FALSE; } PString fmt = tokens[0]; SetCodecFrames(tokens[0], tokens[1].AsInteger()); } } // backwards compatible options if (args.HasOption("gsmframes")) SetCodecFrames(OpalGSM0610, args.GetOptionString("gsmframes").AsInteger()); if (args.HasOption("g711frames")) { // note that G.711 "frames" are really 1/8 millisecond int frames = 8 * args.GetOptionString("g711frames").AsInteger(); SetCodecFrames(OpalG711uLaw64k, frames); SetCodecFrames(OpalG711ALaw64k, frames); } #ifdef DEPRECATED_CU30 PFilePath fileName= PProcess::Current().GetConfigurationFile(); PString statsDir = fileName.GetDirectory(); //Statistics files ("y" "u" "v" and "mc") have to be here. INT _width,_height; _width= 176 << videoSize; _height= 144 << videoSize; cout<<"SetVideo size to "<<_width<<" x " <<_height<= 3) { cout << "User Input Send Mode: as "; switch (GetSendUserInputMode()) { case H323Connection::SendUserInputAsQ931 : cout << "Q.931 Keypad Information Element"; break; case H323Connection::SendUserInputAsString : cout << "H.245 string"; break; case H323Connection::SendUserInputAsTone : cout << "H.245 tone"; break; case H323Connection::SendUserInputAsInlineRFC2833 : cout << "RFC2833"; break; default : cout << "Unknown!"; } cout << '\n'; } //SetCapability(0, P_MAX_INDEX, new H323_T120Capability); if (verbose >= 4) cout << "Codecs (in preference order):\n" << setprecision(2) << capabilities << endl << endl; /* Use IPv6 address family by default if available. */ #ifdef P_HAS_IPV6 if (PIPSocket::IsIpAddressFamilyV6Supported()) PIPSocket::SetDefaultIpAddressFamilyV6(); #endif if (!args.GetOptionString("listenport").IsEmpty()) listenPort = (WORD)args.GetOptionString("listenport").AsInteger(); PStringArray interfaceList; if (!args.GetOptionString('i').IsEmpty()) interfaceList = args.GetOptionString('i').Lines(); PString interfacePrintable; // if no interfaces specified, then bind to all interfaces with a single Listener // otherwise, bind to specific interfaces if (interfaceList.GetSize() == 0) { PIPSocket::Address interfaceAddress(PIPSocket::GetDefaultIpAny()); H323ListenerTCP * listener = new H323ListenerTCP(*this, interfaceAddress, listenPort); if (!StartListener(listener)) { cout << "Could not open H.323 listener port on " << listener->GetListenerPort() << endl; delete listener; return FALSE; } interfacePrintable = psprintf("ALL:%i", listenPort); } else { for (i = 0; i < interfaceList.GetSize(); i++) { PString interfaceStr = interfaceList[i]; WORD interfacePort = listenPort; // Allow for [ipaddr]:port form, especially for IPv6 PINDEX pos = interfaceStr.Find(']'); if (pos == P_MAX_INDEX) pos = 0; pos = interfaceStr.Find(':', pos); if (pos != P_MAX_INDEX) { interfacePort = (WORD)interfaceStr.Mid(pos+1).AsInteger(); interfaceStr = interfaceStr.Left(pos); } interfacePrintable &= interfaceStr + ":" + PString(PString::Unsigned, interfacePort); PIPSocket::Address interfaceAddress(interfaceStr); H323ListenerTCP * listener = new H323ListenerTCP(*this, interfaceAddress, interfacePort); if (!StartListener(listener)) { cout << "Could not open H.323 listener port on " << interfaceAddress << ":" << interfacePort << endl; delete listener; return FALSE; } } } if (verbose >= 3) cout << "Listening interfaces : " << interfacePrintable << endl; if (args.HasOption("translate")) { masqAddressPtr = new PIPSocket::Address(args.GetOptionString("translate")); behind_masq = TRUE; cout << "Masquerading as address " << *(masqAddressPtr) << endl; } else { behind_masq = FALSE; } if (args.HasOption("stun")) { PString stunServer = args.GetOptionString("stun"); SetSTUNServer(stunServer); } // Initialise the security info if (args.HasOption("password")) { SetGatekeeperPassword(args.GetOptionString("password")); cout << "Enabling H.235 security access to gatekeeper." << endl; } #ifdef H323_TRANSNEXUS_OSP if (args.HasOption("osp")) { PDirectory ospDir; if (args.HasOption("ospdir")) ospDir = args.GetOptionString("ospdir"); SetOSPProvider(args.GetOptionString("osp"), ospDir); } else #endif if (args.HasOption('g')) { PString gkName = args.GetOptionString('g'); H323TransportUDP * rasChannel; if (args.GetOptionString('i').IsEmpty()) rasChannel = new H323TransportUDP(*this); else { PIPSocket::Address interfaceAddress(args.GetOptionString('i')); rasChannel = new H323TransportUDP(*this, interfaceAddress); } if (SetGatekeeper(gkName, rasChannel)) { if (verbose >= 3) cout << "Gatekeeper set: " << *gatekeeper << endl; } else { cout << "Error registering with gatekeeper at \"" << gkName << '"' << endl; return FALSE; } } else if (args.HasOption('G')) { PString gkIdentifier = args.GetOptionString('G'); if (verbose >= 2) cout << "Searching for gatekeeper with id \"" << gkIdentifier << "\" ..." << flush; if (LocateGatekeeper(gkIdentifier)) { if (verbose >= 3) cout << "Gatekeeper set: " << *gatekeeper << endl; } else { cout << "Error registering with gatekeeper at \"" << gkIdentifier << '"' << endl; return FALSE; } } else if (!args.HasOption('n') || args.HasOption('r')) { if (verbose >= 2) cout << "Searching for gatekeeper..." << flush; if (DiscoverGatekeeper(new H323TransportUDP(*this))) { if (verbose >= 2) cout << "\nGatekeeper found: " << *gatekeeper << endl; } else { if (verbose >= 2) cout << "\nNo gatekeeper found." << endl; if (args.HasOption("require-gatekeeper")) return FALSE; } } // osptoken option only makes sense if gatekeeper is being used if ((gatekeeper != NULL) && args.HasOption("osptoken")) SetGkAccessTokenOID(OpalOSP::ETSIXMLTokenOID); ringThread = NULL; if (!args.HasOption("autodisconnect")) autoDisconnect = 0; else { autoDisconnect = args.GetOptionString("autodisconnect").AsInteger(); if (autoDisconnect < 0) { cout << "autodisconnect must be > 0" << endl; return FALSE; } } return TRUE; } #ifdef HAS_OSS BOOL MyH323EndPoint::InitialiseMixer(PConfigArgs & args, int _verbose) { mixerDev = -1; // make sure mixer isn't disabled if (args.HasOption("no-sound-mixer")) return TRUE; PString mixerDeviceName = DEFAULT_MIXER; if (args.HasOption("sound-mixer")) mixerDeviceName = args.GetOptionString("sound-mixer"); mixerDev = ::open(mixerDeviceName, O_RDWR); if (mixerDev < 0) { cout << "warning: Cannot open mixer device " << mixerDeviceName << ": " << ::strerror(errno) << endl; return TRUE; } char * mixerChanNames[] = SOUND_DEVICE_NAMES; int numMixerChans = SOUND_MIXER_NRDEVICES; // get the current record channel setting, and save it if (::ioctl(mixerDev, SOUND_MIXER_READ_RECSRC, &savedMixerRecChan) < 0) { cout << "warning: cannot get current mixer record channels" << endl; savedMixerRecChan = -1; } // if the user specified a record channel, then find it // otherwise, find the currently select record channel if (args.HasOption("sound-recchan")) { PCaselessString mixerRecChanName = args.GetOptionString("sound-recchan"); int i; for (i = 0; i < numMixerChans; i++) if (mixerRecChanName *= mixerChanNames[i]) break; if (i == numMixerChans) { cout << "error: Cannot find record mixer channel " << mixerDeviceName << endl; return FALSE; } mixerRecChan = i; } else { int i; for (i = 0; i < numMixerChans; i++) if (savedMixerRecChan & (1 << i)) break; if (i == numMixerChans) mixerRecChan = SOUND_MIXER_MIC; else mixerRecChan = i; } PString volStr; if (args.HasOption("sound-recvol")) volStr = args.GetOptionString("sound-recvol"); else if (args.HasOption("recvol")) volStr = args.GetOptionString("recvol"); if (volStr.IsEmpty()) { ::ioctl(mixerDev, MIXER_READ(mixerRecChan), &ossRecVol); ossRecVol &= 0xff; } else { ossRecVol = (unsigned)volStr.AsReal(); int volVal = ossRecVol | (ossRecVol << 8); ::ioctl(mixerDev, MIXER_WRITE(mixerRecChan), &volVal); } if (args.HasOption("sound-playvol")) volStr = args.GetOptionString("sound-playvol"); else if (args.HasOption("playvol")) volStr = args.GetOptionString("playvol"); if (volStr.IsEmpty()) { ::ioctl(mixerDev, SOUND_MIXER_READ_VOLUME, &ossPlayVol); ossPlayVol &= 0xff; } else { ossPlayVol = (unsigned)volStr.AsReal(); int volVal = ossPlayVol | (ossPlayVol << 8); ::ioctl(mixerDev, SOUND_MIXER_WRITE_PCM, &volVal); } if (verbose >= 3) { cout << "Recording using mixer channel " << mixerChanNames[mixerRecChan] << endl; cout << "Record volume is " << ossRecVol << endl; cout << "Play volume is " << ossPlayVol << endl; } return TRUE; } #endif BOOL MyH323EndPoint::SetSoundDevice(PConfigArgs & args, const char * deviceOptName, const char * driverOptName, PSoundChannel::Directions dir, BOOL force) { if (!force && !args.HasOption(deviceOptName) && !args.HasOption(driverOptName)) return FALSE; PStringList driverNames = PSoundChannel::GetDriverNames(); PString driverName = args.GetOptionString(driverOptName); if (driverNames.IsEmpty()) { cout << "No sound drivers available!" << endl; return FALSE; } if (driverName.IsEmpty()) driverName = args.GetOptionString("sound-driver"); if (!driverName.IsEmpty() && driverNames.GetStringsIndex(driverName) == P_MAX_INDEX) { cout << "Sound driver must be one of\n" << setfill('\n') << driverNames << setfill(' ') << endl; return FALSE; } PStringList deviceNames = driverName.IsEmpty() ? PSoundChannel::GetDeviceNames(dir) : PSoundChannel::GetDriversDeviceNames(driverName, dir); if (deviceNames.IsEmpty()) { cout << "No sound devices available!" << endl; return FALSE; } PString deviceName = args.GetOptionString(deviceOptName); if (deviceName.IsEmpty()) deviceName = deviceNames[0]; if (!driverName.IsEmpty()) driverName += PDevicePluginServiceDescriptor::SeparatorChar; if (dir == PSoundChannel::Player) { if (SetSoundChannelPlayDevice(driverName+deviceName)) return TRUE; } else { if (SetSoundChannelRecordDevice(driverName+deviceName)) return TRUE; } cout << "Argument to " << deviceOptName << " must be one of\n" << setfill('\n') << deviceNames << setfill(' ') << endl; return FALSE; } H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference) { unsigned options = 0; if (currentCallOptions.noFastStart) options |= H323Connection::FastStartOptionDisable; if (currentCallOptions.noH245Tunnelling) options |= H323Connection::H245TunnelingOptionDisable; if (currentCallOptions.noH245InSetup) options |= H323Connection::H245inSetupOptionDisable; return new MyH323Connection(*this, callReference, options, currentCallOptions.minJitter, currentCallOptions.maxJitter, verbose); } BOOL MyH323EndPoint::OnIncomingCall(H323Connection & connection, const H323SignalPDU & setupPDU, H323SignalPDU &) { // get the default call options currentCallOptions = defaultCallOptions; // get remote address so we can call back later PString lastCallingParty = connection.GetSignallingChannel()->GetRemoteAddress().GetHostName(); PConfig config("Callers"); int index = config.GetInteger("index"); PString lastLastCallingParty = config.GetString(PString(PString::Unsigned, index)); index = (index + 1) % LAST_CALL_COUNT; PTime now; PString indexStr = PString(PString::Unsigned, index); config.SetString(indexStr, lastCallingParty); config.SetString(indexStr + "_Time", now.AsString()); config.SetString("index", indexStr); // Check for setup parameter if (setupPDU.m_h323_uu_pdu.HasOptionalField(H225_H323_UU_PDU::e_nonStandardData)) { PString param = setupPDU.m_h323_uu_pdu.m_nonStandardData.m_data.AsString(); if (!param) cout << "Received non-standard parameter data in Setup PDU: \"" << param << "\"." << endl; } if (!alwaysForwardParty.IsEmpty()) { cout << "Forwarding call to \"" << alwaysForwardParty << "\"." << endl; return !connection.ForwardCall(alwaysForwardParty); } // incoming call is accepted if no call in progress // unless the xJack is open and phone is off onhook if (!currentCallToken.IsEmpty()) cout << "WARNING: current call token not empty" << endl; if (currentCallToken.IsEmpty() #if defined(HAS_LIDDEVICE) && !((lidDevice != NULL) && lidDevice->IsOpen() && (!autoHook && lidDevice->IsLineOffHook(POTS_LINE))) #endif ) { // get the current call token currentCallToken = connection.GetCallToken(); return TRUE; } if (busyForwardParty.IsEmpty()) { PTime now; cout << "Incoming call from \"" << connection.GetRemotePartyName() << "\" rejected at " << now << ", line busy!" << endl; connection.ClearCall(H323Connection::EndedByLocalBusy); #ifdef HAS_LIDDEVICE if (isXJack) { #ifdef IXJCTL_VMWI if (callerIdCallWaitingEnable) { PString callerId = ((MyH323Connection &)connection).GetCallerIdString(); cout << "Sending caller on call waiting ID " << callerId << endl; lidDevice->SendCallerIDOnCallWaiting(OpalIxJDevice::POTSLine, callerId); } #endif } #endif return FALSE; } cout << "Forwarding call to \"" << busyForwardParty << "\"." << endl; return !connection.ForwardCall(busyForwardParty); } void MyH323EndPoint::TranslateTCPAddress(PIPSocket::Address &localAddr, const PIPSocket::Address &remoteAddr) { if (this->behind_masq && !remoteAddr.IsRFC1918()) localAddr = *(this->masqAddressPtr); } BOOL MyH323EndPoint::OnConnectionForwarded(H323Connection & /*connection*/, const PString & forwardParty, const H323SignalPDU & /*pdu*/) { if (MakeCall(forwardParty, currentCallToken)) { cout << GetLocalUserName() << " is being forwarded to host " << forwardParty << endl; return TRUE; } cout << "Error forwarding call to \"" << forwardParty << '"' << endl; return FALSE; } void MyH323EndPoint::OnNoAnswerTimeout(PTimer &, INT) { H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection != NULL) { cout << "Forwarding call to \"" << noAnswerForwardParty << "\"." << endl; connection->ForwardCall(noAnswerForwardParty); connection->Unlock(); } } void MyH323EndPoint::OnConnectionEstablished(H323Connection & connection, const PString & /*token*/) { cout << "Call with \"" << connection.GetRemotePartyName() << "\" established." << endl; uiState = uiCallInProgress; } void MyH323EndPoint::OnAutoDisconnect(PTimer &, INT) { PTRACE(3, "main\tOnAutoDisconnect callback. Call=" << currentCallToken); if (currentCallToken.IsEmpty()) cout << "Autodisconnect occurred without current call token" << endl; else { ClearCall(currentCallToken); cout << "Autodisconnect triggered" << endl; } } void MyH323EndPoint::TriggerDisconnect() { // we have an autodisconnect timer specified, start the timer if (autoDisconnect <= 0) PTRACE(2, "Main\tAuto disconnect not triggered"); else { PTRACE(2, "Main\tAuto disconnect triggered"); autoDisconnectTimer.SetNotifier(PCREATE_NOTIFIER(OnAutoDisconnect)); autoDisconnectTimer = PTimeInterval(autoDisconnect * 100); } } void MyH323EndPoint::OnConnectionCleared(H323Connection & connection, const PString & clearedCallToken) { // stop any ringing that is occurring StopRinging(); // ignore connections that are not the current connection if (clearedCallToken != currentCallToken) return; // update values for current call token and call forward call token: if (!callTransferCallToken) { // after clearing the first call during a call proceeding, // the call transfer call token becomes the new call token currentCallToken = callTransferCallToken; callTransferCallToken = PString(); } else currentCallToken = PString(); // indicate that our connection is now cleared // indicate call has hungup uiState = uiCallHungup; if (verbose != 0) { BOOL printDuration = TRUE; PString remoteName = '"' + connection.GetRemotePartyName() + '"'; switch (connection.GetCallEndReason()) { case H323Connection::EndedByCallForwarded : printDuration = FALSE; // Don't print message here, was printed when forwarded break; case H323Connection::EndedByRemoteUser : cout << remoteName << " has cleared the call"; break; case H323Connection::EndedByCallerAbort : cout << remoteName << " has stopped calling"; break; case H323Connection::EndedByRefusal : cout << remoteName << " did not accept your call"; break; case H323Connection::EndedByRemoteBusy : cout << remoteName << " was busy"; break; case H323Connection::EndedByRemoteCongestion : cout << "Congested link to " << remoteName; break; case H323Connection::EndedByNoAnswer : cout << remoteName << " did not answer your call"; break; case H323Connection::EndedByTransportFail : cout << "Call with " << remoteName << " ended abnormally"; break; case H323Connection::EndedByCapabilityExchange : cout << "Could not find common codec with " << remoteName; break; case H323Connection::EndedByNoAccept : cout << "Did not accept incoming call from " << remoteName; break; case H323Connection::EndedByAnswerDenied : cout << "Refused incoming call from " << remoteName; break; case H323Connection::EndedByNoUser : cout << "Gatekeeper could not find user " << remoteName; break; case H323Connection::EndedByNoBandwidth : cout << "Call to " << remoteName << " aborted, insufficient bandwidth."; break; case H323Connection::EndedByUnreachable : cout << remoteName << " could not be reached."; break; case H323Connection::EndedByHostOffline : cout << remoteName << " is not online."; break; case H323Connection::EndedByNoEndPoint : cout << "No phone running for " << remoteName; break; case H323Connection::EndedByConnectFail : cout << "Transport error calling " << remoteName; break; default : cout << "Call with " << remoteName << " completed"; } PTime connectTime = connection.GetConnectionStartTime(); if (printDuration && connectTime.GetTimeInSeconds() != 0) cout << ", duration " << setprecision(0) << setw(5) << (PTime() - connectTime); cout << endl; } if (!hasMenu && terminateOnHangup) { exitFlag.Signal(); } } BOOL MyH323EndPoint::OpenAudioChannel(H323Connection & connection, BOOL isEncoding, unsigned bufferSize, H323AudioCodec & codec) { #if defined(HAS_LIDDEVICE) if ((lidDevice != NULL) && lidDevice->IsOpen()) { PTRACE(2, "xJack\tAttaching channel to codec"); if (!codec.AttachChannel(new OpalLineChannel(*lidDevice, POTS_LINE, codec))) return FALSE; } else #endif if (!H323EndPoint::OpenAudioChannel(connection, isEncoding, bufferSize, codec)) { cout << "Could not open sound device "; if (isEncoding) cout << GetSoundChannelRecordDevice(); else cout << GetSoundChannelPlayDevice(); cout << " - Check permissions or full duplex capability." << endl; return FALSE; } codec.SetSilenceDetectionMode(currentCallOptions.noSilenceSuppression ? H323AudioCodec::NoSilenceDetection : H323AudioCodec::AdaptiveSilenceDetection); return TRUE; } BOOL MyH323EndPoint::OpenVideoChannel(H323Connection & connection, BOOL isEncoding, H323VideoCodec & codec) { PVideoChannel * channel = new PVideoChannel; PVideoOutputDevice * display = NULL; PVideoInputDevice * grabber = NULL; PString nameStr = isEncoding ? PString("Local") : connection.GetRemotePartyName(); if (isEncoding) { PAssert(autoStartTransmitVideo, "video encoder created without enable"); if (0 != videoTxMinQuality) // set MinQuality first so TxQuality cannot be set lower codec.SetTxMinQuality(videoTxMinQuality); if (0 != videoTxQuality) codec.SetTxQualityLevel(videoTxQuality); codec.SetBackgroundFill(videoFill); if (0 != videoBitRate) { codec.SetMaxBitRate(videoBitRate); codec.SetVideoMode( H323VideoCodec::DynamicVideoQuality | H323VideoCodec::AdaptivePacketDelay | codec.GetVideoMode()); } if (0 != frameTimeMs) { codec.SetTargetFrameTimeMs(frameTimeMs); codec.SetVideoMode( H323VideoCodec::DynamicVideoQuality | H323VideoCodec::AdaptivePacketDelay | codec.GetVideoMode()); } unsigned newFrameWidth,newFrameHeight; newFrameWidth = 352 >> (1 - videoSize); newFrameHeight = 288 >> (1 - videoSize); // try and find the driver that handles the device we have been passed PStringList drivers = PVideoInputDevice::GetDriverNames(); PTRACE(3, "H323ep\tServiceDescriptor type User Video Name"); for (int i = 0; i < drivers.GetSize(); i++) { PStringList devices = PVideoInputDevice::GetDriversDeviceNames(drivers[i]); for (int j = 0; j < devices.GetSize(); j++) { PTRACE(3, "H323ep\ttype:" << setw(10) << drivers[i] << " device:" << setw(13) << devices[j] ); } } grabber = PVideoInputDevice::CreateDeviceByName(videoDevice); if (grabber == NULL) { PTRACE(3, "Cannot create video input device for name " << videoDevice); cerr << "Cannot create video input device for name " << videoDevice << endl << "Available video device names are : " << setfill(',') << PVideoInputDevice::GetDriverNames() << setfill(' ')<< endl; goto errVideo; } PTRACE(3, "H323ep\t Available video device names for selected device are :" << grabber->GetDeviceNames()); //Above line populates the user friendly nanes and dico in V4l if (!(pfdColourFormat.IsEmpty())) grabber->SetPreferredColourFormat(pfdColourFormat); PTRACE(3, "Attempt to open videoDevice " << videoDevice << " for reading."); if (!grabber->Open(videoDevice, FALSE)) { PTRACE(3, "Failed to open the camera device"); goto errVideo; } if ( !grabber->SetVideoFormat( videoIsPal ? PVideoDevice::PAL : PVideoDevice::NTSC)) { PTRACE(3, "Failed to set format to " << (videoIsPal ? "PAL" : "NTSC")); goto errVideo; } if (!grabber->SetChannel(videoInput)) { PTRACE(3, "Failed to set channel to " << videoInput); goto errVideo; } if ( !grabber->SetColourFormatConverter("YUV420P") ) { PTRACE(3,"Failed to set format to yuv420p"); goto errVideo; } if ( !grabber->SetFrameRate(videoFramesPS)) { PTRACE(3, "Failed to set framerate to " << videoFramesPS); goto errVideo; } if (!grabber->SetFrameSizeConverter(newFrameWidth,newFrameHeight,FALSE)) { PTRACE(3, "Failed to set framesize to " << newFrameWidth << "x" << newFrameHeight); goto errVideo; } PTRACE(3,"OpenVideoChannel\t done. Successfully opened a video camera"); goto exitVideo; errVideo: delete grabber; grabber = PVideoInputDevice::CreateDevice("FakeVideo"); if (grabber == NULL) { PError << "Cannot create FakeVideo input device" << endl; return FALSE; } grabber->SetColourFormat("YUV420P"); grabber->SetVideoFormat(PVideoDevice::PAL); // not actually used for fake video. grabber->SetChannel(100); //NTSC test image. grabber->SetFrameRate(0); //Select default frame rate. grabber->SetFrameSize(newFrameWidth, newFrameHeight); PTRACE(3,"Made a fictitious video camera showing NTSC test frame"); exitVideo: grabber->Start(); channel->AttachVideoReader(grabber); } if ((!isEncoding) || videoLocal) PAssert(!videoReceiveDevice.IsEmpty(), "video display created without device type"); #ifdef P_SDL // Dump received video to SDL if (videoReceiveDevice *= "sdl") display = new PSDLVideoDevice(); #endif #ifdef HAS_X11 // Dump received video to X11 window if (videoReceiveDevice.Left(3) *= "x11") { PString str = videoReceiveDevice; BOOL shared = str.Right(1) *= "s"; str = str.Mid(3); if (!shared) display = new XlibVideoDevice(nameStr,isEncoding,videoPIP); else { str = str.Left(str.GetLength()-1); display = new ShmXlibVideoDevice(nameStr,isEncoding,videoPIP); } int depth = str.AsInteger(); if (depth > 0) ((GenericXlibVideoDevice *)display)->ForceDepth(depth); } #endif #ifdef WIN32 // code to specify windows 32 display device, // which uses MS windows conversion routines. #endif #ifdef SHOULD_USE_PLUGIN // Dump video to PPM files //can have two ppm video devices. if (videoReceiveDevice *= "ppm") { display = new PVideoOutputDevicePPM(); display->Open(isEncoding ? "local" : "remote"); } } #endif #ifdef HAS_VGALIB //vgalib can only do receive video device. if (!isEncoding) { // Dump received video to VGA if (videoReceiveDevice *= "svga") display = new LinuxSVGAFullOutputDevice(); else if (videoReceiveDevice *= "svga256") display = new LinuxSVGA256OutputDevice(); } #endif #ifdef USE_SHM_VIDEO_DEVICES if (videoReceiveDevice *= "shm") { display = new ShmVideoOutputDevice(); display->Open("shm"); } #endif // Dump video to nowhere if ((videoReceiveDevice *= "null") || (isEncoding&&(!videoLocal))) { display = PVideoOutputDevice::CreateDevice("NULLOutput"); if (display == NULL) { PError << "Cannot create NULLOutput device" << endl; return FALSE; } } if (display == NULL) PError << "unknown video output device \"" << videoReceiveDevice << "\"" << endl; PAssert(display != NULL, "NULL video device"); PTRACE(3,"display->SetFrameSize(" << codec.GetWidth() << "," << codec.GetHeight() << ") from codec"); //NB cannot resize receive video window if the following line is used //display->SetFrameSize(352>>(1-videoSize), 288>>(1-videoSize)); display->SetFrameSize(codec.GetWidth(), codec.GetHeight()); // needed to enable resize display->SetColourFormatConverter("YUV420P"); channel->AttachVideoPlayer(display); //Select true, delete video chanel on closing codec. return codec.AttachChannel(channel,TRUE); } H323Connection * MyH323EndPoint::SetupTransfer(const PString & token, const PString & callIdentity, const PString & remoteParty, PString & newToken, void * /*userData*/) { H323Connection * conn = H323EndPoint::SetupTransfer(token, callIdentity, remoteParty, newToken); callTransferCallToken = newToken; return conn; } // // if gateway is empty, then dest is assumed to be a IP address and optional port // if gateway is non-empty, then gateway is assumed to be an IP address and optional port, and // dest is passed to the gateway as the e164 address // void MyH323EndPoint::MakeOutgoingCall(const PString & dest, const PString & gateway) { MakeOutgoingCall(dest, gateway, defaultCallOptions); } void MyH323EndPoint::MakeOutgoingCall(const PString & _dest, const PString & gateway, CallOptions callOptions) { PString dest = _dest; if (!numberPrefix.IsEmpty()) { // check to see if dest is all digits PINDEX i; for (i = 0; i < dest.GetLength(); ++i) if (!isdigit(dest[i])) break; if (i == dest.GetLength()) dest = numberPrefix + dest; } currentCallOptions = callOptions; PString fullAddress; if (!gateway) fullAddress = gateway; else fullAddress = dest; if ((fullAddress.Find(':') == P_MAX_INDEX) && (callOptions.connectPort != H323EndPoint::DefaultTcpPort)) fullAddress += psprintf(":%i", currentCallOptions.connectPort); if (!gateway) fullAddress = dest.Trim() + '@' + fullAddress; if (!MakeCall(fullAddress, currentCallToken)) { cout << "Error making call to \"" << fullAddress << '"' << endl; return; } PConfig config("Calls"); int index = config.GetInteger("index"); PString lastCalledParty = config.GetString(PString(PString::Unsigned, index)); index = (index + 1) % LAST_CALL_COUNT; PTime now; PString indexStr = PString(PString::Unsigned, index); config.SetString(indexStr, fullAddress); config.SetString(indexStr + "_Time", now.AsString()); cout << GetLocalUserName() << " is calling host " << fullAddress << endl; uiState = uiConnectingCall; } void MyH323EndPoint::NewSpeedDial(const PString & ostr) { PString str = ostr; PINDEX idx = str.Find(' '); if (str.IsEmpty() || (idx == P_MAX_INDEX)) { cout << "Must specify speedial number and string" << endl; return; } PString key = str.Left(idx).Trim(); PString data = str.Mid(idx).Trim(); PConfig config("Speeddial"); config.SetString(key, data); cout << "Speedial " << key << " set to " << data << endl; } void MyH323EndPoint::ListSpeedDials() { PConfig config("Speeddial"); PStringList keys = config.GetKeys(); if (keys.GetSize() == 0) { cout << "No speed dials defined" << endl; return; } PINDEX i; for (i = 0; i < keys.GetSize(); i++) cout << keys[i] << ": " << config.GetString(keys[i]) << endl; } // // StartCall accepts any of the following types of arguments // speedial '#' lookup the string in the registry, and continue processing // ipaddress dial this IP address or hostname // num ' ' gateway dial the number using the specified gateway // void MyH323EndPoint::StartCall(const PString & ostr) { PString str = ostr.Trim(); if (str.IsEmpty()) cout << "Must supply hostname to connect to!\n"; CallOptions callOptions = defaultCallOptions; // check for speed dials, and match wild cards as we go PString key, prefix; if ((str.GetLength() > 1) && (str[str.GetLength()-1] == '#')) { key = str.Left(str.GetLength()-1).Trim(); str = PString(); PConfig config("Speeddial"); PINDEX p; for (p = key.GetLength(); p > 0; p--) { PString newKey = key.Left(p); prefix = newKey; PINDEX q; // look for wild cards str = config.GetString(newKey + '*').Trim(); if (!str.IsEmpty()) break; // look for digit matches for (q = p; q < key.GetLength(); q++) newKey += '?'; str = config.GetString(newKey).Trim(); if (!str.IsEmpty()) break; } if (str.IsEmpty()) { cout << "Speed dial \"" << key << "\" not defined"; if (gatekeeper != NULL) { cout << ", trying gatekeeper ..." << endl; MakeOutgoingCall(key, PString(), callOptions); return; } else cout << endl; } else if ((p = str.Find('(')) != P_MAX_INDEX) { PString argStr = str.Mid(p); if (argStr.GetLength() > 0 && argStr[argStr.GetLength()-1] == ')') argStr = argStr.Mid(1, argStr.GetLength()-2); PArgList strArgs(argStr, "f-fast-disable." "T-h245tunneldisable." "e-silence." "j-jitter:" "-connectport:" "-connectring:"); callOptions.Initialise(strArgs); str = str.Left(p); cout << "Per connection call options set: " << argStr << endl << callOptions << endl; } } if (!str.IsEmpty()) { PINDEX idx = str.Find(' '); if (idx == P_MAX_INDEX) { if (!key && (str[0] == '@')) MakeOutgoingCall(key, str.Mid(1), callOptions); else if (!key && !prefix && (str[0] == '%')) { if (key.Left(prefix.GetLength()) == prefix) key = key.Mid(prefix.GetLength()); MakeOutgoingCall(key, str.Mid(1), callOptions); } else MakeOutgoingCall(str, proxy, callOptions); } else { PString host = str.Left(idx).Trim(); PString gw = str.Mid(idx).Trim(); MakeOutgoingCall(host, gw, callOptions); } return; } uiState = MyH323EndPoint::uiCallHungup; } void MyH323EndPoint::AwaitTermination() { PThread * userInterfaceThread = NULL; if (hasMenu) userInterfaceThread = new UserInterfaceThread(*this); #if ! defined(HAS_LIDDEVICE) exitFlag.Wait(); #else if ((lidDevice == NULL) || !lidDevice->IsOpen()) exitFlag.Wait(); else { speakerphoneSwitch = FALSE; BOOL oldOnHook = TRUE; BOOL oldRealOnHook = TRUE; int olduiState = uiStateIllegal; PTime offHookTime; PString digits; // poll the handset every 100ms looking for state changes while (!exitFlag.Wait(100)) { // lock the user interface state whilst we change it uiStateMutex.Wait(); // get real hook state BOOL realOnHook = !lidDevice->IsLineOffHook(POTS_LINE); BOOL onHook = realOnHook; // if in speakerphone mode, if (speakerphoneSwitch) { // if the phone is onhook then don't look at the real hook state if (realOnHook) onHook = FALSE; // if the handset just went offhook, then get out of speakerphone mode else if (realOnHook != oldRealOnHook) { speakerphoneSwitch = FALSE; lidDevice->EnableAudio(0, TRUE); if (verbose > 1) cout << "Speakerphone off" << endl; } } // handle onhook/offhook transitions if (onHook != oldOnHook) { if (onHook) { digits = PString(); HandleHandsetOnHook(); } else { HandleHandsetOffHook(); offHookTime = PTime(); } HandleStateChange(onHook); olduiState = uiState; } // handle timeouts and DTMF digits if (!onHook) { HandleHandsetTimeouts(offHookTime); HandleHandsetDTMF(digits); } // handle any state changes if (uiState != olduiState) { offHookTime = PTime(); HandleStateChange(onHook); } // save hook state so we can detect changes oldOnHook = onHook; oldRealOnHook = realOnHook; // save the old UI state so we can detect changes olduiState = uiState; uiStateMutex.Signal(); } } #endif if (userInterfaceThread != NULL) { userInterfaceThread->Terminate(); userInterfaceThread->WaitForTermination(); delete userInterfaceThread; } } #if defined(HAS_LIDDEVICE) #if 0 static char * stateNames[] = { "Dialtone", "AnsweringCall", "ConnectingCall", "WaitingForAnswer", "CallInProgress", "CallHungup", "StateIllegal" }; #endif void MyH323EndPoint::HandleStateChange(BOOL onHook) { switch (uiState) { // dialtone whilst no call active case uiDialtone: if (onHook) lidDevice->RingLine(0, 0); else if (autoHook) { lidDevice->StopTone(0); } else { cout << "Playing dialtone" << endl; lidDevice->PlayTone(0, OpalLineInterfaceDevice::DialTone); } break; // no tone whilst waiting for remote party to connect case uiConnectingCall: if (onHook) lidDevice->RingLine(0, 0); else lidDevice->StopTone(0); break; // when connected, play ring tone case uiWaitingForAnswer: if (onHook) lidDevice->RingLine(0, 0); else { cout << "Playing ringtone" << endl; lidDevice->PlayTone(0, OpalLineInterfaceDevice::RingTone); } break; // when a call is in progress, stop all tones and remove DTMF tones from the stream case uiCallInProgress: if (onHook) lidDevice->RingLine(0, 0); else { lidDevice->StopTone(0); lidDevice->SetRemoveDTMF(0, TRUE); } break; // remote end has hungup case uiCallHungup: if (terminateOnHangup) exitFlag.Signal(); if (autoHook || onHook) { uiState = uiDialtone; lidDevice->RingLine(0, 0); } else { if (dialAfterHangup) uiState = uiDialtone; else lidDevice->PlayTone(POTS_LINE, OpalLineInterfaceDevice::BusyTone); } break; case uiAnsweringCall: if (autoHook || !onHook) lidDevice->StopTone(0); else { lidDevice->SetCallerID(POTS_LINE, ""); if (callerIdEnable) { MyH323Connection * connection = (MyH323Connection *)FindConnectionWithLock(currentCallToken); if (connection != NULL) { lidDevice->SetCallerID(POTS_LINE, connection->GetCallerIdString()); connection->Unlock(); } } lidDevice->RingLine(0, 0x33); } break; default: if (!onHook || autoHook) lidDevice->StopTone(0); else lidDevice->RingLine(0, 0); break; } } void MyH323EndPoint::HandleHandsetOffHook() { if (verbose > 1) cout << "Offhook - "; // if (speakerphoneSwitch) { // if (verbose > 1) // cout << "speakerphone off - "; // speakerphoneSwitch = FALSE; // lidDevice->EnableAudio(0, TRUE); // } switch (uiState) { case uiDialtone: if (!autoDial) { if (verbose > 1) cout << "auto-dialing " << autoDial << endl; StartCall(autoDial); } else { if (verbose > 1) cout << "dialtone" << endl; } break; case uiConnectingCall: if (verbose > 1) cout << "call connecting" << endl; break; case uiAnsweringCall: if (verbose > 1) cout << "answering call" << endl; AnswerCall(H323Connection::AnswerCallNow); break; case uiWaitingForAnswer: if (verbose > 1) cout << "waiting for remote answer" << endl; break; case uiCallInProgress: // should never occur! if (verbose > 1) cout << "call in progress" << endl; break; default: if (verbose > 1) cout << "not sure!" << endl; break; } } void MyH323EndPoint::HandleHandsetOnHook() { if (uiState == uiCallHungup) uiState = uiDialtone; if (verbose > 1) cout << "Onhook - "; //#ifndef OLD_IXJ_DRIVER // if (speakerphoneSwitch) { // lidDevice->EnableAudio(0, FALSE); // if (verbose > 1) // cout << "speakerphone on." << endl; // } else //#endif // { if (verbose > 1) cout << "ending call." << endl; ClearCall(currentCallToken); // } speakerphoneSwitch = FALSE; } void MyH323EndPoint::HandleHandsetDTMF(PString & digits) { char newDigit = lidDevice->ReadDTMF(0); if (newDigit != '\0') { digits += newDigit; if (!digits) { switch (uiState) { case uiCallInProgress: { if (verbose > 1) cout << "Sending user indication message " << digits << endl; H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection != NULL) { connection->SendUserInput(digits); connection->Unlock(); } digits = PString(); } break; case uiDialtone: lidDevice->StopTone(0); if (digits.GetLength() > 0) { PINDEX i; for (i = 0; i < digits.GetLength(); i++) if (!isdigit(digits[i])) break; BOOL allDigits = i == digits.GetLength(); // handle strings ending in '#' if (digits[digits.GetLength()-1] == '#') { // if pressed '#', then redial last number if (digits.GetLength() == 1) { PConfig config("Calls"); int index = config.GetInteger("index"); PString lastCalledParty = config.GetString(PString(PString::Unsigned, index)); if (lastCalledParty.IsEmpty()) cout << "No last called party to dial" << endl; else { if (!MakeCall(lastCalledParty, currentCallToken)) cout << "Error making call to \"" << lastCalledParty << '"' << endl; else { if (verbose > 1) cout << "Redialling last number at " << lastCalledParty << endl; uiState = uiConnectingCall; } } } // if pressed '*#', then redial last caller else if ((digits.GetLength() == 2) && (digits[0] == '*')) { PConfig config("Callers"); int index = config.GetInteger("index"); PString lastCallingParty = config.GetString(PString(PString::Unsigned, index)); if (lastCallingParty.IsEmpty()) cout << "No last calling party to dial" << endl; else { if (!MakeCall(lastCallingParty, currentCallToken)) cout << "Error making call to \"" << lastCallingParty << '"' << endl; else { if (verbose > 1) cout << "Calling back last party at " << lastCallingParty << endl; uiState = uiConnectingCall; } } } // if string starts with '*', then convert to IP address else if ((digits.GetLength() >= 9) && (digits[0] == '*')) { digits = digits.Mid(1, digits.GetLength()-2); digits.Replace('*', '.', TRUE); StartCall(digits); // if there are some digits, then use them as a speed dial } else if (digits.GetLength() > 1) StartCall(digits); // clear out the dialled digits digits = PString(); } else if (allDigits && (digits.GetLength() == 20)) { while (digits[0] == '0') digits = digits.Mid(1); if (digits.GetLength() > 0) { StartCall(digits); digits = PString(); } } } break; default: break; } } } } void MyH323EndPoint::HandleHandsetTimeouts(const PTime & offHookTime) { int timeout = 0; switch (uiState) { case uiDialtone: timeout = DEFAULT_TIMEOUT; break; case uiConnectingCall: timeout = DEFAULT_TIMEOUT; break; case uiWaitingForAnswer: timeout = DEFAULT_TIMEOUT; break; default: break; } if (timeout > 0) { PTime now; if ((now - offHookTime) > timeout) { if (verbose > 1) cout << "Operation timed out" << endl; ClearCall(currentCallToken); uiState = uiCallHungup; } } } #endif void MyH323EndPoint::HandleUserInterface() { PConsoleChannel console(PConsoleChannel::StandardInput); PTRACE(2, "OhPhone\tUser interface thread started."); PStringStream help; help << "Select:\n" " 0-9 : send user indication message\n" " *,# : send user indication message\n" " M : send text message to remote user\n" " C : connect to remote host\n" " T : Transfer to another host\n" " O : Hold call\n" " S : Display statistics\n" " H : Hang up phone\n" " L : List speed dials\n" " I : Show call history\n" " D : Create new speed dial\n" " {} : Increase/reduce record volume\n" " [] : Increase/reduce playback volume\n" " V : Display current volumes\n" #ifdef HAS_LIDDEVICE " A : turn AEC up/down\n" #endif " E : Turn silence supression on/off\n" " F : Forward call calls to address\n" " J : Flip video input top to bottom\n"; ; #ifdef HAS_LIDDEVICE if (isXJack) { #ifndef OLD_IXJ_DRIVER if ((lidDevice != NULL) && lidDevice->IsOpen()) help << " P : Enter speakerphone mode\n"; #endif } #endif help << " X : Exit program\n"; for (;;) { // display the prompt cout << "Command ? " << flush; // terminate the menu loop if console finished char ch = (char)tolower(console.peek()); if (console.eof()) { if (verbose) cout << "\nConsole gone - menu disabled" << endl; return; } if (ch == '\n') { console.ignore(INT_MAX, '\n'); continue; } if ((isdigit(ch)) || (ch == '*') || (ch == '#') || (ch == 'm')) { H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection == NULL) { cout << "No call in progress\n"; console.ignore(INT_MAX, '\n'); } else { PString str; console >> str; if (ch == 'm') { // Send message str = str.Mid(1).Trim(); // strip 'm' and trim whitespace str = "MSG"+str.Trim(); // Add GnomeMeeting message header } cout << "Sending user indication: " << str << endl; connection->SendUserInput(str); connection->Unlock(); } } else { console >> ch; switch (tolower(ch)) { case '?' : cout << help << endl; break; case 'x' : case 'q' : cout << "Exiting." << endl; ClearAllCalls(); uiState = uiDialtone; exitFlag.Signal(); console.ignore(INT_MAX, '\n'); return; case 'h' : if (!currentCallToken) { cout << "Hanging up call." << endl; if (!ClearCall(currentCallToken)) cout << "Could not hang up current call!\n"; speakerphoneSwitch = FALSE; } console.ignore(INT_MAX, '\n'); break; case 't' : if (!currentCallToken) { PString str; console >> str; cout << "Transferring call to " << str << endl; TransferCall(currentCallToken, str.Trim()); } else console.ignore(INT_MAX, '\n'); break; case 'o' : if (!currentCallToken) { cout << "Holding call." << endl; HoldCall(currentCallToken, TRUE); } console.ignore(INT_MAX, '\n'); break; case 'y' : AnswerCall(H323Connection::AnswerCallNow); console.ignore(INT_MAX, '\n'); break; case 'n' : AnswerCall(H323Connection::AnswerCallDenied); console.ignore(INT_MAX, '\n'); break; case 'c' : if (!currentCallToken.IsEmpty()) cout << "Cannot make call whilst call in progress\n"; else { PString str; console >> str; StartCall(str.Trim()); } break; case 'l' : ListSpeedDials(); break; case 'd' : { PString str; console >> str; NewSpeedDial(str.Trim()); } break; case 'e' : if (currentCallToken.IsEmpty()) cout << "No call in progress" << endl; else { H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection == NULL) cout << "No connection active.\n"; else { connection->Unlock(); H323Channel * chan = connection->FindChannel(RTP_Session::DefaultAudioSessionID, FALSE); if (chan == NULL) cout << "Cannot find audio channel" << endl; else { H323Codec * rawCodec = chan->GetCodec(); if (!PIsDescendant(rawCodec, H323AudioCodec)) cout << "Audio channel is not audio!" << endl; else { H323AudioCodec * codec = (H323AudioCodec *)rawCodec; H323AudioCodec::SilenceDetectionMode mode = codec->GetSilenceDetectionMode(); if (mode == H323AudioCodec::AdaptiveSilenceDetection) { mode = H323AudioCodec::NoSilenceDetection; cout << "Silence detection off" << endl; } else { mode = H323AudioCodec::AdaptiveSilenceDetection; cout << "Silence detection on" << endl; } codec->SetSilenceDetectionMode(mode); } } // connection->Unlock(); } } break; case 's' : if (currentCallToken.IsEmpty()) cout << "No call in progress" << endl; else { H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection == NULL) cout << "No connection statistics available.\n"; else { PTime now; PTime callStart = connection->GetConnectionStartTime(); cout << "Connection statistics:\n " << "Remote party : " << connection->GetRemotePartyName() << "\n " << "Start : " << callStart << "\n " << "Duration : " << setw(5) << setprecision(0) << (now - callStart) << " mins\n " << "Round trip delay : " << connection->GetRoundTripDelay().GetMilliSeconds() << " msec" << endl; ReportSessionStatistics(connection, RTP_Session::DefaultAudioSessionID); ReportSessionStatistics(connection, RTP_Session::DefaultVideoSessionID); connection->Unlock(); } } console.ignore(INT_MAX, '\n'); break; #ifdef HAS_LIDDEVICE #ifndef OLD_IXJ_DRIVER case 'p' : if (isXJack) { if ((lidDevice != NULL) && lidDevice->IsOpen()) { speakerphoneSwitch = !speakerphoneSwitch; lidDevice->EnableAudio(0, !speakerphoneSwitch); if (verbose > 1) cout << "Speakerphone " << (speakerphoneSwitch ? "on" : "off") << endl; } console.ignore(INT_MAX, '\n'); } break; #endif case 'a' : if ((lidDevice != NULL) && lidDevice->IsOpen()) { int aec = lidDevice->GetAEC(0); if (ch == 'a') aec--; else aec++; if (aec < 0) aec = OpalLineInterfaceDevice::AECAGC; else if (aec > OpalLineInterfaceDevice::AECAGC) aec = OpalLineInterfaceDevice::AECOff; lidDevice->SetAEC(0, (OpalLineInterfaceDevice::AECLevels)aec); if (aec == OpalLineInterfaceDevice::AECAGC || (ch == 'a' && aec == OpalLineInterfaceDevice::AECHigh) || (ch == 'A' && aec == OpalLineInterfaceDevice::AECOff)) { unsigned recvol; lidDevice->GetRecordVolume(0, recvol); if (verbose > 2) cout << "New volume level is " << recvol << endl; } if (verbose > 2) cout << "New AEC level is " << AECLevelNames[aec] << endl; } else cout <<"AEC change ignored as device closed"<IsOpen()) { unsigned vol; lidDevice->GetPlayVolume(0, vol); cout << "Play volume is " << vol << endl; lidDevice->GetRecordVolume(0, vol); cout << "Record volume is " << vol << endl; } #endif #if defined(HAS_LIDDEVICE) && defined(HAS_OSS) else #endif #ifdef HAS_OSS { cout << "Play volume is " << ossPlayVol << endl; cout << "Record volume is " << ossRecVol << endl; } #endif break; case '[' : case ']' : case '{' : case '}' : #ifdef HAS_LIDDEVICE if ((lidDevice != NULL) && lidDevice->IsOpen()) { unsigned vol; if (ch == '{' || ch == '}') lidDevice->GetRecordVolume(0, vol); else lidDevice->GetPlayVolume(0, vol); // adjust volume up or down vol += ((ch == '[') || (ch == '{')) ? -5 : 5; if (vol < 0) vol = 0; else if (vol > 100) vol = 100; // write to hardware if (ch == '{' || ch == '}') { lidDevice->SetRecordVolume(0, vol); if (verbose > 2) cout << "Record volume is " << vol << endl; } else { lidDevice->SetPlayVolume(0, vol); if (verbose > 2) cout << "Play volume is " << vol << endl; } } #endif #if defined(HAS_LIDDEVICE) && defined(HAS_OSS) else #endif #ifdef HAS_OSS { int vol; if (ch == '{' || ch == '}') vol = ossRecVol; else vol = ossPlayVol; vol += ((ch == '[') || (ch == '{')) ? -5 : 5; if (vol < 0) vol = 0; else if (vol > 100) vol = 100; if (mixerDev >= 0) { int volVal = vol | (vol << 8); if (ch == '{' || ch == '}') { ossRecVol = vol; ::ioctl(mixerDev, MIXER_WRITE(mixerRecChan), &volVal); cout << "Record volume is " << ossRecVol << endl; } else { ossPlayVol = vol; ::ioctl(mixerDev, SOUND_MIXER_WRITE_PCM, &volVal); cout << "Play volume is " << ossPlayVol << endl; } } else cout << "Audio setting change ignored as mixer device disabled"<> alwaysForwardParty; alwaysForwardParty = alwaysForwardParty.Trim(); if (!alwaysForwardParty) cout << "Forwarding all calls to \"" << alwaysForwardParty << '"' << endl; else cout << "Call forwarding of all calls disabled." << endl; break; case 'i' : case 'I' : { PString title; if (ch == 'i') title = "Callers"; else title = "Calls"; cout << title << endl; PConfig config(title); int index = config.GetInteger("index"); PINDEX i; for (i = 0; i < LAST_CALL_COUNT; i++) { PString indexStr = PString(PString::Unsigned, index); PString number = config.GetString(indexStr); if (number.IsEmpty()) continue; cout << indexStr << ": " << number << " at " << config.GetString(indexStr + "_Time") << endl; if (index == 0) index = LAST_CALL_COUNT-1; else index--; } } break; case 'j' : if (currentCallToken.IsEmpty()) cout << "No call in progress" << endl; else { H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection == NULL) cout << "No connection active.\n"; else { connection->Unlock(); H323Channel * chan = connection->FindChannel(RTP_Session::DefaultVideoSessionID, FALSE); if (chan == NULL) cout << "Cannot find sending video channel" << endl; else { H323Codec * rawCodec = chan->GetCodec(); if (!PIsDescendant(rawCodec, H323VideoCodec)) cout << "Sending video codec is not video!" << endl; else { H323VideoCodec * codec = (H323VideoCodec *)rawCodec; PChannel * rawChan = codec->GetRawDataChannel(); if (NULL == rawChan) cout << "Cannot find sending video channel" << endl; else { if (!PIsDescendant(rawChan, PVideoChannel)) cout << "Sending video channel is not Class PVideoChannel!" << endl; else { PVideoChannel * videoChan = (PVideoChannel *)rawChan; if (!videoChan->ToggleVFlipInput()) cout << "\nCould not toggle Vflip state of video input device" << endl; } } } } } } break; default: cout << "Unknown command " << ch << endl; console.ignore(INT_MAX, '\n'); break; } } } } void MyH323EndPoint::ReportSessionStatistics(H323Connection *connection, unsigned sessionID) { RTP_Session * session = connection->GetSession(sessionID); if ((session == NULL) && (sessionID == RTP_Session::DefaultAudioSessionID)) cout << "No RTP Audio session statistics available." << endl; if (session == NULL) return; PInt64 elapsedMilliseconds = (PTime() - connection->GetConnectionStartTime()).GetMilliSeconds(); cout << "RTP " << (sessionID == RTP_Session::DefaultAudioSessionID ? "audio" : "video") << " session statistics \n " << session->GetPacketsSent() << '/' << session->GetOctetsSent() << '/' << 8 * 1.024 * session->GetOctetsSent() / elapsedMilliseconds << " packets/bytes/datarate sent\n " << session->GetMaximumSendTime() << '/' << session->GetAverageSendTime() << '/' << session->GetMinimumSendTime() << " max/avg/min send time\n " << session->GetPacketsReceived() << '/' << session->GetOctetsReceived() << '/' << 8* 1.024 * session->GetOctetsReceived() / elapsedMilliseconds << " packets/bytes/datarate received\n " << session->GetMaximumReceiveTime() << '/' << session->GetAverageReceiveTime() << '/' << session->GetMinimumReceiveTime() << " max/avg/min receive time\n " << session->GetPacketsLost() << '/' << session->GetPacketsOutOfOrder() << '/' << session->GetPacketsTooLate() << " packets dropped/out of order/late\n " << endl; } void MyH323EndPoint::AnswerCall(H323Connection::AnswerCallResponse response) { if (uiState != uiAnsweringCall) return; StopRinging(); H323Connection * connection = FindConnectionWithLock(currentCallToken); if (connection == NULL) return; connection->AnsweringCall(response); connection->Unlock(); if (response == H323Connection::AnswerCallNow) { cout << "Accepting call." << endl; uiState = uiCallInProgress; } else { cout << "Rejecting call." << endl; uiState = uiCallHungup; } } void MyH323EndPoint::HandleRinging() { PSoundChannel dev(GetSoundChannelPlayDevice(), PSoundChannel::Player); if (!dev.IsOpen()) { PTRACE(2, "Cannot open sound device for ring"); return; } if (ringDelay < 0) { PTRACE(2, "Playing " << ringFile); dev.PlayFile(ringFile, TRUE); } else { PTimeInterval delay(0, ringDelay); PTRACE(2, "Playing " << ringFile << " with repeat of " << delay << " ms"); do { dev.PlayFile(ringFile, TRUE); } while (!ringFlag.Wait(delay)); } } void MyH323EndPoint::StartRinging() { PAssert(ringThread == NULL, "Ringing thread already present"); if (!noAnswerForwardParty) noAnswerTimer = PTimeInterval(0, noAnswerTime); if (!ringFile) ringThread = new RingThread(*this); } void MyH323EndPoint::StopRinging() { noAnswerTimer.Stop(); if (ringThread == NULL) return; ringFlag.Signal(); ringThread->WaitForTermination(); delete ringThread; ringThread = NULL; } void MyH323EndPoint::SendDTMF(const char * tone) { #ifdef HAS_LIDDEVICE if (lidDevice != NULL && lidDevice->IsOpen()) lidDevice->PlayDTMF(0, tone, 200, 100); #endif } void MyH323EndPoint::TestVideoGrabber(PConfigArgs & args) { double lossRate = 0; if (args.HasOption("videolose")) lossRate = args.GetOptionString("videolose").AsReal(); #ifndef NO_H323_VIDEO if (lossRate > 100) lossRate = 100; else if (lossRate < 0) lossRate = 0; lossRate = lossRate / 100.0; unsigned maxint = 0xffffffff; unsigned divisor = (unsigned)(lossRate * (double)maxint); PRandom rand; MyH323Connection tempConnection(*this, 2, 0, 0, FALSE, FALSE); PThread * userInterfaceThread = NULL; if (hasMenu) userInterfaceThread = new TestUserInterfaceThread(*this); H323Capability * cap; PString capName; #ifdef DEPRECATED_CU30 if (videoCu30Stats) capName="Cu30"; else #endif cap = GetCapabilities().FindCapability("H.26"); if (cap == NULL) { cout << "Unable to find the codec associated with \"" << capName << "\". Exiting"<CreateCodec(H323Codec::Encoder); H323Codec *DecodingCodec = cap->CreateCodec(H323Codec::Decoder); autoStartTransmitVideo = TRUE; //Force these options to be on. Guarantees that OpenVideoChannel works. videoLocal = TRUE; OpenVideoChannel(tempConnection, TRUE, *((H323VideoCodec *)EncodingCodec)); //Which creates local video display. OpenVideoChannel(tempConnection, FALSE, *((H323VideoCodec *)DecodingCodec)); localVideoChannel = (PVideoChannel *)EncodingCodec->GetRawDataChannel(); int packetCount, frameCount, skipCount; RTP_DataFrame frame(2048); unsigned length, written; if (PIsDescendant(cap, H323_H261Capability)) frame.SetPayloadType(RTP_DataFrame::H261); #if H323_AVCODEC else if (PIsDescendant(cap, H323_FFH263Capability)) frame.SetPayloadType(RTP_DataFrame::DynamicBase); #endif #if H323_RFC2190_AVCODEC else if (PIsDescendant(cap, H323_RFC2190_H263Capability)) frame.SetPayloadType(RTP_DataFrame::H263); #endif frameCount = 0; skipCount = 0; PINDEX bitsEncoded = 0; PTime startTime; unsigned sequenceMask = (1 << (8 * sizeof(WORD))) - 1; for(packetCount = 2; ; packetCount++) { //H323Codec::lastSequenceNumber starts at 1 so next is 2 PTRACE(3,"Video\t Packet " << packetCount << " of test video program"); EncodingCodec->Read(frame.GetPayloadPtr(), length, frame); frame.SetPayloadSize(length); bitsEncoded += length * 8; frame.SetSequenceNumber((WORD)(packetCount & sequenceMask)); if (frame.GetMarker()) frameCount++; //#define ENABLE_FRAME_TESTING 1 // uncomment to enable out of order packet testing #ifdef ENABLE_FRAME_TESTING {// test effect of same frame video packets arriving out of order static RTP_DataFrame testframe(2048); static unsigned testlength = 0; static BOOL tryTest = FALSE; if (0 == packetCount % 7) tryTest = TRUE; if (tryTest) { if (0 == testlength) { if (!frame.GetMarker()) { testframe.SetMarker(frame.GetMarker()); testframe.SetSequenceNumber(frame.GetSequenceNumber()); memcpy(testframe.GetPayloadPtr(), frame.GetPayloadPtr(), length); testlength = length; } else // delay test if end of frame DecodingCodec->Write(frame.GetPayloadPtr(), length, frame, written); } else { if (frame.GetMarker()) { // send both packets in order and restart test DecodingCodec->Write(testframe.GetPayloadPtr(), testlength, testframe, written); DecodingCodec->Write(frame.GetPayloadPtr(), length, frame, written); testlength = 0; } else { // send packets out of order DecodingCodec->Write(frame.GetPayloadPtr(), length, frame, written); DecodingCodec->Write(testframe.GetPayloadPtr(), testlength, testframe, written); testlength = 0; tryTest = FALSE; } } } else DecodingCodec->Write(frame.GetPayloadPtr(), length, frame, written); } #else //ENABLE_FRAME_TESTING if (divisor < rand) DecodingCodec->Write(frame.GetPayloadPtr(), length, frame, written); else skipCount++; #endif //ENABLE_FRAME_TESTING #ifdef DEPRECATED_CU30 // if (videoCu30Stats) // ((H323_Cu30Codec *)Codec)->RecordStatistics(videoBuffer); if ((packetCount + 1) == videoCu30Stats) break; #endif if (!exitFlag.WillBlock()) break; } PTimeInterval diffTime= PTime() - startTime; double data_rate = ((double)bitsEncoded) / (((double)diffTime.GetMilliSeconds()) * 1.024); PStringStream str; str << " Video frames per second: "<< setprecision(2) << (double) 1000 * frameCount/diffTime.GetMilliSeconds() << endl << " Data rate " << setprecision(3) << data_rate << " Kilo bits/second" << endl << " Packets Tot " << packetCount << " skipped packets " << skipCount; PTRACE(3, str); cout << endl << str << endl; str = "Elapsed time is "; str << diffTime << " seconds" ; PTRACE(3, str); cout << str << endl; delete EncodingCodec; delete DecodingCodec; if (NULL != userInterfaceThread) { userInterfaceThread->Terminate(); userInterfaceThread->WaitForTermination(); delete userInterfaceThread; } #endif //NO_H323_VIDEO return; } void MyH323EndPoint::TestHandleUserInterface() { #ifndef NO_H323_VIDEO PConsoleChannel console(PConsoleChannel::StandardInput); PTRACE(2, "OhPhone\tTESTING interface thread started."); PStringStream help; help << "Select:\n" " J : Flip video input top to bottom\n" " Q : Exit program\n" " X : Exit program\n"; for (;;) { // display the prompt cout << "(testing) Command ? " << flush; // terminate the menu loop if console finished char ch = (char)console.peek(); if (console.eof()) { if (verbose) cout << "\nConsole gone - menu disabled" << endl; return; } console >> ch; switch (tolower(ch)) { case 'j' : if (NULL == localVideoChannel) { cout << "\nNo video Channel" << endl; break; } if (!localVideoChannel->ToggleVFlipInput()) cout << "\nCould not toggle Vflip state of video input device" << endl; break; case 'x' : case 'q' : cout << "Exiting." << endl; exitFlag.Signal(); console.ignore(INT_MAX, '\n'); return; break; case '?' : default: cout << help << endl; break; } // end switch } // end for #endif //NO_H323_VIDEO } /////////////////////////////////////////////////////////////// BOOL CallOptions::Initialise(PArgList & args) { // set default connection options noFastStart = args.HasOption('f'); noH245Tunnelling = args.HasOption('T'); noSilenceSuppression = args.HasOption('e'); noH245InSetup = args.HasOption('S'); if (args.HasOption("connectring")) connectRing = args.HasOption("connectring"); if (!args.GetOptionString("connectport").IsEmpty()) connectPort = (WORD)args.GetOptionString("connectport").AsInteger(); if (args.HasOption('j')) { unsigned minJitterNew; unsigned maxJitterNew; PStringArray delays = args.GetOptionString('j').Tokenise(",-"); if (delays.GetSize() > 1) { minJitterNew = delays[0].AsUnsigned(); maxJitterNew = delays[1].AsUnsigned(); } else { maxJitterNew = delays[0].AsUnsigned(); minJitterNew = PMIN(minJitter, maxJitterNew); } if (minJitterNew >= 20 && minJitterNew <= maxJitterNew && maxJitterNew <= 1000) { minJitter = minJitterNew; maxJitter = maxJitterNew; } else { cout << "Jitter should be between 20 milliseconds and 1 seconds, is " << minJitter << '-' << maxJitter << endl; return FALSE; } } return TRUE; } void CallOptions::PrintOn(ostream & strm) const { strm << "FastStart is " << !noFastStart << "\n" << "H245Tunnelling is " << !noH245Tunnelling << "\n" << "SilenceSupression is " << !noSilenceSuppression << "\n" << "H245InSetup is " << !noH245InSetup << "\n" << "Jitter buffer: " << minJitter << '-' << maxJitter << " ms\n" << "Connect port: " << connectPort << "\n"; } /////////////////////////////////////////////////////////////// MyH323Connection::MyH323Connection(MyH323EndPoint & ep, unsigned callReference, unsigned options, unsigned minJitter, unsigned maxJitter, int _verbose) : H323Connection(ep, callReference, options), myEndpoint(ep), verbose(_verbose) { SetAudioJitterDelay(minJitter, maxJitter); channelsOpen = 0; } BOOL MyH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU) { if (!myEndpoint.setupParameter) { setupPDU.m_h323_uu_pdu.IncludeOptionalField(H225_H323_UU_PDU::e_nonStandardData); setupPDU.m_h323_uu_pdu.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); endpoint.SetH221NonStandardInfo(setupPDU.m_h323_uu_pdu.m_nonStandardData.m_nonStandardIdentifier); setupPDU.m_h323_uu_pdu.m_nonStandardData.m_data = myEndpoint.setupParameter; } //setupPDU.GetQ931().SetBearerCapabilities(Q931::TransferUnrestrictedDigital, 1, 0, 3); // 88 93 a5 return H323Connection::OnSendSignalSetup(setupPDU); } H323Connection::AnswerCallResponse MyH323Connection::OnAnswerCall(const PString & caller, const H323SignalPDU & /*setupPDU*/, H323SignalPDU & /*connectPDU*/) { PTRACE(1, "H225\tOnWaitForAnswer"); PTime now; if (myEndpoint.autoAnswer) { cout << "Automatically accepting call at " << now << endl; return AnswerCallNow; } myEndpoint.currentCallToken = GetCallToken(); myEndpoint.uiState = MyH323EndPoint::uiAnsweringCall; cout << "Incoming call from \"" << caller << "\" at " << now << ", answer call (Y/n)? " << flush; myEndpoint.StartRinging(); return AnswerCallPending; } BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel) { if (!H323Connection::OnStartLogicalChannel(channel)) return FALSE; if (verbose >= 2) { 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; } if (channel.GetDirection() == H323Channel::IsReceiver) { int videoQuality = myEndpoint.videoQuality; if (PIsDescendant(channel.GetCodec(), H323VideoCodec) && (videoQuality >= 0)) { //const H323_H261Capability & h261Capability = (const H323_H261Capability &)channel.GetCapability(); //if (!h261Capability.GetTemporalSpatialTradeOffCapability()) // cout << "Remote endpoint does not allow video quality configuration" << endl; //else { cout << "Requesting remote endpoint to send video quality " << videoQuality << "/31" << endl; // kludge to wait for channel to ACK to be sent PThread::Current()->Sleep(2000); H323ControlPDU pdu; H245_CommandMessage & command = pdu.Build(H245_CommandMessage::e_miscellaneousCommand); H245_MiscellaneousCommand & miscCommand = command; miscCommand.m_logicalChannelNumber = (unsigned)channel.GetNumber(); miscCommand.m_type.SetTag(H245_MiscellaneousCommand_type::e_videoTemporalSpatialTradeOff); PASN_Integer & value = miscCommand.m_type; value = videoQuality; WriteControlPDU(pdu); //} } } // adjust the count of channels we have open channelsOpen++; PTRACE(2, "Main\tchannelsOpen = " << channelsOpen); // if we get to two channels (one open, one receive), then (perhaps) trigger a timeout for shutdown // have one channel for video receive, one for video transmit. channelsOpenLimit can be 2,3 or 4. if (channelsOpen == myEndpoint.GetChannelsOpenLimit()) myEndpoint.TriggerDisconnect(); return TRUE; } void MyH323Connection::OnClosedLogicalChannel(H323Channel & channel) { channelsOpen--; H323Connection::OnClosedLogicalChannel(channel); } BOOL MyH323Connection::OnAlerting(const H323SignalPDU & /*alertingPDU*/, const PString & username) { PAssert((myEndpoint.uiState == MyH323EndPoint::uiConnectingCall) || (myEndpoint.uiState == MyH323EndPoint::uiWaitingForAnswer) || !myEndpoint.callTransferCallToken, psprintf("Alerting received in state %i whilst not waiting for incoming call!", myEndpoint.uiState)); if (verbose > 0) cout << "Ringing phone for \"" << username << "\" ..." << endl; myEndpoint.uiState = MyH323EndPoint::uiWaitingForAnswer; return TRUE; } void MyH323Connection::OnUserInputString(const PString & value) { // GnomeMeeting uses UserInputStrings to send text messages between users. // If the string begins "MSG", do not send the string to the LID hardware. if (value.Left(3) == "MSG") { cout << "User message received: \"" << value.Mid(3) << '"' << endl; return; } cout << "User input received: \"" << value << '"' << endl; #ifdef HAS_LIDDEVICE myEndpoint.SendDTMF(value); #endif } PString MyH323Connection::GetCallerIdString() const { H323TransportAddress addr = GetControlChannel().GetRemoteAddress(); PIPSocket::Address ip; WORD port; addr.GetIpAndPort(ip, port); DWORD decimalIp = (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]; PString remotePartyName = GetRemotePartyName(); PINDEX bracket = remotePartyName.Find('['); if (bracket != P_MAX_INDEX) remotePartyName = remotePartyName.Left(bracket); bracket = remotePartyName.Find('('); if (bracket != P_MAX_INDEX) remotePartyName = remotePartyName.Left(bracket); return psprintf("%010li\t\t", decimalIp) + remotePartyName; } ////////////////////////////////////////////////////////////////////// // End of File ///////////////////////////////////////////////////////////////