明星网站建设,wordpress问答中心,如何分析网站开发语言,南阳市建网站公文章目录 1. uuid_bridge 命令执行2. 源码分析3. uuid_bridge 问题处理 1. uuid_bridge 命令执行
uuid_bridge 命令用于将两条腿桥接在一起#xff0c;以实现双方的通话。下图是该命令在 FreeSWITCH 中的处理流程#xff0c;大致的流程可以分成以下几个步骤#xff1a; uui… 文章目录 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);}}
#endifswitch_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);}}
#endifswitch_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;
}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_THREADSstruct vid_helper vh { 0 };uint32_t vid_launch 0;
#endifdata-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_THREADSif (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);}}
#endifif (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_THREADSif (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);}
#endifif (!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_THREADSif (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);}
#endifif (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_THREADSif (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);}
#endifswitch_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-leg CS_EXECUTE 状态后再发送这样就避免了命令执行时序混乱导致的状态机流转异常