1. 呼叫信令流程

freeswitch 拨号计划生成录音 freeswitch呼叫流程_freeswitch

 

呼叫的信令交互是比较复杂的,大概分3个阶段:

阶段1:

  1. A发送invate请求给服务器,服务器发现未认证,回复407;
  2. A再次发送invite,带上认证信息,服务器通过。

阶段2:

  1. 服务器向另一方B发送invite请求,B回复180。
  2. 服务器向A回复180,A开始响铃。

阶段3:

  1. B接起电话,发送200OK给服务器
  2. 服务器发送200OK给A
  3. 阶段1

sip协议栈通知应用是通过回调sofia_event_callback,和注册一样,这里会创建session和channel,然后入消息队列。

1. void sofia_event_callback(nua_event_t event,  
2.                           int status,  
3.                           char const *phrase,  
4.                           nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,  
5.                           tagi_t tags[])  
6. {  
7.     sofia_dispatch_event_t *de;  
8.   
9. //sip消息是一系列nua_开头的,nua_i表示Indications消息,还有一类nua_r表示Responses消息
10.     switch(event) {  
11.     case nua_i_terminated:  
12.     case nua_i_invite:  
13.     case nua_i_register:  
14.     case nua_i_options:  
15.     case nua_i_notify:  
16.     case nua_i_info:  
17.         ...  
18.     }  
19.   
20. //invite消息,则要创建session,同时也是创建channel
21.     if (event == nua_i_invite && !sofia_private) {  
22.         session = switch_core_session_request_uuid(sofia_endpoint_interface,...);  
23.         switch_core_session_thread_launch(session)  
24.     }  
25.       
26. //最后把sip消息投入到消息队列mod_sofia_globals.msg_queue
27.     sofia_queue_message(de);  
28. }

 

消息处理线程调用sofia_process_dispatch_event去处理,这个函数里面又调用our_sofia_event_callback。

 

1. case nua_i_invite:  
2.     if (session && sofia_private) {  
3.         if (sofia_private->is_call > 1) {  
4.             sofia_handle_sip_i_reinvite(session, nua, profile, nh, sofia_private, sip, de, tags);  
5.         } else {  
6.             sofia_private->is_call++;  
7.             sofia_handle_sip_i_invite(session, nua, profile, nh, sofia_private, sip, de, tags);  
8.         }  
9.     }  
10.     break;

 

sofia_handle_sip_i_invite内部调用注册函数,然后就跟注册一样的流程,在sofia_reg_auth_challenge回复407给A。

1. if (sofia_reg_handle_register(nua, profile, nh, sip, de, REG_INVITE, key, sizeof(key), &v_event, NULL, NULL, &x_user)) {  
2.     if (v_event) {  
3.         switch_event_destroy(&v_event);  
4.     }  
5.     if (x_user) {  
6.         switch_xml_free(x_user);  
7.     }  
8.   
9.     if (sip->sip_authorization || sip->sip_proxy_authorization) {  
10.         goto fail;  
 
1. if (regtype == REG_REGISTER) {  
2.     nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END());  
3. } else if (regtype == REG_INVITE) {  
4.     nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED,   
5.                 TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)),   
6.                 SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());  
7. }

然后A带上认证信息,重新发起invite请求,基本上也是这个流程,只是到最后没有回复40X。接着sip协议层的状态会改变,然后通知应用。状态改变即nua_i_state,在our_sofia_event_callback中,找到如下代码。

1. case nua_i_state:  
2.     sofia_handle_sip_i_state(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);  
3.     break;

从日志看,这里会改变channel状态,cs_new->cs_init->cs_route->cs_execute,总的来说就是解析拨号规则,整理出action,然后去执行。

freeswitch 拨号计划生成录音 freeswitch呼叫流程_消息队列_02

 

这里根据日志去跟踪代码即可,就不再列出代码。

  1. 阶段2

 

freeswitch 拨号计划生成录音 freeswitch呼叫流程_消息队列_03

执行action的时候,会调用app中的bridge,这个在mod_dptools.c中定义。

SWITCH_STANDARD_APP(audio_bridge_function)

 

bridge会桥接两个leg,所以会先使用originate发起和B的呼叫。

1. status = switch_ivr_originate(NULL, &peer_session,   
2.                               &cause, camp_data, campon_timeout, NULL, NULL, NULL, NULL, NULL, SOF_NONE,   
3.                               switch_channel_get_cause_ptr(caller_channel));

这个函数很长,只需要知道,会创建新的一路的session。如果B返回响铃180,则originate函数就返回,这样就可以获取新b-leg的session和channel。

 

switch_channel_t *peer_channel = switch_core_session_get_channel(peer_session);

 

两个leg都有了,就可以进行桥接。

 

switch_ivr_multi_threaded_bridge(session, peer_session, func, a_key, b_key);

这时候,A和服务器会进行媒体通信,服务器给A发送回铃音。

freeswitch 拨号计划生成录音 freeswitch呼叫流程_消息队列_04

从日志可以看出来这个流程,注意最后一行的early media。

  1. 阶段3

B接起来后,会进行媒体的协商。完成后,A会停掉和服务器的回铃音,和B进行通话。

freeswitch 拨号计划生成录音 freeswitch呼叫流程_消息队列_05

 

这部分通过日志跟代码就行了。