2019独角兽企业重金招聘Python工程师标准>>>
/**源码基于FreeSWITCH 1.4.20版本进行讲解**/
一次呼叫的过程,从mod_freetdm发起,经过路由,执行bridge过程,然后到mod_sofia模块上的另外一个端口。
从上一个文章看起,拿起模拟电话拨号出去,然后触发,mod_freetdm.c文件中的函数,
1815 ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session_t **sp)
这个函数不长,主要的功能是new出一个session,创建一个线程来正式执行呼叫过程:
2102 switch_channel_set_state(channel, CS_INIT);
2103 if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) {
2104 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error spawning thread\n");
2105 switch_core_session_destroy(&session);
2106 return FTDM_FAIL;
2107 }
函数执行在src/switch_core_session.c 文件中:
1959 if (switch_thread_create(&thread, thd_attr, switch_core_session_thread, session, session->pool) == SWITCH_STATUS_SUCCESS) {
1960 switch_set_flag(session, SSF_THREAD_STARTED);
1961 status = SWITCH_STATUS_SUCCESS;
跳转之后,最后的线程执行函数在 src/switch_core_state_machine.c 文件中:
419 SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session)
整个线程函数是基于呼叫过程的状态迁移进行执行的,主要的有新建(CS_NEW),对应路由XML的呼叫的路由(CS_ROUTING),执行路由表中的APP(CS_EXCUTE),挂机(CS_HANGUP)或者错误结束(CS_DESTROY)。
在这个过程中,路由和执行对应的APP,一般是bridge建立呼叫的过程,是最重要的,代码如下:
454 while ((state = switch_channel_get_state(session->channel)) != CS_DESTROY) {
...//省略了部分代码
463 midstate = state;
464 if (state != switch_channel_get_running_state(session->channel) || state >= CS_HANGUP) {
465 int index = 0;
466 int proceed = 1;
执行APP的状态为:
534 case CS_EXECUTE: /* Execute an Operation */
535 STATE_MACRO(execute, "EXECUTE");
536 break;
所有的执行过程都在宏:
STATE_MACRO
此宏的定义就在这个C文件中,主要是执行对应状态的endpoint的回调函数,和Switch_core得全局对状态的回调函数。宏当中最重要的两个执行地方为:
382 while (do_extra_handlers && proceed && (application_state_handler = switch_core_get_state_handler(index++)) != 0) { \
383 if (!application_state_handler || !application_state_handler->on_##__STATE || \
384 (application_state_handler->on_##__STATE && \
385 application_state_handler->on_##__STATE(session) == SWITCH_STATUS_SUCCESS \
...//篇幅有限,删除这部分代码
395 if (global_proceed) { \
396 switch_core_standard_on_##__STATE(session); \
397 } \
398 }
我们要执行的状态为:excute,下面
application_state_handler->on_##__STATE(session)
转换为:
application_state_handler->on_excute(session);
这个函数会执行每个endpoint的对应的状态迁移的回调函数,如:mod_sofia模块中对应的所有回调为:
4234 switch_state_handler_table_t sofia_event_handlers = {
4235 /*.on_init */ sofia_on_init,
4236 /*.on_routing */ sofia_on_routing,
4237 /*.on_execute */ sofia_on_execute,
....//这个代码忽略
4247 };
一个非常重要的switch_core的执行接口:
396 switch_core_standard_on_##__STATE(session); \
转换为:
396 switch_core_standard_on_execute; \
这个函数会实际调用bridge的APP对应的函数来执行,大部分的APP对应的函数,都在mod_dptools.c文件中,在FreeSWITCH启动的时候都加载到系统的APP的借口的全局链表中。这个函数会从全局链表中找到对应的APP的函数接口,我们用到bridge对用的函数在mod_dptools.c:
6090 SWITCH_ADD_APP(app_interface, "bridge", "Bridge Audio", "Bridge the audio between two sessions", audio_bridge_function, "",
6091 SAF_SUPPORT_NOMEDIA);
执行对应的函数:
3112 SWITCH_STANDARD_APP(audio_bridge_function)
3113 {
3114 switch_channel_t *caller_channel = switch_core_session_get_channel(session);
我们看下bridge在conf/dialplan/default.xml中的用法:
我们顺着函数继续往下看,一些判断和赋值可以忽略,代码一般会执行到这个函数:
switch_ivr_originate(session, &peer_session, &cause, data, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL)) != SWITCH_STATUS_SUCCESS)
这个函数会真正的发起INVITE呼叫,并等待对方摘机(或者对方回的183的彩铃),如果对方没有,那么我们会一直听回铃音,一般来说,这个函数会阻塞一定时间。
比较好的一点,我们的代码终于实际的状态结合在一起了。这时候对方摘机了,函数继续执行下去,到:
3282 switch_channel_t *peer_channel = switch_core_session_get_channel(peer_session);
3311 switch_ivr_multi_threaded_bridge(session, peer_session, func, a_key, b_key);
函数的实现在文件src/switch_ivr_bridge.c中:
1296 SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session,
1297 switch_core_session_t *peer_session,
1298 switch_input_callback_function_t input_callback, void *session_data,
1299 void *peer_session_data)
1300 {
1301 switch_ivr_bridge_data_t *a_leg = switch_core_session_alloc(session, sizeof(*a_leg));
1302 switch_ivr_bridge_data_t *b_leg = switch_core_session_alloc(peer_session, sizeof(*b_leg));
在函数的开始,会在b-leg上设置好回调函数,这样,当a-leg状态改变,或者b-leg状态改变时,互相可以知道。并执行设置好的回调函数,来执行对应的动作。设置过程如:
1347 switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers);
回调函数也在整个文件中,如下:
793 static const switch_state_handler_table_t audio_bridge_peer_state_handlers = {
794 /*.on_init */ NULL,
795 /*.on_routing */ audio_bridge_on_routing,
796 /*.on_execute */ NULL,
797 /*.on_hangup */ NULL,
798 /*.on_exchange_media */ audio_bridge_on_exchange_media,
799 /*.on_soft_execute */ NULL,
800 /*.on_consume_media */ audio_bridge_on_consume_media,
801 };
绕过其他的代码,函数会执行到,设置了b-leg的状态,并执行到一个for循环的函数:
1467 switch_channel_set_private(peer_channel, "_bridge_", b_leg);
1468 switch_channel_set_state(peer_channel, CS_EXCHANGE_MEDIA);//设置B-leg状态
1469
1470 audio_bridge_thread(NULL, (void *) a_leg);//a-leg的函数,包含for循环
执行完成之后,a-leg和b-leg在通话的过程中,都执行在函数
audio_bridge_thread
这个函数是含有一个无线循环,并循环获取通道对应事件并且执行:
345 for (;;) {
346 switch_channel_state_t b_state;
347 switch_status_t status;
348 switch_event_t *event;
更主要的功能,是通过读取自己的语音包发送到对方的通道中,实现语音通话。
558 /* read audio from 1 channel and write it to the other */
559 status = switch_core_session_read_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, stream_id);
560
561 if (SWITCH_READ_ACCEPTABLE(status)) {
562 read_frame_count++;
...//部分代码隐藏
578 if (switch_core_session_write_frame(session_b, read_frame, SWITCH_IO_FLAG_NONE, stream_id) != SWITCH_STATUS_SUCCESS) {
579 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG,
580 "%s ending bridge by request from write function\n", switch_channel_get_name(chan_b));
581 goto end_of_bridge_loop;
582 }
在两个人正常通话的过程中,这个函数会一直会执行。并一直读取通道的状态,是否已经挂机,是否通道当中还有语音,或者错误,如果出现了上面的状态,函数退出,整个通话过程结束。
好了,就这么多了。