文章目录
- 1. uuid_bridge 命令执行
- 2. 源码分析
- 3. uuid_bridge 问题处理
1. uuid_bridge 命令执行
uuid_bridge
命令用于将两条腿桥接在一起,以实现双方的通话。下图是该命令在 FreeSWITCH 中的处理流程,大致的流程可以分成以下几个步骤:
- uuid_bridge 命令的执行
- a-leg 与 b-leg 的状态机流转
- 桥接时的处理
2. 源码分析
-
uuid_bridge
命令被下发送到 FreeSWITCH 时,会被mod_commands.c#uuid_bridge_function()
函数处理,这个函数将解析命令参数,接着调用switch_ivr_bridge.c#switch_ivr_uuid_bridge()
函数去触发桥接#define UUID_SYNTAX "<uuid> <other_uuid>" SWITCH_STANDARD_API(uuid_bridge_function) { char *mycmd = NULL, *argv[4] = { 0 }; int argc = 0; if (!zstr(cmd) && (mycmd = strdup(cmd))) { argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); } if (zstr(cmd) || argc < 2) { stream->write_function(stream, "-USAGE: %s\n", UUID_SYNTAX); } else { switch_status_t status; char *who = NULL; if ((status = switch_ivr_uuid_bridge(argv[0], argv[1])) != SWITCH_STATUS_SUCCESS) { if (argv[2]) { if ((status = switch_ivr_uuid_bridge(argv[0], argv[2])) == SWITCH_STATUS_SUCCESS) { who = argv[2]; } } } else { who = argv[1]; } if (status == SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "+OK %s\n", who); } else { stream->write_function(stream, "-ERR Invalid uuid\n"); } } switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; }
-
switch_ivr_bridge.c#switch_ivr_uuid_bridge()
函数会做许多状态校验之类的工作,但是核心其实只有两步:- 调用
switch_channel.c#switch_channel_add_state_handler()
将 switch_ivr_bridge.c#uuid_bridge_state_handlers 状态机回调函数表加在将要桥接的两条腿上,当这两条腿状态流转时会回调函数表中相应的函数,不了解这部分的读者可参考 FreeSWITCH 1.10 源码阅读(4)-从呼入处理分析核心状态机 - 调用
switch_channel.h#switch_channel_set_state()
将两条腿切换为指定状态CS_HIBERNATE
,从而驱动状态机流转
SWITCH_DECLARE(switch_status_t) switch_ivr_uuid_bridge(const char *originator_uuid, const char *originatee_uuid) { switch_core_session_t *originator_session, *originatee_session, *swap_session; switch_channel_t *originator_channel, *originatee_channel, *swap_channel; switch_status_t status = SWITCH_STATUS_FALSE; switch_caller_profile_t *originator_cp, *originatee_cp; switch_channel_state_t state; if ((originator_session = switch_core_session_locate(originator_uuid))) { if ((originatee_session = switch_core_session_locate(originatee_uuid))) { originator_channel = switch_core_session_get_channel(originator_session); originatee_channel = switch_core_session_get_channel(originatee_session); switch_ivr_check_hold(originator_session); switch_ivr_check_hold(originatee_session); if (switch_channel_test_flag(originator_channel, CF_LEG_HOLDING)) { switch_channel_set_flag(originator_channel, CF_HOLD_ON_BRIDGE); } if (switch_channel_test_flag(originatee_channel, CF_LEG_HOLDING)) { switch_channel_set_flag(originatee_channel, CF_HOLD_ON_BRIDGE); } if (switch_channel_direction(originator_channel) == SWITCH_CALL_DIRECTION_OUTBOUND && !switch_channel_test_flag(originator_channel, CF_DIALPLAN)) { if (!switch_channel_test_flag(originator_channel, CF_RECOVERING_BRIDGE)) { switch_channel_flip_cid(originator_channel); } switch_channel_set_flag(originator_channel, CF_DIALPLAN); } if (switch_channel_down_nosig(originator_channel)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "%s is hungup refusing to bridge.\n", switch_channel_get_name(originatee_channel)); switch_core_session_rwunlock(originator_session); switch_core_session_rwunlock(originatee_session); return SWITCH_STATUS_FALSE; } if (!switch_channel_media_up(originator_channel)) { if (switch_channel_media_up(originatee_channel)) { swap_session = originator_session; originator_session = originatee_session; originatee_session = swap_session; swap_channel = originator_channel; originator_channel = originatee_channel; originatee_channel = swap_channel; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originatee_session), SWITCH_LOG_WARNING, "reversing order of channels so this will work!\n"); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_CRIT, "Neither channel is answered, cannot bridge them.\n"); switch_core_session_rwunlock(originator_session); switch_core_session_rwunlock(originatee_session); return SWITCH_STATUS_FALSE; } } if (switch_channel_direction(originatee_channel) == SWITCH_CALL_DIRECTION_OUTBOUND && switch_channel_test_flag(originatee_channel, CF_DIALPLAN)) { switch_channel_clear_flag(originatee_channel, CF_DIALPLAN); } cleanup_proxy_mode_a(originator_session); cleanup_proxy_mode_a(originatee_session); /* override transmit state for originator_channel to bridge to originatee_channel * install pointer to originatee_session into originator_channel * set CF_TRANSFER on both channels and change state to CS_SOFT_EXECUTE to * interrupt anything they are already doing. * originatee_session will fall asleep and originator_session will bridge to it */ switch_channel_set_flag(originator_channel, CF_REDIRECT); switch_channel_set_flag(originatee_channel, CF_REDIRECT); switch_channel_set_variable(originator_channel, SWITCH_UUID_BRIDGE, switch_core_session_get_uuid(originatee_session)); switch_channel_set_variable(originator_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(originatee_channel)); switch_channel_set_variable(originator_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(originatee_session)); switch_channel_set_variable(originator_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(originatee_session)); switch_channel_set_variable(originatee_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(originator_channel)); switch_channel_set_variable(originatee_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(originator_session)); switch_channel_set_variable(originatee_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(originator_session)); originator_cp = switch_channel_get_caller_profile(originator_channel); originatee_cp = switch_channel_get_caller_profile(originatee_channel); if (switch_channel_outbound_display(originator_channel)) { switch_channel_invert_cid(originator_channel); if (switch_channel_direction(originator_channel) == SWITCH_CALL_DIRECTION_INBOUND) { switch_channel_clear_flag(originatee_channel, CF_BLEG); } } if (switch_channel_inbound_display(originatee_channel)) { switch_channel_invert_cid(originatee_channel); if (switch_channel_direction(originatee_channel) == SWITCH_CALL_DIRECTION_INBOUND) { switch_channel_set_flag(originatee_channel, CF_BLEG); } } switch_channel_set_variable(originatee_channel, "original_destination_number", originatee_cp->destination_number); switch_channel_set_variable(originatee_channel, "original_caller_id_name", originatee_cp->caller_id_name); switch_channel_set_variable(originatee_channel, "original_caller_id_number", originatee_cp->caller_id_number); switch_channel_set_variable(originator_channel, "original_destination_number", originator_cp->destination_number); switch_channel_set_variable(originator_channel, "original_caller_id_name", originator_cp->caller_id_name); switch_channel_set_variable(originator_channel, "original_caller_id_number", originator_cp->caller_id_number); switch_channel_step_caller_profile(originatee_channel); switch_channel_step_caller_profile(originator_channel); originator_cp = switch_channel_get_caller_profile(originator_channel); originatee_cp = switch_channel_get_caller_profile(originatee_channel); #ifdef DEEP_DEBUG_CID { switch_event_t *event; if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { //switch_channel_event_set_basic_data(originator_channel, event); switch_caller_profile_event_set_data(originator_cp, "ORIGINATOR", event); switch_caller_profile_event_set_data(originatee_cp, "ORIGINATEE", event); DUMP_EVENT(event); switch_event_destroy(&event); } } #endif switch_channel_set_originator_caller_profile(originatee_channel, switch_caller_profile_clone(originatee_session, originator_cp)); switch_channel_set_originatee_caller_profile(originator_channel, switch_caller_profile_clone(originator_session, originatee_cp)); originator_cp->callee_id_name = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_name); originator_cp->callee_id_number = switch_core_strdup(originator_cp->pool, originatee_cp->callee_id_number); originatee_cp->caller_id_name = switch_core_strdup(originatee_cp->pool, originator_cp->caller_id_name); originatee_cp->caller_id_number = switch_core_strdup(originatee_cp->pool, originator_cp->caller_id_number); #ifdef DEEP_DEBUG_CID { switch_event_t *event; if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { //switch_channel_event_set_basic_data(originator_channel, event); switch_caller_profile_event_set_data(originator_cp, "POST-ORIGINATOR", event); switch_caller_profile_event_set_data(originatee_cp, "POST-ORIGINATEE", event); DUMP_EVENT(event); switch_event_destroy(&event); } } #endif switch_channel_stop_broadcast(originator_channel); switch_channel_stop_broadcast(originatee_channel); switch_channel_set_flag(originator_channel, CF_TRANSFER); switch_channel_set_flag(originatee_channel, CF_TRANSFER); switch_channel_clear_flag(originator_channel, CF_ORIGINATING); switch_channel_clear_flag(originatee_channel, CF_ORIGINATING); originator_cp->transfer_source = switch_core_sprintf(originator_cp->pool, "%ld:%s:uuid_br:%s", (long)switch_epoch_time_now(NULL), originator_cp->uuid_str, switch_core_session_get_uuid(originatee_session)); switch_channel_add_variable_var_check(originator_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, originator_cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH); switch_channel_set_variable(originator_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, originator_cp->transfer_source); originatee_cp->transfer_source = switch_core_sprintf(originatee_cp->pool, "%ld:%s:uuid_br:%s", (long)switch_epoch_time_now(NULL), originatee_cp->uuid_str, switch_core_session_get_uuid(originator_session)); switch_channel_add_variable_var_check(originatee_channel, SWITCH_TRANSFER_HISTORY_VARIABLE, originatee_cp->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH); switch_channel_set_variable(originatee_channel, SWITCH_TRANSFER_SOURCE_VARIABLE, originatee_cp->transfer_source); /* change the states and let the chips fall where they may */ //switch_channel_set_variable(originator_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, NULL); //switch_channel_set_variable(originatee_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE, NULL); switch_channel_clear_state_handler(originator_channel, NULL); switch_channel_clear_state_handler(originatee_channel, NULL); switch_channel_clear_state_flag(originator_channel, CF_BRIDGE_ORIGINATOR); switch_channel_clear_state_flag(originatee_channel, CF_BRIDGE_ORIGINATOR); switch_channel_clear_flag(originator_channel, CF_UUID_BRIDGE_ORIGINATOR); switch_channel_clear_flag(originatee_channel, CF_UUID_BRIDGE_ORIGINATOR); switch_channel_set_state_flag(originator_channel, CF_UUID_BRIDGE_ORIGINATOR); switch_channel_add_state_handler(originator_channel, &uuid_bridge_state_handlers); switch_channel_add_state_handler(originatee_channel, &uuid_bridge_state_handlers); state = switch_channel_get_state(originator_channel); switch_channel_set_state(originator_channel, state == CS_HIBERNATE ? CS_CONSUME_MEDIA : CS_HIBERNATE); state = switch_channel_get_state(originatee_channel); switch_channel_set_state(originatee_channel, state == CS_HIBERNATE ? CS_CONSUME_MEDIA : CS_HIBERNATE); status = SWITCH_STATUS_SUCCESS; //switch_ivr_bridge_display(originator_session, originatee_session); /* release the read locks we have on the channels */ switch_core_session_rwunlock(originator_session); switch_core_session_rwunlock(originatee_session); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "originatee uuid %s is not present\n", originatee_uuid); switch_core_session_rwunlock(originator_session); } } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(originator_session), SWITCH_LOG_DEBUG, "originator uuid %s is not present\n", originator_uuid); } return status; }
- 调用
-
当 a-leg 状态切换为
CS_HIBERNATE
,在核心状态机的调度下,安装在这条腿上的回调函数switch_ivr_bridge.c#uuid_bridge_state_handlers.uuid_bridge_on_hibernate()
将被执行,推动当前腿的状态向CS_RESET
切换。当状态发生改变,同理switch_ivr_bridge.c#uuid_bridge_state_handlers.uuid_bridge_on_reset()
将被执行static switch_status_t uuid_bridge_on_hibernate(switch_core_session_t *session) { switch_channel_set_state(switch_core_session_get_channel(session), CS_RESET); return SWITCH_STATUS_FALSE; }
-
switch_ivr_bridge.c#uuid_bridge_state_handlers.uuid_bridge_on_reset()
函数会判断当前腿是否为主叫腿,也就是是否为 a-leg,如是则将其状态进一步驱动为CS_SOFT_EXECUTE
,而 b-leg 在此处则停留在CS_RESET
状态static switch_status_t uuid_bridge_on_reset(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM RESET\n", switch_channel_get_name(channel)); switch_channel_clear_flag(channel, CF_ORIGINATING); cleanup_proxy_mode_b(session); if (switch_channel_test_flag(channel, CF_UUID_BRIDGE_ORIGINATOR)) { switch_channel_set_state(channel, CS_SOFT_EXECUTE); } return SWITCH_STATUS_SUCCESS; }
-
switch_ivr_bridge.c#uuid_bridge_state_handlers.uuid_bridge_on_soft_execute()
函数只会在 a-leg 上执行,其核心逻辑为以下几步:- 清除 a-leg 上的回调函数表 switch_ivr_bridge.c#uuid_bridge_state_handlers,检测 b-leg 状态,如果 b-leg 抵达了
CS_RESET
状态,则将其向CS_SOFT_EXECUTE
状态推动,只有 b-leg 也到达这个状态才能继续向下处理 - 两条腿的状态都就绪后,继续执行
switch_ivr_bridge.c#switch_ivr_multi_threaded_bridge()
完成实际的桥接处理
static switch_status_t uuid_bridge_on_soft_execute(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_core_session_t *other_session = NULL; const char *other_uuid = NULL; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CUSTOM SOFT_EXECUTE\n", switch_channel_get_name(channel)); switch_channel_clear_state_handler(channel, &uuid_bridge_state_handlers); if (!switch_channel_test_flag(channel, CF_UUID_BRIDGE_ORIGINATOR)) { return SWITCH_STATUS_SUCCESS; } if ((other_uuid = switch_channel_get_variable(channel, SWITCH_UUID_BRIDGE)) && (other_session = switch_core_session_locate(other_uuid))) { switch_channel_t *other_channel = switch_core_session_get_channel(other_session); switch_event_t *event; int ready_a, ready_b; switch_channel_state_t state, running_state; int max = 1000, loops = max; switch_channel_set_variable(channel, SWITCH_UUID_BRIDGE, NULL); for (;;) { state = switch_channel_get_state(other_channel); running_state = switch_channel_get_running_state(other_channel); if (switch_channel_down_nosig(other_channel) || switch_channel_down(channel)) { break; } if (state < CS_HANGUP && state == running_state) { if (--loops < 1) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } if (running_state == CS_RESET) { switch_channel_set_state(other_channel, CS_SOFT_EXECUTE); } if (running_state == CS_SOFT_EXECUTE) { if (switch_channel_test_flag(other_channel, CF_UUID_BRIDGE_ORIGINATOR)) { goto done; } else { break; } } } else { loops = max; } switch_yield(20000); } switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); if (switch_ivr_wait_for_answer(session, other_session) != SWITCH_STATUS_SUCCESS) { if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_continue_on_cancel"))) { switch_channel_set_state(channel, CS_EXECUTE); } else if (switch_true(switch_channel_get_variable(channel, "uuid_bridge_park_on_cancel"))) { switch_ivr_park_session(session); } else if (!switch_channel_test_flag(channel, CF_TRANSFER)) { switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL); } goto done; } ready_a = switch_channel_ready(channel); ready_b = switch_channel_ready(other_channel); if (!ready_a || !ready_b) { if (!ready_a) { switch_channel_hangup(other_channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } if (!ready_b) { const char *cid = switch_channel_get_variable(other_channel, "rdnis"); if (ready_a && cid) { switch_ivr_session_transfer(session, cid, NULL, NULL); } else { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } } goto done; } /* fire events that will change the data table from "show channels" */ if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(channel, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(other_session)); switch_event_fire(&event); } if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_EXECUTE) == SWITCH_STATUS_SUCCESS) { switch_channel_event_set_data(other_channel, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application", "uuid_bridge"); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Application-Data", switch_core_session_get_uuid(session)); switch_event_fire(&event); } switch_ivr_multi_threaded_bridge(session, other_session, NULL, NULL, NULL); state = switch_channel_get_state(channel); if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_REDIRECT) && state < CS_HANGUP && state != CS_ROUTING && state != CS_PARK) { switch_channel_set_state(channel, CS_EXECUTE); } } else { int hup = 1; const char *var; if (switch_true(switch_channel_get_variable(channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) { switch_ivr_park_session(session); hup = 0; } else if ((var = switch_channel_get_variable(channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) { transfer_after_bridge(session, var); hup = 0; } if (hup) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } } done: if (other_session) { switch_core_session_rwunlock(other_session); other_session = NULL; } switch_channel_clear_flag(channel, CF_UUID_BRIDGE_ORIGINATOR); return SWITCH_STATUS_FALSE; }
- 清除 a-leg 上的回调函数表 switch_ivr_bridge.c#uuid_bridge_state_handlers,检测 b-leg 状态,如果 b-leg 抵达了
-
switch_ivr_bridge.c#switch_ivr_multi_threaded_bridge()
函数比较长,不过核心步骤显而易见:- 首先调用
switch_channel.c#switch_channel_add_state_handler()
将回调函数表switch_ivr_bridge.c#audio_bridge_peer_state_handlers 安装在 b-leg 上,随后发送SWITCH_MESSAGE_INDICATE_BRIDGE 消息
通知底层 Sofia 模块将要进行桥接处理 - 完成后首先将 b-leg 驱动到
CS_EXCHANGE_MEDIA
状态,然后 a-leg 执行switch_ivr_bridge.c#audio_bridge_thread()
函数开始和 b-leg 的媒体交互处理 - 媒体交互结束后还要检查 b-leg 是否停留在
CS_EXCHANGE_MEDIA
状态,不是则表明对端的媒体交互也结束了,此时发送SWITCH_MESSAGE_INDICATE_UNBRIDGE消息
通知底层 Sofia 模块桥接结束 - 桥接结束后,检查相关配置,如果 a-leg 上有 park_after_bridge 之类的参数,则调用
switch_ivr_park_session()
函数将 a-leg 设置为CS_PARK
状态
SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_session_t *session, switch_core_session_t *peer_session, switch_input_callback_function_t input_callback, void *session_data, void *peer_session_data) { switch_ivr_bridge_data_t *a_leg = switch_core_session_alloc(session, sizeof(*a_leg)); switch_ivr_bridge_data_t *b_leg = switch_core_session_alloc(peer_session, sizeof(*b_leg)); switch_channel_t *caller_channel = switch_core_session_get_channel(session); switch_channel_t *peer_channel = switch_core_session_get_channel(peer_session); int stream_id = 0; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_channel_state_t state; switch_event_t *event; int br = 0; int inner_bridge = switch_channel_test_flag(caller_channel, CF_INNER_BRIDGE); const char *var; switch_call_cause_t cause; switch_core_session_message_t msg = { 0 }; if (switch_channel_test_flag(caller_channel, CF_PROXY_MODE)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Call has no media... Redirecting to signal bridge.\n"); return switch_ivr_signal_bridge(session, peer_session); } check_bridge_export(caller_channel, peer_channel); switch_channel_set_flag_recursive(caller_channel, CF_MEDIA_BRIDGE_TTL); switch_channel_set_flag_recursive(peer_channel, CF_MEDIA_BRIDGE_TTL); switch_channel_set_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR); switch_channel_clear_flag(peer_channel, CF_BRIDGE_ORIGINATOR); switch_channel_audio_sync(caller_channel); switch_channel_audio_sync(peer_channel); b_leg->session = peer_session; switch_copy_string(b_leg->b_uuid, switch_core_session_get_uuid(session), sizeof(b_leg->b_uuid)); b_leg->stream_id = stream_id; b_leg->input_callback = input_callback; b_leg->session_data = peer_session_data; b_leg->clean_exit = 0; b_leg->other_leg_data = a_leg; a_leg->session = session; switch_copy_string(a_leg->b_uuid, switch_core_session_get_uuid(peer_session), sizeof(a_leg->b_uuid)); a_leg->stream_id = stream_id; a_leg->input_callback = input_callback; a_leg->session_data = session_data; a_leg->clean_exit = 0; a_leg->other_leg_data = b_leg; switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers); if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) { switch_channel_pass_callee_id(peer_channel, caller_channel); switch_channel_answer(caller_channel); } if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) || switch_channel_test_flag(peer_channel, CF_RING_READY)) { const char *app, *data; if (!switch_channel_ready(caller_channel)) { abort_call(caller_channel, peer_channel); goto done; } if (!switch_channel_test_flag(peer_channel, CF_ARRANGED_BRIDGE)) { switch_channel_set_state(peer_channel, CS_CONSUME_MEDIA); } switch_channel_set_variable(peer_channel, "call_uuid", switch_core_session_get_uuid(session)); switch_channel_set_bridge_time(caller_channel); switch_channel_set_bridge_time(peer_channel); if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_BRIDGE) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", switch_core_session_get_uuid(peer_session)); switch_channel_event_set_data(caller_channel, event); switch_event_add_presence_data_cols(peer_channel, event, "Bridge-B-PD-"); switch_event_fire(&event); br = 1; } if (switch_core_session_read_lock(peer_session) == SWITCH_STATUS_SUCCESS) { switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session)); switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); switch_channel_set_variable(caller_channel, SWITCH_LAST_BRIDGE_VARIABLE, switch_core_session_get_uuid(peer_session)); switch_channel_set_variable(peer_channel, SWITCH_LAST_BRIDGE_VARIABLE, switch_core_session_get_uuid(session)); if (!switch_channel_ready(caller_channel)) { abort_call(caller_channel, peer_channel); switch_core_session_rwunlock(peer_session); goto done; } if (!switch_channel_media_ready(caller_channel) || (!switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA))) { if ((status = switch_ivr_wait_for_answer(session, peer_session)) != SWITCH_STATUS_SUCCESS || !switch_channel_ready(caller_channel)) { switch_channel_state_t w_state = switch_channel_get_state(caller_channel); switch_channel_hangup(peer_channel, SWITCH_CAUSE_ALLOTTED_TIMEOUT); if (w_state < CS_HANGUP && w_state != CS_ROUTING && w_state != CS_PARK && !switch_channel_test_flag(caller_channel, CF_REDIRECT) && !switch_channel_test_flag(caller_channel, CF_TRANSFER) && w_state != CS_EXECUTE) { const char *ext = switch_channel_get_variable(peer_channel, "original_destination_number"); if (!ext) { ext = switch_channel_get_variable(peer_channel, "destination_number"); } if (ext) { switch_ivr_session_transfer(session, ext, NULL, NULL); } else { switch_channel_hangup(caller_channel, SWITCH_CAUSE_ALLOTTED_TIMEOUT); } } abort_call(caller_channel, peer_channel); switch_core_session_rwunlock(peer_session); goto done; } } if (switch_channel_test_flag(peer_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_ANSWERED)) { switch_channel_answer(caller_channel); } switch_channel_wait_for_flag(peer_channel, CF_BROADCAST, SWITCH_FALSE, 10000, caller_channel); switch_ivr_parse_all_events(peer_session); switch_ivr_parse_all_events(session); msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; msg.from = __FILE__; msg.string_arg = switch_core_session_strdup(peer_session, switch_core_session_get_uuid(session)); if (switch_core_session_receive_message(peer_session, &msg) != SWITCH_STATUS_SUCCESS) { status = SWITCH_STATUS_FALSE; abort_call(caller_channel, peer_channel); switch_core_session_rwunlock(peer_session); goto done; } msg.string_arg = switch_core_session_strdup(session, switch_core_session_get_uuid(peer_session)); if (switch_core_session_receive_message(session, &msg) != SWITCH_STATUS_SUCCESS) { status = SWITCH_STATUS_FALSE; abort_call(caller_channel, peer_channel); switch_core_session_rwunlock(peer_session); goto done; } switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(peer_channel)); switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(peer_session)); switch_channel_set_variable(caller_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(peer_session)); switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_CHANNEL_VARIABLE, switch_channel_get_name(caller_channel)); switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_UUID_VARIABLE, switch_core_session_get_uuid(session)); switch_channel_set_variable(peer_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session)); if ((app = switch_channel_get_variable(caller_channel, "bridge_pre_execute_aleg_app"))) { switch_channel_set_variable(caller_channel, "bridge_pre_execute_app", app); if ((data = switch_channel_get_variable(caller_channel, "bridge_pre_execute_aleg_data"))) { switch_channel_set_variable(caller_channel, "bridge_pre_execute_data", data); } } if ((app = switch_channel_get_variable(caller_channel, "bridge_pre_execute_bleg_app"))) { switch_channel_set_variable(peer_channel, "bridge_pre_execute_app", app); if ((data = switch_channel_get_variable(caller_channel, "bridge_pre_execute_bleg_data"))) { switch_channel_set_variable(peer_channel, "bridge_pre_execute_data", data); } } switch_channel_set_private(peer_channel, "_bridge_", b_leg); if (switch_channel_test_flag(peer_channel, CF_ARRANGED_BRIDGE)) { switch_channel_clear_flag(peer_channel, CF_ARRANGED_BRIDGE); } else { switch_channel_set_state(peer_channel, CS_EXCHANGE_MEDIA); } audio_bridge_thread(NULL, (void *) a_leg); switch_channel_clear_flag_recursive(caller_channel, CF_BRIDGE_ORIGINATOR); switch_channel_stop_broadcast(peer_channel); while (switch_channel_get_state(peer_channel) == CS_EXCHANGE_MEDIA) { switch_ivr_parse_all_messages(session); switch_cond_next(); } if (inner_bridge) { if (switch_channel_ready(caller_channel)) { switch_channel_set_flag(caller_channel, CF_BRIDGED); } if (switch_channel_ready(peer_channel)) { switch_channel_set_flag(peer_channel, CF_BRIDGED); } } if ((cause = switch_channel_get_cause(caller_channel))) { switch_channel_set_variable(peer_channel, SWITCH_BRIDGE_HANGUP_CAUSE_VARIABLE, switch_channel_cause2str(cause)); } if ((cause = switch_channel_get_cause(peer_channel))) { switch_channel_set_variable(caller_channel, SWITCH_BRIDGE_HANGUP_CAUSE_VARIABLE, switch_channel_cause2str(cause)); } if (switch_channel_down_nosig(peer_channel)) { switch_bool_t copy_xml_cdr = switch_true(switch_channel_get_variable(peer_channel, SWITCH_COPY_XML_CDR_VARIABLE)); switch_bool_t copy_json_cdr = switch_true(switch_channel_get_variable(peer_channel, SWITCH_COPY_JSON_CDR_VARIABLE)); if (copy_xml_cdr || copy_json_cdr) { char *cdr_text = NULL; switch_channel_wait_for_state(peer_channel, caller_channel, CS_DESTROY); if (copy_xml_cdr) { switch_xml_t cdr = NULL; if (switch_ivr_generate_xml_cdr(peer_session, &cdr) == SWITCH_STATUS_SUCCESS) { cdr_text = switch_xml_toxml(cdr, SWITCH_FALSE); switch_xml_free(cdr); } } if (copy_json_cdr) { cJSON *cdr = NULL; if (switch_ivr_generate_json_cdr(peer_session, &cdr, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) { cdr_text = cJSON_PrintUnformatted(cdr); cJSON_Delete(cdr); } } if (cdr_text) { switch_channel_set_variable(caller_channel, "b_leg_cdr", cdr_text); switch_channel_set_variable_name_printf(caller_channel, cdr_text, "b_leg_cdr_%s", switch_core_session_get_uuid(peer_session)); switch_safe_free(cdr_text); } } } switch_core_session_rwunlock(peer_session); } else { status = SWITCH_STATUS_FALSE; } } else { status = SWITCH_STATUS_FALSE; } if (status != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Bridge Failed %s->%s\n", switch_channel_get_name(caller_channel), switch_channel_get_name(peer_channel) ); switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ANSWER); } done: switch_channel_set_variable(peer_channel, "call_uuid", switch_core_session_get_uuid(peer_session)); if (br && switch_event_create(&event, SWITCH_EVENT_CHANNEL_UNBRIDGE) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-A-Unique-ID", switch_core_session_get_uuid(session)); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-B-Unique-ID", switch_core_session_get_uuid(peer_session)); switch_channel_event_set_data(caller_channel, event); switch_event_add_presence_data_cols(peer_channel, event, "Bridge-B-PD-"); switch_event_fire(&event); } msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; msg.from = __FILE__; msg.string_arg = switch_core_session_strdup(peer_session, switch_core_session_get_uuid(session)); switch_core_session_receive_message(peer_session, &msg); msg.string_arg = switch_core_session_strdup(session, switch_core_session_get_uuid(peer_session)); switch_core_session_receive_message(session, &msg); state = switch_channel_get_state(caller_channel); if (!switch_channel_test_flag(caller_channel, CF_TRANSFER) && !switch_channel_test_flag(caller_channel, CF_REDIRECT) && !switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE) && !a_leg->clean_exit && !inner_bridge) { switch_call_cause_t cause = switch_channel_get_cause(peer_channel); const char *hup = switch_channel_get_variable(caller_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE); int explicit = 0; int answered = 0; int early = 0; if (cause == SWITCH_CAUSE_NONE) { cause = SWITCH_CAUSE_NORMAL_CLEARING; } if (hup) { explicit = !strcasecmp(hup, "explicit"); } if (!switch_channel_test_flag(peer_channel, CF_ANSWERED)) { switch_channel_handle_cause(caller_channel, cause); } if (explicit) { if (switch_channel_test_flag(peer_channel, CF_INTERCEPTED)) { switch_channel_set_flag(peer_channel, CF_INTERCEPT); } switch_channel_hangup(caller_channel, cause); } answered = switch_channel_test_flag(peer_channel, CF_ANSWERED); early = switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA); if ((state != CS_EXECUTE && state != CS_SOFT_EXECUTE && state != CS_PARK && state != CS_ROUTING) || ((answered || early) && state < CS_HANGUP)) { if (!switch_channel_test_flag(caller_channel, CF_TRANSFER)) { if ((answered && switch_true(switch_channel_get_variable(caller_channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) || switch_true(switch_channel_get_variable(caller_channel, SWITCH_PARK_AFTER_EARLY_BRIDGE_VARIABLE))) { switch_ivr_park_session(session); } else if ((answered && (var = switch_channel_get_variable(caller_channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) || (var = switch_channel_get_variable(caller_channel, SWITCH_TRANSFER_AFTER_EARLY_BRIDGE_VARIABLE))) { transfer_after_bridge(session, var); } else if (answered) { if (switch_true(hup)) { if (switch_channel_test_flag(peer_channel, CF_INTERCEPTED)) { switch_channel_set_flag(peer_channel, CF_INTERCEPT); } switch_channel_hangup(caller_channel, cause); } } } } } if (switch_channel_test_flag(caller_channel, CF_REDIRECT)) { if (switch_channel_test_flag(caller_channel, CF_RESET)) { switch_channel_clear_flag(caller_channel, CF_RESET); } else { state = switch_channel_get_state(caller_channel); if (!(state == CS_RESET || state == CS_PARK || state == CS_ROUTING || state == CS_EXECUTE)) { switch_channel_set_state(caller_channel, CS_RESET); } } } return status; }
- 首先调用
-
b-leg 状态流转到
CS_EXCHANGE_MEDIA
,则将回调执行switch_ivr_bridge.c#audio_bridge_peer_state_handlers.audio_bridge_on_exchange_media()
函数,此处核心是在 b-leg 上执行switch_ivr_bridge.c#audio_bridge_thread()
函数进行与 a-leg 的媒体交互static switch_status_t audio_bridge_on_exchange_media(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_bridge_data_t *bd = switch_channel_get_private(channel, "_bridge_"); switch_channel_state_t state; const char *var; if (bd) { switch_channel_set_private(channel, "_bridge_", NULL); if (bd->session == session && *bd->b_uuid) { audio_bridge_thread(NULL, (void *) bd); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); } else { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } } else { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } switch_channel_clear_state_handler(channel, &audio_bridge_peer_state_handlers); state = switch_channel_get_state(channel); if (!switch_channel_test_flag(channel, CF_TRANSFER) && !switch_channel_test_flag(channel, CF_REDIRECT) && !switch_channel_test_flag(channel, CF_XFER_ZOMBIE) && bd && !bd->clean_exit && state != CS_PARK && state != CS_ROUTING && state == CS_EXCHANGE_MEDIA && !switch_channel_test_flag(channel, CF_INNER_BRIDGE)) { if (state < CS_HANGUP && switch_true(switch_channel_get_variable(channel, SWITCH_PARK_AFTER_BRIDGE_VARIABLE))) { switch_ivr_park_session(session); return SWITCH_STATUS_FALSE; } else if (state < CS_HANGUP && (var = switch_channel_get_variable(channel, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE))) { transfer_after_bridge(session, var); return SWITCH_STATUS_FALSE; } if (switch_channel_test_flag(channel, CF_INTERCEPTED)) { switch_channel_clear_flag(channel, CF_INTERCEPT); switch_channel_clear_flag(channel, CF_INTERCEPTED); return SWITCH_STATUS_FALSE; } else { if (switch_channel_test_flag(channel, CF_INTERCEPT)) { switch_channel_hangup(channel, SWITCH_CAUSE_PICKED_OFF); } else { if (!switch_channel_test_flag(channel, CF_ANSWERED)) { int x = 0; if (switch_channel_execute_on(channel, "execute_on_orphaned_bleg") == SWITCH_STATUS_SUCCESS) { x++; } if (switch_channel_api_on(channel, "api_on_orphaned_bleg") == SWITCH_STATUS_SUCCESS) { x++; } if (!x) { switch_channel_hangup(channel, SWITCH_CAUSE_ORIGINATOR_CANCEL); } } else { switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); } } } } if (switch_channel_get_state(channel) == CS_EXCHANGE_MEDIA) { switch_channel_set_variable(channel, "park_timeout", "3"); switch_channel_set_state(channel, CS_PARK); } return SWITCH_STATUS_FALSE; }
-
switch_ivr_bridge.c#audio_bridge_thread()
函数的核心是从当前腿调用switch_core_io.c#switch_core_session_read_frame()
读取媒体流,再调用switch_core_io.c#switch_core_session_write_frame()
将其写入对端,完成媒体的交换,至此桥接的主体逻辑基本结束static void *audio_bridge_thread(switch_thread_t *thread, void *obj) { switch_ivr_bridge_data_t *data = obj; int stream_id = 0, pre_b = 0, ans_a = 0, ans_b = 0, originator = 0; switch_input_callback_function_t input_callback; switch_core_session_message_t msg = { 0 }; void *user_data; switch_channel_t *chan_a, *chan_b; switch_frame_t *read_frame; switch_core_session_t *session_a, *session_b; uint32_t read_frame_count = 0; const char *app_name = NULL, *app_arg = NULL; int inner_bridge = 0, exec_check = 0; switch_codec_t silence_codec = { 0 }; switch_frame_t silence_frame = { 0 }; int16_t silence_data[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; const char *silence_var; int silence_val = 0, bypass_media_after_bridge = 0; const char *bridge_answer_timeout = NULL; int bridge_filter_dtmf, answer_timeout, sent_update = 0; time_t answer_limit = 0; const char *exec_app = NULL; const char *exec_data = NULL; switch_codec_implementation_t read_impl = { 0 }; uint32_t txt_launch = 0; struct vid_helper th = { 0 }; const char *banner_file = NULL; int played_banner = 0, banner_counter = 0; int pass_val = 0, last_pass_val = 0; #ifdef SWITCH_VIDEO_IN_THREADS struct vid_helper vh = { 0 }; uint32_t vid_launch = 0; #endif data->clean_exit = 0; session_a = data->session; if (!(session_b = switch_core_session_locate(data->b_uuid))) { return NULL; } switch_core_session_get_read_impl(session_a, &read_impl); input_callback = data->input_callback; user_data = data->session_data; stream_id = data->stream_id; chan_a = switch_core_session_get_channel(session_a); chan_b = switch_core_session_get_channel(session_b); if ((exec_app = switch_channel_get_variable(chan_a, "bridge_pre_execute_app"))) { exec_data = switch_channel_get_variable(chan_a, "bridge_pre_execute_data"); } bypass_media_after_bridge = switch_channel_test_flag(chan_a, CF_BYPASS_MEDIA_AFTER_BRIDGE); switch_channel_clear_flag(chan_a, CF_BYPASS_MEDIA_AFTER_BRIDGE); ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED); if ((originator = switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR))) { pre_b = switch_channel_test_flag(chan_a, CF_EARLY_MEDIA); ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); } inner_bridge = switch_channel_test_flag(chan_a, CF_INNER_BRIDGE); if (!switch_channel_test_flag(chan_a, CF_ANSWERED) && (bridge_answer_timeout = switch_channel_get_variable(chan_a, "bridge_answer_timeout"))) { if ((answer_timeout = atoi(bridge_answer_timeout)) >= 0) { answer_limit = switch_epoch_time_now(NULL) + answer_timeout; } } switch_channel_clear_flag(chan_a, CF_INTERCEPT); switch_channel_clear_flag(chan_a, CF_INTERCEPTED); switch_channel_set_flag(chan_a, CF_BRIDGED); switch_channel_wait_for_flag(chan_b, CF_BRIDGED, SWITCH_TRUE, 10000, chan_a); if (!switch_channel_test_flag(chan_b, CF_BRIDGED)) { if (!(switch_channel_test_flag(chan_b, CF_TRANSFER) || switch_channel_test_flag(chan_b, CF_REDIRECT) || switch_channel_get_state(chan_b) == CS_RESET)) { switch_channel_hangup(chan_b, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } goto end_of_bridge_loop; } if (switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR) && (banner_file = switch_channel_get_variable(chan_a, "video_pre_call_banner"))) { switch_channel_set_flag(chan_a, CF_AUDIO_PAUSE_READ); switch_channel_set_flag(chan_b, CF_AUDIO_PAUSE_READ); switch_channel_set_flag(chan_a, CF_AUDIO_PAUSE_WRITE); switch_channel_set_flag(chan_b, CF_AUDIO_PAUSE_WRITE); switch_channel_set_flag(chan_a, CF_VIDEO_PAUSE_READ); switch_channel_set_flag(chan_b, CF_VIDEO_PAUSE_READ); } if (bypass_media_after_bridge) { const char *source_a = switch_channel_get_variable(chan_a, "source"); const char *source_b = switch_channel_get_variable(chan_b, "source"); if (!source_a) source_a = ""; if (!source_b) source_b = ""; if (switch_stristr("loopback", source_a) || switch_stristr("loopback", source_b)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_WARNING, "Cannot bypass media while bridged to a loopback address.\n"); bypass_media_after_bridge = 0; } } if ((silence_var = switch_channel_get_variable(chan_a, "bridge_generate_comfort_noise"))) { if (!switch_channel_media_up(chan_a)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_ERROR, "Channel has no media!\n"); goto end_of_bridge_loop; } if (switch_true(silence_var)) { silence_val = 1400; } else { if ((silence_val = atoi(silence_var)) < -1) { silence_val = 0; } } if (silence_val) { if (switch_core_codec_init(&silence_codec, "L16", NULL, NULL, read_impl.actual_samples_per_second, read_impl.microseconds_per_packet / 1000, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session_a)) != SWITCH_STATUS_SUCCESS) { silence_val = 0; } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Setup generated silence from %s to %s at %d\n", switch_channel_get_name(chan_a), switch_channel_get_name(chan_b), silence_val); silence_frame.codec = &silence_codec; silence_frame.data = silence_data; silence_frame.buflen = sizeof(silence_data); silence_frame.datalen = read_impl.decoded_bytes_per_packet; silence_frame.samples = silence_frame.datalen / sizeof(int16_t); } } } bridge_filter_dtmf = switch_true(switch_channel_get_variable(chan_a, "bridge_filter_dtmf")); for (;;) { switch_channel_state_t b_state; switch_status_t status; switch_event_t *event; if (switch_core_session_transcoding(session_a, session_b, SWITCH_MEDIA_TYPE_AUDIO)) { pass_val = 1; } else { pass_val = 2; } if (pass_val != last_pass_val) { switch_core_session_passthru(session_a, SWITCH_MEDIA_TYPE_AUDIO, pass_val == 2 ? SWITCH_TRUE : SWITCH_FALSE); last_pass_val = pass_val; } if (switch_channel_test_flag(chan_a, CF_TRANSFER)) { data->clean_exit = 1; } if (data->clean_exit || switch_channel_test_flag(chan_b, CF_TRANSFER)) { switch_channel_clear_flag(chan_a, CF_HOLD); switch_channel_clear_flag(chan_a, CF_SUSPEND); goto end_of_bridge_loop; } if (!switch_channel_test_flag(chan_b, CF_BRIDGED)) { goto end_of_bridge_loop; } if (!switch_channel_ready(chan_a)) { if (switch_channel_up(chan_a)) { data->clean_exit = 1; } goto end_of_bridge_loop; } if ((b_state = switch_channel_down_nosig(chan_b))) { goto end_of_bridge_loop; } if (switch_channel_test_flag(chan_a, CF_HOLD_ON_BRIDGE)) { switch_core_session_message_t hmsg = { 0 }; switch_channel_clear_flag(chan_a, CF_HOLD_ON_BRIDGE); hmsg.message_id = SWITCH_MESSAGE_INDICATE_HOLD; hmsg.from = __FILE__; hmsg.numeric_arg = 1; switch_core_session_receive_message(session_a, &hmsg); } if (read_frame_count > DEFAULT_LEAD_FRAMES && switch_channel_media_ack(chan_a) && switch_core_session_private_event_count(session_a)) { switch_channel_set_flag(chan_b, CF_SUSPEND); msg.numeric_arg = 42; msg.string_arg = data->b_uuid; msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE; msg.from = __FILE__; switch_core_session_receive_message(session_a, &msg); switch_ivr_parse_next_event(session_a); msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE; switch_core_session_receive_message(session_a, &msg); switch_channel_clear_flag(chan_b, CF_SUSPEND); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); } switch_ivr_parse_all_messages(session_a); if (!inner_bridge && (switch_channel_test_flag(chan_a, CF_SUSPEND) || switch_channel_test_flag(chan_b, CF_SUSPEND))) { status = switch_core_session_read_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, stream_id); if (!SWITCH_READ_ACCEPTABLE(status)) { goto end_of_bridge_loop; } continue; } if (switch_channel_test_flag(chan_a, CF_HAS_TEXT) && switch_channel_test_flag(chan_b, CF_HAS_TEXT) && !txt_launch) { txt_launch++; th.session_a = session_a; th.session_b = session_b; switch_core_media_start_engine_function(th.session_a, SWITCH_MEDIA_TYPE_TEXT, text_bridge_thread, &th); } #ifdef SWITCH_VIDEO_IN_THREADS if (switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO) && !vid_launch) { vid_launch++; vh.session_a = session_a; vh.session_b = session_b; switch_channel_clear_flag(chan_a, CF_VIDEO_BLANK); switch_channel_clear_flag(chan_b, CF_VIDEO_BLANK); launch_video(&vh); } else { if (switch_channel_test_flag(chan_a, CF_VIDEO)) { switch_channel_set_flag(chan_a, CF_VIDEO_BLANK); } if (switch_channel_test_flag(chan_b, CF_VIDEO)) { switch_channel_set_flag(chan_b, CF_VIDEO_BLANK); } } #endif if (read_frame_count >= DEFAULT_LEAD_FRAMES && switch_channel_media_ack(chan_a)) { if (!played_banner && switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO) && switch_channel_test_flag(chan_a, CF_ANSWERED) && switch_channel_test_flag(chan_b, CF_ANSWERED) && ++banner_counter > 100 && switch_channel_test_flag(chan_a, CF_VIDEO_READY) && switch_channel_test_flag(chan_b, CF_VIDEO_READY) && switch_channel_test_flag(chan_a, CF_BRIDGE_ORIGINATOR) && banner_file) { const char *b_banner_file = switch_channel_get_variable(chan_b, "video_pre_call_banner"); if (!b_banner_file) { b_banner_file = switch_channel_get_variable(chan_a, "video_pre_call_banner_bleg"); } switch_channel_clear_flag(chan_a, CF_VIDEO_PAUSE_READ); switch_channel_clear_flag(chan_b, CF_VIDEO_PAUSE_READ); if (b_banner_file) { switch_ivr_broadcast(switch_core_session_get_uuid(session_a), banner_file, SMF_ECHO_ALEG); switch_ivr_broadcast(switch_core_session_get_uuid(session_b), b_banner_file, SMF_ECHO_ALEG); } else { switch_ivr_broadcast(switch_core_session_get_uuid(session_a), banner_file, SMF_ECHO_ALEG | SMF_ECHO_BLEG); } played_banner = 1; switch_channel_clear_flag(chan_a, CF_AUDIO_PAUSE_READ); switch_channel_clear_flag(chan_b, CF_AUDIO_PAUSE_READ); switch_channel_clear_flag(chan_a, CF_AUDIO_PAUSE_WRITE); switch_channel_clear_flag(chan_b, CF_AUDIO_PAUSE_WRITE); } if (!exec_check) { switch_channel_execute_on(chan_a, SWITCH_CHANNEL_EXECUTE_ON_PRE_BRIDGE_VARIABLE); if (!inner_bridge) { switch_channel_api_on(chan_a, SWITCH_API_BRIDGE_START_VARIABLE); } exec_check = 1; } if (exec_app) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Bridge execute app %s(%s)\n", switch_channel_get_name(chan_a), exec_app, exec_data); switch_core_session_execute_application_async(session_a, exec_app, exec_data); exec_app = exec_data = NULL; } if ((bypass_media_after_bridge || switch_channel_test_flag(chan_b, CF_BYPASS_MEDIA_AFTER_BRIDGE)) && switch_channel_test_flag(chan_a, CF_ANSWERED) && switch_channel_test_flag(chan_b, CF_ANSWERED)) { if (switch_true(switch_channel_get_variable_dup(chan_a, "bypass_media_after_bridge_oldschool", SWITCH_FALSE, -1))) { switch_ivr_nomedia(switch_core_session_get_uuid(session_a), SMF_REBRIDGE); } else { switch_ivr_3p_nomedia(switch_core_session_get_uuid(session_a), SMF_REBRIDGE); } switch_channel_clear_flag(chan_b, CF_BYPASS_MEDIA_AFTER_BRIDGE); goto end_of_bridge_loop; } } /* if 1 channel has DTMF pass it to the other */ while (switch_channel_has_dtmf(chan_a)) { switch_dtmf_t dtmf = { 0, 0 }; if (switch_channel_dequeue_dtmf(chan_a, &dtmf) == SWITCH_STATUS_SUCCESS) { int send_dtmf = 1; if (input_callback) { switch_status_t cb_status = input_callback(session_a, (void *) &dtmf, SWITCH_INPUT_TYPE_DTMF, user_data, 0); if (cb_status == SWITCH_STATUS_IGNORE) { send_dtmf = 0; } else if (cb_status != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s ended call via DTMF\n", switch_channel_get_name(chan_a)); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); goto end_of_bridge_loop; } } if (bridge_filter_dtmf) { send_dtmf = 0; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Dropping filtered DTMF received on %s\n", switch_channel_get_name(chan_a)); } if (send_dtmf) { switch_core_session_send_dtmf(session_b, &dtmf); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); } } } if (switch_core_session_dequeue_event(session_a, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { if (input_callback) { input_callback(session_a, event, SWITCH_INPUT_TYPE_EVENT, user_data, 0); } if ((event->event_id != SWITCH_EVENT_COMMAND && event->event_id != SWITCH_EVENT_MESSAGE) || switch_core_session_receive_event(session_b, &event) != SWITCH_STATUS_SUCCESS) { switch_event_destroy(&event); } } if (!switch_channel_test_flag(chan_a, CF_ANSWERED) && answer_limit && switch_epoch_time_now(NULL) > answer_limit) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Answer timeout hit on %s.\n", switch_channel_get_name(chan_a)); if (switch_true(switch_channel_get_variable_dup(chan_a, "continue_on_answer_timeout", SWITCH_FALSE, -1))) { data->clean_exit = 1; goto end_of_bridge_loop; } else { switch_channel_hangup(chan_a, SWITCH_CAUSE_ALLOTTED_TIMEOUT); } } if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) { if (originator) { if (!ans_b && switch_channel_test_flag(chan_b, CF_ANSWERED)) { switch_channel_pass_callee_id(chan_b, chan_a); if (switch_channel_answer(chan_a) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(chan_a)); goto end_of_bridge_loop; } ans_a = 1; } else if (!pre_b && switch_channel_test_flag(chan_b, CF_EARLY_MEDIA)) { if (switch_channel_pre_answer(chan_a) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(chan_a)); goto end_of_bridge_loop; } pre_b = 1; } if (!pre_b) { switch_yield(10000); continue; } } else { ans_a = switch_channel_test_flag(chan_b, CF_ANSWERED); } } if (ans_a != ans_b) { switch_channel_t *un = ans_a ? chan_b : chan_a; switch_channel_t *a = un == chan_b ? chan_a : chan_b; if (switch_channel_direction(un) == SWITCH_CALL_DIRECTION_INBOUND) { if (switch_channel_direction(a) == SWITCH_CALL_DIRECTION_OUTBOUND || (un == chan_a && !originator)) { switch_channel_pass_callee_id(a, un); } if (switch_channel_answer(un) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "%s Media Establishment Failed.\n", switch_channel_get_name(un)); goto end_of_bridge_loop; } if (ans_a) { ans_b = 1; } else { ans_a = 1; } } } if (originator && !ans_b) ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); if (originator && !sent_update && ans_a && ans_b && switch_channel_media_ack(chan_a) && switch_channel_media_ack(chan_b)) { switch_ivr_bridge_display(session_a, session_b); sent_update = 1; } #ifndef SWITCH_VIDEO_IN_THREADS if (switch_channel_test_flag(chan_a, CF_VIDEO) && switch_channel_test_flag(chan_b, CF_VIDEO)) { /* read video from 1 channel and write it to the other */ status = switch_core_session_read_video_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0); if (!SWITCH_READ_ACCEPTABLE(status)) { goto end_of_bridge_loop; } switch_core_session_write_video_frame(session_b, read_frame, SWITCH_IO_FLAG_NONE, 0); } #endif if (!switch_channel_test_flag(chan_a, CF_AUDIO)) { switch_ivr_sleep(session_a, 5000, SWITCH_FALSE, NULL); continue; } /* 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; } } end_of_bridge_loop: switch_core_session_passthru(session_a, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_FALSE); #ifdef SWITCH_VIDEO_IN_THREADS if (vh.up > 0) { vh.up = -1; switch_channel_set_flag(chan_a, CF_NOT_READY); //switch_channel_set_flag(chan_b, CF_NOT_READY); switch_core_session_kill_channel(session_a, SWITCH_SIG_BREAK); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending video thread.\n"); } #endif if (silence_val) { switch_core_codec_destroy(&silence_codec); } switch_channel_execute_on(chan_a, SWITCH_CHANNEL_EXECUTE_ON_POST_BRIDGE_VARIABLE); if (!inner_bridge) { switch_channel_api_on(chan_a, SWITCH_API_BRIDGE_END_VARIABLE); } if (!inner_bridge && switch_channel_up_nosig(chan_a) && !switch_channel_test_flag(chan_a, CF_REDIRECT)) { if ((app_name = switch_channel_get_variable(chan_a, SWITCH_EXEC_AFTER_BRIDGE_APP_VARIABLE))) { switch_caller_extension_t *extension = NULL; if ((extension = switch_caller_extension_new(session_a, app_name, app_name)) == 0) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_CRIT, "memory error!\n"); goto end; } app_arg = switch_channel_get_variable(chan_a, SWITCH_EXEC_AFTER_BRIDGE_ARG_VARIABLE); switch_caller_extension_add_application(session_a, extension, (char *) app_name, app_arg); switch_channel_set_caller_extension(chan_a, extension); if (switch_channel_get_state(chan_a) == CS_EXECUTE) { switch_channel_set_flag(chan_a, CF_RESET); } else { switch_channel_set_state(chan_a, CS_EXECUTE); } } } end: if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT)) { if (th.up == 1) { th.up = -1; } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending text thread.\n"); switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_TEXT); } #ifdef SWITCH_VIDEO_IN_THREADS if (switch_core_media_check_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO)) { if (vh.up == 1) { vh.up = -1; } switch_channel_set_flag(chan_a, CF_VIDEO_BREAK); switch_channel_set_flag(chan_b, CF_VIDEO_BREAK); switch_core_session_kill_channel(session_a, SWITCH_SIG_BREAK); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "Ending video thread.\n"); switch_core_media_end_engine_function(session_a, SWITCH_MEDIA_TYPE_VIDEO); switch_channel_clear_flag(chan_a, CF_NOT_READY); switch_channel_clear_flag(chan_b, CF_NOT_READY); } #endif switch_core_session_reset(session_a, SWITCH_TRUE, SWITCH_TRUE); switch_channel_set_variable(chan_a, SWITCH_BRIDGE_VARIABLE, NULL); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a), SWITCH_LOG_DEBUG, "BRIDGE THREAD DONE [%s]\n", switch_channel_get_name(chan_a)); switch_channel_clear_flag(chan_a, CF_BRIDGED); if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING) || switch_channel_test_flag(chan_a, CF_HANGUP_HELD)) { if (switch_channel_ready(chan_b) && switch_channel_get_state(chan_b) != CS_PARK && !data->other_leg_data->clean_exit && !switch_channel_test_flag(chan_b, CF_3P_NOMEDIA_REQUESTED)) { const char *ext = switch_channel_get_variable(chan_a, "hold_hangup_xfer_exten"); switch_channel_stop_broadcast(chan_b); if (zstr(ext)) { switch_call_cause_t cause = switch_channel_get_cause(chan_b); if (cause == SWITCH_CAUSE_NONE) { cause = SWITCH_CAUSE_NORMAL_CLEARING; } switch_channel_hangup(chan_b, cause); } else { switch_channel_set_variable(chan_b, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, ext); } } if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING)) { switch_channel_mark_hold(chan_a, SWITCH_FALSE); } } if (switch_channel_test_flag(chan_a, CF_INTERCEPTED)) { switch_channel_set_flag(chan_b, CF_INTERCEPT); } switch_channel_clear_flag(chan_a, CF_VIDEO_BLANK); switch_channel_clear_flag(chan_b, CF_VIDEO_BLANK); switch_core_session_kill_channel(session_b, SWITCH_SIG_BREAK); data->done = 1; switch_core_session_video_reset(session_a); switch_core_session_video_reset(session_b); switch_core_session_rwunlock(session_b); return NULL; }
3. uuid_bridge 问题处理
uuid_bridge
命令的使用实际上有一个时机问题需要格外注意,当监听到 CHANNEL_PROGRESS_MEDIA 事件就立即使用这个命令时,非常有可能遇到命令执行不成功的问题。这个问题发生的前提是使用尚未接通的腿作为 a-leg 去 bridge 另一条就绪的 b-leg,具体流程如下所示:
- originate 命令呼叫,拉起 a-leg。在完整的 originate 命令执行周期内状态机流转为
CS_NEW -> CS_INIT -> CS_ROUTING -> CS_EXECUTE
,而在流转到CS_ROUTING
之前 CHANNEL_PROGRESS_MEDIA 事件 就发出来了- esl 监听到 CHANNEL_PROGRESS_MEDIA 事件 时下发 uuid_bridge 命令,在这个命令执行后 a-leg 完整的状态流转为
ANY -> CS_HIBERNATE/CS_CONSUME_MEDIA -> CS_RESET -> CS_SOFT_EXECUTE
。a-leg 在CS_SOFT_EXECUTE
状态时会驱动 b-leg 也到达CS_SOFT_EXECUTE
状态,所以一旦 a-leg 在 uuid_bridge 周期内的状态流转出现异常,则 b-leg 会一直停留在CS_RESET
- 从上图可以看到,如果 originate 和 uuid_bridge 执行后推动的状态机流转在时间上有重叠,则 a-leg 桥接 b-leg 时预期的状态流转很可能被打断,无法到达
CS_SOFT_EXECUTE
导致桥接事实上失败,双方无法通话。另一个影响是导致 b-leg 一直停留在CS_RESET
,而在这个状态时 FreeSWITCH 不会读这条腿上发过来的 RTP 数据,也就不会反馈 RTCP 数据给发送端,如果 b-leg 的 SIP 设备有 RTP 超时检测则可能认为媒体服务器异常,会主动发送 BYE 结束会话
这个问题的本质是 uuid_bridge 执行后 a-leg 的状态机未按照预期流转,解决方案有 2 个,读者如遇到相似问题可以参考:
- 修改 FreeSWITCH 源码
uuid_bridge 命令执行后,a-leg 上安装的回调函数表uuid_bridge_state_handlers
函数会在状态机流转相应状态时执行,从此处将 a-leg 向CS_SOFT_EXECUTE
状态驱动。这个过程被CS_EXECUTE
状态打断后,只需要在uuid_bridge_state_handlers
的 on_execute 阶段安装相应的回调函数让状态机重新流转起来即可- 修改上层业务逻辑
修改 uuid_bridge 命令的发送时机,将其延后到 a-legCS_EXECUTE
状态后再发送,这样就避免了命令执行时序混乱导致的状态机流转异常