#include "StdAfx.h" #include "VoipChannel.h" #include "ControlShell.h" #include "Config.h" #include "MC.h" #include "ProxyShell.h" #include "ProxyExten.h" #include "IpmChannel.h" #include "DspChannel.h" #include "SessionShell.h" CVoipChannel::CImplement4Trunk CVoipChannel::ms_Implement4Trunk; CVoipChannel::CImplement4Exten CVoipChannel::ms_Implement4Exten; /***************************************************************** **【函数名称】 __constructMakeCallBody **【函数功能】 构造呼叫SIP协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Trunk::__constructMakeCallBody( GCPARAMEX_MAKECALL* pMakeCallEx, CVoipChannel& Chan, CIpmChannel* pImpChan, SIP_ACCOUNT* pAccount ) { ASSERT(pImpChan != NULL); int nIndex; pMakeCallEx->Protocol = eGCPro_SIP; #pragma region To,From,contact,Auth pMakeCallEx->u.SIPInviteExReq.To.Valid = 1; pMakeCallEx->u.SIPInviteExReq.From.Valid = 1; ISXSIP_IE_ADDR* pMakeCallTo = &pMakeCallEx->u.SIPInviteExReq.To.Addr; ISXSIP_IE_ADDR* pMakeCallFrom = &pMakeCallEx->u.SIPInviteExReq.From.Addr; //TO域 pMakeCallTo->Valid = 1; pMakeCallTo->AddType= ISXSIP_ADDTYPE_URL; pMakeCallTo->u.AddrUrl.PortNum = pAccount->ProxyPort; pMakeCallTo->u.AddrUrl.Ttl = 0xFFFF; pMakeCallTo->u.AddrUrl.LrType = 0xFF; pMakeCallTo->u.AddrUrl.MethodType = 0xFF; lstrcpy(pMakeCallTo->u.AddrUrl.User, Chan.m_CalleeNum); lstrcpy(pMakeCallTo->u.AddrUrl.Host, pAccount->Proxy); //FROM域 pMakeCallFrom->Valid = 1; pMakeCallFrom->AddType = ISXSIP_ADDTYPE_URL; pMakeCallFrom->u.AddrUrl.PortNum = 0xFFFF; pMakeCallFrom->u.AddrUrl.Ttl = 0xFFFF; pMakeCallFrom->u.AddrUrl.LrType = 0xFF; pMakeCallFrom->u.AddrUrl.MethodType = 0xFF; lstrcpy(pMakeCallFrom->u.AddrUrl.User, Chan.m_CallerNum); lstrcpy(pMakeCallFrom->u.AddrUrl.Host, pAccount->Proxy); // CONTACT域 ISXSIP_IE_REFERRED_BY *pReferBy = &pMakeCallEx->u.SIPInviteExReq.RefBy; pReferBy->Valid = 2; ISXSIP_IE_ADDR_EX *pContact = &pReferBy->cont_ruri.Contact; pContact->AddType = ISXSIP_ADDTYPE_URL; ISX_sr_default(PARMID_ISXSIP_ADDR_URL_EX, &pContact->u.AddrUrl); lstrcpy(pContact->u.AddrUrl.User, pAccount->Account); lstrcpy(pContact->u.AddrUrl.Host, CConfig::voipSignallingAddr()); ISXSIP_IE_ADDR_EX *pRequestURI = &pReferBy->cont_ruri.RequestURI; pRequestURI->AddType = ISXSIP_ADDTYPE_URL; ISX_sr_default(PARMID_ISXSIP_ADDR_URL_EX, &pRequestURI->u.AddrUrl); lstrcpy(pRequestURI->u.AddrUrl.User, Chan.m_CalleeNum); lstrcpy(pRequestURI->u.AddrUrl.Host, pAccount->Proxy); //Auth char* pUser = pMakeCallEx->u.SIPInviteExReq.LongAuthInfo.AuthStr; ZeroMemory(pUser, 100); lstrcpy(pUser, pAccount->AuthAccount); char *pPwd = pUser + strlen(pUser) + 1; lstrcpy(pPwd, pAccount->Password); #pragma endregion #pragma region Media Part TCHAR MediaAddr[MAX_PATH] = { 0 }; getMediaAddr(pImpChan, MediaAddr); pMakeCallEx->u.SIPInviteExReq.Body.Valid = 1; pMakeCallEx->u.SIPInviteExReq.Body.BodyPartyType = BodyPartyType_SDP; ISXSIP_IE_BODY_SDP_EX* pSdp = &pMakeCallEx->u.SIPInviteExReq.Body.u.BodySdp; pSdp->SdpNum = 0; //o nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Connection.Addr, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m int iCodecId; int iPayLoad; ISXSIP_IE_SDP_MEDIA_DESC_EX DspMediaDescEx; DspMediaDescEx.MediaPort = pImpChan->mediaPort(); DspMediaDescEx.MediaProtocol = ISXSIP_SDPPROTOCOL_RTP; DspMediaDescEx.MediaType = ISXSIP_SDPMEDIATYPE_AUDIO; int iPLIndex = 0; iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; iCodecId = AudioCodec_G729_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; DspMediaDescEx.Payload[iPLIndex++] = 101; DspMediaDescEx.PayLoadNum = iPLIndex; ISX_sr_insertparm(&pMakeCallEx->u.SIPInviteExReq.Body, SIPPARMID_MEDIADESC, sizeof(DspMediaDescEx), &DspMediaDescEx); IP_AUDIO_CAPABILITY AudioCodec; AudioCodec.iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_insertparm(&pMakeCallEx->u.SIPInviteExReq.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); AudioCodec.iCodecId = AudioCodec_G729_20MS; ISX_sr_insertparm(&pMakeCallEx->u.SIPInviteExReq.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_RTP_MAP; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_RTP_MAP); ISXSIP_IE_SDP_RTP_MAP* pSdpRtpMap = &pSdp->Sdp[nIndex].u.RtpMap; pSdpRtpMap->Payload = 101; pSdpRtpMap->ClkRate = 8000; lstrcpy(pSdpRtpMap->EncodingName, "telephone-event"); lstrcpy(pSdpRtpMap->EncodingParam, "1"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "fmtp"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "101 0-15"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "ptime"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "20"); pSdp->SdpNum++; #pragma endregion } /***************************************************************** **【函数名称】 __onOffered **【函数功能】 处理来电呼叫 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Trunk::onOffered( CVoipChannel& Chan, GCPARAMEX_MAKECALL* pMakeCallEx ) { CSessionShell::GetInstance().onLineStateChanged(Chan.m_Id, Chan.m_SessionCode, VIRTUAL_LINE_STATE_ALERTING, Chan.m_CallerNum, Chan.m_CalleeNum); //发送TRY ISX_gc_SIPSendTrying(Chan.m_CallId, EV_ASYNC); ASSERT(Chan.m_pBindIpmCh == NULL); if((Chan.m_pBindIpmCh = CMC::GetInstance().allocIpmCh4SipCh(Chan.m_Id.NodeNo, Chan.m_Id.BoardNo)) == NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理中继来电出错, 无可用的IPM资源, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus486BusyHere); return; } Chan.m_pBindIpmCh->bind(&Chan); // 协商媒体编解码 if(!Chan.__getCallMediaDesc(&pMakeCallEx->u.SIPInvite.Body)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理中继来电时协商媒体编解码失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus415UnsupportedMediaType); return; } // 启动媒体 if(!Chan.m_pBindIpmCh->isStarted()) { if(!Chan.m_pBindIpmCh->startMedia()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理中继来电时IPM资源启动失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } } GCPARAMEX_ANSWERCALL AnswerCallEx; Chan.__constructAnswerBody(&AnswerCallEx); if(ISX_gc_AnswerCall(Chan.m_CallId, 0, EV_ASYNC, &AnswerCallEx) != 0) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]应答中继来电失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus486BusyHere); } } /***************************************************************** **【函数名称】 onAlerting **【函数功能】 处理远端振铃 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Trunk::onAlerting( CVoipChannel& Chan, METAEVENT* pMetaEvent ) { GCPARAMEX_ACCEPTCALL* pAcceptCall = (GCPARAMEX_ACCEPTCALL*)ISX_sr_getevtdatap(); ASSERT(pAcceptCall != NULL); if(pAcceptCall->u.SIPRing.Body.Valid == 1) { // 协商媒体编解码 if(!Chan.__getCallMediaDesc(&pAcceptCall->u.SIPRing.Body)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理Alerting时协商媒体编解码失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus415UnsupportedMediaType); return; } // 启动媒体 ASSERT(Chan.m_pBindIpmCh != NULL); if(!Chan.m_pBindIpmCh->isStarted()) { if(!Chan.m_pBindIpmCh->startMedia()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理Alerting时IPM资源启动失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } } CSessionShell::GetInstance().onLineStateChanged(Chan.m_Id, Chan.m_SessionCode, VIRTUAL_LINE_STATE_PROGRESSING, Chan.m_CallerNum, Chan.m_CalleeNum); } CSessionShell::GetInstance().onLineStateChanged(Chan.m_Id, Chan.m_SessionCode, VIRTUAL_LINE_STATE_RING_BACK, Chan.m_CallerNum, Chan.m_CalleeNum); } /***************************************************************** **【函数名称】 makeCall **【函数功能】 呼叫 **【参数】 **【返回值】 成功true,失败false ****************************************************************/ bool CVoipChannel::CImplement4Trunk::makeCall( CVoipChannel& Chan, int AccountId ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]中继模式下执行呼叫请求, caller = %s, callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); SIP_ACCOUNT* pAccount = CConfig::sipAccount().getAccount(AccountId); ASSERT(pAccount != NULL); if(pAccount == NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]进行中继呼叫时查找SIP账户[%d]失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, AccountId, Chan.m_CallerNum, Chan.m_CalleeNum); return false; } ASSERT(Chan.m_pBindIpmCh == NULL); if((Chan.m_pBindIpmCh = CMC::GetInstance().allocIpmCh4SipCh(Chan.m_Id.NodeNo, Chan.m_Id.BoardNo)) == NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]进行中继呼叫时分配IPM资源失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); return false; } Chan.m_pBindIpmCh->bind(&Chan); GCPARAMEX_MAKECALL MakeCallEx; ISX_sr_default(PARMID_SIP_INVITE, &MakeCallEx.u.SIPInviteExReq); __constructMakeCallBody(&MakeCallEx, Chan, Chan.m_pBindIpmCh, pAccount); return ISXE_gc_MakeCall(Chan.m_Handle, &Chan.m_CallId, NULL, NULL, -1, EV_ASYNC, &MakeCallEx) == 0; } /***************************************************************** **【函数名称】 getMediaAddr **【函数功能】 获取媒体地址 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Trunk::getMediaAddr( CIpmChannel* pImpChan, LPTSTR Buffer ) { ASSERT(pImpChan != NULL); pImpChan->mediaAddrTrunk(Buffer); } /***************************************************************** **【函数名称】 __constructMakeCallBody **【函数功能】 构造呼叫SIP协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Exten::__constructMakeCallBody( GCPARAMEX_MAKECALL* pMakeCallEx, CIpmChannel* pImpChan ) { ASSERT(pImpChan != NULL); TCHAR MediaAddr[MAX_PATH] = { 0 }; getMediaAddr(pImpChan, MediaAddr); int nIndex; pMakeCallEx->Protocol = eGCPro_SIP; pMakeCallEx->u.SIPInvite.Body.Valid = 1; pMakeCallEx->u.SIPInvite.Body.BodyPartyType = BodyPartyType_SDP; ISXSIP_IE_BODY_SDP_EX* pSdp = &pMakeCallEx->u.SIPInvite.Body.u.BodySdp; pSdp->SdpNum = 0; //o nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; sprintf_s(pSdp->Sdp[nIndex].u.Connection.Addr, 32, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m int iCodecId; int iPayLoad; ISXSIP_IE_SDP_MEDIA_DESC_EX DspMediaDescEx; DspMediaDescEx.MediaPort = pImpChan->mediaPort(); DspMediaDescEx.MediaProtocol = ISXSIP_SDPPROTOCOL_RTP; DspMediaDescEx.MediaType = ISXSIP_SDPMEDIATYPE_AUDIO; int iPLIndex = 0; iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; iCodecId = AudioCodec_G729_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; DspMediaDescEx.Payload[iPLIndex++] = 101; DspMediaDescEx.PayLoadNum = iPLIndex; ISX_sr_insertparm(&pMakeCallEx->u.SIPInvite.Body, SIPPARMID_MEDIADESC, sizeof(DspMediaDescEx), &DspMediaDescEx); IP_AUDIO_CAPABILITY AudioCodec; AudioCodec.iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_insertparm(&pMakeCallEx->u.SIPInvite.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); AudioCodec.iCodecId = AudioCodec_G729_20MS; ISX_sr_insertparm(&pMakeCallEx->u.SIPInvite.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_RTP_MAP; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_RTP_MAP); ISXSIP_IE_SDP_RTP_MAP* pSdpRtpMap = &pSdp->Sdp[nIndex].u.RtpMap; pSdpRtpMap->Payload = 101; pSdpRtpMap->ClkRate = 8000; lstrcpy(pSdpRtpMap->EncodingName, "telephone-event"); lstrcpy(pSdpRtpMap->EncodingParam, "1"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "fmtp"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "101 0-15"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "ptime"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "20"); pSdp->SdpNum++; } /***************************************************************** **【函数名称】 __onOffered **【函数功能】 处理来电呼叫 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Exten::onOffered( CVoipChannel& Chan, GCPARAMEX_MAKECALL* pMakeCallEx ) { //发送TRY ISX_gc_SIPSendTrying(Chan.m_CallId, EV_ASYNC); Chan.m_ExtenNo = atoi(Chan.m_CallerNum); ASSERT(Chan.m_pBindIpmCh == NULL); if((Chan.m_pBindIpmCh = CMC::GetInstance().allocIpmCh4SipCh(Chan.m_Id.NodeNo, Chan.m_Id.BoardNo)) == NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理分机来电出错, 无可用的IPM资源, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } Chan.m_pBindIpmCh->bind(&Chan); // 协商媒体编解码 if(!Chan.__getCallMediaDesc(&pMakeCallEx->u.SIPInvite.Body)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理分机来电时协商媒体编解码失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus415UnsupportedMediaType); return; } if(!CSessionShell::GetInstance().onLineChannelEvent(Chan.m_Id, Chan.m_SessionCode, DEV_CH_EVT_FUN_CODE, Chan.m_CalleeNum, Chan.m_ExtenNo)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理座席分机来电失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); Chan.__clearCall(IPEC_SIPReasonStatus500ServerInternalError); } } /***************************************************************** **【函数名称】 onAlerting **【函数功能】 处理远端振铃 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Exten::onAlerting( CVoipChannel& Chan, METAEVENT* pMetaEvent ) { if(Chan.m_Job == DEV_LINE_CH_JOB_NONE) CSessionShell::GetInstance().onLineStateChanged(Chan.m_Id, Chan.m_SessionCode, VIRTUAL_LINE_STATE_ALERTING, Chan.m_CallerNum, Chan.m_CalleeNum); } /***************************************************************** **【函数名称】 makeCall **【函数功能】 呼叫 **【参数】 **【返回值】 成功true,失败false ****************************************************************/ bool CVoipChannel::CImplement4Exten::makeCall( CVoipChannel& Chan, int AccountId ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]分机模式下执行呼叫请求, caller = %s, callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); ASSERT(Chan.m_pBindIpmCh == NULL); if((Chan.m_pBindIpmCh = CMC::GetInstance().allocIpmCh4SipCh(Chan.m_Id.NodeNo, Chan.m_Id.BoardNo)) == NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]进行分机呼叫时分配IPM资源失败, 呼叫将释放, Caller = %s, Callee = %s"), Chan.m_Id.NodeNo, Chan.m_Id.BoardNo, Chan.m_Id.ChanNo, Chan.m_CallerNum, Chan.m_CalleeNum); return false; } Chan.m_pBindIpmCh->bind(&Chan); TCHAR Callee[SIP_URI_LEN] = { 0 }; TCHAR Caller[SIP_URI_LEN] = { 0 }; TCHAR ProxyIp[MAX_PATH] = { 0 }; Chan.__getVoipAddr(ProxyIp); Chan.m_ExtenNo = atoi(Chan.m_CalleeNum); Chan.__getExtenAddrInfo(Chan.m_CalleeNum, Callee, SIP_URI_LEN); sprintf_s(Caller, SIP_URI_LEN, _T("%s@%s\0"), Chan.m_CallerNum, ProxyIp); GCPARAMEX_MAKECALL MakeCallEx; ISX_sr_default(PARMID_SIP_INVITE, &MakeCallEx.u.SIPInvite); ISX_sr_setparm(&MakeCallEx.u.SIPInvite, SIPPARMID_DESTADD, strlen(Callee), Callee); ISX_sr_setparm(&MakeCallEx.u.SIPInvite, SIPPARMID_SRCADD, strlen(Caller), Caller); __constructMakeCallBody(&MakeCallEx, Chan.m_pBindIpmCh); return ISX_gc_MakeCall(Chan.m_Handle, &Chan.m_CallId, Callee, NULL, -1, EV_ASYNC, &MakeCallEx) == 0; } /***************************************************************** **【函数名称】 getMediaAddr **【函数功能】 获取媒体地址 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::CImplement4Exten::getMediaAddr( CIpmChannel* pImpChan, LPTSTR Buffer ) { ASSERT(pImpChan != NULL); pImpChan->mediaAddrBoard(Buffer); } CVoipChannel::CVoipChannel(DEV_RES_CH_TYPE ChannelNo, DEV_RES_NO_TYPE NodeNo, DEV_RES_NO_TYPE BoardNo) : CChannelResource(DEV_RES_TYPE_VOIP, NodeNo, BoardNo, DEVICE_RES_NO_ANY, ChannelNo), m_pImplement(NULL), m_CallId(0), m_IsBlocked(false), m_Job(DEV_LINE_CH_JOB_NONE), m_ExtenNo(0), m_Mixer(*this), m_pBindIpmCh(NULL), m_MediaPort(0), m_CodecId(-1) { } CVoipChannel::~CVoipChannel(void) { close(); } /***************************************************************** **【函数名称】 __clearCall **【函数功能】 清除呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::__clearCall( int Cause ) { if(m_CallId == 0) return false; m_State = GCST_DROPCALLING; if(m_MeetingInfo.MeetingId != MEETING_ID_INVALID) CMC::GetInstance().meetingRemove(this, m_MeetingInfo); m_Mixer.release(); if(ISX_gc_DropCall(m_CallId, Cause, EV_ASYNC) == 0) { m_CallId = 0; return true; } else return false; } /***************************************************************** **【函数名称】 __getVoipAddr **【函数功能】 获取 **【参数】 **【返回值】 成功true,失败false ****************************************************************/ bool CVoipChannel::__getVoipAddr( LPTSTR AddrBuf ) { return ISX_sr_getnet2cfg(m_Id.NodeNo, m_Id.BoardNo, BT_SIP, AddrBuf, NULL) == 0; } /***************************************************************** **【函数名称】 __getExtenAddrInfo **【函数功能】 获取SIP分机地址信息 **【参数】 **【返回值】 成功true,失败false ****************************************************************/ bool CVoipChannel::__getExtenAddrInfo( LPCTSTR Exten, LPTSTR pContactAddr, int Len ) { ISXSIP_REGUSER RegUser; if(ISXE_gc_SipGetRegisterUser(Exten, &RegUser) == 0) { if(RegUser.PortNum != 0xffff && RegUser.PortNum != 0) sprintf_s(pContactAddr, Len, _T("%s@%s:%d\0"), Exten, RegUser.IpAddr, RegUser.PortNum); else sprintf_s(pContactAddr, Len, _T("%s@%s\0"), Exten, RegUser.IpAddr); return true; } return false; } /***************************************************************** **【函数名称】 __constructAnswerBody **【函数功能】 构造应答SIP协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__constructAnswerBody( GCPARAMEX_ANSWERCALL* pAnswerCallEx ) { memset(pAnswerCallEx, 0, sizeof(*pAnswerCallEx)); pAnswerCallEx->Protocol = eGCPro_SIP; pAnswerCallEx->u.SIPConn.Body.Valid = 1; pAnswerCallEx->u.SIPConn.Body.BodyPartyType = BodyPartyType_SDP; ISXSIP_IE_BODY_SDP_EX* pSdp = &pAnswerCallEx->u.SIPConn.Body.u.BodySdp; pSdp->SdpNum = 0; TCHAR MediaAddr[MAX_PATH] = { 0 }; ASSERT(m_pBindIpmCh != NULL); m_pImplement->getMediaAddr(m_pBindIpmCh, MediaAddr); //o int nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Connection.Addr, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m ISXSIP_IE_SDP_MEDIA_DESC_EX DspMediaDescEx; DspMediaDescEx.MediaPort = m_pBindIpmCh->mediaPort(); DspMediaDescEx.MediaProtocol = ISXSIP_SDPPROTOCOL_RTP; DspMediaDescEx.MediaType = ISXSIP_SDPMEDIATYPE_AUDIO; int iPLIndex = 0; int nPayLoad = 0; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &nPayLoad, &m_CodecId); DspMediaDescEx.Payload[iPLIndex++] = nPayLoad; DspMediaDescEx.Payload[iPLIndex++] = 101; DspMediaDescEx.PayLoadNum = iPLIndex; ISX_sr_insertparm(&pAnswerCallEx->u.SIPConn.Body, SIPPARMID_MEDIADESC, sizeof(DspMediaDescEx), &DspMediaDescEx); IP_AUDIO_CAPABILITY AudioCodec; AudioCodec.iCodecId = (eAUDIOCODEC)m_CodecId; ISX_sr_insertparm(&pAnswerCallEx->u.SIPConn.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_RTP_MAP; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_RTP_MAP); ISXSIP_IE_SDP_RTP_MAP* pSdpRtpMap = &pSdp->Sdp[nIndex].u.RtpMap; pSdpRtpMap->Payload = 101; pSdpRtpMap->ClkRate = 8000; lstrcpy(pSdpRtpMap->EncodingName, "telephone-event"); lstrcpy(pSdpRtpMap->EncodingParam, "1"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "fmtp"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "101 0-15"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "ptime"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "20"); pSdp->SdpNum++; } /***************************************************************** **【函数名称】 __constructProgressBody **【函数功能】 构造呼叫进展SIP协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__constructProgressBody( GCPARAMEX_CALLPROGRESS* pProgressEx ) { memset(pProgressEx, 0, sizeof(*pProgressEx)); pProgressEx->Protocol = eGCPro_SIP; pProgressEx->u.SIPProgress.Body.Valid = 1; pProgressEx->u.SIPProgress.Body.BodyPartyType = BodyPartyType_SDP; ISXSIP_IE_BODY_SDP_EX* pSdp = &pProgressEx->u.SIPProgress.Body.u.BodySdp; pSdp->SdpNum = 0; TCHAR MediaAddr[MAX_PATH] = { 0 }; ASSERT(m_pBindIpmCh != NULL); m_pImplement->getMediaAddr(m_pBindIpmCh, MediaAddr); int nIndex; //o nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Connection.Addr, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m ISXSIP_IE_SDP_MEDIA_DESC_EX DspMediaDescEx; DspMediaDescEx.MediaPort = m_pBindIpmCh->mediaPort(); DspMediaDescEx.MediaProtocol = ISXSIP_SDPPROTOCOL_RTP; DspMediaDescEx.MediaType = ISXSIP_SDPMEDIATYPE_AUDIO; int iPLIndex = 0; int iPayLoad = 0; if(m_CodecId != -1) { ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &m_CodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; } else { int iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; iCodecId = AudioCodec_G729_20MS; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &iPayLoad, &iCodecId); DspMediaDescEx.Payload[iPLIndex++] = iPayLoad; } DspMediaDescEx.Payload[iPLIndex++] = 101; DspMediaDescEx.PayLoadNum = iPLIndex; ISX_sr_insertparm(&pProgressEx->u.SIPProgress.Body, SIPPARMID_MEDIADESC, sizeof(DspMediaDescEx), &DspMediaDescEx); IP_AUDIO_CAPABILITY AudioCodec; if(m_CodecId != -1) { AudioCodec.iCodecId = (eAUDIOCODEC)m_CodecId; ISX_sr_insertparm(&pProgressEx->u.SIPProgress.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); } else { AudioCodec.iCodecId = AudioCodec_G711_ALAW_20MS; ISX_sr_insertparm(&pProgressEx->u.SIPProgress.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); AudioCodec.iCodecId = AudioCodec_G729_20MS; ISX_sr_insertparm(&pProgressEx->u.SIPProgress.Body, SIPPARMID_CHCAP, sizeof(AudioCodec), &AudioCodec); } //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_RTP_MAP; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_RTP_MAP); ISXSIP_IE_SDP_RTP_MAP* pSdpRtpMap = &pSdp->Sdp[nIndex].u.RtpMap; pSdpRtpMap->Payload = 101; pSdpRtpMap->ClkRate = 8000; lstrcpy(pSdpRtpMap->EncodingName, "telephone-event"); lstrcpy(pSdpRtpMap->EncodingParam, "1"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "fmtp"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "101 0-15"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "ptime"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "20"); pSdp->SdpNum++; } /***************************************************************** **【函数名称】 __constructReinviteBody4Hold **【函数功能】 构造ReinviteACK协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__constructReinviteBody4Hold( ISXSIP_IE_BODY_EX* pBody, bool Hold ) { pBody->Valid = 1; pBody->BodyPartyType = BodyPartyType_SDP; TCHAR MediaAddr[MAX_PATH] = { 0 }; ASSERT(m_pBindIpmCh != NULL); m_pImplement->getMediaAddr(m_pBindIpmCh, MediaAddr); int nIndex; ISXSIP_IE_BODY_SDP_EX* pSdp = &pBody->u.BodySdp; pSdp->SdpNum = 0; //o nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Connection.Addr, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m int nPLIndex = 0; int nPayLoad; ISX_sr_convert(CONID_CODECID2PLAYLOAD, &nPayLoad, &m_CodecId); nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_MEDIA_DESC; pSdp->Sdp[nIndex].u.MediaDesc.MediaPort = m_pBindIpmCh->mediaPort(); pSdp->Sdp[nIndex].u.MediaDesc.MediaProtocol = ISXSIP_SDPPROTOCOL_RTP; pSdp->Sdp[nIndex].u.MediaDesc.MediaType = ISXSIP_SDPMEDIATYPE_AUDIO; pSdp->Sdp[nIndex].u.MediaDesc.Payload[nPLIndex++] = nPayLoad; pSdp->Sdp[nIndex].u.MediaDesc.Payload[nPLIndex++] = 101; pSdp->Sdp[nIndex].u.MediaDesc.PayLoadNum = nPLIndex; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_MEDIA_DESC) - 1 + pSdp->Sdp[nIndex].u.MediaDesc.PayLoadNum; pSdp->SdpNum++; // a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_RTP_MAP; pSdp->Sdp[nIndex].u.RtpMap.Payload = g_SipSdpRtpMap[m_CodecId].Payload; pSdp->Sdp[nIndex].u.RtpMap.ClkRate = g_SipSdpRtpMap[m_CodecId].ClkRate; lstrcpy(pSdp->Sdp[nIndex].u.RtpMap.EncodingName, g_SipSdpRtpMap[m_CodecId].EncodingName); lstrcpy(pSdp->Sdp[nIndex].u.RtpMap.EncodingParam, g_SipSdpRtpMap[m_CodecId].EncodingParam); pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_RTP_MAP); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "fmtp"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "101 0-15"); pSdp->SdpNum++; if(Hold) { // a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONN_MODE; pSdp->Sdp[nIndex].u.ConnMode.ConnMode = ISXSIP_SDPCONNECTMODE_RECVONLY; pSdp->SdpNum++; } //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "ptime"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "20"); pSdp->SdpNum++; } /***************************************************************** **【函数名称】 __constructReinviteBody4T38 **【函数功能】 构造ReinviteACK协议体 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__constructReinviteBody4T38( ISXSIP_IE_BODY_EX* pBody ) { pBody->Valid = 1; pBody->BodyPartyType = BodyPartyType_SDP; TCHAR MediaAddr[MAX_PATH] = { 0 }; ASSERT(m_pBindIpmCh != NULL); m_pImplement->getMediaAddr(m_pBindIpmCh, MediaAddr); int nIndex; ISXSIP_IE_BODY_SDP_EX* pSdp = &pBody->u.BodySdp; pSdp->SdpNum = 0; //o nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ORIGIN; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_ORIGIN); pSdp->Sdp[nIndex].u.Origin.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Origin.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Origin.SessionId, "20"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.UserName, "EHang"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Version, "001"); lstrcpy(pSdp->Sdp[nIndex].u.Origin.Addr, MediaAddr); pSdp->SdpNum++; //s nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION); lstrcpy(pSdp->Sdp[nIndex].u.Session.Name, "EH-Call"); lstrcpy(pSdp->Sdp[nIndex].u.Session.Info, ""); pSdp->SdpNum++; //c nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_CONNECTION; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_CONNECTION); pSdp->Sdp[nIndex].u.Connection.NetType = ISXSIP_SDPNETTYPE_IN; pSdp->Sdp[nIndex].u.Connection.AddrType = ISXSIP_SDPADDRTYPE_IP4; lstrcpy(pSdp->Sdp[nIndex].u.Connection.Addr, MediaAddr); pSdp->SdpNum++; //t nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_SESSION_TIME; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_SESSION_TIME); pSdp->Sdp[nIndex].u.SessionTime.Start = 0; pSdp->Sdp[nIndex].u.SessionTime.End = 0; pSdp->SdpNum++; //m nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_MEDIA_DESC; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_MEDIA_DESC_EX); pSdp->Sdp[nIndex].u.MediaDesc.MediaPort = m_pBindIpmCh->mediaPort(); pSdp->Sdp[nIndex].u.MediaDesc.MediaProtocol = ISXSIP_SDPPROTOCOL_UDPTL; pSdp->Sdp[nIndex].u.MediaDesc.MediaType = ISXSIP_SDPMEDIATYPE_IMAGE; pSdp->Sdp[nIndex].u.MediaDesc.PayLoadNum = 1; pSdp->Sdp[nIndex].u.MediaDesc.Payload[0] = 0; pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "T38FaxVersion"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "0"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "T38MaxBitRate"); //strcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "14400"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "9600"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "T38FaxFillBitRemoval"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "0"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "T38FaxTranscodingMMR"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "0"); pSdp->SdpNum++; //a nIndex = pSdp->SdpNum; pSdp->Sdp[nIndex].Tag = ISXSIP_IE_SDP_TAG_ATTRIBUTE; pSdp->Sdp[nIndex].Len = sizeof(ISXSIP_IE_SDP_TAG_ATTRIBUTE); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Name, "T38FaxTranscodingJBIG"); lstrcpy(pSdp->Sdp[nIndex].u.Attribute.Value, "0"); pSdp->SdpNum++; } /***************************************************************** **【函数名称】 __getCallNumFromSipAddr **【函数功能】 从SIP URI中解析号码 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__getCallNumFromSipAddr( LPCTSTR Called, CString& NumBuf ) { ASSERT(Called != NULL); NumBuf = _T(""); TCHAR szTmpAddr[GC_ADDRSIZE]; lstrcpy(szTmpAddr, Called); TCHAR *p = strstr(szTmpAddr, _T("@")); if(p != NULL) *p=0; p = strstr(szTmpAddr, _T(":")); if(p == NULL) NumBuf = szTmpAddr; else NumBuf = p+1; } /***************************************************************** **【函数名称】 __getCallMediaDesc **【函数功能】 获取呼叫的媒体描述 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::__getCallMediaDesc( ISXSIP_IE_BODY_EX* pSdp, int CodecId /*= -1*/ ) { if(pSdp->Valid == 0 || pSdp->BodyPartyType == BodyPartyType_TRANS) return false; int iLoopNum = pSdp->u.BodySdp.SdpNum; ISXSIP_IE_BODY_SDP_EX* pSdpex = &pSdp->u.BodySdp; for(int i = 0; i < iLoopNum; i++) { if(pSdpex->Sdp[i].Tag == ISXSIP_IE_SDP_TAG_CONNECTION) { m_ConnectAddr = pSdpex->Sdp[i].u.Connection.Addr; continue; } if(pSdpex->Sdp[i].Tag != ISXSIP_IE_SDP_TAG_MEDIA_DESC) continue; if(pSdpex->Sdp[i].u.MediaDesc.MediaType != ISXSIP_SDPMEDIATYPE_AUDIO) continue; int iPayLoadNum = pSdpex->Sdp[i].u.MediaDesc.PayLoadNum; m_MediaPort = pSdpex->Sdp[i].u.MediaDesc.MediaPort; ISXSIP_IE_SDP_MEDIA_DESC_EX* pmdex = &pSdpex->Sdp[i].u.MediaDesc; if(CodecId >= 0) //如果指定了CODEC { for(int j = 0; j < iPayLoadNum; j++) { if(g_SipSdpRtpMap[CodecId].Payload == pmdex->Payload[j]) { //取此指定AUDIO编码作为协商类型 m_CodecId = CodecId; return true; } } } else { for(int j = 0; j < iPayLoadNum; j++) { for(int k = 0; k < AudioCodec_MaxNum; k++) { if(g_SipSdpRtpMap[k].Payload == pmdex->Payload[j]) { // 从给出的媒体编码类型中取一个AUDIO编码作为协商类型 m_CodecId = k; return true; } } } } } return false; } /***************************************************************** **【函数名称】 __consultMedia **【函数功能】 协商媒体 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::__consultMedia( ISXSIP_IE_BODY_EX* pSdp ) { if(pSdp->Valid == 0 || pSdp->BodyPartyType == BodyPartyType_TRANS) return false; int iLoopNum = pSdp->u.BodySdp.SdpNum; ISXSIP_IE_BODY_SDP_EX* pSdpex = &pSdp->u.BodySdp; for(int i = 0; i < iLoopNum; i++) { if(pSdpex->Sdp[i].Tag != ISXSIP_IE_SDP_TAG_MEDIA_DESC) continue; if(pSdpex->Sdp[i].u.MediaDesc.MediaType != ISXSIP_SDPMEDIATYPE_AUDIO) continue; int iPayLoadNum = pSdpex->Sdp[i].u.MediaDesc.PayLoadNum; ISXSIP_IE_SDP_MEDIA_DESC_EX* pmdex = &pSdpex->Sdp[i].u.MediaDesc; for(int j = 0; j < iPayLoadNum; j++) { for(int k = 0; k < AudioCodec_MaxNum; k++) { if(g_SipSdpRtpMap[k].Payload == pmdex->Payload[j]) { pmdex->PayLoadNum = 2; pmdex->Payload[0] = pmdex->Payload[j]; pmdex->Payload[1] = 101; return true; } } } } return false; } /***************************************************************** **【函数名称】 __analyzeReinvite **【函数功能】 分析reinvite的目的 **【参数】 **【返回值】 ****************************************************************/ ReinvitePurpose CVoipChannel::__analyzeReinvite( ISXSIP_IE_BODY_SDP_EX* pSdp ) { for(int i = 0; i < pSdp->SdpNum; ++i) { USHORT Tag = pSdp->Sdp[i].Tag; if(Tag == ISXSIP_IE_SDP_TAG_CONN_MODE) { UCHAR ConMode = pSdp->Sdp[i].u.ConnMode.ConnMode; if(ConMode == ISXSIP_SDPCONNECTMODE_SENDONLY) return REINVITE_FOR_HOLD; else if(ConMode == ISXSIP_SDPCONNECTMODE_SENDRECV) return REINVITE_FOR_TAKEBACK; } else if(Tag == ISXSIP_IE_SDP_TAG_MEDIA_DESC) { if(pSdp->Sdp[i].u.MediaDesc.MediaType == ISXSIP_SDPMEDIATYPE_IMAGE) return REINVITE_FOR_FAX; } } return REINVITE_FOR_NULL; } /***************************************************************** **【函数名称】 __onOffered **【函数功能】 处理来电呼叫 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onOffered( METAEVENT* pMetaEvent ) { if(m_State != GCST_NULL) { ISX_gc_DropCall(pMetaEvent->crn, IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]检测到呼叫, 但通道状态异常, 呼叫将忽略"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return; } m_State = GCST_OFFERED; m_CallId = pMetaEvent->crn; GCPARAMEX_MAKECALL* pMakeCallEx = (GCPARAMEX_MAKECALL*)pMetaEvent->evtdatap; ASSERT(pMakeCallEx != NULL); CHAR DNIZ[GC_ADDRSIZE] = { 0 }; CHAR ANI[GC_ADDRSIZE] = { 0 }; ISX_gc_GetCallInfo(m_CallId, DESTINATION_ADDRESS, DNIZ); ISX_gc_GetCallInfo(m_CallId, ORIGINATION_ADDRESS, ANI); // 获取主、被叫号码 __getCallNumFromSipAddr(ANI, m_CallerNum); __getCallNumFromSipAddr(DNIZ, m_CalleeNum); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]检测到呼叫, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, ANI, DNIZ); // 判断主叫是内线还是中继 if(isInnerSipUser(m_CallerNum)) // 内线 m_pImplement = &ms_Implement4Exten; else // 外线 m_pImplement = &ms_Implement4Trunk; m_pImplement->onOffered(*this, pMakeCallEx); CMC::GetInstance().onDevResState(m_Id); } /***************************************************************** **【函数名称】 __onAlerting **【函数功能】 处理远端振铃 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onAlerting( METAEVENT* pMetaEvent ) { if(m_State == GCST_ALERTING || m_State == GCST_DROPCALLING) return; m_State = GCST_ALERTING; ASSERT(m_pImplement != NULL); m_pImplement->onAlerting(*this, pMetaEvent); CMC::GetInstance().onDevResState(m_Id); } /***************************************************************** **【函数名称】 __onProgressing **【函数功能】 处理Progressing **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onProgressing( void ) { if(m_State == GCST_PROGRESS || m_State == GCST_DROPCALLING) return; GCPARAMEX_CALLPROGRESS* pProgress = (GCPARAMEX_CALLPROGRESS*)ISX_sr_getevtdatap(); ASSERT(pProgress != NULL); // 存在收到183而无SDP紧接着又再次收到携带了SDP的183这种情况,所以在if语句判断成功后再将m_State设置为GCST_PROGRESS if(pProgress->u.SIPProgress.Body.Valid == 1) { m_State = GCST_PROGRESS; // 协商媒体编解码 if(!__getCallMediaDesc(&pProgress->u.SIPProgress.Body)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理Progressing时协商媒体编解码失败, 呼叫将释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus415UnsupportedMediaType); return; } // 启动媒体 ASSERT(m_pBindIpmCh != NULL); if(!m_pBindIpmCh->isStarted()) { if(!m_pBindIpmCh->startMedia()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理Progressing时IPM资源启动失败, 呼叫将释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } } CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_PROGRESSING, m_CallerNum, m_CalleeNum); } } /***************************************************************** **【函数名称】 __onConnected **【函数功能】 处理远端接通 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onConnected( METAEVENT* pMetaEvent ) { if(m_State == GCST_DROPCALLING) return; m_State = GCST_CONNECTED; GCPARAMEX_ANSWERCALL *pOutAnswerCallEx = (GCPARAMEX_ANSWERCALL*)pMetaEvent->evtdatap; ASSERT(pOutAnswerCallEx != NULL); if(pOutAnswerCallEx->u.SIPConn.Body.Valid == 1) { // 协商媒体编解码 if(!__getCallMediaDesc(&pOutAnswerCallEx->u.SIPConn.Body)) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理远端应答时协商媒体编解码失败, 呼叫将释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus415UnsupportedMediaType); return; } } // 启动媒体 ASSERT(m_pBindIpmCh != NULL); if(!m_pBindIpmCh->isStarted()) { if(!m_pBindIpmCh->startMedia()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]处理远端应答时启动IPM资源失败, 呼叫将释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } } LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]远端应答, 开始通话, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); if(m_Job == DEV_LINE_CH_JOB_PRE_DIAL) { CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_INIT, m_CallerNum, m_CalleeNum); m_Job = DEV_LINE_CH_JOB_NONE; } else if(m_Job == DEV_LINE_CH_JOB_OFF_HOOK) { CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_OFF_HOOK, NULL); m_Job = DEV_LINE_CH_JOB_NONE; } else { CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_TALKING, m_CallerNum, m_CalleeNum); } CMC::GetInstance().onDevResState(m_Id); } /***************************************************************** **【函数名称】 __onAnswered **【函数功能】 处理应答成功 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onAnswered( void ) { if(m_State == GCST_DROPCALLING) return; m_State = GCST_CONNECTED; // 启动媒体 ASSERT(m_pBindIpmCh != NULL); if(!m_pBindIpmCh->isStarted()) { if(!m_pBindIpmCh->startMedia()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]应答远端时启动IPM资源失败, 呼叫将释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); return; } } LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]应答成功, 开始通话, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_TALKING, m_CallerNum, m_CalleeNum); CMC::GetInstance().onDevResState(m_Id); } /***************************************************************** **【函数名称】 __onCallFinish **【函数功能】 处理呼叫成功 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onCallFinish( void ) { m_State = GCST_NULL; LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]空闲"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); // 因为发现调用__ClearCall之后又收到GCEV_CONNECTED等事件,导致IPM通道stop之后又start,从而使IPM通道 // 一直处于start状态未释放,后续的呼叫若使用该IPM通道会因为start失败而造成呼叫异常,鉴于此把IPM通道的 // stop方法从__ClearCall函数移至此处调用。 if(m_pBindIpmCh != NULL) { m_pBindIpmCh->stopMedia(); // 未关联会话则IPM通道无法被会话释放,所以这里提前释放 if(m_SessionCode == SESSION_INVALID_CALL) { m_pBindIpmCh->reset(); m_pBindIpmCh = NULL; } } // 通各线路状态变化 CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_FREE, NULL, NULL); m_pImplement = NULL; m_CallId = 0; m_Job = DEV_LINE_CH_JOB_NONE; m_ExtenNo = 0; m_MediaPort = 0; m_CodecId = -1; m_ConnectAddr = _T(""); reset(); CMC::GetInstance().onDevResState(m_Id); } /***************************************************************** **【函数名称】 __onDiconnected **【函数功能】 处理呼叫中断 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onDiconnected( METAEVENT* pMetaEvent ) { GCPARAMEX_DROPCALL* pDropCall = (GCPARAMEX_DROPCALL*)pMetaEvent->evtdatap; ASSERT(pDropCall != NULL); if(m_State != GCST_CONNECTED) // 非通话中挂机 { int ErrCode; switch(pDropCall->u.SIPDrop.usCause) { case IPEC_SIPReasonStatus404NotFound: ErrCode = CALL_FAILED_CAUSE_NUM_INVALID; break; case IPEC_SIPReasonStatus500ServerInternalError: case IPEC_SIPReasonStatus480TemporarilyUnavailable: ErrCode = CALL_FAILED_CAUSE_NO_ANSWER; break; case IPEC_SIPReasonStatus486BusyHere: ErrCode = CALL_FAILED_CAUSE_USER_BUSY; break; case IPEC_SIPReasonStatus410Gone: ErrCode = CALL_FAILED_CAUSE_DST_OFF; break; default: ErrCode = CALL_FAILED_CAUSE_NETWORK_ERR; break; } CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_CALL_FAILD_CAUSE, (LPCTSTR)ErrCode); } m_State = GCST_DISCONNECTED; LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]检测到远端挂机, Caller = %s, Callee = %s, Cause = %d"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum, pDropCall->u.SIPDrop.usCause); // 远端挂机,结束两方通道通话 __clearCall(pDropCall->u.SIPDrop.usCause); } /***************************************************************** **【函数名称】 __onTaskFail **【函数功能】 处理通道任务失败 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onTaskFail( void ) { switch(ISX_sr_getevtopertype()) { case OPER_ACCEPTCALL: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]accept呼叫失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } break; case OPER_ANSWERCALL: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]应答来电失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } break; case OPER_MAKECALL: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus200OK); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]外呼失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } break; case OPER_DROPCALL: { ASSERT(FALSE); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]挂机失败, 已强制释放资源, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __onCallFinish(); } break; case OPER_SIP_SENDREINVITE: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]发送Reinvite失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } break; case OPER_SIP_SENDREINVITEACK: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]发送ReinviteACK失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } break; } } /***************************************************************** **【函数名称】 __onRecvInfo **【函数功能】 处理收到的SIP消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvInfo( METAEVENT* pMetaEvent ) { SIP_INFO* pSipInfo = (SIP_INFO*)pMetaEvent->evtdatap; ASSERT(pSipInfo != NULL); SIP_INFO_ACK Ack; memcpy(&Ack.Body, &pSipInfo->Body, sizeof(ISXSIP_IE_BODY_EX)); Ack.ulSerialNo = pSipInfo->ulSerialNo; Ack.usCause = IPEC_SIPReasonStatus200OK; ISX_gc_SIPSendInfoAck(pMetaEvent->crn, EV_ASYNC, &Ack); } /***************************************************************** **【函数名称】 __onRecvReinvite **【函数功能】 处理Reinvite消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvReinvite( METAEVENT* pMetaEvent ) { ISXSIP_IE_BODY_EX* pSdp = (ISXSIP_IE_BODY_EX*)pMetaEvent->evtdatap; ASSERT(pSdp != NULL); if(pSdp->Valid == 0 || pSdp->BodyPartyType == BodyPartyType_TRANS) return; ReinvitePurpose Purpose = __analyzeReinvite(&pSdp->u.BodySdp); SIP_REINVITE_ACK Ack; switch(Purpose) { case REINVITE_FOR_HOLD: { CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_HOLD_ON, NULL, m_ExtenNo); Ack.usCause = IPEC_SIPReasonStatus200OK; __constructReinviteBody4Hold(&Ack.Body, true); } break; case REINVITE_FOR_TAKEBACK: { CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_TAKE_BACK, NULL, m_ExtenNo); Ack.usCause = IPEC_SIPReasonStatus200OK; __constructReinviteBody4Hold(&Ack.Body, false); } break; case REINVITE_FOR_FAX: { __constructReinviteBody4T38(&Ack.Body); ASSERT(m_pBindIpmCh != NULL); Ack.usCause = m_pBindIpmCh->switchVF(pSdp) ? IPEC_SIPReasonStatus200OK : IPEC_SIPReasonStatus403Forbidden; } break; default: Ack.usCause = IPEC_SIPReasonStatus403Forbidden; } ISX_gc_SIPSendReinviteAck(m_CallId, EV_ASYNC, &Ack); } /***************************************************************** **【函数名称】 __onRecvReinviteAck **【函数功能】 处理ReinviteACK消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvReinviteAck( METAEVENT* pMetaEvent ) { SIP_REINVITE_ACK* pReinviteAck = (SIP_REINVITE_ACK*)pMetaEvent->evtdatap; ASSERT(pReinviteAck != NULL); ISXSIP_IE_BODY_EX* pSdp = &pReinviteAck->Body; if(pSdp->Valid == 0 || pSdp->BodyPartyType == BodyPartyType_TRANS) return; ASSERT(m_pBindIpmCh != NULL); if(m_pBindIpmCh->switchVF(pSdp)) { __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]响应ReinviteAck消息,切换传真模式失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } } /***************************************************************** **【函数名称】 __onRecvUpdate **【函数功能】 处理Update消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvUpdate( METAEVENT* pMetaEvent ) { SIP_UPDATE_IND *pUpdate = (SIP_UPDATE_IND *)pMetaEvent->evtdatap; ASSERT(pUpdate != NULL); SIP_GENTRANSC_ACK ack; memcpy(&ack.Body, &pUpdate->Body, sizeof(ISXSIP_IE_BODY_EX)); ack.ulSerialNo = pUpdate->ulSerialNo; ack.usCause = IPEC_SIPReasonStatus200OK; ISX_gc_SIPSendUpdateAck (pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 __onRecvSubscribe **【函数功能】 处理subscribe消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvSubscribe( METAEVENT* pMetaEvent ) { SIP_SUBSCRIBE_IND* pInd = (SIP_SUBSCRIBE_IND *)pMetaEvent->evtdatap; ASSERT(pInd != NULL); SIP_SUBSCRIBE_ACK ack; memcpy(&ack.Expires, &pInd->Expires, sizeof(ISXSIP_IE_EXPIRES)); memcpy(&ack.Body, &pInd->Body, sizeof(ISXSIP_IE_BODY_EX)); ack.ulSerialNo = pInd->ulSerialNo; ack.usCause = IPEC_SIPReasonStatus406NotAcceptable; ISX_gc_SIPSendSubscribeAck(pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 __onRecvMessage **【函数功能】 处理message消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvMessage( METAEVENT* pMetaEvent ) { SIP_MESSAGE_IND* pInd = (SIP_MESSAGE_IND *)pMetaEvent->evtdatap; ASSERT(pInd != NULL); SIP_GENTRANSC_ACK ack; ack.usCause = IPEC_SIPReasonStatus200OK; memcpy(&ack.Body, &pInd->Body, sizeof(ISXSIP_IE_BODY_EX)); ack.ulSerialNo = pInd->ulSerialNo; ISX_gc_SIPSendMessageAck (pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 __onRecvNotify **【函数功能】 处理notify消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvNotify( METAEVENT* pMetaEvent ) { SIP_NOTIFY_IND* pInd = (SIP_NOTIFY_IND *)pMetaEvent->evtdatap; ASSERT(pInd != NULL); SIP_GENTRANSC_ACK ack; memcpy(&ack.Body, &pInd->Body, sizeof(ISXSIP_IE_BODY_EX)); ack.usCause = IPEC_SIPReasonStatus200OK; ack.ulSerialNo = pInd->ulSerialNo; ISX_gc_SIPSendNotifyAck (pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 __onRecvRefer **【函数功能】 处理refer消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvRefer( METAEVENT* pMetaEvent ) { SIP_REFER_IND* pInd = (SIP_REFER_IND *)ISX_sr_getevtdatap(); ASSERT(pInd != NULL); SIP_REFER_ACK ack; memcpy(&ack.Body, &pInd->Body, sizeof(ISXSIP_IE_BODY_EX)); memcpy(&ack.Contact, &pInd->Contact, sizeof(ISXSIP_IE_CONTACT)); ack.usCause = IPEC_SIPReasonStatus403Forbidden; ack.ulSerialNo = pInd->ulSerialNo; ISX_gc_SIPSendReferAck(pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 __onRecvOption **【函数功能】 处理option消息 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::__onRecvOption( METAEVENT* pMetaEvent ) { SIP_OPTIONS_IND* pInd = (SIP_OPTIONS_IND *)ISX_sr_getevtdatap(); ASSERT(pInd != NULL); SIP_GENTRANSC_ACK ack; ZeroMemory(&ack, sizeof(ack)); ack.ulSerialNo = pInd->ulSerialNo; ack.usCause = IPEC_SIPReasonStatus200OK; if(__consultMedia(&pInd->Body)) memcpy(&ack.Body, &pInd->Body, sizeof(ISXSIP_IE_BODY_EX)); ISX_gc_SIPSendOptionsAck(pMetaEvent->crn, EV_ASYNC, &ack); } /***************************************************************** **【函数名称】 open **【函数功能】 打开系统资源 **【参数】 **【返回值】 成功true,失败false ****************************************************************/ bool CVoipChannel::open( void ) { ASSERT(m_Handle == DEV_HANDLE_INVALID); if(ISX_gc_OpenEx(&m_Handle, m_Id.NodeNo, m_Id.BoardNo, -1, m_Id.ChanNo, EV_SYNC, this, LINETYPE_SIP) < 0) { m_Handle = DEV_HANDLE_INVALID; LOGGER(LOG_CLASS_DEV, LOG_LEVEL_ERROR, _T("{VOIPCh}: VOIP通道[%d-%d-%d]打开失败"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } ISX_gc_SetEvtMsk(m_Handle, GCMSK_PROGRESS, GCACT_ADDMSK); if( m_Id.ChanNo % 2 != 0 ) { if(ISX_gc_WaitCall(m_Handle, NULL, NULL, -1, EV_ASYNC) < 0) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_ERROR, _T("{VOIPCh}: VOIP通道[%d-%d-%d]等待呼叫失败"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } } //LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: VOIP通道[%d-%d-%d]打开成功"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return true; } /***************************************************************** **【函数名称】 close **【函数功能】 关闭系统资源 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::close( void ) { if(m_Handle != DEV_HANDLE_INVALID) { ISX_gc_Close(m_Handle); m_Handle = DEV_HANDLE_INVALID; //LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: VOIPCh通道[%d-%d-%d]关闭"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); } } /***************************************************************** **【函数名称】 offhook **【函数功能】 内线摘机(只对内线有效) **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::offhook( bool IgnoreSession, LPCTSTR CallerNum ) { ASSERT(CallerNum != NULL); if(m_State != GCST_NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]振铃座席分机失败, 通道状态非空闲, Exten = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, CallerNum); return false; } LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行振铃座席分机请求, Exten = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, CallerNum); if(IgnoreSession) m_Job = DEV_LINE_CH_JOB_OFF_HOOK; else m_Job = DEV_LINE_CH_JOB_PRE_DIAL; m_CalleeNum = CallerNum; m_CallerNum = CallerNum; m_pImplement = &ms_Implement4Exten; return m_pImplement->makeCall(*this, INVALID_ID_SIP_ACCOUNT); } /***************************************************************** **【函数名称】 dropCall **【函数功能】 挂机 **【参数】 IsPassive 是否为被动挂机 **【返回值】 ****************************************************************/ bool CVoipChannel::dropCall( bool IsPassive ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行挂机请求, caller = %s, callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return __clearCall(IPEC_SIPReasonStatus200OK); } /***************************************************************** **【函数名称】 makeCall **【函数功能】 启动线路拨号 **【参数】 lpCalleeNum 被叫号码 lpCallerNum 主叫号码 **【返回值】 ****************************************************************/ bool CVoipChannel::makeCall( LPCTSTR Callee, LPCTSTR Caller, int AccountId ) { ASSERT(Callee != NULL); ASSERT(Caller != NULL); if(m_State != GCST_NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]执行呼叫请求失败, 通道未在空闲状态, caller = %s, callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, Caller, Callee); return false; } m_CalleeNum = Callee; m_CallerNum = Caller; if(AccountId == INVALID_ID_SIP_ACCOUNT) // 呼叫内线 m_pImplement = &ms_Implement4Exten; else m_pImplement = &ms_Implement4Trunk; return m_pImplement->makeCall(*this, AccountId); } /***************************************************************** **【函数名称】 answer **【函数功能】 应答呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::answer( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行应答请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); if(m_State == GCST_CONNECTED) { CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_TALKING, m_CallerNum, m_CalleeNum); return true; } else return false; } /***************************************************************** **【函数名称】 playSound **【函数功能】 放音收号 **【参数】 pContent 放音内容 **【返回值】 ****************************************************************/ bool CVoipChannel::playSound( PlayVoiceContent* pContent ) { ASSERT(pContent != NULL); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行放音请求, AudioFile = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, pContent->szFileName); if(m_State != GCST_CONNECTED) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]放音失败, 通道未处于连接状态, AudioFile = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, pContent->szFileName); return false; } return m_Mixer.playSound(pContent); } /***************************************************************** **【函数名称】 playTone **【函数功能】 播放信号音 **【参数】 Type 信号音类型 **【返回值】 ****************************************************************/ bool CVoipChannel::playTone( int Type ) { return m_Mixer.playTone(Type); } /***************************************************************** **【函数名称】 playStop **【函数功能】 停止放音 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::playStop( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行停止放音请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return m_Mixer.playStop(); } /***************************************************************** **【函数名称】 startRecord **【函数功能】 开始录音 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::startRecord( RecordContent* pContent ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行录音请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); if(m_State == GCST_NULL) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]录音失败, 通道状态空闲"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } return m_Mixer.startRecord(pContent); } /***************************************************************** **【函数名称】 stopRecord **【函数功能】 停止录音 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::stopRecord( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行停止录音请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return m_Mixer.stopRecord(); } /***************************************************************** **【函数名称】 monitor **【函数功能】 监听 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::monitor( COneLeg* pTalker, bool IsStop ) { ASSERT(pTalker != NULL); if(IsStop) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行取消监听请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return pTalker->cancelMonitor(this); } else { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行监听请求"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); if(m_State != GCST_CONNECTED) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]监听失败, 通道未处于连接状态"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } return pTalker->allowMonitor(this); } } /***************************************************************** **【函数名称】 allowMonitor **【函数功能】 允许监听 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::allowMonitor( COneLeg* pMonitorParty ) { if(m_State != GCST_CONNECTED) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]接受监听失败, 通道未处于连接状态"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } if(!m_Mixer.allowedMonitor()) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]接受监听失败, 无空闲混音发端可连接"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); return false; } return m_Mixer.setupMonitor(pMonitorParty->routeChannel()); } /***************************************************************** **【函数名称】 cancelMonitor **【函数功能】 取消监听 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::cancelMonitor( COneLeg* pMonitorParty ) { ASSERT(pMonitorParty != NULL); return m_Mixer.undoMonitor(pMonitorParty->routeChannel()); } /***************************************************************** **【函数名称】 freeResBinded **【函数功能】 释放绑定资源 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::freeResBinded( void ) { if(m_pBindIpmCh != NULL) { m_pBindIpmCh->reset(); m_pBindIpmCh = NULL; } } /***************************************************************** **【函数名称】 onDevEvent **【函数功能】 设备事件处理函数 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::onDevEvent( METAEVENT* pMetaEvent ) { ASSERT(pMetaEvent != NULL); switch(pMetaEvent->evttype) { case GCEV_OFFERED: // 呼入 __onOffered(pMetaEvent); break; case GCEV_DIALING: { m_State = GCST_DIALING; CMC::GetInstance().onDevResState(m_Id); } break; case GCEV_ACCEPT: m_State = GCST_ACCEPTED; break; case GCEV_ALERTING: __onAlerting(pMetaEvent); break; case GCEV_PROGRESSING: __onProgressing(); break; case GCEV_CONNECTED: __onConnected(pMetaEvent); break; case GCEV_ANSWERED: __onAnswered(); break; case GCEV_DROPCALL: __onCallFinish(); break; case GCEV_DISCONNECTED: __onDiconnected(pMetaEvent); break; case GCEV_SIP_RECVREINVITE: __onRecvReinvite(pMetaEvent); break; case GCEV_SIP_RECVREINVITEACK: __onRecvReinviteAck(pMetaEvent); break; case GCEV_SIP_RECVINFO: __onRecvInfo(pMetaEvent); break; case GCEV_SIP_RECVUPDATE: __onRecvUpdate(pMetaEvent); break; case GCEV_SIP_RECVSUBSCRIBE: __onRecvSubscribe(pMetaEvent); break; case GCEV_SIP_RECVMESSAGE: __onRecvMessage(pMetaEvent); break; case GCEV_SIP_RECVNOTIFY: __onRecvNotify(pMetaEvent); break; case GCEV_SIP_RECVREFER: __onRecvRefer(pMetaEvent); break; case GCEV_SIP_RECVOPTIONS: __onRecvOption(pMetaEvent); break; case GCEV_TASKFAIL: __onTaskFail(); break; case GCEV_BLOCKED: { m_IsBlocked = true; LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]阻塞"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); } break; case GCEV_UNBLOCKED: { m_IsBlocked = false; LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]解除阻塞"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo); } break; default: break; } } /***************************************************************** **【函数名称】 isInnerSipUser **【函数功能】 判断是否是内线SIP用户 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::isInnerSipUser( LPCTSTR User ) { ASSERT(User != NULL); ISXSIP_REGUSER RegUser; return ISXE_gc_SipGetRegisterUser(User, &RegUser) == 0; } /***************************************************************** **【函数名称】 reinvite4T38 **【函数功能】 为T38传真发送reinvite **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::reinvite4T38( void ) { ISXSIP_IE_BODY_EX Body; __constructReinviteBody4T38(&Body); return ISX_gc_SIPSendReinvite(m_CallId, EV_ASYNC, &Body) == 0; } /***************************************************************** **【函数名称】 reply **【函数功能】 响应呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::reply( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行reply, State = %d"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_State); if(m_State == GCST_OFFERED) // 话机直接发起的呼叫请求 { if(ISX_gc_AcceptCall(m_CallId, 0, EV_ASYNC) == 0) { CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_RING_BACK, m_CallerNum, m_CalleeNum); return true; } else { __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]AcceptCall失败, 呼叫释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return false; } } else if(m_State == GCST_CONNECTED) // ocx发出的呼叫请求 { if(!playTone(CHANNEL_TONE_RINGBACK)) // 对a-leg放振铃音 { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]播放回铃音失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); } return true; } else { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]响应呼叫时状态异常, 呼叫释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return false; } } /***************************************************************** **【函数名称】 progress **【函数功能】 呼叫进展 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::progress( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行progress, State = %d"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_State); switch(m_State) { case GCST_OFFERED: case GCST_ACCEPTED: // 话机直接发起的呼叫请求 { GCPARAMEX_CALLPROGRESS gcCallProcess; __constructProgressBody(&gcCallProcess); ASSERT(m_pBindIpmCh != NULL); if(ISX_gc_CallProgress(m_CallId, 0, EV_ASYNC, &gcCallProcess) != 0 || !m_pBindIpmCh->startMedia()) { __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]progress失败, 呼叫释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return false; } return true; } break; case GCST_CONNECTED: // ocx发出的呼叫请求 { // 停止A-Leg的振铃音 playTone(CHANNEL_TONE_NULL); return true; } break; default: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]progress状态异常, 呼叫释放, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return false; } } } /***************************************************************** **【函数名称】 accept **【函数功能】 应答呼叫 **【参数】 **【返回值】 ****************************************************************/ bool CVoipChannel::accept( void ) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_NORMAL, _T("{VOIPCh}: 通道[%d-%d-%d]执行accept, State = %d"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_State); // b-leg接通 switch(m_State) { case GCST_OFFERED: case GCST_ACCEPTED: // 话机直接发起的呼叫请求 { GCPARAMEX_ANSWERCALL AnswerCallEx; __constructAnswerBody(&AnswerCallEx); if(ISX_gc_AnswerCall(m_CallId, 0, EV_ASYNC, &AnswerCallEx) != 0) { LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]呼叫释放, a-leg应答呼叫失败, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); return false; } } break; case GCST_CONNECTED: // ocx发出的呼叫请求 { // 停止A-Leg的振铃音 playTone(CHANNEL_TONE_NULL); CSessionShell::GetInstance().onLineStateChanged(m_Id, m_SessionCode, VIRTUAL_LINE_STATE_TALKING, m_CallerNum, m_CalleeNum); } break; default: { ASSERT(FALSE); __clearCall(IPEC_SIPReasonStatus500ServerInternalError); LOGGER(LOG_CLASS_DEV, LOG_LEVEL_WARNING, _T("{VOIPCh}: 通道[%d-%d-%d]呼叫释放, a-leg应答呼叫时处于异常状态, Caller = %s, Callee = %s"), m_Id.NodeNo, m_Id.BoardNo, m_Id.ChanNo, m_CallerNum, m_CalleeNum); return false; } } return true; } /***************************************************************** **【函数名称】 onPlayEnd **【函数功能】 放音结束处理函数 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::onPlayEnd( LPCTSTR lpDtmf ) { // 通知呼叫控制层放音结束事件 CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_PLAY_END, lpDtmf); } /***************************************************************** **【函数名称】 onRecEnd **【函数功能】 录音结束处理函数 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::onRecEnd( UINT TaskId ) { // 通知呼叫控制层录音结束事件 CString strTmp; strTmp.Format(_T("%lu"), TaskId); CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, DEV_CH_EVT_REC_END, strTmp); } /***************************************************************** **【函数名称】 onToneEnd **【函数功能】 tone音结束处理函数 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::onToneEnd( int ToneTp ) { // TODO: do nothing } /***************************************************************** **【函数名称】 onFaxEnd **【函数功能】 传真结束处理函数 **【参数】 **【返回值】 ****************************************************************/ void CVoipChannel::onFaxEnd( bool IsSuccess, LPCTSTR Reason ) { CSessionShell::GetInstance().onLineChannelEvent(m_Id, m_SessionCode, IsSuccess ? DEV_CH_EVT_FAX_OK : DEV_CH_EVT_FAX_FAILED, Reason); __clearCall(IPEC_SIPReasonStatus200OK); } /***************************************************************** **【函数名称】 getStateStr **【函数功能】 返回状态字符串 **【参数】 **【返回值】 ****************************************************************/ LPCTSTR CVoipChannel::getStateStr( void ) const { if(m_IsBlocked) return _T("block"); switch(m_State) { case GCST_NULL: return _T("free"); case GCST_DIALING: return _T("calling"); case GCST_OFFERED: return _T("alerting"); case GCST_ALERTING: return _T("ringback"); case GCST_CONNECTED: return _T("talking"); default: return _T("unknown"); } }