一、配置
cmCallConfig
//更新telephone编码的payload,如果用户配置的范围在动态编码范围,则使用用户
//配置的值,否则使用CCRTPC_NEGOTIATED协商值做为传参。
callUpdateTelEvtMap( ((rtpPT >= CCRTPC_DYNAMIC_MIN) &&
(rtpPT <= CCRTPC_DYNAMIC_MAX)) ? rtpPT : CCRTPC_NEGOTIATED )
//如果用户配置的payload值与当前callCodecMap中固定的值冲突,则将
//callCodecMap中冲突的值替换为旧的telephone值来解决冲突。
map = &callCodecMap[0]
while (map->ccCodec != CCEC_MAX)
if (map->rtpCode == rtpcode)
map->rtpCode = callCodecMapNte;
break;
//全局变量telephone的payload更新为新的值。
callCodecMapNte = rtpcode;
二、上电初始化媒体流控制块
cmStreamInit
cmSetCfgCodec()
cfgCodecp = &cmCfgCodec[endpt];
//遍历线路编码集
for ( i = 0; i < CODEC_MAX_TYPES && cfgCodecp->num < CC_MAXCODECS; i++ )
//如果DSP支持
if( cmEndpt[endpt].capabilities.codecCap[codec] == CODEC_SUPPORTED )
switch( codec )
//telephone类型编码
case CODEC_NTE:
//将telephone编码加入到线路编码集中
cfgCodecp->codec[cfgCodecp->num].type = codec; //CODEC_NTE
cfgCodecp->codec[cfgCodecp->num].size=
cmEndpt[endpt].capabilities.nteCap;
cfgCodecp->codec[cfgCodecp->num].silsupp = CCSDPSILSUPP_OFF;
//这里telephone的编码payload为协商
cfgCodecp->codec[cfgCodecp->num].rtpcode=CCRTPC_NEGOTIATED;
cfgCodecp->num++;
三、发送含有SDP的INVITE
1、callSetup
//设置呼叫控制块
callSetupInternal
//分配呼叫控制块
callCreateCallId()
callgcb.call[cid] = ccdefNewCALLCB();
//将全局的payload编码映射表设置到呼叫控制块的rtplist数组中
map = &callCodecMap[0];
while (map->ccCodec != CCEC_MAX)
pCall->rtplist[map->ccCodec] = (UINT8)map->rtpCode;
map++;
//CCEC_MAX做为特殊的telephone编码类型,其中callCodecMapNte的
//值为用户设置的RFC2833的payload值。
pCall->rtplist[CCEC_MAX] = (UINT8)callCodecMapNte;
2、cmCnxStateEngine
switch ( FSM( event, cx->state))
//在空间状态进行向外呼出
case FSM( CMCMD_ORIGINATE, CMST_IDLE):
//callmgr的rtp资源控制块关联线路的cmCfgCodec编码集
cmUpdateCodec( endpt, cx->rtpCnxId );
cmRtpCnx[rtpCnxId].cfgCodec = (CMCODEC *)&cmCfgCodec[endpt];
//将本地编码集映射到callctrl类型sdp变量中
cmBuildLocalSdp( endpt, rtpcx, &localSdp );
cmBuildMedia( endpt, pRtpCx, &pSdp->streamlist, &num, FALSE );
//遍历本地编码集
for( i = 0 ; i < pRtpCx->cfgCodec->num ; i++ )
//如果当前编码为telephone编码,并且用户配置了dtmf使用
//RFC2833方式,则更新sdp变量的媒体列表支持telephone编码
if( pRtpCx->cfgCodec->codec[i].type == CODEC_NTE &&
cmProvisionGet(PROVIS_COMMON_DTMFRELAY, endpt)
== VRGCMGR_DTMF_RELAY_RTP
pStreamList->stream[sIdx]->media.fmt.list.televtUsed = TRUE;
pStreamList->stream[sIdx]->media.fmt.list.televt.rtpcode =
//注意这时rtpcode还等于CCRTPC_NEGOTIATED
pRtpCx->cfgCodec->codec[i].rtpcode;
pStreamList->stream[sIdx]->media.fmt.list.televt.
televtTypes =pRtpCx->cfgCodec->codec[i].size;
//将本地编码构造的callctrl类型sdp存储到callgcb呼叫控制块中
callSetParm( cid, CCPARM_LOCAL_SDP, &localSdp );
callgcb.call[cid]->locSdp = callSdpDup( sdp );
callgcb.call[cid]->locSdpSet = TRUE;
//发起INVITE呼叫
callOriginate( cid )
//将callctrl类型的sdp转化为radvision格式的sdp
// callcb->locSdp为本地编码集
// callcb->rtplist为payload编码映射表
callSdpConvertToRv(callcb->locSdp,&callcb->rtplist[0],
callcb->sdpVersion,&sdpMsg);
//遍历所有本地编码集加入radvision媒体流中
for (i = 0; i < sdp->streamlist.num; i++)
AddCodecMap(pMediaDesc, &media->fmt.list, rtplist)
//如果本地支持telephone编码
if (list->televtUsed)
//如果本地编码集中的telephone编码的payload设置为
// CCRTPC_NEGOTIATED协商,则使用callCodecMap映射的
//payload值,这里CCEC_MAX比较特殊,表示是telephone
//编码类型,这里即把用户配置的RFC2833设置的payload
//值设置到sdp中。
televt = &list->televt;
rtpcode = (televt->rtpcode ==
(UINT8)CCRTPC_NEGOTIATED?
rtplist[CCEC_MAX]: televt->rtpcode);
rvSdpMediaDescrAddPayloadNumber(pMediaDesc,
rtpcode);
rvSdpMediaDescrAddRtpMap(pMediaDesc, rtpcode,
"telephone-event", 8000);
四、接收含有SDP的INVITE
1、EvCalledA
//创建呼叫控制块
CreateNewCall
callCreateCallId
callgcb.call[cid] = ccdefNewCALLCB()
//将全局的payload编码映射表设置到呼叫控制块的rtplist数组中
map = &callCodecMap[0];
while (map->ccCodec != CCEC_MAX)
pCall->rtplist[map->ccCodec] = (UINT8)map->rtpCode;
map++;
//CCEC_MAX做为特殊的telephone编码类型,其中callCodecMapNte的
//值为用户设置的RFC2833的payload值。
pCall->rtplist[CCEC_MAX] = (UINT8)callCodecMapNte;
//将sdp信息存储到呼叫控制块的callcb->remSdp
GetMediaParm(call, hBody, NULL)
callSdpConvertFromRv(&sdpMsg, callcb->remSdp);
//遍历远端所有媒体对象
for (i = 0, k = 0; i < mediaCount && k < CC_MAXSDPSTREAM; i++)
GetSdpRtpParm(pSdpMsg, i, &media->fmt.list);
//遍历远端payload列表
for (i = 0; i < payloadsCount; i++)
//如果payload值为动态payload类型,则查找是否有rtpmap
//是否匹配telephone编码。
payload = rvSdpMediaDescrGetPayload(p_media, i)
codecMap = FindCodecInfoByRv(payload, p_media)
//如果远端含有telephone编码,则标记为true,同时保存远端
//RFC2833的payload值
if ( codecMap->ccCodec == CCEC_TELEPHONE_EVENT )
rtplst->televtUsed = true;
rtplst->televt.rtpcode = payload;
//发送setup事件通知callmgr有新呼叫
GCBEVTSETUP(cid, reason, pp)
//发送CCEVT_STATUS/CCRSN_SDP_OFFER事件通过收到媒体信息
GCBEVTSTATUS(cid, callSdpOfferOrAnswer(call->sdpState), NULL);
2、处理setup事件通知
cmCnxStateEngine( endpt, cid, cnxevent, reason, packet );
switch ( FSM( event, cx->state))
case FSM( CMEVT_SETUP, CMST_IDLE):
// callmgr的rtp资源控制块关联线路的cmCfgCodec编码集
cmUpdateCodec( endpt, cx->rtpCnxId );
cmRtpCnx[rtpCnxId].cfgCodec = (CMCODEC *)&cmCfgCodec[endpt];
//将本地编码集映射到callctrl类型sdp变量中
cmBuildLocalSdp( endpt, rtpcx, &localSdp );
cmBuildMedia( endpt, pRtpCx, &pSdp->streamlist, &num, FALSE );
//遍历本地编码集
for( i = 0 ; i < pRtpCx->cfgCodec->num ; i++ )
//如果当前编码为telephone编码,并且用户配置了dtmf使用
//RFC2833方式,则更新sdp变量的媒体列表支持telephone编码
if( pRtpCx->cfgCodec->codec[i].type == CODEC_NTE &&
cmProvisionGet(PROVIS_COMMON_DTMFRELAY, endpt)
== VRGCMGR_DTMF_RELAY_RTP)
pStreamList->stream[sIdx]->media.fmt.list.televtUsed =
TRUE;
pStreamList->stream[sIdx]->media.fmt.list.televt.
rtpcode =
//注意这时rtpcode还等于CCRTPC_NEGOTIATED
pRtpCx->cfgCodec->codec[i].rtpcode;
pStreamList->stream[sIdx]->media.fmt.list.televt.
televtTypes =pRtpCx->cfgCodec->codec[i].size;
//将本地编码构造的callctrl类型sdp存储到callgcb呼叫控制块中
callSetParm( cid, CCPARM_LOCAL_SDP, &localSdp );
callgcb.call[cid]->locSdp = callSdpDup( sdp );
callgcb.call[cid]->locSdpSet = TRUE;
//通知CMEVT_NEWCALL事件
cmEndptNotify( endpt, cid, CMEVT_NEWCALL, data );
switch ( FSM( event, state))
case FSM( CMEVT_NEWCALL, CMST_ONHOOK):
//发送回铃请求
cmCnxNotify( endpt, cid, CMCMD_ALERTING, 0);
cmCnxStateEngine( endpt, cid, event, data, NULL )
switch ( FSM( event, cx->state))
case FSM( CMCMD_ALERTING, CMST_INCOMING):
//给远端发送180
callProgress( cid, CCRSN_ALERTING);
3、处理CCEVT_STATUS/CCRSN_SDP_OFFER事件通知
cmCnxStateEngine( endpt, cid, cnxevent, reason, packet );
switch ( FSM( event, cx->state))
case FSM( CMEVT_SDP_OFFER, CMST_INCOMING):
//更新媒体信息
cmStreamInfo( cid, 1, 1 );
//获取协商好的媒体信息
callGetParm( cid, CCPARM_MEDIAINFO, &mediaInfo )
GetMediaInfo(cid, (CCMEDIAINFO *)val);
offer = callcb->remSdp;
answer = callcb->ansSdp
//生成协商好的应答SDP
callSdpGenerateAnswer(offer, callcb->locSdp,
&callcb->rtplist[0],answer)
//遍历远端SDP编码列表
for (i = 0; i < offer->streamlist.num; i++)
//生成应答流
GenerateAnswerStream(offer->streamlist.stream[i],
local, rtpList, answer->streamlist.stream[i]);
//如果远端含有telephone编码,并且本端也设置了
//DTMF使用RFC2833方式,则设置应答流使用
//telephone编码。
if (offerMedia->fmt.list.televtUsed &&
localMedia->fmt.list.televtUsed)
//更新应答流的telephone编码使用远端的
//payload
if (localMedia->fmt.list.televt.rtpcode ==
CCRTPC_NEGOTIATED)
answerRtpLst->televt.rtpcode =
offerMedia->fmt.list.televt.rtpcode;
//当前已经使用新的telephone编码的
//payload,检查当前payload映射表中是否
//有冲突,如果有冲突则和老的telephone
//编码payload进行替换。
UpdateRtpPayloadLst(CCEC_MAX,
offerMedia->fmt.list.televt.rtpcode,
rtpLst);
//标记使用telephone编码
answerRtpLst->televtUsed = TRUE;
//获取发送信息
callSdpGetTxMedia(isOfferer, offer, answer,
&callcb->rtplist[0], mediaInfo->tx)
FilterSdp(answer, offer, rtplist, tx);
//获取接收信息
callSdpGetRxMedia(isOfferer, offer, answer,
&callcb->rtplist[0], mediaInfo->rx))
//更新发送信息
cmStreamTxInfo( cid, rtpcx, mediaInfo.tx )
//如果协商的应答SDP标记使用telephone编码,则更新
// cmRtpCnx[cx->rtpCnxId]包含telephone编码。
if( pSdp->streamlist.stream[saIdx]->media.fmt.list.televtUsed )
pRtpCx->ingressMap.codecs[pRtpCx->ingressMap.numCodecs].
type = CODEC_NTE;
pRtpCx->ingressMap.codecs[pRtpCx->ingressMap.numCodecs].
rtpPayloadType= pSdp->streamlist.stream[saIdx]->media.fmt.list.televt.
rtpcode;
pRtpCx->ingressMap.numCodecs++;
//当前rtp资源控制块还没有关联stream,所以不进行DSP参数下发的
//处理。
if( pRtpCx->stream != UNKNOWN )
//DSP参数下发
//更新接收信息,同上
cmStreamRxInfo( cid, rtpcx, mediaInfo.rx )
五、发送含有SDP的200
cmEndptStateEngine
switch ( FSM( event, state))
//发送建立连接
cmCnxNotify( endpt, ep->curCid, CMCMD_CONNECT, 0);
cmCnxStateEngine( endpt, cid, event, data, NULL );
switch ( FSM( event, cx->state))
case FSM( CMCMD_CONNECT, CMST_INCOMING):
//创建媒体流对象
cmStreamCreate( cid)
//进行远端与本端的媒体协商,并用协商结果填充双向的媒体信息
callGetParm( cid, CCPARM_MEDIAINFO, &mediaInfo )
//从用户配置中获取DTMF的配置方式
rtpcx->parm.digitRelayType = cmGetToneRelayType(cx->endpt);
//分配媒体流对象,并关联到rtp资源控制块中
rtpcx->stream = cmAssignStream( cx->endpt, cx->rtpCnxId);
//将参数下发的DSP中
endptCreateConnection( &cmEndpt[cx->endpt].endptObjState,
rtpcx->stream, &rtpcx->parm );
//发送200应答
callConnect( cid )
//之前没有发送sdp,并且还没有回应远端的SDP协商,则选择已经协
//商好的应答sdp发送给远端.
if (!call->sdpSent)
if (call->sdpState == CCSDPSTATE_OFFER_RECEIVED)
sdpToSend = call->ansSdp;
//将sdp转化成radvision格式,并发送协带SDP的200应答
callSdpConvertToRv(sdpToSend, &call->rtplist[0],
call->sdpVersion, &rvSdpMsg);
AnswerA(……)
六、接收含有SDP的200
1、EvAnsweredA
//获取远端SDP信息到呼叫控制块中
GetMediaParm(call, hBody, &unholdOnly);
callSdpConvertFromRv(&sdpMsg, callcb->remSdp)
GetSdpRtpParm(pSdpMsg, i, &media->fmt.list);
//遍历远端payload列表
for (i = 0; i < payloadsCount; i++)
//如果payload值为动态payload类型,则查找是否有rtpmap
//是否匹配telephone编码。
payload = rvSdpMediaDescrGetPayload(p_media, i)
codecMap = FindCodecInfoByRv(payload, p_media)
//如果远端含有telephone编码,则标记为true,同时保存远端
//RFC2833的payload值
if ( codecMap->ccCodec == CCEC_TELEPHONE_EVENT )
rtplst->televtUsed = true;
rtplst->televt.rtpcode = payload;
//发送CCEVT_STATUS/CCRSN_SDP_ANSWER事件
GCBEVTSTATUS(cid, CCRSN_SDP_ANSWER, NULL);
//发送CCEVT_CONNECT事件
GCBEVTCONNECT(cid, CCRSN_NOTUSED, pp);
2、处理CCEVT_STATUS/CCRSN_SDP_ANSWER事件
cmCnxStateEngine
switch ( FSM( event, cx->state))
case FSM( CMEVT_SDP_ANSWER, CMST_OUTGOING):
//更新媒体信息
cmStreamInfo(cid,1,1)
//根据本端及远端的SDP信息,填充mediaInfo,这里不详细分析,该函数在
//上面分析过,不同的地方只是在该函数里没有进行协商这个流程。
callGetParm( cid, CCPARM_MEDIAINFO, &mediaInfo )
//更新媒体信息,将媒体信息更新到rtp资源控制块中,同时因为当前rtp
//资源控制块还没有关联stream控制块,所以不进行DSP参数下发。
cmStreamTxInfo( cid, rtpcx, mediaInfo.tx )
cmStreamRxInfo( cid, rtpcx, mediaInfo.rx )
//创建媒体控制块,并将参数下发到DSP中。
cmStreamCreate(cid)
3、处理CCEVT_CONNECT事件
cmCnxStateEngine
switch ( FSM( event, cx->state))
case FSM( CMEVT_CONNECT, CMST_OUTGOING):
//通知callmgr endpt模块已经有应答接收
cmEndptNotify( endpt, cid, CMEVT_ANSWER, data);
//执行媒体控制块创建,之前在处理CMEVT_SDP_ANSWER时已经进行媒体控制块创
//建,及DSP参数下发等处理,所以这里不再进行处理。
cmStreamCreate( cid)