BCM VOIP RFC2833方式DTMF处理分析

一、配置
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)

你可能感兴趣的:(broadcom,4.X,原版VOIP代码分析)