1、摘机dsp driver线程
//上报摘机事件
SipDrv_EventRecvCb
Drv_Event.CnxId = cnxId;
Drv_Event.Data = data;
Drv_Event.Event = event;
Drv_Event.Length = length;
Drv_Event.pData = pData;
Drv_Event.pEndptState = pEndptState;
//将底层事件投入驱动事件队列,并激活事件处理信号量
SipDrv_EventPost(&Drv_Event);
bosSemGive(&gstSipDrvDC.DrvSemId);
2、摘机用户sip main线程
//处理事件队列中的事件
SipUCC_ProcessEvent()
//转化为rv事件
//eRvEvent = RV_CCTERMEVENT_OFFHOOK
SipUCC_DrvEventToMtfEvent(pstSipDriverEvent,&eRvEvent,&stMtfEventInfo);
//检索线路终端id
termIdx = pstSipDriverEvent->pEndptState->lineId;
//向MTF上报用户事件
//gstIppTerm[termIdx]存储了MTF模拟终端对象的句柄,该句柄是在
//用户层向MTF注册模拟终端时创建的。
rvMtfSendKeyEvent(gstIppTerm[termIdx],eRvEvent,&stMtfEventInfo);
//从终端基类中提取mdm终端对象
mdmTerm = rvIppMdmTerminalGetMdmTerm(hTerm);
//构造参数列表
rvMdmParameterListConstruct(¶msList);
//处理摘机事件
switch (event)
case RV_CCTERMEVENT_OFFHOOK:
//为当前事件构造keyed=kh参数
addParam(¶msList, "keyid", "kh");
//处理kf/kd事件
rvMdmTermProcessEvent(mdmTerm, "kf", "kd", NULL, ¶msList);
rvMdmTermQueueObsEvent(term, pkg, id, media, args);
//如果该终端没有激活,则忽略该终端所有事件
//该标记在用户层注册物理终端时设置为false来激活
if(term->inactive)
return
//构造请求事件对象,将该请求事件放置到终端blockedEventBuffer
//中。
queueBlockedObsEvent(term, &item, media, args);
//递送请求事件
rvMdmTermPostEvent(t);
//如果终端未注册,则忽略该终端所有事件
if (rvCCTerminalMdmBlockedEventIsUnregOn(term) == RV_TRUE)
return
//向MTF发送消息,触发MTF处理终端事件,这里与MTF事件
//处理线程的关键也能过终端对象句柄指针。
sendEventToMsgSocket(x);
//资源释放
rvMdmParameterListDestruct(¶msList);
3、摘机MTF主线程
//mdm管理处理终端事件
rvCCProviderMdmProcessTerminationEvent(data)
rvMdmTermProcessBlockedEvent(term);
//判断当前终端是否激活处理事件上报的标记,该标记在注册模拟线路时开启,
//如果开启,则进行上报事件的处理
if (rvCCTerminalMdmBlockedEventIsObsOn(term))
processBlockedEventObserved(x);
//当前终端如果有待处理事件,则进行处理
while (rvListSize(&term->blockedEventBuffer))
//获取第一个待处理事件对象
event = rvListFront(&term->blockedEventBuffer);
//从事件对象中分别提取媒体描述符、包对象名、包对象子分类名
//和当前事件参数
media= (RvMdmMediaStream*)
rvCCTerminalMdmGetMediaEventData(term);
pkg = rvMdmPackageItemGetPackage(rvMdmEventGetName(event));
id =rvMdmPackageItemGetItem(rvMdmEventGetName(event));
args = (RvMdmParameterList*)rvMdmEventGetParameterList(event);
//事件处理
rvMdmTermProcessObsEvent(rvCCTerminalMdmGetMdmTerm(term),
pkg,id,media,args);
//如果是数图事件,则进行单独处理
if(isInDigitMapPkg(&item) )
//当前事件为摘机流程,暂不关心
//处理其它事件
rvCCTerminalMdmProcessEvent(t, pkg, id, media, args);
//将事件包映射为事件枚举RV_CCTERMEVENT_OFFHOOK
event = rvCCTerminalMapEvent(x, pkg, id, args, keyId);
//如果上报拨号完成事件,则将最终的号码串存入终端
//dialString中
if (event == RV_CCTERMEVENT_DIALCOMPLETED)
//当前摘机处理暂不关心
//存储最后事件处理到终端的lastEvent中
setLastEvent(term, pkg, id, args);
//事件处理
rvCCTerminalProcessEventId(x, event, keyId, callMgr, media);
//获取该终端当前活动的连接对象
c = rvCCTerminalGetActiveConnection(t);
switch(eventId)
case RV_CCTERMEVENT_OFFHOOK:
if (eventId==RV_CCTERMEVENT_OFFHOOK)
//标记音频终端状态为已经激活话柄
t->audioTermState |=
RV_CCAUDIOTERM_HS_ACTIVE;
//对音频终端对象进行事件处理
eventId = rvProcessAudioTermEvent(t, c, eventId);
//保存音频终端对象,如果是模拟线路类型则音频//终端对象为模拟终端对象自身。
oldAt = rvCCTerminalMdmGetActiveAudioTerm(t);
//获取当前活动连接的状态
activeConnState =rvCCConnectionGetState(
rvCCTerminalGetActiveConnection(t));
//如果当前终端所有连接都是空闲,或者当前活动 //连接是在振铃,则进行处理。
if ((rvCCTerminalGetNumActiveConnections(t) == 0) ||(activeConnState== RV_CCCONNSTATE_ALERTING)
||(activeConnState==RV_CCCONNSTATE_TRANSFER_ALERTING))
//设置模拟终端对象的音频终端对象为自身
//term.activeAudioTerm = term
Term_evn = setActiveAudioTerminal(t, event);
returnterm_evn;
//基本呼叫摘机时连接都是空闲,因此不走后继
//流程代码。
//获取当前线路终端连接是否存在呼叫控制对象,如果
//不存在呼叫控制对象,则构造一个新的呼叫对象
call = rvCCConnectionGetCall(c);
if (call == NULL)
rvInitNewCall(callMgr, c);
//创建一个呼叫对象,放入callMgr列表中
//callMgr. Calls call
//call.callMgr = callMgr
call = rvCCCallMgrCreateCall(callMgr);
//进行呼叫控制对象与连接对象的关链
//conn.call = call
//call->connections conn
//call->connNum++
rvCCCallAddParty(call, conn);
break;
//继续处理摘机事件
rvProcessEvent(c, eventId, reason);
//处理终端连接事件,这里调用回调函数为
//rvCCConnMdmProcessEvent,下面单独分析
rvCCConnectionProcessTermEvent(c, eventId,
&callStillAlive, reason);
x->funcs->processTermEventF(x, eventId,
callStillAlive, reason);
//在挂机事件处理中,如果该线路还存在其它呼叫,则进行 //其它呼叫的挂机判断处理。当前是摘机事件,暂不关心。
if ((origEventId == RV_CCTERMEVENT_LINE) ||
(origEventId == RV_CCTERMEVENT_ONHOOK) ||
(origEventId == RV_CCTERMEVENT_REJECT_KEY))
xxx
//事件处理完成后从列表中消除
rvListPopFront(RvMdmEvent)(&term->blockedEventBuffer);
------------------------------------------------------------------------------------------------------------------------------
//MDM连接对象处理终端事件(这个函数在下文分析时省略了do while的循环处理,这里根据返回下一个事件的值进行循环处理,比如当前摘机事件在处理,摘机事件处理完成后,设置下一个事件为RV_CCTERMEVENT_DIALTONE,则继续调用rvCCConnMdmProcessEvent
函数进行处理)
rvCCConnMdmProcessEvent
//终端连接对象进行事件处理,当前摘机事件不做任何处理
eventId = rvCCTermConnProcessEvents(conn, eventId, &eventReason);
//调用回调mdmExtClbksDeprecated.preProcessEvent或
//mdmExtClbks.preProcessEventCB通知用户,在事件处理之前是否需要进行其它操作
rvIppMdmExtPreProcessEventCB(conn, eventId, &eventReason);
//进行call ctrl状态机处理。
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_OFFHOOK:
//获取当前连接状态,当前在IDLE状态
switch (rvCCConnectionGetState(conn))
case RV_CCCONNSTATE_IDLE:
//conn. State = RV_CCCONNSTATE_INITIATED
rvCCConnectionSetState(conn, RV_CCCONNSTATE_INITIATED);
//调用rvCCConnMdmInitiatedCB回调进行mdm连接对象的
//初始化,rvCCConnMdmInitiatedCB回调在下面单独分析。
//播号音的播放及数图的激活则在此回调进行。
rvCCConnectionInitiatedCB(conn);
x->clbks->initiatedCB(x);
//调用mdmExtClbksDeprecated.connectionCreated回调通知用户层
//一个新的MDM连接已经建立
rvIppMdmExtConnectionCreatedCB(conn);
//清除终端的数图收集池,为数图收号做准备
//term.dialString = 清空
rvCCTerminalMdmResetDialString(t);
//返回放播号音事件,继续调用rvCCConnMdmProcessEvent函数进行处理
return RV_CCTERMEVENT_DIALTONE;
//调用用户回调显示待处理显示
rvCCConnectionDisplay(conn, t, eventId, eventReason,
rvCCProviderMdmGetDisplayData(p));
//调用回调通知用户,在事件处理之后是否需要进行其它操作。
rvIppMdmExtPostProcessEventCB(conn, eventId,eventReason);
-------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmInitiatedCB
//这里rvCCConnectionGetLineId获取conn对象中的lineId标识,在终端对象注册时
//和终端关联的3个conn对象都进行顺序赋值lineId标识,从1到3。
rvCCTerminalMdmSetLineInd(t, rvCCConnectionGetLineId(x), RV_INDSTATE_ON);
//构造包Indid参数
rvMdmParameterListConstruct(¶ms);
RvSnprintf(strLineId, sizeof(strLineId), "l%03d", lineId);
addSignalParam(¶ms, "Indid", strLineId);
switch (state)
case RV_INDSTATE_ON:
//添加state参数
addSignalParam(¶ms, "state", "on");
//触发线路连接指示包ind/is,该包中协带两个参数Indid=l00x,
//state=on
rvCCTerminalMdmStartSignalUI(x, "ind", "is", ¶ms);
//给终端触发信号
startSignal(term, pkg, id, args, RV_MDMSIGNAL_BRIEF);
//构建信号描述符对象
buildSignalDescr(x, &signalDesc, pkg, id, args, type);
//处理信号描述符
rvMdmTermProcessNewSignals(x, &signalDesc);
//遍历信号描述符中所有信号
for(i=0;icurSignals);
iter!=rvListEnd(&x->curSignals);
iter = rvListIterNext(iter))
curSignal = rvListIterData(iter);
if (signalMatch(signal,&curSignal->signal))
startTheSignal = RV_FALSE;
//如果上面条件没有触发,则继续进行信号处理
if (startTheSignal)
//信号启动
rvMdmTermStartSignal(x,signal,&type,&timeout)
//判断当前终端类是否支持此包对象,如果支持才进
//行处理。终端类对包的支持在rvInitIppClasses函数中
//设置。
if (rvMdmTermIsPkgSupported_
(rvCCTerminalMdmGetMdmTerm(x),
rvMdmPackageItemGetPackage(name)))
//检查当前ind包中是否存在is信号对象
//如果不存在则取当前signal自身,如果包中含有
//该信号注册,则取包中的信号对象做为默认参考
//对象,后面进行信号处理时,如果signal对象的
//参数值没有协带,则从info中的信号对象中获取。
info = rvMdmTermMgrGetSignalInfo_
(mdmMgr,&name->package,&name->item);
//映射信号参数到临时变量中
//signal->id = id;
//signal->pkg = pkg;
//signal->params = args;
buildMdmSignal(x,&mdmSignal,signal);
//当前播号音信号属于brief类型,则
//调用term->termClass->playSignalF回调进行
//处理。
if (*type==RV_MDMSIGNAL_BRIEF)
//term->termClass->playSignalF =
//rvStartSignalIntCB,该回调函数最终根据上
//面传来的信号确定为
//RV_MTF_SIGNAL_IND_LINE指示类型信号。
//之后再次调用应用层注册的终端管理回调
//SipStack_MtfStartSignalCB进行最终的信号 //处理。当前代码不处理
//RV_MTF_SIGNAL_IND_LINE类型信号,暂不
//关心。
rvMdmTermPlaySignal_(&x->mdmTerm,
&mdmSignal,reportCompletion,&error))
else
//no thing
//如果信号不是短暂类形,则将此信号加入到mdm终端
//当前信号处理列表中mdmterm.curSignals
if( type!=RV_MDMSIGNAL_BRIEF )
addCurSignal(x,signal,timeout);
//设置终端指示线路激活(参照上面线路指示rvCCTerminalMdmSetLineInd信号处理,
//最后跟到rvStartSignalIntCB中,没有对该信号的处理)。
rvCCTerminalMdmSendLineActive(t, rvCCConnectionGetLineId(x), RV_TRUE);
//指示终端放播号音,这里不进行分析了,进行几次回调,最终触发应用层执行放
//播号音。首先调用终端类的回调rvStartSignalIntCB,之后该函数调用了用户注册到
//终端管理的回调SipStack_MtfStartSignalCB。
rvCCTerminalMdmStartDialToneSignal(t);
//启动播号音启始定时器,该定时器的超时回调函数是在模拟终端注册时在
//rvCCTerminalMdmConstruct函数中创建的,回调函数为
//rvCCTerminalMdmDialToneTimerExpired。
IppTimerStart(rvCCTerminalMdmGetDialToneTimer(t),IPP_TIMER_RESTART_IF_STARTED,
rvCCProviderMdmGetDialToneDuration(p));
//启动了digitMapCurTimer定时器,并设置digitMapActive = RV_TRUE数图激动标记。
//该定时器是在终端注册时,rvMdmTermDigitMapTimerConstruct函数中创建,回调函
//数为digitMapTimerProcess0,该函数用于数图处理时,数图模块在该时间超时之后,
//自动触发数图完成事件。注意上面还有一个播号音放音超时事件,两个不同配置项。
rvCCTerminalMdmSetWaitForDigits(t);
4、MTF主线程继续使用rvCCConnMdmProcessEvent函数处理内部RV_CCTERMEVENT_DIALTONE事件。
rvCCConnMdmProcessEvent
//term连接对象处理事件,当前该函数没有对RV_CCTERMEVENT_DIALTONE事件处理
rvCCTermConnProcessEvents(conn, eventId, &eventReason);
//call控制主状态机处理
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_DIALTONE:
//仅仅将term连接对象的连接状态迁为DIALING
//conn.state =RV_CCCONNSTATE_DIALING
if (rvCCConnectionGetState(conn) == RV_CCCONNSTATE_INITIATED)
rvCCConnectionSetState(conn, RV_CCCONNSTATE_DIALING);
returnRV_CCTERMEVENT_NONE;
5、按键dsp driver线程
SipDrv_EventRecvCb
//发送按键事件到gstSipDrvDC.DrvEventQ队列中
SipDrv_EventPost(&Drv_Event);
6、按键用户sip main线程
SipUCC_ProcessEvent
//将DSP事件映射到MTF事件参数对象
//eMtfEventId=RV_CCTERMEVENT_DIGIT_END;
//info->digit = RV_MTF_DIGIT_x;
SipUCC_DrvEventToMtfEvent(pstSipDriverEvent,&eRvEvent,&stMtfEventInfo);
//向MTF发送事件
rvMtfSendKeyEvent(gstIppTerm[termIdx],eRvEvent,&stMtfEventInfo);
switch (event)
case RV_CCTERMEVENT_DIGIT_END:
//将info->digit转换转换为包的参数对象keyid= xxxxxx
mapDigitToEvent(info->digit, strParam)
addParam(¶msList, "keyid", strParam);
//构成kp/ku包请求事件,给MTF发送消息,触使MTF线程处理。
rvMdmTermProcessEvent(mdmTerm, "kp", "ku", NULL, ¶msList);
7、按键MTF主线程处理
//mdm管理处理终端事件
rvCCProviderMdmProcessTerminationEvent
rvMdmTermProcessBlockedEvent(x);
processBlockedEventObserved(x);
rvMdmTermProcessObsEvent(rvCCTerminalMdmGetMdmTerm(term),
pkg,id,media,args);
//当前是数据事件包,该判断条件成立
if(isInDigitMapPkg(&item) )
//上面摘机流程已经激活数图处理
if(term->digitMapActive )
//进行数图算法处理,该函数在单个文件中进行分析。
processDigitMapEvent(term,&item,media,args);
//mdm终端对象进行事件处理
rvCCTerminalMdmProcessEvent(t, pkg, id, media, args);
//映射为RV_CCTERMEVENT_DIGIT_END事件
event = rvCCTerminalMapEvent(x, pkg, id, args, keyId);
//将最后处理事件存储在term.lastEvent中
setLastEvent(term, pkg, id, args);
rvCCTerminalProcessEventId(x, event, keyId, callMgr, media);
//获取当前终端的活动连接对象
c = rvCCTerminalGetActiveConnection(t);
//进行事件处理
rvProcessEvent(c, eventId, reason);
//呼叫控制连接进行终端事件处理
//调用x->funcs->processTermEventF回调进行事件处理,
//该回调函数为rvCCConnMdmProcessEvent,最终进行
//mdm的连接对象进行事件处理。该函数下面单独分析。
rvCCConnectionProcessTermEvent(c, eventId, &callStillAlive,
reason);
--------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmProcessEvent
//term连接对象处理事件
rvCCTermConnProcessEvents(conn, eventId, &eventReason);
switch(eventId)
case RV_CCTERMEVENT_DIGIT_END:
//收到按键事件,在连接为通话状态,且非忙转时,处理outbanddtmf,当前
//普通呼叫流程不会进入。
if ((rvCCConnectionGetTermState(conn) == RV_CCTERMCONSTATE_TALKING) &&
(rvCCCallGetTransferType(rvCCConnectionGetCall(conn)) !=
RV_CCCALL_TRANSFER_BLIND))
//out band dtmf处理
//调用mdmExtClbksDeprecated.preProcessEvent或mdmExtClbks.preProcessEventCB回调
//通知用户进行事件处理之前是否执行其它操作。
rvIppMdmExtPreProcessEventCB(conn, eventId, &eventReason);
//呼叫控制状态机处理
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_DIGIT_END:
//当前连接状态正在拨号状态
if (rvCCConnectionGetState(conn) == RV_CCCONNSTATE_DIALING)
//执行新的按键处理,一个按键会触发on/off两个按键事件上报,这里在
//分析时只列举了off。最终调用x->clbks->newDigitCB回调进行处理。
//该回调函数为rvCCConnMdmNewDigitCB,在下面单独分析。
rvCCConnectionNewDigitCB(conn, RV_CCCAUSE_EVENT_END);
return RV_CCTERMEVENT_NONE;
//调用回调通知用户进行状态显示
rvCCConnectionDisplay(……);
//调用回调mdmExtClbks.postProcessEventCB通知用户进行事件处理之后是否执行其它
//操作。
rvIppMdmExtPostProcessEventCB(conn, eventId,eventReason);
-------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmNewDigitCB
//如果当前是第一次按键,并且是按键on事件,则停止MDM终端对象的dialToneTimer
//定时器,及播号音。
if ((rvCCTerminalMdmIsFirstDigit(t)) && (reason == RV_CCCAUSE_EVENT_BEGIN))
//停止dialToneTimer定时器
IppTimerStop(rvCCTerminalMdmGetDialToneTimer(t));
//给用户层发送信号停止
rvCCTerminalMdmStopSignals(t);
//获取当前终端关联的音频处理终端,当前终端为模拟终端,则音频处理终端
//为自身。
at = rvCCTerminalMdmGetActiveAudioTerm(x);
term = rvCCTerminalMdmGetImpl(at);
rvMdmTermEventStopSignals(term);
//停止终端对象的curSignals中当前所有正在执行的信号
stopActiveSignals(x,NULL,RV_MDMSIGNAL_GOTEVENT);
for (i=rvListBegin(&x->curSignals);i!=rvListEnd(&x->curSignals);i=next)
next=rvListIterNext(i);
//这里最终调用了用户在终端管理中设置的回调函数
// SipStack_MtfStopSignalCB来停止放音。
stopCurSignal(x,&i,reason);
//简单跟了一下代码,curSignalLists好像不会触发。
processNewSignalLists(x,NULL,RV_MDMSIGNAL_GOTEVENT);
//如果当前终端设置playDtmfEnabled参数,则需要触发用户放播号音信号,当前该标
//记没有开启。
if (rvCCTerminalMdmIsDtmfPlayEnabled(t))
//让终端自己放播号音
8、后续按键在数图匹配成功后,给MTF上报“dd/ce”事件处理包。其中协带参数,“ds=最终用户按键号码”,“Meth=PM或FM或UM,匹配结果,如果匹配失败,则不协带此参数”
rvMdmTermProcessObsEvent
//当前虽然是dd数图包,但包id是“ce”,不符合按当前键处理事件,所以这不执
//行这里函数
if(isInDigitMapPkg(&item) )
//xxx
rvCCTerminalMdmProcessEvent(t, pkg, id, media, args);
//映射为 RV_CCTERMEVENT_DIALCOMPLETED 事件
event = rvCCTerminalMapEvent(x, pkg, id, args, keyId);
if (event == RV_CCTERMEVENT_DIALCOMPLETED)
//如果是数图完成事件,则从ds参数中提取最终按键字符串存入终端对象
//的dialString中。
//将事件及参数存入终端对象的lastEvent中,这里数图事件参数比较关键,后面
//进行数图结果处理时,则从这里提出结果参数。
setLastEvent(term, pkg, id, args);
//调用x->funcs->processTermEventF回调进行事件处理,该回调函数为
// rvCCConnMdmProcessEvent,在后面单独分析。
rvCCTerminalProcessEventId(x, event, keyId, callMgr, media);
rvProcessEvent(c, eventId, reason);
rvCCConnectionProcessTermEvent(c, eventId, &callStillAlive, reason);
x->funcs->processTermEventF(x, eventId, callStillAlive, reason);
------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmProcessEvent
//进行呼叫控制状态机处理
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_DIALCOMPLETED:
//当前MDM连接状态为 DIALING
switch(rvCCConnectionGetState(conn))
case RV_CCCONNSTATE_DIALING:
//调用x->funcs->addressAnalyzeF回调进行用户地址解析,并构造sip连接
//对象,该回调函数为rvCCConnMdmAddressAnalyze,下面单独分析。
otherParty = rvCCConnectionAddressAnalyze(conn, cause);
//获取当前mdm连接对象是否有transferLine,当前普通呼叫没有此值
transferLineConnection = rvCCConnectionGetTransferLine(conn);
//当前数图匹配成功,已经成功构建了sip连接对象
if (otherParty != NULL)
if (rvCCTerminalGetState(t) ==
RV_CCTERMINAL_CFW_ACTIVATING_STATE)
//当前普通呼叫不进此流程
else
if ((transferLineConnection != NULL)…)
//不走此流程
else
//conn->curParty = party;
//将sip连接对象关联到mdm连接对象上。
rvCCConnectionSetConnectParty(conn, otherParty);
//更新当前mdm连接对象协商状态
//conn. offerAnswerState =
//RV_MTF_OFFERANSWER_OFFER_BUILDING
oldOfferAnswerState =
rvCCConnSipGetOfferAnswerState(otherParty);
rvCCConnSipSetOfferAnswerState(otherParty,
RV_MTF_OFFERANSWER_OFFER_BUILDING);
//调用x->funcs->createMediaF回调创建媒体
//该回调函数为rvCCConnMdmCreateMedia,该函数在下面
//单独分析。
mediaState = rvCCConnectionCreateMedia(conn, NULL);
//更新媒体状态
//conn. mediaState = RV_CCTERMEVENT_MEDIAOK
rvCCConnectionSetMediaState(conn, mediaState);
//更新连接状态,conn.state =
//RV_CCCONNSTATE_ADDRESS_ANALYZE
rvCCConnectionSetState(conn,
RV_CCCONNSTATE_ADDRESS_ANALYZE);
//返回媒体OK,循环调用rvCCConnMdmProcessEvent
//进行内部事件MEDIAOK处理。
return RV_CCTERMEVENT_MEDIAOK;
-------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmAddressAnalyze
//将用户播的号码存储在连接对象的dialString中
rvStringCopy(&conn->dialString, &term->dialString);
//获取数图匹配结果,如果数图匹配失败,则返回空连接对象。这里取数图匹配结果
//的流程为,从终端lastEvent中取出最后事件参数列表,并找出“Meth”参数,如果
//没有找到,则表示数图不匹配,否则如果找到了,则结果为“UM或FM表示”
//匹配成功,其它表示匹配失败,也就是“不匹配”和“部分匹配”都认为是失败。
if (isDialStringMatching(term) == RV_FALSE)
return NULL;
//调用呼叫控制的回调x->clbks->addressAnalyzeCB进行地址解析处理。该回调函数为
// addressAnalyzeCB,该回调下面单独分析。
rvCCConnectionAddressAnalyzeCB(x, rvStringGetData(&conn->dialString),
RV_CCCAUSE_OUTGOING_CALL, reason);
//存储用户按键到“redialString”中,并复位用户按键
rvStringCopy(&term->redialString, &term->dialString);
rvStringAssign(&term->dialString, "");
-------------------------------------------------------------------------------------------------------------------------------
addressAnalyzeCB
if (inReason == RV_CCCAUSE_OUTGOING_CALL)
//调用x->termClass->mapDialStringToAddressF回调进行数图字符到SIP地址的映
//射,最终会调用用户层注册的SipStack_MtfMapDialStringToAddressCB函数进行
//地址映射处理。
rvMdmTermMapDialStringToAddress_(mdmTerm, address, destAddress);
//根据用户层处理的地址结果,判断当前地址类型
switch(getProviderType(destAddress))
case RV_MTF_PROTOCOL_SIP:
//创建sip连接对象
createNewSipConnection(x, destAddress, outReason);
//设置一些业务转移地址
findDestinationAddress(c, address, destAddress, sizeof(destAddress)
if (((strchr(address, '@') != NULL)…)
strncpy(foundDestAddress, address, sizeof(foundDestAddress)-1);
//转移业务地址的设置,暂不关心
//检测当前是否呼叫自己,如果是则返回错误
if (rvCCSipPhoneIsCallingMyself(c, destAddress) == rvTrue)
*reason = RV_CCCAUSE_BUSY;
return NULL
//创建sip连接对象
rvCCProviderSipCreateConnection(p, ts)
//构建sip连接对象,初始化一些连接对象参数,并构造了三个定时器
// referTimer、ringingTimer、updateResendTimer,分别对应的超时函数为
// rvCCConnSipReferTimerExpires、rvTimerSendRinging、
// rvCCConnSipResendUpdate
rvCCConnSipConstruct(c, p, t, provider->alloc);
//conn. State = RV_CCCONNSTATE_IDLE
rvCCConnectionSetState(c, RV_CCCONNSTATE_IDLE);
//将sip连接对象加入到呼叫控制对象中
rvCCCallAddParty(call, newConn);
rvCCConnectionSetCall(party, call);
rvPtrListPushBack(&call->connections, party);
call->connNum++;
//将目标地址存储到sip连接对象的remoteAddress中。
rvCCConnSipSetRemoteAddress(newConn, destAddress);
//如果用户帐号不为空,则存储到sip连接对象的localAddress中。
if ((termId != NULL) && (strcmp(termId, "")))
RvSnprintf(localAddress,RV_SIPCTRL_ADDRESS_SIZE, "%s@%s", termId,
tmpIp);
rvCCConnSipSetLocalAddress(newConn, localAddress);
--------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmCreateMedia
//创建线路媒体
rvCCTerminalMdmCreateLineMedia(t, at);
//term. streamList向量为空
if (rvCCTerminalMdmGetNumOfStreams(term) == 0)
//构造一个媒体流信息对象,并分配一个新的id,该值从1开始
id = (int)rvCCTerminalMdmAddEmptyMediaStream(term);
//设置媒体流信息对象的本地编码能力
media = rvCCTerminalMdmSetDefaultMedia(activeTerm, id,
rvCCTerminalMdmGetTermId(t));
//从终端对象中获取本地存储的编码能力
sdp = rvCCTerminalMdmGetMediaCaps(term);
x->mediaCaps;
descr = rvMdmMediaDescriptorGetStream(mediaCaps, 0);
rvMdmStreamDescriptorGetLocalDescriptor(descr, 0);
//设置本地SDP一些默认字段,如“o”、“v”等。
rvCCTerminalMdmFillSdpDefaultInfo(t, sdp, username);
//将本地sdp对象加入到媒体流描述符对象的本地描述符中
media = rvCCTerminalMdmGetMediaStream(term, (RvUint32)streamId);
rvMdmMediaStreamInfoAddLocalDescriptor(media, (RvSdpMsg*)sdp);
//设置媒体流描述符对象模式为 RV_MDMSTREAMMODE_SENDRECV
rvMdmStreamDescriptorSetMode(&media->streamDescr,
RV_MDMSTREAMMODE_SENDRECV);
//创建媒体
rvCCTerminalMdmCreateMedia(term, media, NULL)
rvMdmMediaStreamInfoCreateMdmMedia(media, term, rspMedia);
//构造临时媒体描述符变量对象mdmMediaInfo
fillMdmInfo(x, &mdmMediaInfo);
//设置媒体描述符信息对象参数
param = &mdmMediaInfo.param;
param->cmd = RVMDM_CMD_CREATE;
param->normal.localSdp =
rvSdpMsgListGetElement(mdmMediaInfo.localDescr, 0);
param->normal.remoteSdp = NULL;
param->normal.localCapsSdp = NULL;
//调用term->termClass->createMediaF回调进行媒体流创建
//该回调函数为rvPhysCreateMediaIntCB,该函数最终内部又 //调用mtfMgr->mediaClbks.startPhysicalDeviceCB,当前用户 //层没有设置此回调,暂不关心。
rvMdmTermCreateMediaStream_(mdmTerm, mediaObsolete,
&mdmMediaInfo, &mdmError)
//获取当前已经构造的媒体流描述符并返回该对象
mediaStream=rvCCTerminalMdmGetMediaStream(term, PHYS_TERM_STREAM_ID);
returnmediaStream
//创建RTP对象
mediaState = createRtpMedia(x, t);
//创建临时终端对象
initRtpMedia(x, conn, term, &eph)
if (conn->streamId == RV_MDM_STREAMID_NONE)
//构造一个临时终端对象,关联到当前mdm连接对象中
eph = rvCCTerminalMdmSelectRtpTerm(term, x)
//创建一个EPHEMERAL类型的终端对象,id为“rtp”,并加入到
//终端管理列表。这里创建的终端对象在代码中没有用,因为下面
//流程因为mgr->selectF回调为空,不走处理该对象的流程,在下面 //该对象最终又删除。
rvCCProviderMdmCreateTerminal(p, RV_CCTERMINALTYPE_EPHEMERAL,
"rtp", 0, &termPropertiesTmp);
//创建mdmterm对象,基类就是上面创建的,该对象下面被删除,
//没有用到该对象。
rvMdmTermConstruct_(mdmEphTerm, NULL, tempT);
//调用回调函数mgr->xTermMgrClbks->registerEphTermF注册临时
//终端对象,id为“rtp/xxx”,该回调函数为
//mdmProviderRegisterEphTerm,该回调函数在下面单独分析。
sprintf(termName, "rtp/%d", termNum++);
rtpTerm = rvMdmTermMgrRegisterEphemeralTermination(mgr,
mtfMgr->rtpClass, termName, NULL);
//取物理终端对象的用户数据做为临时终端对象的数据。
rvMdmTermSetUserData(rtpTerm, userData);
//删除上面创建的id为“rtp”的无用终端对象。
rvCCProviderMdmRemoveTerminal(p, tempT);
rvCCProviderMdmReleaseTerminal(tempT);
//将新建的rtpTerm对象关联到当前mdm连接对象的ephTerm中
rvCCConnMdmSetEphTerm(c, rtpTerm);
//关联mdm连接对象到rtpterm对象中
//x->activeConnection = 0;
//x->connections[0] = conn;
ephT = rvCCConnMdmGetEphXTerm(c);
rvCCTerminalSetEphemeralActiveConnection(ephT, c);
//给当前临时终端对象分配一个媒体描述符对象,并将分配得到的ID存
//入到当前mdm连接对象的streamId中。
ephTerm = rvCCTerminalMdmGetImpl((*eph));
conn->streamId = rvCCTerminalMdmAddEmptyMediaStream(ephTerm);
//存储本地地址到临时终端对象中
ephTerm = rvCCTerminalMdmGetImpl(eph);
ephTerm->localAddress = rvCCTerminalMdmGetLocalAddress(mdmterm)
///将本地sdp对象加入到媒体流描述符对象的本地描述符中
rvCCTerminalMdmSetDefaultMedia(eph, (int)conn->streamId,
rvCCTerminalMdmGetTermId(t));
//构造命令处理参数
param = &mdmMediaInfo.param;
param->cmd = RVMDM_CMD_CREATE;
param->normal.localSdp = rvSdpMsgListGetElement( &media->streamDescr.localDescriptors, 0);
param->normal.remoteSdp = rvSdpMsgListGetElement( &media->streamDescr.remoteDescriptors, 0);
param->normal.localCapsSdp = rvCCConnMdmGetMediaCaps(x);
fillMdmInfo( media, &mdmMediaInfo);
//通过用户层注册的回调处理媒体创建处理
rvRtpCreateMediaIntCB(x, &ephTerm->mdmTerm, &mdmMediaInfo)
params.action = RVMTF_MEDIA_NEW;
params.localSdp = streamDescr->param.normal.localSdp;
params.remoteSdp = streamDescr->param.normal.remoteSdp;
params.localCapsSdp = streamDescr->param.normal.localCapsSdp;
//当前回调函数为SipStack_MtfCreateMediaCB,该函数下面单独分析
mtfMgr->connMediaClbks.connCreateMediaStreamCB(…)
//如果当前连接对象设置getRemoteVendorInfoF回调才进行处理,该处理应该
//是和H323协商的媒体能力有关系,这里不关心。
rvCCConnectionGetRemoteVendorInfo(……)
returnRV_CCMEDIASTATE_CREATED
//记载连接对象媒体处理状态,x.mediaState = RV_CCMEDIASTATE_CREATED
rvCCConnectionSetMediaState(x, mediaState);
---------------------------------------------------------------------------------------------------------------------------------
SipStack_MtfCreateMediaCB
//获取当前终端对应的用户终端ID
RvCCTerminal *pCCTerminal = (RvCCTerminal*)hTerm;
RvCCTerminalMdm *pCCTerminalMdm = rvCCTerminalMdmGetImpl(pCCTerminal);
RvMdmTerm *pMdmTerm =
rvCCTerminalMdmGetMdmTerm(pCCTerminalMdm);
SIP_TERM_DC_T *pTermDc = (SIP_TERM_DC_T*)pMdmTerm->userData;
termIdx = pTermDc->ulTermIndex;
//创建RTP会话
SipUCC_RtpOpen(&cnxId,&rtpPort);
SipDrv_RtpOpen(pCnxId,pRtpPort);
//获取一个资源ID,当前总共有总线路数 * 2个资源对象
SipDrv_GetIdleCnx(&cnxId);
pCnx = &gstSipDrvCnx[cnxId];
//这里每次都取本地端口号,是不是问题?
pCnx = &gstSipDrvCnx[cnxId];
pCnx->localPortNum = gstSipUserConfig.localRtpPort;
pPort = &pCnx->localPortNum;
ip = pCnx->sourceIpaddr;
//调用rv rtp stack创建RTP会话
pCnx->rtpHandle = SipStack_RtpOpenWithIp(ip, *pPort, "Sender");
//端口基数增2,但如上面注释,是不是这里的递增基乎没有用处。
*pPort+=2;
pCnx->localPortNum = *pPort;
////调用rv rtp stack创建RTCP会话
pCnx->rtcpHandle = SipStack_RtpOpenWithIp(ip, *pPort+1, "SenderRtcp");
//关联gstSipDrvCnx到gstRtpCnxPort中
gstRtpCnxPort[cnxId].rtpPort = rtpPort;
gstRtpCnxPort[cnxId].cnxId = cnxId;
//媒体处理
SipStack_ProcessMedia(params, termIdx, cnxId)
switch(param->action)
case RVMTF_MEDIA_NEW:
pSdpParamRemote = &gstSipCallRtpInfo[cnxId].stRemoteRtp;
pSdpParamLocal = &gstSipCallRtpInfo[cnxId].stLocalRtp;
//转换SDP信息到pSdpParamLocal中
SipStack_GetSdpMediaInfo(localSdp, pSdpParamLocal);
//调用DSP创建连接
SipUCC_CreateConnection(&gstSipCallRtpInfo[cnxId],termIndex,cnxId);
//更新最后媒体流处理类型
pSdpParamLocal->CallType = SIP_CALL_TYPE_AUDIO;
pSdpParamRemote->CallType = SIP_CALL_TYPE_AUDIO;
//将当前资源ID关联到终端对象中
pTermDc->CurrentCnxId = cnxId;
--------------------------------------------------------------------------------------------------------------------------------
//注册临时终端对象
mdmProviderRegisterEphTerm
//创建临时终端对象,并加入到终端管理列表中
rvCCProviderMdmCreateTerminal(p, RV_CCTERMINALTYPE_EPHEMERAL, id, 0,
&termPropertiesTmp);
//构造mdm终端对象,其它终端类为rtpClass
rvMdmTermConstruct_(&term->mdmTerm, c, t);
//设置终端类型
rvCCTerminalMdmSetType(term, RV_MDMTERMTYPE_EPHEMERAL);
//从终端类中获取本地媒体能力
term->mediaCaps = rvMdmTermClassGetMediaCapabilites_(c);
term->inactive = RV_FALSE; //激活标记
9、rvCCConnMdmProcessEvent处理内部事件RV_CCTERMEVENT_MEDIAOK
rvCCConnMdmProcessEvent
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
//媒体创建成功
case RV_CCTERMEVENT_MEDIAOK:
switch (rvCCConnectionGetState(conn))
//数图处理完成
case RV_CCCONNSTATE_ADDRESS_ANALYZE:
//更新连接状态为state = RV_CCCONNSTATE_INPROCESS
rvCCConnectionSetState(conn, RV_CCCONNSTATE_INPROCESS);
//调用mdm连接对象的x->clbks->inProcessCB回调进行处理
//该回调函数为sipphone.c文件里的inProcessCB,下面单独
//分析。
rvCCConnectionInProcessCB(conn, RV_CCCAUSE_NEW_CALL);
return RV_CCTERMEVENT_NONE;
------------------------------------------------------------------------------------------------------------------------------
inProcessCB
//进行呼叫连接,这里的curParty是sip连接对象
rvCCCallConnect(x, x->curParty);
//将mdm连接对象关联到sip连接对象destConn. curParty中。
rvCCConnectionSetConnectParty(destConn, origConn);
destConn->userData = origConn->userData; //复制两个连接对象的用户句柄
//调用x->funcs->getMediaF回调,从mdm连接对象中获取媒体能力,该回调函数
//为rvCCConnMdmGetMedia,该函数仅仅从mdm终端对象媒体流列表中获取本地
//媒体能力。
origMedia = (RvSdpMsg*)rvCCConnectionGetMedia(origConn);
//调用sip连接对象中的回调x->funcs->makeCallF进行呼叫创建,该回调函数为
// rvCCConnSipMakeCall
rvCCConnectionMakeCall(destConn, origMedia);
-------------------------------------------------------------------------------------------------------------------------------
rvCCConnSipMakeCall
//设置远端能力集,这里能力集的值当前是从本端能力中取出的。
rvCCConnSipSetRemoteMedia(x, inMedia, RV_TRUE);
//sip连接对象构造媒体流描述符对象,并关联到sip连接对象中。这里关联id
//也是从1开始分配,注意这里一个呼叫有两个连接对象,两个连接对象分别有
//各自的媒体关联ID。
conn->streamId = rvCCProviderSipAddEmptyMediaStream(provider);
//在媒体流描述符对象中设置远端媒体能力。
media = rvCCProviderSipGetMediaStream(provider, conn->streamId);
rvMdmMediaStreamInfoClearRemote(media);
rvMdmMediaStreamInfoAddRemoteDescriptor(media, inMedia);
//发起一个SIP呼叫事件
rvCCConnSipProcessEvent(x, RV_CCTERMEVENT_MAKECALL, NULL,
RV_CCCAUSE_NEW_CALL);
//SIP连接状态机处理
connStateMachine(conn, event, reason, callStillAlive);
switch(event)
case RV_CCTERMEVENT_MAKECALL:
if (conn->state == RV_CCCONNSTATE_IDLE)
//将sip连接状态迁为RV_CCCONNSTATE_OFFERED
rvCCConnectionSetState(conn, RV_CCCONNSTATE_OFFERED);
//创建呼叫
makeCall(conn);
//使用SIP管理模块创建呼叫
rvSipControlCallMake(&provider->sipMgr,
(RvSipAppCallLegHandle)x, conn->remoteAddress,
conn->localAddress);
//取出sip stack的对话管理句柄
RvSipStackGetCallLegMgrHandle( rvSipControlGetStackHandle(
x), &hCallLegMgr);
//创建一个sip呼叫对话框对象,将sip连接对象做为用户
//数据。
RvSipCallLegMgrCreateCallLeg( hCallLegMgr, call,&hCallLeg);
//将创建的sip呼叫对话框对象关联到sip连接对象中
// conn->callLegHndl = hCallLeg
rvCCConnSipSetCallLegHandle(c, hCallLeg);
//调用sipExtClbks.preCallLegCreatedOutgoingCB回调通知 //用户是否在创建sip对话过程前阶段中进行其它处理。
rvIppSipExtPreCallLegCreatedOutgoingCB(
(RvIppSipControlHandle)x, (RvIppConnectionHandle)c,
hCallLeg, to, from);
//设置sip对话框本端contact,远端contact
//调用sipExtClbks.postCallLegCreatedOutgoingCB回调通知
//用户是否在创建sip对话过程后阶段中进行其它处理
rvIppSipExtPostCallLegCreatedOutgoingCB(
(RvIppSipControlHandle)x, (RvIppConnectionHandle)c,
hCallLeg);
//route头域设置
RvSipCallLegGetOutboundMsg(hCallLeg, &hMsg);
rvSipControlSetRouteHeaderInMsg(hCallLeg, c, hMsg);
//设置远端信息保存到sip连接对象中
//conn->remoteUserName = toUser
//conn->remotePartyUri = to
//conn->remotePartyAddress = addr
rvCCConnSipSetRemoteData(c,to);
//调用RV Stack发送呼叫
RvSipCallLegMake(hCallLeg, longfromOut, longTo);
//保存呼叫发起方到呼叫对话中。
rvCCSipPhoneSetFromAddress(call, longFrom);
10、INVITE消息发送处理sip stack对话状态改变回调
AppCallLegStateChangedEvHandler
//更改mdm连接对象呼叫状态改变原因
callLegStateChangedToCallStateChangedReason(c, eReason);
//获取mdm连接对象
party = rvCCConnectionGetConnectParty(c);
//更新mdm连接对象的lastReceivedCallStateReason
switch(callLegReason)
case RVSIP_CALL_LEG_REASON_LOCAL_INVITING:
reason = RV_MTF_CALL_STATE_REASON_LOCAL_INVITING;
rvCCConnectionSetLastReceivedCallStateReason(party, reason);
//调用sipExtClbksDeprecated.preStateChangedF或sipExtClbks.preStateChangedCB回调
//通知用户在状态改变处理过程中是否进行其它处理。
rvIppSipExtPreStateChangedCB(……)
//SIP管理对象进行事件处理
rvCCProviderSipProcessEvent(hAppCallLeg, hCallLeg, eState, eReason);
event = RV_CCTERMEVENT_NONE;
//仅进行接收到远端的处理进行事件映射
if (RvSipCallLegGetReceivedMsg(hCallLeg, &phMsg) == RV_OK)
processMessageCode(phMsg, c, &event, &cause);
//当前RVSIP_CALL_LEG_STATE_IDLE状态没有要触发的动作。
processCallState(hCallLeg, c, state, reason, &event, &cause);
//调用sipExtClbksDeprecated.postStateChangedF回调通知用户在状态改变处理过程中是
//否进行其它处理。
rvIppSipExtPostStateChangedCB(……);
11、INVITE消息发送处理sip stack对话框中消息发送回调
AppCallLegMsgToSendEvHandler
//调用sipExtClbksDeprecated.preMsgToSendF回调通知用户在消息发送之前是否进行其
//它处理。
rvIppSipExtPreMsgToSendCB
//构造Allow头域字段
setAllowHeader(hMsg);
//构造UserAgent头域字段
rvSipMgrSetUserAgentHeader(hMsg);
//获取sip对话框对象状态
RvSipCallLegGetCurrentState (hCallLeg, &state);
switch (state)
case RVSIP_CALL_LEG_STATE_IDLE:
//提取远端编码集力设置到消息的body中。
rvCCProviderSipSetMedia(hMsg, hAppCallLeg);
//将协商协商状态迁为RV_MTF_OFFERANSWER_OFFER_SENT
//con.offerAnswerState = RV_MTF_OFFERANSWER_OFFER_SENT
rvCCConnSipChangeOfferAnswerState(c, RV_TRUE, RV_FALSE)
//调用sipExtClbks.postMsgToSendCB回调通知用户在消息发送之前是否进行其它处理
rvIppSipExtPostMsgToSendCB(……);
12、18X应答接收处理sip stack 对话消息接收回调
AppCallLegMsgReceivedEvHandler
//调用回调通知用户层在处理消息接收时是否进行其它处理
rvIppSipExtPreMsgReceivedCB
//根据远端Allow字段,确认远端是否允许UPDATE更新媒体,并记录在当前SIP连接对
//象中。conn. updateAllowedByRemoteParty = TRUE 或 FALSE
RvSipMsgGetHeaderByType(hMsg,RVSIP_HEADERTYPE_ALLOW,RVSIP_FIRST_HEADER,
&listElem);
if (isItemInAllowHeader(hMsg, RVSIP_METHOD_OTHER, "UPDATE"))
rvCCConnSipSetUpdateAllowedByRemoteParty(c, RV_TRUE);
else
rvCCConnSipSetUpdateAllowedByRemoteParty(c, RV_FALSE);
//判断远端是否协带SDP,这里假设18X不含SDP信息。
sdpInMsg = rvSipControlIsMediaBody(hMsg);
switch (msgType)
//应答的消息类型统一为UNDEFINED
case RVSIP_METHOD_UNDEFINED:
//获取应答码
code = RvSipMsgGetStatusCode(hMsg);
//如果应答码为180,则进行alert-info头域的处理,来实现区别振铃,当前假设
//远端不含有该字段,则conn.distinctiveRingback = “”
if (code == RV_SIPCTRL_STATUS_RINGING)
storeDistinctiveRingFromMsg(hAppCallLeg, hMsg, RvDistinctiveRingback);
//当前假设18X不含有SDP,则不进行媒体处理
if ((sdpInMsg == RV_TRUE) && (code < 300))
//no thing
//SIP管理处理接收消息
rvCCProviderSipMsgReceived(hMsg, hAppCallLeg);
//根据接收消息转换为MTF终端对象事件
processMessageCode(hMsg, c, &event, &cause);
switch (statusCode)
case RV_SIPCTRL_STATUS_RINGING:
*cause = RV_CCCAUSE_NORMAL;
//将当前消息映射为RV_CCTERMEVENT_RINGING事件
//当前判断条件为大体为正在进行媒体创建过程时,不
//能再触发振铃信号。
mediaState = rvCCConnectionGetMediaState(c);
if (!(((mediaState == RV_CCMEDIASTATE_CREATING) ||
(mediaState == RV_CCMEDIASTATE_CREATED) ||
(mediaState == RV_CCMEDIASTATE_CONNECTED))&&
((statusCode == RV_SIPCTRL_STATUS_SESSIONPROGRESS)||
((statusCode == RV_SIPCTRL_STATUS_RINGING) &&
(g_sipControl->connectMediaOn180 == RV_TRUE)))))
*event = RV_CCTERMEVENT_RINGING;
//sip连接对象处理事件
rvCCConnSipProcessEvent(c, event, &callStillAlive, cause);
connStateMachine(conn, event, reason, callStillAlive);
switch(event)
case RV_CCTERMEVENT_RINGING:
//更新sip连接对象状态
//conn. State = RV_CCCONNSTATE_ALERTING
rvCCConnectionSetState(conn, RV_CCCONNSTATE_ALERTING);
//触发回铃
rvCCCallAlerting(conn, reason);
//获取mdm连接对象
origin = rvCCConnectionGetConnectParty(x);
//调用mdm连接对象回调x->funcs->ringbackF进行放回铃操作
//该回调函数为rvCCConnMdmRingback,在下面单独分析
rvCCConnectionRingback(origin, reason);
//调用回调通知用户层在处理消息接收时是否进行其它处理
rvIppSipExtPostMsgReceivedCB(……)
------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmRingback
//发送RINGBACK事件处理
rvCCConnMdmProcessEvent(x, RV_CCTERMEVENT_RINGBACK, NULL, reason);
//当前term连接没有该事件处理
rvCCTermConnProcessEvents(conn, eventId, &eventReason);
//呼叫控制状态机处理
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_RINGBACK:
//变迁mdm连接状态
//conn.state = RV_CCCONNSTATE_CALL_DELIVERED
rvCCConnectionSetState(conn, RV_CCCONNSTATE_CALL_DELIVERED);
//调用x->clbks->callDeliveredCB回调进行区别振铃处理,该回调函数为
//rvCCConnMdmCallDeliveredCB
rvCCConnectionCallDeliveredCB(conn, *cause);
--------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmCallDeliveredCB
rvCCTerminalMdmStartRingbackSignal(t);
if (term->termType == RV_CCTERMINALTYPE_ANALOG)
//给MTF发送“cg/rt”事件包,触使MTF调用用户回调进行放音,该流程后
//面不细分析。
startSignal(term, "cg", "rt", NULL, RV_MDMSIGNAL_TIMEOUT);
13、18X应答接收处理sip stack对话状态改变回调
AppCallLegStateChangedEvHandler
//更新mdm连接对象lastReceivedCallStateReason为
//RV_MTF_CALL_STATE_REASON_PROVISIONAL_RESP_RECEIVED
callLegStateChangedToCallStateChangedReason(c, eReason);
//sip管理事件处理
rvCCProviderSipProcessEvent(hAppCallLeg, hCallLeg, eState, eReason);
//关联SIP对话框对象到sip连接对象中
rvCCConnSipSetCallLegHandle(c, hCallLeg);
//根据接收消息转换为MTF终端对象事件,当前转换为
//RV_CCTERMEVENT_RINGING
processMessageCode(phMsg, c, &event, &cause);
//sip连接事件处理,这里的过程调用在上面消息接收回调中已经处理过一次了,
//不再进行分析。
rvCCConnSipProcessEvent(c, event, &callStillAlive, cause);
connStateMachine(conn, event, reason, callStillAlive);
switch(event)
case RV_CCTERMEVENT_RINGING:
rvCCConnectionSetState(conn, RV_CCCONNSTATE_ALERTING);
rvCCCallAlerting(conn, reason);
//sip状态处理,当前没有RVSIP_CALL_LEG_STATE_PROCEEDING状态处理
processCallState(hCallLeg, c, state, reason, &event, &cause);
14、200应答接收处理sip stack 对话消息接收回调
AppCallLegMsgReceivedEvHandler
switch (msgType)
case RVSIP_METHOD_UNDEFINED:
//获取应答码
code = RvSipMsgGetStatusCode(hMsg);
//假设200应答中含有SDP
if ((sdpInMsg == RV_TRUE) && (code < 300))
//变迁sip连接对象中的媒体协商状态
//conn->offerAnswerState =RV_MTF_OFFERANSWER_OFFER_ANSWERED
rvCCConnSipChangeOfferAnswerState(c, RV_FALSE, RV_FALSE);
//处理sdp
rvCCProviderSipProcessIncomingSdp(hMsg, hAppCallLeg);
//SDP解析
RvSipMsgGetBody(hMsg, buf, (RvUint)strLen, &actlen);
rvSdpMsgConstructParseA(&sdp, buf, (int *)&actlen, &stat, conn->alloc)
//sip连接创建媒体
rvCCConnSipCreateMedia(c, &sdp)
//获取sip连接对象中的媒体描述符对象
media = rvCCProviderSipGetMediaStream(provider, conn->streamId);
//如果本地能力存在,则清除。
if (rvMdmMediaStreamInfoIsLocalDescriptorSet(media) == rvTrue)
rvMdmMediaStreamInfoClearLocal(media);
//将远端的SDP设置到媒体描述符的本地能力中。
rvMdmMediaStreamInfoAddLocalDescriptor(media, inMedia);
//调用sip连接的x->clbks->mediaUpdatedCB回调触发媒体更新,该
//回调函数为rvCCCallMediaUpdatedCB,下面单独分析
rvCCConnectionMediaUpdatedCB(x, inMedia)
//更新sip连接对象的媒体状态
//conn.mediaState = RV_CCMEDIASTATE_CREATING
mediaState = rvCCConnectionGetMediaState(x);
if (mediaState == RV_CCMEDIASTATE_NONE)
mediaState = RV_CCMEDIASTATE_CREATING;
rvCCConnectionSetMediaState(x, mediaState);
//复位sip连接对象的拒绝呼叫标记
//conn.rejectCall = FALSE
rvCCConnSipSetRejectCall(c, RV_FALSE)
//sip管理处理消息接收,该函数当前仅处理200以下的应答码,当前不会进行任何处
//理。200以上应答码处理在对话状态改变回调中。
rvCCProviderSipMsgReceived(hMsg, hAppCallLeg);
--------------------------------------------------------------------------------------------------------------------------------
rvCCCallMediaUpdatedCB
//获取mdm连接对象
party = rvCCConnectionGetConnectParty(conn);
//调用mdm连接对象的x->funcs->setRemoteMediaF回调更新媒体,该回调函数为
// rvCCConnMdmSetRemoteMedia
rvCCConnectionSetRemoteMedia(party, inMedia);
--------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmSetRemoteMedia
//获取临时终端对象,就是RTP终端对象
eph = rvCCConnMdmGetEphXTerm(x);
ephTerm = rvCCTerminalMdmGetImpl(eph);
//从临时终端对象中提取媒体描述符
media = rvCCTerminalMdmGetMediaStream(ephTerm, conn->streamId);
//标记下面需要进行媒体更新
if (modify)
needModifyMedia = RV_TRUE;
//将媒体描述符对象中的远端能力清除
if (rvMdmMediaStreamInfoIsRemoteDescriptorSet(media) == RV_TRUE)
rvMdmMediaStreamInfoClearRemote(media);
//将远端SDP设置到媒体描述符对象的远端能力中
rvMdmMediaStreamInfoAddRemoteDescriptor(media, inMedia);
//更新收发模式
rvMdmStreamDescriptorSetMode(&media->streamDescr,
RV_MDMSTREAMMODE_SENDRECV);
if (needModifyMedia)
//媒体更改,rvCCConnMdmGetMediaCaps函数从音频终端中获取本地媒体能力,
//当前模拟终端的音频终端就是自身。
modifyMedia(x, RVMDM_CMD_NORMAL,ephTerm,media,
rvCCConnMdmGetMediaCaps(x))
//更新收发模式,将媒体描述符模式设置为 SENDRECV
setStreamMode(x, media);
switch (rvCCConnectionGetTermState(x))
default:
streamMode = RV_MDMSTREAMMODE_SENDRECV;
rvMdmStreamDescriptorSetMode(streamDescr, streamMode);
//将远端媒体信息设置到临时变量mdmMediaInfo中,mdmMediaInfo在下面 //做为传参进行媒体处理。
fillMdmInfo( media, &mdmMediaInfo);
param = &mdmMediaInfo.param;
param->cmd = cmd; //RVMDM_CMD_NORMAL
param->normal.localSdp = localSdp;
param->normal.remoteSdp= remoteSdp;
param->normal.localCapsSdp = sdpMediaCaps; //本地DSP能力集
//媒体更改
rvRtpModifyMediaIntCB(x, &ephTerm->mdmTerm, &mdmMediaInfo)
switch (streamDescr->param.cmd)
case RVMDM_CMD_NORMAL:
params.action = RVMTF_MEDIA_MODIFY_SESSION;
switch (streamDescr->param.cmd)
case RVMDM_CMD_NORMAL:
params.action = RVMTF_MEDIA_MODIFY_SESSION;
params.localSdp = streamDescr->param.normal.localSdp;
params.remoteSdp = streamDescr->param.normal.remoteSdp;
params.localCapsSdp = streamDescr->param.normal.localCapsSdp;
//调用回调进行媒体更新,当前回调在用户层注册为
//SipStack_MtfModifyMediaCB
mtfMgr->connMediaClbks.connModifyMediaStreamCB(……)
-------------------------------------------------------------------------------------------------------------------------------
SipStack_MtfModifyMediaCB
//从应用层数据关联中提取当前终端索引
termIdx = pTermDc->ulTermIndex;
//提取资源索引
cnxId = pTermDc->CurrentCnxId;
//处理媒体
SipStack_ProcessMedia(params, termIdx, cnxId)
//当前在用户侧保存的媒体资源参数
pSdpParamRemote = &gstSipCallRtpInfo[cnxId].stRemoteRtp;
pSdpParamLocal = &gstSipCallRtpInfo[cnxId].stLocalRtp;
switch(param->action)
case RVMTF_MEDIA_MODIFY_SESSION:
//将远端SDP信息转换为本地DSP格式参数
SipStack_GetSdpMediaInfo(remoteSdp, pSdpParamRemote);
pDescr = rvSdpMsgGetMediaDescr(pSdpMsg, i);
//获取SDP中payload列表数
ulFormateNum = rvSdpMediaDescrGetNumOfPayloads(pDescr);
//获取媒体类型,并标记启用
mediaType = rvSdpMediaDescrGetMediaType(pDescr);
pSdpParam->bMediaFlag[mediaType] = 1;
//从SDP中提取如下参数
// pSdpParam->stSdpMedia[mediaType].Port
// pSdpParam->ConnMode
// pSdpParam->stSdpMedia[mediaType].NetType
// pSdpParam->stSdpMedia[mediaType].AddrType
// pSdpParam->ConnAddress
// pSdpParam->stSdpMedia[mediaType].strConnAddr
SipStack_FillConnectionInfoByMedia(pDescr, mediaType, pSdpParam)
//获取端口号,payload列表数
pSdpParam->ConnPort = pSdpParam->stSdpMedia[mediaType].Port =
rvSdpMediaDescrGetPort(pDescr);
pSdpParam->stSdpMedia[mediaType].FormatsNum = ulFormateNum;
//从SDP的payload列表中 获取编码pt值和编码名
for(j = 0;j < ulFormateNum; j++)
if(mediaType != RV_SDPMEDIATYPE_IMAGE)
int pt = rvSdpMediaDescrGetPayload(pDescr, j);
pSdpParam->stSdpMedia[mediaType].sFormat[j].strFormateName=
gstSipStackencodingMap[i].encodingName
pSdpParam->stSdpMedia[mediaType].sFormat[j].Payload = pt;
//获取SDP中rtpmap条目数
ulRtpMapNum = rvSdpMediaDescrGetNumOfRtpMap(pDescr);
pSdpParam->stSdpMedia[mediaType].RtpMapNum = ulRtpMapNum;
//获取rtpmap列表的编码名和pt值
for(j=0;j< ulRtpMapNum; j++)
pRtpMap = rvSdpMediaDescrGetRtpMap(pDescr, j);
pSdpParam->stSdpMedia[mediaType].stRtpMap[j].PayloadValue =
rvSdpRtpMapGetPayload(pRtpMap);
pSdpParam->stSdpMedia[mediaType].stRtpMap[j].strPayloadName =
rvSdpRtpMapGetEncodingName(pRtpMap)
//获取SDP属性个数
pSdpParam->stSdpMedia[mediaType].AttributeNum =
rvSdpMsgGetNumOfAttr2(pSdpMsg);
//遍历所有SDP属性字段
for(j = 0; j< pSdpParam->stSdpMedia[mediaType].AttributeNum &&
j< SIP_ATTRIBUTE_NUM_MAX;j++)
pAttr = rvSdpMsgGetAttribute(pSdpMsg, j);
//这个代码逻辑可能有问题,本意应该是跳过rtpmap,但现在代码
//逻辑是跳过非rtpamp字段
if(strcasecmp(pAttr->iAttrName,"rtpmap"))
continue;
//处理“ptime”、“fax”、“telephone-event”、“sendonly”等SDP属性
//字段。
//更新呼叫类型
rvSdpMsgGetMediaDescr(remoteSdp, i);
mediaType = rvSdpMediaDescrGetMediaType(pMedia);
if(mediaType == RV_SDPMEDIATYPE_IMAGE)
……
else if(mediaType == RV_SDPMEDIATYPE_DATA)
…..
else
pSdpParamRemote->CallType = SIP_CALL_TYPE_AUDIO;
//媒体协商处理,以远端优先进行媒体协商,最后存储到pSdpParamLocal中
SipStack_ProcessMediaNegotiate(pSdpParamLocal, pSdpParamRemote);
//修改媒体连接
SipUCC_ModifyConnection(&gstSipCallRtpInfo[cnxId],termIndex,cnxId);
//填充DSP设置参数
//更新RTP进行收发包处理
SipDrv_RtpUpdate(&drvCNX, cnxId);
//在之前媒体创建时,已经开启,所以条件无效
if(!pCnx->cnxOn)
//no thing
else
//更新RTP会话,设置对端媒体地址,此时DSP发送媒体的
//回调函数SipDrv_PktSendCb中就可以使用当前RTP会话句柄
//向远端发送媒体。
SipDrv_SetRemoteAddr(pCnxParam->destIPaddr,
pCnxParam->destPortNum,cnxId);
//设置媒体接收回调函数,当收到远端媒体时,调用
// SipDrv_RtpRecv函数中的vrgEndptPacket接口,交接收到的
//媒体数据交给DSP处理。
RvRtpSetEventHandler (pCnx->rtpHandle,SipDrv_RtpRecv,pCnx);
RvRtpSetEventHandler (pCnx->rtcpHandle,SipDrv_RtcpRecv,pCnx);
//调用DSP接口进行通道更新
SipDrv_ModifyConnection(&drvCNX, eptId,cnxId);
15、200应答接收处理sip stack对话状态改变回调
AppCallLegStateChangedEvHandler
//更新mdm连接对象的lastReceivedCallStateReason为
//RV_MTF_CALL_STATE_REASON_CALL_ACCEPTED
callLegStateChangedToCallStateChangedReason(c, eReason);
//sip管理进行事件处理
rvCCProviderSipProcessEvent(hAppCallLeg, hCallLeg, eState, eReason);
//根据应答码映射事件,当前200应答映射为RV_CCTERMEVENT_NONE
processMessageCode(phMsg, c, &event, &cause);
//忽略处理
if (event != RV_CCTERMEVENT_NONE)
rvCCConnSipProcessEvent(c, event, &callStillAlive, cause);
//sip对话状态处理
processCallState(hCallLeg, c, state, reason, &event, &cause);
switch (state)
case RVSIP_CALL_LEG_STATE_CONNECTED:
//当前协议栈配置为自动发送ACK,所以收到200应答后对话框的状
//态是变迁为CONNECTED。
//这里处理仅将事件映射为 RV_CCTERMEVENT_CALLANSWERED
handleConnectedState(c, hCallLeg, event, cause);
*event = RV_CCTERMEVENT_CALLANSWERED;
*cause = RV_CCCAUSE_OUTGOING_CALL;
if (event != RV_CCTERMEVENT_NONE)
//处理RV_CCTERMEVENT_CALLANSWERED事件
rvCCConnSipProcessEvent(c, event, &callStillAlive, cause);
connStateMachine(conn, event, reason, callStillAlive);
switch(event)
case RV_CCTERMEVENT_CALLANSWERED:
switch (conn->state)
case RV_CCCONNSTATE_ALERTING:
//更新sip连接对象状态为 RV_CCCONNSTATE_CONNECTED
rvCCConnectionSetState(conn,
RV_CCCONNSTATE_CONNECTED);
//处理连接
rvCCCallConnected(conn, RV_CCCAUSE_INCOMING_CALL);
//获取mdm连接对象
origin = rvCCConnectionGetConnectParty(x);
//调用x->funcs->callAnsweredF回调处理mdm侧应答
//该回调函数为rvCCConnMdmCallAnswered,下面分析
rvCCConnectionCallAnswered(origin, NULL);
//标记后面可以处理forked对话应答
connSip->forkedCallAnswered = RV_TRUE;
------------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmCallAnswered
//触发所有信号音停止,之前分析过,后面不详细分析。
rvCCTerminalMdmStopSignals(t);
rvCCTerminalMdmStopRingingSignal(t);
//媒体连接
rvCCConnMdmConnectMedia(x);
//获取RTP临时终端,以及媒体描述符对象
getRtpMediaObjects(x, &ephTerm, &rtpTerm, &rtpMedia)
//判断当前本地支持该媒体类型时才处理。
if ((RvMdmMediaStreamInfoDoesMediaTypeExist(
rtpMedia, RV_SDPMEDIATYPE_AUDIO)) == RV_TRUE)
//连接线路终端与RTP终端
rvCCConnMdmConnectLineAndRtpTerms(x, at, ephTerm, rtpTerm, rtpMedia)
//获取线路终端的媒体描述符对象
getLineMediaObjects(x, at, &atTerm, &mdmTerm, &lineMedia)
//调用mtfMgr->mediaClbks.connectMediaCB回调进行线路与RTP对象的
//连接处理,当前用户层注册了该回调,但没有做实际处理。
rvRtpConnectIntCB(x, rvCCTerminalMdmGetTermMgr(ephTerm), mdmTerm,
lineMedia, rtpTerm, rtpMedia, RV_MDMSTREAMDIRECTION_BOTHWAYS)
//更新线路终端媒体描述符对象 status=RV_MDMSTREAM_CONNECTED
rvMdmMediaStreamInfoSetStatusConnected(lineMedia);
//更新RTP终端媒体描述符对象 status=RV_MDMSTREAM_CONNECTED
rvMdmMediaStreamInfoSetStatusConnected(rtpMedia);
//更新mdm连接对象的mediaState =RV_CCMEDIASTATE_CONNECTED
rvCCConnectionSetMediaState(x, RV_CCMEDIASTATE_CONNECTED);
//发送线路指示,不详细分析
rvCCTerminalMdmSetLineInd(t, rvCCConnectionGetLineId(x), RV_INDSTATE_ON);
//模拟线路不做处理。
audioType = rvCCTerminalMdmGetActiveAudio(t);
switch (audioType)
case RV_CCTERMAUDIO_NONE:
break;
//发送保持指示,不详细分析
rvCCConnectionTermSetHoldIndicator(x, RV_FALSE);
//mdm连接处理事件
rvCCConnMdmProcessEvent(x, RV_CCTERMEVENT_CALLANSWERED, &callStillAlive, reason);
//没有RV_CCTERMEVENT_CALLANSWERED事件处理
rvCCTermConnProcessEvents(conn, eventId, &eventReason);
rvCCConnectionStateMachine(conn, eventId, &eventReason,callStillAlive);
switch (event)
case RV_CCTERMEVENT_CALLANSWERED:
switch (rvCCConnectionGetState(conn))
case RV_CCCONNSTATE_CALL_DELIVERED:
//设置mdm连接对象状态为state= RV_CCCONNSTATE_CONNECTED
rvCCConnectionSetState(conn, RV_CCCONNSTATE_CONNECTED);
//设置mdm连接对象终端状态为
//termState= RV_CCTERMCONSTATE_TALKING
rvCCConnectionSetTermState(conn, RV_CCTERMCONSTATE_TALKING);
//进行呼叫连接处理,当前cause值为RV_CCCAUSE_OUTGOING_CALL
rvCCCallConnected(conn, *cause);
//调用x->clbks->callAnsweredCB回调进行呼叫应答处理,该回调
//函数为rvCCConnMdmCallAnsweredCB
rvCCConnectionCallAnsweredCB(x);
-------------------------------------------------------------------------------------------------------------------------
rvCCConnMdmCallAnsweredCB
//停止当前所有信号,不详细分析
rvCCTerminalMdmStopRingingSignal(t);
rvCCTerminalMdmStopSignals(t);
//发送线路指示,不详细分析
rvCCTerminalMdmSetLineInd(t, rvCCConnectionGetLineId(x), RV_INDSTATE_ON);
//当前mdm连接对象的连接状态已经为RV_CCCONNSTATE_CONNECTED,不进行
//此流程处理。
if (rvCCConnectionGetState(x) != RV_CCCONNSTATE_CONNECTED)
//no thing