FreeSWITCH源码阅读--一次呼叫

原创
2016/05/09 20:41
阅读数 2.1K
/**源码基于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, "<channel_url>",
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中的用法:

<action application="bridge" data="user/1002"/>

我们顺着函数继续往下看,一些判断和赋值可以忽略,代码一般会执行到这个函数:

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                 }

在两个人正常通话的过程中,这个函数会一直会执行。并一直读取通道的状态,是否已经挂机,是否通道当中还有语音,或者错误,如果出现了上面的状态,函数退出,整个通话过程结束。

好了,就这么多了。

 

 

展开阅读全文
打赏
2
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
2
分享
返回顶部
顶部