在Vovida的基础上实现自己的SIP协议栈(六)
卢政 2003/08/08
3.3 等待对方的呼叫:上面花了那么长的时间叙述了如何发起一个呼叫,我们再来介绍一下如何接收一个呼叫:
当用户进入Idle状态以后,如果系统接收到一个INVITE消息,系统将进入Ring状态,并且进入Opring操作中,这个时候硬件设备将播放振铃声,这个时候如果用户决定摘机通话,那么offhook事件就会产生,同时OpAnswerCall将使状态机进入InCall状态,向主叫发送200响应消息,同样RTP/RTCP通道打开,开始通话,如果通话完毕,双方挂机,那么互相发送SIP Bye消息,OpEndCall将使系统重新回到Idle状态。
下图表示了从接收到INVITE消息通话完毕的各个状态之间程序各种类之间的迁移过程,和主动呼叫的情况一样,如果协议栈软件应用于Marshal或者是Redirection Server的话,那么采用的协议流程和下面又有一些不一样了,在后续章节会对这些做详细介绍。
在本图中粗体的部分表示加入MyEntryOperator队列中的操作符
正体的部分表示加入MyOperator队列中的操作符
斜体的部分表示加入MyExitOperator队列中的操作符
下面我们来详细地介绍每个操作:
3.3.1 OpRing等待对方的振铃消息
OpRing:获取对端向本地发送的INVITE消息
const Sptr < State >
OpRing::process( const Sptr < SipProxyEvent > event )
{
Sptr < SipEvent > sipEvent;
sipEvent.dynamicCast( event );
if ( sipEvent == 0 )
{
return 0;
}
Sptr < SipMsg > sipMsg = sipEvent->getSipMsg();
assert( sipMsg != 0 );
//接收INVITE消息;
Sptr < InviteMsg > msg;
msg.dynamicCast( sipMsg );
… …
Sptr < UaCallInfo > call;
call.dynamicCast( event->getCallInfo() );
assert( call != 0 );
//在UaCallInfo中存储当前的INVITE消息;
call->setRingInvite( new InviteMsg( *msg ) );
call->setContactMsg(*msg);
//保存当前的路由消息;
call->setCalleeRoute1List( msg->getrecordrouteList() );
int numContact = msg->getNumContact();
if ( numContact )
{//保存连接
SipContact contact = msg->getContact( numContact - 1 );
Sptr < SipRoute > route = new SipRoute;
route->setUrl( contact.getUrl() );
call->addRoute1( route );
}
… …
Sptr< BaseUrl > baseUrl = msg->getFrom().getUrl();
assert( baseUrl != 0 );
// Assume we have a SIP_URL
Sptr< SipUrl > sipUrl;
sipUrl.dynamicCast( baseUrl );
assert( sipUrl != 0 );
//获取主叫的Sip URL
Data callingNum = sipUrl->getUserValue();
callingNum += "@";
callingNum += sipUrl->getHost();
signal->dataList.push_back( callingNum.getData(lo) );
//把主叫和被叫的地址(URL)都装入设备的信号队列中,为媒体流和铃声回放的RTP信道做准备
SipRequestLine reqLine = msg->getRequestLine();
baseUrl = reqLine.getUrl();
assert( baseUrl != 0 );
sipUrl.dynamicCast( baseUrl );
assert( sipUrl != 0 );
string calledNum = sipUrl->getUserValue().getData(lo);
signal->dataList.push_back( calledNum );
UaDevice::getDeviceQueue()->add( signal );
//获取主叫的SDP
Sptr remoteSdp;
remoteSdp.dynamicCast (msg->getContentData(0));
bool ringbackTone = false;
//创建本地的SDP
SipSdp localSdp;
if ( remoteSdp != 0 )
{
localSdp = *remoteSdp;
Data host = theSystem.gethostAddress();
if(UaConfiguration::instance()->getNATAddress() != "")
{
host = UaConfiguration::instance()->getNATAddress();
}
//设定本地的SDP
setStandardSdp(localSdp, host,UaDevice::instance()->getRtpPort());
}
//获取本地状态是否当前的硬件状态支持Call Waiting
HardwareStatusType hdwStatus = UaDevice::instance()->getHardwareStatus();
//检验本地是否支持零声回放(回放的话要在零声消息里增加本地的SDP)
StatusMsg statusMsg;
if (UaC