//////////////////////////////////////////////////////////////////////////////// // // Module: PhoneBase.cpp // // Purpose: // // This sofware module was originally part of the LanScape VOIP Media Engine(tm) // version 5.10 distribution. The LanScape VOIP Media Engine(tm) is a complete drop in // call engine technology designed for all versions of the Microsoft Windows family // of operating systems. // // The updates to this module allow the LanScape VOIP software examples to use // SIP domain, registrar and proxy server support. This file will become part of // the LanScape VOIP Media Engine(tm) version 5.11 distribution when it becomes available. // To see what has changesd in this module from the original v5.10 release, please // perform a diff against this new module using your original source module. // // // Web Site: http://www.lanscapecorp.com // // // Related Keywords: // // Voice over IP, VOIP, SIP, RTP, library, SDK, Software Development. Visual C++, // Visual Studio, Visual Studio .NET, Library SDK, Software Development Kit, // activeX, Vax Voip SDK, VoIP ActiveX SDK. // // // Introduction: // // The LanScape VOIP Media Engine(tm) is a complete SIP/RTP and media handling call // engine development solution for the Microsoft Windows® family of operating // systems. Tailored to meet your needs in stand alone applications and/or as // part of client/server web based environments. The capabilities of the LanScape // VOIP Media Engine(tm) are particularly suited for applications such as Soft Phones, // Conference Servers, Interactive Voice Response systems, Telephony enabled // web sites, PSTN Gateways, and software based PBXs. The LanScape VOIP Media // Engine(tm) performs and excels brilliantly in Wi-Fi and 802.11 wireless ethernet // environments. End user's who integrate the LanScape VOIP Media Engine(tm) into // their architectures purchase a development specific license to deploy the technology. // // If you've ever developed a network telephony application, you already know how // demanding it is to get all of the details correct. From handling SIP protocol // for session initiation to managing media streaming using RTP payloads. The details // associated with session initiation, call states, real-time telephony data // handling/streaming, network level programming, minimizing voice latency, and // the inherent idiosyncrasies of the protocols are exactly the areas that will // burn development time. // // If this is your first time developing a network telephony application, be // prepared for a journey. You will be faced will all of the same issues that // seasoned telephony professionals deal with on a daily basis. Historically, // it has been very difficult to develop all of the required telephony features // you need without completely impacting your development schedule. // // That is, until now. When you decide to integrate the capabilities of the // LanScape VOIP Media Engine(tm) into your telephony design, you are on a fast // track to success. By integrating the capabilities of the LanScape VOIP Media // Engine(tm) into your development, you have instantly leveled the playing field // and are now ready to complete with the big boys in the area of Voice over // IP network telephony. // // Instantly leverage LanScape's knowledge of Voice over IP telephony to your // advantage. No longer do you have to intimately understand SIP and RTP // protocols. Remember the days you spent digging through all those SIP and // RTP protocol packets to find that little bug that caused big headaches? We // feel your pain. // // No longer do you have to develop software to manage call states. Forget about // the format and rate conversion issues you once faced, it's now a thing of the // past. Remember how difficult it was to minimize voice path latency? The list // goes on and on. // // When you integrate the LanScape VOIP Media Engine(tm) into your next telephony // development project, you will be giving yourself the freedom to focus on // the product you are trying to develop. You will be able to concentrate on // your solution and forget about the underlying technology. // // // Features: // // Designed for the Microsoft Windows® family of operating systems - Versions // 9x/Me/NT/2000/XP/2003. // // Initiate phone calls. // // Receive phone calls. // // Place calls on hold. // // Transfer calls to a new destination. // // "Busy out" phone lines individually. // // Perform multiparty conference calling. Conference call members can be // using any of the supported formats and rates. // // Supports phone calls using the following rates and formats: // 8kHz uLaw, 8kHz PCM, 11kHz PCM and 22kHz PCM. // // Complete call state handling. // // Complete access to all telephony media data streams. // // Provides speech recognition interface for command and control applications. // Supports 11kHz PCM and 22kHz PCM voice recognition data streams. // // Complete phone line IVR interfaces. // // Access the inbound media stream for each supported phone line. This allows // applications to perform DSP operations or speech recognition on all phone // line received telephony voice data. // // Access the outbound media stream for each supported phone line. An application // can stream voice data directly to any supported phone line. // // Supports inbound and outbound Digest authentication using MD5 hashing algorithm // for REGISTER, INVITE, BYE, SUBSCRIBE and NOTIY SIP messages. // // Will completely manage host multimedia hardware if required by the application. // // Supports a full compliment of internal telephony sounds. // // Perfect for stand alone Windows applications in addition to client or server // side WEB telephony solutions. // // Stream data to/from local multimedia hardware. // // Incorporates smart RTP transceivers that perform jitter compensation and noise // discrimination/gating of telephony audio data. Incorporates silence // detection/suppression of transmitted audio data to minimize transmit audio // bandwidth requirements. // // Build superior class telephony applications that have small memory // requirements and small operating system demands. // // Only purchase the telephony features you require. // // Instantiate the telephony engine as many times as allowed by licensing requirements. // // Create as many phone lines as allowed by licensing requirements. // // Comes bundled with a family of reference designs/software examples: Multi-line // Conference Server, Dual Line IVR Server, Telephony Echo Server, Dual line // multi-lined soft phone and single line soft phone. // // Complete development documentation. Documentation in Adobe PDF format // and Windows compressed HTML. // // The LanScape VOIP Media Engine(tm) is shipped as a single 32 bit DLL with an // associated API. Language support includes Microsoft Visual C++ version 5 and 6. // However you can use any language that you want assuming you can access exported // DLL functions and describe the telephony API to your language. // // //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "PhoneBase.h" ///////////////////////////////////////////////////////////////////////////// // // Include the Personality Microcode info from the source file and set a // global pointer for easy access. // ///////////////////////////////////////////////////////////////////////////// #include "..\Microcode\LanScapeVME.C" BYTE *pPersonalityMicrocode = LanScapeVME_F186495C_7AFF_4742_ADB7_87EA48A42633; #define DEFAULT_PHONE_NAME "LanScapePhone" // the default name of your media engine. #define ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT 0 // set to a non zero value to enable // support for registrar and proxy support. #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT // SIP proxy support: // #define SIP_PROXY_SERVER_ADDRESS "192.168.1.80" // the IP address or host name of your // SIP proxy server. #define SIP_PROXY_SERVER_PORT 7000 // the server port of the sip proxy server #define SIP_DOMAIN_NAME "lanscapecorp.com" // your SIP domain name. // Registrar support: // #define REGISTRAR_SERVER_ADDRESS "192.168.1.80" // the IP address or host name of your // SIP registrar server. usually the same // as your SIP proxy server. #define REGISTRAR_SERVER_PORT 7000 // the server port the registrar server // is listening on. #define REGISTRATION_INTERVAL_SECONDS 20 // registrations will be sent out at this // periodic rate. #define REGISTRATION_RESPONSE_TIMEOUT_MS 4000 // the expected registration response time. #endif // #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT ////////////////////////////////////////////////////////////////////// // // A user specified callback procedure executed by the telephony media engine // to notify the app of call state changes. Called directly by the // telephony media engine. // // Note: // // This callback procedure's thread context is not the same as // the thread you used to initialize the media engine. Perform // proper thread safe programming as appropriate. // // Also, the telephony engine will serialize its access to this callback // handler. Do not perform lengthy operations in the context of this thread. // ////////////////////////////////////////////////////////////////////// void TelephonyEngineCallback(SIPHANDLE hStateMachine, SIP_NOTIFY_TYPE NotificationType, int PhoneLine, TELEPHONY_RETURN_VALUE TelephonyEvent, void *pUserDefinedData, void *pEventData) { CPhoneBase *pCPhoneBase; SipStatusData *pSipStatusData; // access the user specified data. pCPhoneBase = (CPhoneBase *)pUserDefinedData; // make sure this is our state machine. if(pCPhoneBase->hSipEngine == hStateMachine) { // Determine the type of notification. There are 3 domains of notifications: // // IMMEDIATE_NOTIFICATION: // // This notification type must be handled immediately in this primary // callback procedure. Notifications of this type can not be defered. // // GLOBAL_NOTIFICATION: // // These give an indication of overall telephony engine // operations and/or activity. These notifications can be processed safely // outside of this primary callback. // // PHONE_LINE_NOTIFICATION // // These notifications give you the information you will // need to manage the phone line(s). These notifications can be // processed safely outside of this primary callback. // // For GLOBAL_NOTIFICATION and PHONE_LINE_NOTIFICATION event groups, LanScape recommends // that you queue all required event data to a fifo that your primary event thread will // read from to perform final event processing. You may however perfrom all processing in // this promary callback procedure if you desire. // if(NotificationType == IMMEDIATE_NOTIFICATION) { switch(TelephonyEvent) { ////////////////////////////////////////// // Inbound Event subscriptions. ////////////////////////////////////////// case SipSubscriptionReceived: { // somebody is asking us to allow them to subscribe to an event we are // offering. SUBSCRIBE_REQUEST *pSubscribeRequest; // access the event subscription request data. pSubscribeRequest = (SUBSCRIBE_REQUEST *)pEventData; if(pSubscribeRequest) { // see if the event is one our application wants to support. if(1) { // we will suport sending this event to the requestor. allow the // media engine to send back a successful ststtua. pSubscribeRequest->AcceptRequest = TRUE; } else { // we do not support this event. allow the media engine // to send back an error. pSubscribeRequest->AcceptRequest = FALSE; } } } break; ////////////////////////////////////////// // Outbound Event subscriptions. ////////////////////////////////////////// case SipSubscriptionTrying: { // we are attempting to subscribe to events offered by another // device. we have not received a response. SUBSCRIBE_RESULTS *pSubscribeResults; // access the event subscription request data. pSubscribeResults = (SUBSCRIBE_RESULTS *)pEventData; if(pSubscribeResults) { // perform additional processing here for your application } } break; case SipSubscriptionMemoryError: { // we are attempting to subscribe to events offered by another // device. the event sunscription faild because this machine // experienced a memory failure. SUBSCRIBE_RESULTS *pSubscribeResults; // access the event subscription request data. pSubscribeResults = (SUBSCRIBE_RESULTS *)pEventData; if(pSubscribeResults) { // perform additional processing here for your application } } break; case SipSubscriptionSuccess: { // we are now registered to receive events from the far end device. SUBSCRIBE_RESULTS *pSubscribeResults; // access the event subscription request data. pSubscribeResults = (SUBSCRIBE_RESULTS *)pEventData; if(pSubscribeResults) { // perform additional processing here for your application } } break; case SipSubscriptionTimeOut: { // we were attempting to subscribe to events offered by another // device. we timed out waiting for a response. SUBSCRIBE_RESULTS *pSubscribeResults; // access the event subscription request data. pSubscribeResults = (SUBSCRIBE_RESULTS *)pEventData; if(pSubscribeResults) { // perform additional processing here for your application } } break; case SipSubscriptionNotAccepted: { // the event we tried to subscribe to was not accepted by the far end. SUBSCRIBE_RESULTS *pSubscribeResults; // access the event subscription request data. pSubscribeResults = (SUBSCRIBE_RESULTS *)pEventData; if(pSubscribeResults) { // perform additional processing here for your application } } break; ////////////////////////////////////////// // Inbound Event Notifications. ////////////////////////////////////////// case SipEventNotifyReceived: { // a far end device has send us an event we previously registered for. NOTIFY_REQUEST *pNotifyRequest; // access the event data. pNotifyRequest = (NOTIFY_REQUEST *)pEventData; if(pNotifyRequest) { // perform additional processing here for your application } } break; default: // do nothing. break; } } else { // Handle GLOBAL_NOTIFICATION, and PHONE_LINE_NOTIFICATION event groups. // Events of these types can be deferred. // // We use a technique here that posts the event information to // another thread. that thread in turn updates the GUI and performs additional // processing for this test app. // pSipStatusData = new SipStatusData( hStateMachine, NotificationType, PhoneLine, TelephonyEvent, pUserDefinedData); if(pSipStatusData) { // forward the info to the status thread. pCPhoneBase->StatusFifo.FifoWrite(pSipStatusData); } } } } ////////////////////////////////////////////////////////////////////////// // // This is the actual event handler we use for processing the telephony engine // event notifications. // ////////////////////////////////////////////////////////////////////////// static void EventThreadCallback(void *pUserDefinedData) { SIP_NOTIFY_TYPE NotificationType; int PhoneLine; TELEPHONY_RETURN_VALUE SipStatus; CPhoneBase *pCPhoneBase; SipStatusData *pSipStatusData; BOOL *pHaltRequest; SIPHANDLE hSip; LINE_STATE LineState; // access our data. pCPhoneBase = (CPhoneBase *)pUserDefinedData; pHaltRequest = &(pCPhoneBase->EventThread.HaltThreadReq); // set the initial status of the phone lines. // for(PhoneLine=0;PhoneLinePhoneLineStatus[PhoneLine] = SipOnHook; } // make sure fifo is empty when we start. pCPhoneBase->StatusFifo.EmptyFifo(); // run until terminated. while(!(*pHaltRequest)) { // wait for call status to arrive. pSipStatusData = pCPhoneBase->StatusFifo.FifoRead(); if(pSipStatusData) { // see if we were terminated. if(!(*pHaltRequest)) { // break out needed data. hSip = pSipStatusData->hSip; NotificationType = pSipStatusData->NotificationType; PhoneLine = pSipStatusData->PhoneLine; SipStatus = pSipStatusData->SipStatus; // determine the type of notification. There are two domains of notifications. // // Global notifications: // // These give an indication of overall SIP state machine // operations and/or activity. // // Phone line specific state changes: // // These notifications give you the information you will // need to manage the phone line(s). // if(NotificationType == GLOBAL_NOTIFICATION) { switch(SipStatus) { case SipCallEngineReady: // the telephony engine has been created. break; case SipCallEngineTerminated: // the telephony engine has been terminated. break; case SipLineCapacityReached: // the telephony engine detected a new incoming phone call // but all phone lines were in use. break; case SipRegisterTrying: // attempting to register with a proxy or // sip registrar server. break; case SipRegisterSuccess: // registration was successful. break; case SipRegistrationIntervalError: // registration time interval that was specified was // rejected by the server. the interval is too small. // registration failed. break; case SipRegistrationTimeOut: // we timed out waiting for the registration response to be // sent back from the server. break; case SipRegisterError: // unknown error. registration failed. break; default: // do nothing. break; } } else { // handle this event as a phone line notification. // if(PhoneLine >= MAX_PHONE_LINES_SUPPORTED) { // this is an event notification for a phone line that // was created by the telephony engine but is not supported by // this app. just ignore it. continue; } // save current line status. pCPhoneBase->PhoneLineStatus[PhoneLine] = SipStatus; // get the current line state so we know the direction of the call. SipStatus = GetLineStatus(pCPhoneBase->hSipEngine,PhoneLine,&LineState); if(SipStatus == SipSuccess) { switch(pCPhoneBase->PhoneLineStatus[PhoneLine]) { case SipTransferingCall: // turn the call indicator ON. pCPhoneBase->CallIndicatorOn(pCPhoneBase->hSipEngine,PhoneLine); if(PhoneLine == 0) { pCPhoneBase->FarEndHoldLine0 = FALSE; } else if(PhoneLine == 1) { pCPhoneBase->FarEndHoldLine1 = FALSE; } break; case SipIncomingCallStart: pCPhoneBase->IncomingCallStart(pCPhoneBase->hSipEngine,PhoneLine); break; case SipIncomingCallConnected: // the inbound call is now connected. pCPhoneBase->NumReceivedCalls++; pCPhoneBase->IncomingCallConnected(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipOutgoingCallStart: // a new outgoing call is starting. display info // about who we are attempting to call. pCPhoneBase->OutgoingCallStart(pCPhoneBase->hSipEngine,PhoneLine); break; case SipOutgoingCallConnected: // the outboound call is now connected. pCPhoneBase->NumCallsPlaced++; pCPhoneBase->OutgoingCallConnected(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipInCall: // the phone line is now actively in a call. pCPhoneBase->PhoneCallActive(pCPhoneBase->hSipEngine,PhoneLine); break; case SipOkToAnswerCall: // see if auto answer is enabled. if it is, we answer the // current incoming call. all other calls are placed on hold. pCPhoneBase->OkToAnswerCall(pCPhoneBase->hSipEngine,PhoneLine); break; case SipCallHoldOn: // the user has put the call on hold at this end. // pCPhoneBase->CallHoldOn(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipCallHold: // the line is in the "on hold" state. break; case SipCallHoldOff: // the user has taken the call out of hold at this end. // pCPhoneBase->CallHoldOff(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipFarEndHoldOn: // the far end has put us on hold. at this time, our rtp // transmitter for this phone line is disabled and transmitted // rtp data from this end is consuming network bandwidth. our rtp // receiver is still accepting data however. pCPhoneBase->FarEndHoldOn(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipFarEndHoldOff: // the far end has removed us from "on hold". our rtp // tranceiver for this phone line will be enabled if we // are not locally held as well. pCPhoneBase->FarEndHoldOn(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipBusyOutOn: pCPhoneBase->BusyOutOn(pCPhoneBase->hSipEngine,PhoneLine); break; case SipBusyOut: // the line is now busied out. break; case SipBusyOutOff: pCPhoneBase->BusyOutOff(pCPhoneBase->hSipEngine,PhoneLine); break; case SipInConferenceOn: pCPhoneBase->InConferenceOn(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipInConference: // the line is now participating in a conference session. break; case SipInConferenceOff: pCPhoneBase->InConferenceOff(pCPhoneBase->hSipEngine,PhoneLine,&LineState); break; case SipByeReceived: pCPhoneBase->ByeReceived(pCPhoneBase->hSipEngine,PhoneLine); break; case SipSendBye: pCPhoneBase->SendBye(pCPhoneBase->hSipEngine,PhoneLine); break; case SipCallComplete: pCPhoneBase->CallComplete(pCPhoneBase->hSipEngine,PhoneLine); if(PhoneLine == 0) { pCPhoneBase->FarEndHoldLine0 = FALSE; } else if(PhoneLine == 1) { pCPhoneBase->FarEndHoldLine1 = FALSE; } break; case SipCallCanceled: pCPhoneBase->CallCanceled(pCPhoneBase->hSipEngine,PhoneLine); break; case SipOffHook: pCPhoneBase->OffHook(pCPhoneBase->hSipEngine,PhoneLine); break; case SipOnHook: pCPhoneBase->OnHook(pCPhoneBase->hSipEngine,PhoneLine); break; case SipFarEndIsBusy: // the far end is busy. pCPhoneBase->NumBusyCalls++; pCPhoneBase->FarEndIsBusy(pCPhoneBase->hSipEngine,PhoneLine); break; case SipInviteAckNotReceived: // we never received the final ACK from the far end. pCPhoneBase->InviteAckNotReceived(pCPhoneBase->hSipEngine,PhoneLine); break; case SipFarEndError: // the far end had an error. pCPhoneBase->FarEndError(pCPhoneBase->hSipEngine,PhoneLine); break; case SipCallTimeOut: // time out attempting to connect. pCPhoneBase->NumCallTimeouts++; break; case SipCallFailure: // the call completely failed. pCPhoneBase->NumCallsFailed++; break; default: // do nothing. break; } } } } delete pSipStatusData; pSipStatusData = 0; } } // make sure fifo is empty when we terminate. pCPhoneBase->StatusFifo.EmptyFifo(); } ////////////////////////////////////////////////////////////////////////// // // A callback thread procedure that handles all details associated with // starting the telephony engine. // ////////////////////////////////////////////////////////////////////////// static void StartUpCallback(void *pUserDefinedData) { TELEPHONY_RETURN_VALUE status; CPhoneBase *pCPhoneBase; START_SIP_TELEPHONY_PARAMS StartupParams; BOOL FatalError = FALSE; int PhoneLine; CString tmp; // access the user specified data. pCPhoneBase = (CPhoneBase *)pUserDefinedData; // start progress indicator. tmp.Format("Starting %d line telephony engine...",pCPhoneBase->NumPhoneLinesRequested); pCPhoneBase->StartProgressIndicator(CSPTR(tmp)); // start the thread that will handle telephony events. if(!pCPhoneBase->StartEventThread()) { pCPhoneBase->pParentWnd->MessageBox( "Error: The event thread could not be started.", "Initialization Error", MB_ERROR); FatalError = TRUE; } else { // initialize the media engine. this is always the first // telephony engine API procedure to call. status = InitializeMediaEngine(NULL); if(status != SipSuccess) { pCPhoneBase->pParentWnd->MessageBox( "Error: Telephony engine could not be initialized.", "Initialization Error", MB_ERROR); FatalError = TRUE; } else { // attempt to create a telephony engine that can support the number of lines requested. // // Note: // // Depending on what telephony engine options you purchased, you will know // before hand the maximum number of phone lines supported by your telephony engine // and what options are enabled (i.e. conferencing, call hold, transfer, etc). // // We first attempt to create an instance of the telephony engine that will // support the number of phone lines requested. If that fails, we reduce the // phone line count by one and attempt to instantiate the telephony engine again. // We continue this instantiation loop until we are successful or our requested phone // line count goes to zero. // StartupParams.pPersonalityMicrocode = pPersonalityMicrocode; StartupParams.LineMode = pCPhoneBase->LineMode; StartupParams.UserNotifyCallbackProc = TelephonyEngineCallback; StartupParams.pUserDefinedData = pCPhoneBase; memcpy(StartupParams.IpAddressOfThisHost,pCPhoneBase->IpAddressThisMachine,NUM_BYTES_IP_ADDRESS); StartupParams.SipPort = DEFAULT_SIP_PORT; StartupParams.pPhoneName = DEFAULT_PHONE_NAME; StartupParams.pPhoneDisplayName = "LanScape Phone"; StartupParams.MinLocalRtpPort = 8000; StartupParams.MaxLocalRtpPort = 8020; if(pCPhoneBase->EnableInternalAudio) { // allow the telephony engine to manage record and playback. StartupParams.ZeroBasedAudioDeviceId = SIP_USE_PREFERED_AUDIO_DEVICE; StartupParams.AudioRecordBandWidth = AUDIO_BW_PCM_22K; StartupParams.AudioPlaybackBandWidth = AUDIO_BW_PCM_22K; } else { // disable telephony engine audio record and playback. StartupParams.ZeroBasedAudioDeviceId = SIP_AUDIO_DEVICE_NOT_USED; StartupParams.AudioRecordBandWidth = AUDIO_BW_UNDEFINED; StartupParams.AudioPlaybackBandWidth = AUDIO_BW_UNDEFINED; } StartupParams.FarEndCallTransferEnabled = pCPhoneBase->FarEndCallTransferEnabled; // allow internal phone voice paths to be double buffered. Always set this // parameter to a value of 2 or greater. StartupParams.PlaybackBufferingDefault = 2; // allow internal telephony sounds and the audio output interface to be quad // buffered. If you have a fast machine (faster than a Pentuim II 450Mhz), use // double buffering (set the value to 2). Always set this // parameter to a value of 2 or greater. StartupParams.PlaybackBufferingDuringSounds = 4; // allow transmitted phone line sample data blocks to be double // buffered before transmission. Always set this // parameter to a value of 2 or greater. StartupParams.PhoneLineTransmitBuffering = 2; StartupParams.LogSipMessages = TRUE; StartupParams.pSipLogFileName = "SipMessageLog.log"; StartupParams.StartupFlags = ENABLE_START_AUDIO_SPLASH; // loop to instantiate the telephony engine until we // are successful. StartupParams.NumPhoneLinesRequested = pCPhoneBase->NumPhoneLinesRequested; for(;(StartupParams.NumPhoneLinesRequested > 0)&& !pCPhoneBase->Terminating;) { // start the telephony engine. status = StartSipTelephony(&StartupParams,&(pCPhoneBase->hSipEngine)); // see if the number of phone lines we requested is supported. if(status == SipSuccess) { pCPhoneBase->NumPhoneLinesCreated = StartupParams.NumPhoneLinesRequested; break; } else if(status == SipBadNumberOfPhoneLines) { tmp.Format("Your telephony engine does not support %d phone lines.",StartupParams.NumPhoneLinesRequested); pCPhoneBase->UpdateProgressBar(CSPTR(tmp)); Sleep(3000); // reduce the number of lines requested. StartupParams.NumPhoneLinesRequested--; tmp.Format("Starting %d line telephony engine...",StartupParams.NumPhoneLinesRequested); pCPhoneBase->UpdateProgressBar(CSPTR(tmp)); } else if(status == SipInvalidInstance) { tmp.Format("Too many instances of the telephony engine are currently running."); pCPhoneBase->NumPhoneLinesCreated = 0; pCPhoneBase->UpdateProgressBar(CSPTR(tmp)); break; } else if(status == SipCallFailure) { FatalError = TRUE; pCPhoneBase->pParentWnd->MessageBox( "A fatal error was detected while booting this example application.\n" "Before you build and test these application examples, make sure\n" "you copy your \"Media Engine Microcode/Personality Data\" file into\n" "the \\Software Examples\\Microcode folder. The media engine microcode file\n" "you received is named LanScapeVME.C. You should have received this file on\n" "a 3.5\" floppy disk image that came along with your installation CDROM.", "Example Application Error", MB_ERROR); break; } } if(!FatalError) { for(PhoneLine=0;PhoneLineNumPhoneLinesCreated;PhoneLine++) { // give the user interface a chance to enable controls. pCPhoneBase->EnableInterfaceControls(PhoneLine,TRUE); } // check for errors. if(status != SipSuccess) { // display the error and exit this app. pCPhoneBase->CallErrorHandler(status); FatalError = TRUE; } else { // update line status. for(PhoneLine=0;PhoneLineNumPhoneLinesCreated;PhoneLine++) { pCPhoneBase->UpdateStatus(PhoneLine,"On Hook"); } // the telephony engine was created ok. perform minimal setup. // // allow the call engine to increase our process priority when // a call is active. // // Note: // // Normally you will not have to specify an increase in the process priority // that is managed by the telephony engine. If your telephony development // will be used on host machines that are highly stressed, you will obtain // better multimedia performance if you allow the telephony engine to enter either // the high priority or the realtime priority states. // // the priority value specified here could also be "PriorityHigh". SetInCallProcessPriority(pCPhoneBase->hSipEngine,PriorityRealtime); // enble noise discrimination. SetSilenceDecay(pCPhoneBase->hSipEngine,300); SetNoiseThreshold(pCPhoneBase->hSipEngine,200); SetNoiseDiscriminationEnableState(pCPhoneBase->hSipEngine,TRUE); // set the data rate and format of line data. If you are // using this example app to talk to other soft phones or // desktop IP phones, use uLaw sample data. If you are using this // app to talk to another instance of this app running on another // machine, try different line data types so you can hear the // sonic difference. pCPhoneBase->SetDataFormatOfAllPhoneLines( pCPhoneBase->NumPhoneLinesCreated, pCPhoneBase->PhoneLineMediaType); // set volume gain of the phone lines. pCPhoneBase->InitPhoneLineVolume(); // call into the parent class just in case they want to do some extra // work before we enable the engine. pCPhoneBase->InitializationComplete(pCPhoneBase->hSipEngine); // enable the VOIP Media Engine's capability to receive network // data. SipTelephonyEnable(pCPhoneBase->hSipEngine); #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT // Note: SIP domain, proxy and registrar support. // // These last steps are used if you are going to deploy SIP // registrar and proxy server support. For most VOIP deployments // you will need to use SIP proxy and registrar functions. // // set the sip domain name for proxy and registrar functions. EnableSipDomain(pCPhoneBase->hSipEngine,SIP_DOMAIN_NAME); // set our sip proxy info. status = EnableSipProxyServer( pCPhoneBase->hSipEngine, SIP_PROXY_SERVER_ADDRESS, SIP_PROXY_SERVER_PORT ); if(status == SipSuccess) { // no errors. } else if(status == SipUnknownHost) { pCPhoneBase->pParentWnd->MessageBox( "The sip proxy you have configured could not be found.", "Sip Proxy Error", MB_ERROR); } else { pCPhoneBase->pParentWnd->MessageBox( "A fatal error was detected while initializing sip proxy support.", "Sip Proxy Error", MB_ERROR); } if(status == SipSuccess) { // register with the registrar. we assume here that the SIP proxy // supports registrar functiuons. status = EnableSipRegisterServer( pCPhoneBase->hSipEngine, DEFAULT_PHONE_NAME, FALSE, REGISTRAR_SERVER_ADDRESS, REGISTRAR_SERVER_PORT, REGISTRATION_INTERVAL_SECONDS, REGISTRATION_INTERVAL_SECONDS, REGISTRATION_RESPONSE_TIMEOUT_MS, FALSE ); switch(status) { case SipSuccess: // success. we are registerd. break; case SipDomainNameNotDefined: // the app must first specify the sip domain name before // attempting to start registrar services. The app needs to call // the "Enable Sip Domain" API. pCPhoneBase->pParentWnd->MessageBox( "SIP registrar services can not be started. A SIP domain name\n" "has not yet beed defined.", "Sip Domain Name Error", MB_ERROR); break; case SipRegistrationIntervalError: // the server wants a larger registration interval. pCPhoneBase->pParentWnd->MessageBox( "The sip registration server requires a larger register interval.", "Sip Registration Server Error", MB_ERROR); break; case SipUnknownHost: // the server does not exist. pCPhoneBase->pParentWnd->MessageBox( "The sip registration server you have configured could not be found.", "Sip Registration Server Error", MB_ERROR); break; case SipRegistrationTimeOut: // we never received a response back from the server. pCPhoneBase->pParentWnd->MessageBox( "We timed out waiting for the sip registration server to respond.", "Sip Registration Server Error", MB_ERROR); break; case SipRegisterAuthorizationError: pCPhoneBase->pParentWnd->MessageBox( "One or more of the servers you are attempting to register with requires\n" "authentication credentials. Please configure your authentication settings\n" "and try reregistering with the server again.", "Authentication Error", MB_ERROR); break; case SipRegisterError: default: // other unknown errors pCPhoneBase->pParentWnd->MessageBox( "A fatal error was detected while attempting to contact the\n" "configured SIP registration server. Registration functions will\n" "not be available.", "Sip Registration Server Error", MB_ERROR); break; } } #endif // #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT } } } } // stop progress indicator. pCPhoneBase->StopProgressIndicator(); if(!pCPhoneBase->Terminating) { // hide progress indicator. pCPhoneBase->HideProgressIndicator(); } if(FatalError) { // terminate this application. pCPhoneBase->pParentWnd->PostMessage(WM_CLOSE,0,0); } // make sure this thread terminates. pCPhoneBase->StartStopThread.HaltThreadReq = TRUE; } ////////////////////////////////////////////////////////////////////////// // // A callback thread procedure that handles all details associated with // terminating the telephony engine. // ////////////////////////////////////////////////////////////////////////// static void TerminateCallback(void *pUserDefinedData) { BOOL ret = FALSE; TELEPHONY_RETURN_VALUE status; CPhoneBase *pCPhoneBase; // access the user specified data. pCPhoneBase = (CPhoneBase *)pUserDefinedData; // show and start the progress indicator. pCPhoneBase->ShowProgressIndicator(); pCPhoneBase->StartProgressIndicator("Stopping the telephony engine..."); if(pCPhoneBase->hSipEngine) { #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT // unregister with the registrar server. DisableSipRegisterServer(pCPhoneBase->hSipEngine); // disable the use of SIP proxy functions. DisableSipProxyServer(pCPhoneBase->hSipEngine); #endif // #if ENABLE_SIP_REGISTRAR_AND_PROXY_SUPPORT // tell the LanScape VOIP Media Engine to terminate. // if there are any calls active, they are terminated // before exit. status = StopSipTelephony(pCPhoneBase->hSipEngine); if(status == SipSuccess) { pCPhoneBase->hSipEngine = 0; } else { pCPhoneBase->pParentWnd->MessageBox( "Error: Telephony Engine termination error.", "Termination Error", MB_ERROR); } } // terminate our event thread. pCPhoneBase->StopEventThread(); // stop and hide progress indicator. pCPhoneBase->StopProgressIndicator(); pCPhoneBase->HideProgressIndicator(); // uninitialize the media engine. this is always the last // telephony engine API procedure to call. UnInitializeMediaEngine(); } CPhoneBase::CPhoneBase() { NumPhoneLinesRequested = 0; LineMode = PHONE_LINE; EnableInternalAudio = TRUE; FarEndCallTransferEnabled = TRUE; Terminating = FALSE; pParentWnd = 0; hSipEngine = 0; NumPhoneLinesCreated = 0; AutoAnswerEnabled = FALSE; NumCallsPlaced = 0; NumCallsFailed = 0; NumBusyCalls = 0; NumCallTimeouts = 0; NumReceivedCalls = 0; FarEndHoldLine0 = FALSE; FarEndHoldLine1 = FALSE; PhoneLineVolume0 = DEFAULT_PHONE_LINE_VOLUME; PhoneLineVolume1 = DEFAULT_PHONE_LINE_VOLUME; PhoneLineMediaType = AUDIO_BW_MULAW_8K; } CPhoneBase::~CPhoneBase() { } void CPhoneBase::SetParentCWnd(CWnd *pCWnd) { pParentWnd = pCWnd; } ////////////////////////////////////////////////////////////////////////// // // Start a worker thread that will handle all details associated with // booting up the telephony engine. // ////////////////////////////////////////////////////////////////////////// BOOL CPhoneBase::StartTelephonyEngine(int _NumPhoneLinesRequested, LINE_MODE _LineMode, BOOL _EnableInternalAudio, BOOL _FarEndCallTransferEnabled) { BOOL ret = FALSE; NumPhoneLinesRequested = _NumPhoneLinesRequested; LineMode = _LineMode; EnableInternalAudio = _EnableInternalAudio; FarEndCallTransferEnabled = _FarEndCallTransferEnabled; // start the worker thread. if(StartStopThread.StartThread(StartUpCallback,this,1000,TRUE) == THREAD_SUCCESS) { ret = TRUE; } return(ret); } ////////////////////////////////////////////////////////////////////////// // // Start a worker thread that will handle all details associated with // terminating the telephony engine. // ////////////////////////////////////////////////////////////////////////// BOOL CPhoneBase::StopTelephonyEngine(void) { BOOL ret = FALSE; Terminating = TRUE; // make sure the boot initialization is completed before we try to // take things down. if(StartStopThread.ThreadStarted) { UpdateProgressIndicator("Waiting to terminate telephony functions..."); // wait for initialization to complete. StartStopThread.HaltThread(); } // start the worker thread. if(StartStopThread.StartThread(TerminateCallback,this,1000,TRUE) == THREAD_SUCCESS) { // wait for the thread to complete its tasks. StartStopThread.HaltThread(); ret= TRUE; } return(ret); } ////////////////////////////////////////////////////////////////////////// // // Set volume gain of the phone lines and notify the parent class // of volume settings. // ////////////////////////////////////////////////////////////////////////// void CPhoneBase::InitPhoneLineVolume(void) { int PhoneLine; if(hSipEngine) { for(PhoneLine=0;PhoneLine