freeswitch系列32通话流程
呼叫的信令交互是比较复杂的,大概分3个阶段:
阶段1:
- A发送invate请求给服务器,服务器发现未认证,回复407;
- A再次发送invite,带上认证信息,服务器通过。
阶段2:
- 服务器向另一方B发送invite请求,B回复180。
- 服务器向A回复180,A开始响铃。
阶段3:
- B接起电话,发送200OK给服务器
- 服务器发送200OK给A
sip协议栈通知应用是通过回调sofia_event_callback,和注册一样,这里会创建session和channel,然后入消息队列。
- void sofia_event_callback(nua_event_t event,
- int status,
- char const *phrase,
- nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
- tagi_t tags[])
- {
- sofia_dispatch_event_t *de;
- //sip消息是一系列nua_开头的,nua_i表示Indications消息,还有一类nua_r表示Responses消息
- switch(event) {
- case nua_i_terminated:
- case nua_i_invite:
- case nua_i_register:
- case nua_i_options:
- case nua_i_notify:
- case nua_i_info:
- ...
- }
- //invite消息,则要创建session,同时也是创建channel
- if (event == nua_i_invite && !sofia_private) {
- session = switch_core_session_request_uuid(sofia_endpoint_interface,...);
- switch_core_session_thread_launch(session)
- }
- //最后把sip消息投入到消息队列mod_sofia_globals.msg_queue
- sofia_queue_message(de);
- }
消息处理线程调用sofia_process_dispatch_event去处理,这个函数里面又调用our_sofia_event_callback。
- case nua_i_invite:
- if (session && sofia_private) {
- if (sofia_private->is_call > 1) {
- sofia_handle_sip_i_reinvite(session, nua, profile, nh, sofia_private, sip, de, tags);
- } else {
- sofia_private->is_call++;
- sofia_handle_sip_i_invite(session, nua, profile, nh, sofia_private, sip, de, tags);
- }
- }
- break;
sofia_handle_sip_i_invite内部调用注册函数,然后就跟注册一样的流程,在sofia_reg_auth_challenge回复407给A。
- if (sofia_reg_handle_register(nua, profile, nh, sip, de, REG_INVITE, key, sizeof(key), &v_event, NULL, NULL, &x_user)) {
- if (v_event) {
- switch_event_destroy(&v_event);
- }
- if (x_user) {
- switch_xml_free(x_user);
- }
- if (sip->sip_authorization || sip->sip_proxy_authorization) {
- goto fail;
- if (regtype == REG_REGISTER) {
- nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END());
- } else if (regtype == REG_INVITE) {
- nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED,
- TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)),
- SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());
- }
然后A带上认证信息,重新发起invite请求,基本上也是这个流程,只是到最后没有回复40X。接着sip协议层的状态会改变,然后通知应用。状态改变即nua_i_state,在our_sofia_event_callback中,找到如下代码。
- case nua_i_state:
- sofia_handle_sip_i_state(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
- break;
从日志看,这里会改变channel状态,cs_new->cs_init->cs_route->cs_execute,总的来说就是解析拨号规则,整理出action,然后去执行。
这里根据日志去跟踪代码即可,就不再列出代码。
执行action的时候,会调用app中的bridge,这个在mod_dptools.c中定义。
SWITCH_STANDARD_APP(audio_bridge_function)
bridge会桥接两个leg,所以会先使用originate发起和B的呼叫。
- status = switch_ivr_originate(NULL, &peer_session,
- &cause, camp_data, campon_timeout, NULL, NULL, NULL, NULL, NULL, SOF_NONE,
- 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发送回铃音。
从日志可以看出来这个流程,注意最后一行的early media。
B接起来后,会进行媒体的协商。完成后,A会停掉和服务器的回铃音,和B进行通话。
这部分通过日志跟代码就行了。