FreeSwitch在各种实际应用场景中,在不修改代码的情况下完整的支持业务场景是一件不容易的事情,大部分实际应用场景需要修改FreeSwitch的代码。所以了解清楚FreeSwitch代码是一件FreeSwitch应用必备的技能,本文将对FreeSwitch常修改和调试的常见位置进行介绍。
本文章操作是从freeswitch的1.10.9版本上进行的,过早的版本,或者过于未来的版本可能有改动,如果时间不长应该大同小异。
1.A腿和B腿进行Bridge时媒体包交换的线程函数
2.mod_sofia中处理会话SIP消息的响应函数
1.A腿和B腿进行Bridge时媒体包交换的线程函数
文件:switch_ivr_bridge.c
所在行数:356
函数名:audio_bridge_thread
关键代码:799行开始
/* read audio from 1 channel and write it to the other */
status = switch_core_session_read_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, stream_id);
if (SWITCH_READ_ACCEPTABLE(status)) {
read_frame_count++;
if (switch_test_flag(read_frame, SFF_CNG)) {
if (silence_val) {
switch_generate_sln_silence((int16_t *) silence_frame.data, silence_frame.samples,
read_impl.number_of_channels, silence_val);
read_frame = &silence_frame;
} else if (!switch_channel_test_flag(chan_b, CF_ACCEPT_CNG)) {
continue;
}
}
if (switch_channel_test_flag(chan_a, CF_BRIDGE_NOWRITE)) {
continue;
}
if (status != SWITCH_STATUS_BREAK && !switch_channel_test_flag(chan_a, CF_HOLD) && !switch_channel_test_flag(chan_b, CF_LEG_HOLDING)) {
if (switch_core_session_write_frame(session_b, read_frame, SWITCH_IO_FLAG_NONE, stream_id) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG,
"%s ending bridge by request from write function\n", switch_channel_get_name(chan_b));
goto end_of_bridge_loop;
}
}
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s ending bridge by request from read function\n", switch_channel_get_name(chan_a));
goto end_of_bridge_loop;
}
此部分代码在该函数的一个循环中,从session_a使用switch_core_session_read_frame函数读取音频帧,判断合法后判断是否为CNG帧,如果B腿状态正常则使用switch_core_session_write_frame函数写入到B腿。
2.A腿和B腿进行Bridge时媒体包交换的线程函数
文件:sofia.c
所在行数:1465
函数名:our_sofia_event_callback
关键代码:1627行开始
switch (event) {
case nua_r_get_params:
case nua_i_fork:
case nua_r_info:
break;
case nua_r_unregister:
case nua_r_unsubscribe:
case nua_i_terminated:
case nua_r_publish:
case nua_i_error:
case nua_i_active:
case nua_r_set_params:
case nua_i_prack:
case nua_r_prack:
break;
case nua_i_cancel:
...
break;
case nua_r_cancel:
...
break;
case nua_i_ack:
...
break;
case nua_r_ack:
if (channel)
switch_channel_set_flag(channel, CF_MEDIA_ACK);
break;
case nua_r_shutdown:
if (status >= 200) {
sofia_set_pflag(profile, PFLAG_SHUTDOWN);
su_root_break(profile->s_root);
}
break;
case nua_r_message:
sofia_handle_sip_r_message(status, profile, nh, sip);
break;
case nua_r_invite:
sofia_handle_sip_r_invite(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_options:
sofia_handle_sip_r_options(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_bye:
sofia_handle_sip_i_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_bye:
sofia_handle_sip_r_bye(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_notify:
if (session) {
sofia_handle_sip_r_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
}
break;
case nua_i_notify:
sofia_handle_sip_i_notify(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_r_register:
sofia_reg_handle_sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_options:
sofia_handle_sip_i_options(status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
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;
case nua_i_publish:
sofia_presence_handle_sip_i_publish(nua, profile, nh, sofia_private, sip, de, tags);
break;
case nua_i_register:
//nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END());
//nua_handle_destroy(nh);
sofia_reg_handle_sip_i_register(nua, profile, nh, &sofia_private, sip, de, tags);
break;
case nua_i_state:
sofia_handle_sip_i_state(session, status, phrase, nua, profile, nh, sofia_private, sip, de, tags);
break;
...
}
此部分代码处理所有的SIP请求和响应,对于请求类消息,下表举例部分nua的消息类型和对应的SIP消息:
nua事件 | SIP消息 |
nua_i_invite | INVITE消息事件 |
nua_r_invite | 响应INVITE的状态码事件,如180、181、183、200、403、404、603等回复INVITE的状态码。 |
nua_i_prack | PRACK消息事件 |
nua_r_prack | 响应PRACK消息的状态码事件 |
nua_i_update | UPDATE消息事件 |
nua_r_update | 响应UPDATE消息的状态码事件 |
nua_i_ack | ACK消息事件 |
nua_i_cancel | CANCEL消息事件 |
nua_r_cancel | 响应CANCEL消息的状态码事件 |
nua_i_bye | BYE消息事件 |
nua_r_bye | 响应BYE消息的状态码事件 |
注意our_sofia_event_callback函数并不是Sofia-SIP库对外回调消息的回调函数,而是FreeSwitch的mod_sofia模块自己的回调函数,Sofia-SIP库对外回调函数是sofia_event_callback。
our_sofia_event_callback函数处理所有SIP会话的消息,如果区分具体的哪个SIP会话,要根据our_sofia_event_callback函数的sofia_private参数的uuid字段来获取session信息。
一般情况下our_sofia_event_callback函数不直接处理SIP消息,而是定义了一些处理特定消息的函数,如处理INVITE消息的sofia_handle_sip_i_invite、处理BYE消息的sofia_handle_sip_i_bye函数等。
对于UPDATE消息、PRACK消息等在不修改代码的情况下可能无法触发回调函数,因为Sofia-SIP库内部会自动处理相关消息, 需要使用Sofia-SIP库提供的NUTAG_APPL_METHOD宏来设定是否回调到上层处理。