DSP

主叫基本呼叫流程分析

2019-07-13 16:12发布

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