- 呼叫信令流程
呼叫的信令交互是比较复杂的,大概分3个阶段:
阶段1:
- A发送invate请求给服务器,服务器发现未认证,回复407;
- A再次发送invite,带上认证信息,服务器通过。
阶段2:
- 服务器向另一方B发送invite请求,B回复180。
- 服务器向A回复180,A开始响铃。
阶段3:
- B接起电话,发送200OK给服务器
- 服务器发送200OK给A
- 阶段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,然后去执行。
这里根据日志去跟踪代码即可,就不再列出代码。
- 阶段2
执行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发送回铃音。
从日志可以看出来这个流程,注意最后一行的early media。
- 阶段3
B接起来后,会进行媒体的协商。完成后,A会停掉和服务器的回铃音,和B进行通话。
这部分通过日志跟代码就行了。