在freeswitch上解决被叫回183信令但不发RTP包的问题

17 篇文章 2 订阅
2 篇文章 3 订阅

问题描述:

在网络电话应用中,主叫收到IMS发送的183信令,但IMS不发送RTP包,导致主叫呼出后没有振铃声。

 

解决思路:

可以在客户端上解决,也可以在freeswitch服务器上解决

1、客户端上解决:收到183信令之后就播放本地铃声同时监测是否收到RTP数据,如果收到RTP数据就停止播放本地铃声;

2、服务器上解决:同样道理,如果被叫处于183振铃(early_media)状态,则监测是否收到RTP数据,如果没有收到则播放本地铃声,否则不再播放本地铃声。

以下记录为在服务器上解决的过程。因本人刚接触freeswitch,所以有些地方可能分析或描述不正确,请大家仅做参考,不要尽信。另外,本人使用的测试环境和通话过程与读者可能会有所不同,所以描述的现象和流程可能与读者也有不同。我使用的freeswitch版本为1.4.26。

 

解决过程:

因为大多数sip客户端程序作为被叫回复的都是180信令,因此我用两台freeswitch级联来模拟被叫回复183信令。具体拓扑结构为:主叫注册到fs1服务器,fs1与fs2级联,被叫注册到fs2服务器。

另外,因我做级联主要是为了模拟被叫回复183信令,所以在此约定除特别说明外,本文以后出现的被叫用词所指即为fs2。

 

-----------------------------------------【知识点】-----------------------------------------

1、从fs1上呼叫被叫拨号计划为(freeswitch默认外部端口为5080):

<action application="bridge" data="sofia/external/12345@12.34.56.78:5080"/>

2、fs2上的拨号计划要在conf/dialplan/public.xml中修改

3、freeswitch关于内部和外部的定义是:注册到本机的为内部,未注册在本机的为外部(包括级联freeswitch);

 

最初的解决方案是:被叫回复183信令后,在fs1上监测被叫发过来的RTP包,如果没有收到RTP包就读入本地铃声伪造为收到的RTP包发给主叫。要实现这个方案必须要两步:第一是在fs2上屏蔽掉发送RTP包的代码,这样才能模拟被叫只发183信令而不发RTP包的情况;第二是在fs1上找到接收RTP包的地方,读入本地铃声伪造为收到的RTP包。

刚开始着手的时候在网上查到freeswtich有个rtp_timeout_sec是控制RTP包超时的,就是说如果freeswitch在设定的时间内收不到RTP包就结束通话。所以我通过查找这个参数相关的代码找到freeswitch收发RTP包的地方。

 

-----------------------------------------【知识点】-----------------------------------------
1、在配置文件conf/sip_profiles/internal.xml中设置的rtp_timeout_sec只对内部用户有效,在配置文件conf/sip_profiles/external.xml中设置的rtp_timeout_sec只对外部用户有效。并且这两个配置文件的设置是在freeswitch启动的时候读入的,因此如果修改配置之后,在控制台下的reloadxml命令是无法更新配置的,只能重新启动freeswitch;

2、拨号计划中设置rtp_timeout_sec只对内部主叫有效(对被叫是否有效未验证),并且会覆盖conf/sip_profiles/internal.xml中的设置;

3、控制台下的reloadxml命令只对拨号计划有效,对其他配置文件无效。

 

经过一番努力,终于找到freeswitch接收RTP数据是在src/switch_rtp.c文件的read_rtp_packet函数中,而发送RTP数据是在该文件的rtp_common_write函数中。至此,方案的第一步完成了,第二步完成了一半,接下来就该是怎么伪造RTP包的问题了。

因为RTP包的数据是经过语音编码的,所以首先要解决的就是语音编码问题,于是我通过在fs1的拨号计划中设置ringback来跟踪freeswitch在哪里读入本地文件并进行编码,然后怎么发送给主叫。拨号计划中的配置例子为:

<action application="set" data="ringback=/usr/local/freeswitch/sounds/test/test.wav"/>

 

-----------------------------------------【知识点】-----------------------------------------
1、freeswitch在拨号计划中不设置ringback,则回复的是180信令;

2、被叫回复180信令时:如果fs1的拨号计划不设置ringback,则fs1回复给主叫的是180信令;如果fs1的拨号计划设置了ringback参数,则fs1回复给主叫的是183信令,fs1会将本地铃声发送给主叫;

3、被叫回复183信令时:无论fs1的拨号计划是否设置ringback,fs1回复给主叫的都是183信令,并且主叫收到的铃声都是被叫传过来的铃声;如果被叫没有发送RTP包,即使fs1的拨号计划设置了ringback,fs1也不会将本地铃声发送给主叫。

 

第一步,先搞清楚需要发给主叫的数据是从哪里来的。在跟踪过程中,发现fs1和fs2不同的配置情况所执行的流程也不相同。

【第一种情况】fs1设置ringback,fs2不设置ringback时的流程为:

a、在switch_ivr_originate函数(src/switch_ivr_originate.c文件中)中调用setup_ringback函数执行初始化编码器、ringback结构体,打开本地铃声文件等操作;

b、在switch_ivr_originate函数(src/switch_ivr_originate.c文件中)中调用switch_core_file_read函数从本地文件读取一帧铃声数据;

c、在switch_ivr_originate函数(src/switch_ivr_originate.c文件中)中调用switch_core_session_write_frame,并传入从本地文件读取的铃声数据进行第二步。

 

【第二种情况】fs1设置ringback,fs2不设置ringback,并且fs1设置ring_ready、return_ring_ready为true时的流程为:

a、在switch_ivr_wait_for_answer函数(src/switch_ivr_originate.c文件中)中执行初始化编码器、ringback结构体,打开本地铃声文件等操作;

b、在switch_ivr_wait_for_answer函数(src/switch_ivr_originate.c文件中)中调用switch_core_file_read函数从本地文件读取一帧铃声数据。

c、在switch_ivr_wait_for_answer函数(src/switch_ivr_originate.c文件中)中调用switch_core_session_write_frame函数,并传入从本地文件读取的铃声数据进行第二步。

 

【第三种情况】fs2设置ringback时的流程为:

a、在audio_bridge_thread函数(src/switch_ivr_bridge.c文件中)中调用switch_core_session_read_frame函数(src/switch_core_io.c文件中)得到被叫发过来的铃声。获取的具体流程为:

(1)、在switch_core_session_read_frame函数中执行session->endpoint_interface->io_routines->read_frame,即调用sofia_read_frame函数(src/mod/endpoints/mod_sofia/mod_sofia.c文件中),因为session->endpoint_interface->io_routines->read_frame是一个函数指针,指向的就是该函数;

(2)、switch_core_media_read_frame函数(src/switch_core_media.c文件中);

(3)、switch_rtp_zerocopy_read_frame函数(src/switch_rtp.c文件中):从接收到的RTP包中取出铃声数据;

(4)、rtp_common_read函数(src/switch_rtp.c文件中);

(5)、read_rtp_packet函数(src/switch_rtp.c文件中);

(6)、switch_socket_recvfrom函数(src/switch_apr.c文件中):接收被叫的RTP包。

b、在audio_bridge_thread函数(src/switch_ivr_originate.c文件中)中调用switch_core_session_write_frame函数,并传入被叫发过来的铃声进行第二步。

 

第二步,向主叫发送铃声,流程为:

a、switch_core_session_write_frame函数(src/switch_core_io.c文件中):检测是否需要对铃声数据进行二次编码,如果需要则调用switch_core_codec_encode函数进行编码;

b、perform_write函数(src/switch_core_io.c文件中):执行session->endpoint_interface->io_routines->write_frame,即调用sofia_write_frame函数(src/mod/endpoints/mod_sofia/mod_sofia.c文件中),因为session->endpoint_interface->io_routines->write_frame是一个函数指针,指向的就是该函数;

c、switch_core_media_write_frame函数(src/switch_core_media.c文件中);

d、switch_rtp_write_frame函数(src/switch_rtp.c文件中);

e、rtp_common_write函数(src/switch_rtp.c文件中);

f、switch_socket_sendto函数(src/switch_apr.c文件中):发送RTP包。

 

根据以上的跟踪和分析,我们知道只需要读取一帧铃声数据,然后调用switch_core_session_write_frame就可以很简单地完成编码和发送铃声,因此就没有必要在接收RTP包的地方,读入本地铃声再伪造为收到的RTP包,因为那样要增加读入铃声数据、编码和伪造RTP包三个步骤,明显更复杂。所以我们要对原方案进行优化。

很明显,我需要解决的问题属于上述的第三种情况(即fs2设置ringback时的流程),因此我只需要在audio_bridge_thread函数中从本地文件读取一帧铃声数据,然后传入switch_core_session_write_frame就能解决问题。

在以上的跟踪和分析中,我发现当被叫没有发送RTP包时,switch_core_session_read_frame函数得到的结果是一个SFF_CNG帧,即舒适噪声。所以具体的处理逻辑就是:如果被叫当前是183振铃状态(CCS_EARLY),并且得到的是SFF_CNG帧,那么就从本地文件读取一帧铃声数据,然后传入switch_core_session_write_frame。经测试问题解决。

 

 

最后需要说明的是:

1、ringback也可以是实时产生的单音铃声。这种情况下就不是从switch_core_file_read函数读取铃声数据了,而是调用switch_buffer_create_dynamic来实时产生铃声数据。拨号计划中的配置例子为:

<action application="set" data="ringback=${us-ring}"/>

2、我要解决的问题所处的环境中,被叫是IMS,所以被叫相对于freeswitch一定是外部用户。所以我在判断被叫当前是183振铃状态的时候加了一个条件:判断被叫是外部用户;

3、如果被叫是本机用户,并且回复183信令的时候,相关流程可能会与我上述的跟踪有所不同。我并未做测试。

另外,偶然发现一个问题:如果fs1和fs2都不设置ringback,并且fs1设置ring_ready、return_ring_ready为true,则fs1不会给主叫回180。至于原因还没去分析。

 

 

 

以下是我修改的全部代码,都在src/switch_ivr_bridge.c文件中。由宏_RINGBACK_包含的部分就是我增加的代码。

#ifdef _RINGBACK_
struct ringback {
	switch_buffer_t *audio_buffer;
	teletone_generation_session_t ts;
	switch_file_handle_t fhb;
	switch_file_handle_t *fh;
	int silence;
	uint8_t asis;
	int channels;
	void *mux_buf;
	int mux_buflen;
};

typedef struct ringback ringback_t;

static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map)
{
	ringback_t *tto = ts->user_data;
	int wrote;
	void *buf;
	int buflen;

	if (!tto) {
		return -1;
	}
	wrote = teletone_mux_tones(ts, map);
	
	if (tto->channels != 1) {
		if (tto->mux_buflen < wrote * 2 * tto->channels) {
			tto->mux_buflen = wrote * 2 * tto->channels;
			tto->mux_buf = realloc(tto->mux_buf, tto->mux_buflen);
		}
		memcpy(tto->mux_buf, ts->buffer, wrote * 2);
		switch_mux_channels((int16_t *) tto->mux_buf, wrote, 1, tto->channels);
		buf = tto->mux_buf;
		buflen = wrote * 2 * tto->channels;
	} else {
		buf = ts->buffer;
		buflen = wrote * 2;
	}

	switch_buffer_write(tto->audio_buffer, buf, buflen);

	return 0;
}

static void ringback_uninit(ringback_t ringback, switch_frame_t frame, switch_codec_t codec)
{
	if (ringback.fh)
	{
	    switch_core_file_close(ringback.fh);

	    ringback.fh = NULL;
	}
	else if (ringback.audio_buffer)
	{
	    teletone_destroy_session(&ringback.ts);
	    switch_safe_free(ringback.mux_buf);
	    switch_buffer_destroy(&ringback.audio_buffer);
	}

	if (switch_core_codec_ready(&codec))
	{
	    switch_core_codec_destroy(&codec);
	}

	switch_safe_free(frame.data);
}
#endif

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;

#ifdef SWITCH_VIDEO_IN_THREADS
	switch_thread_t *vid_thread = NULL;
	struct vid_helper vh = { 0 };
	uint32_t vid_launch = 0;
#endif

#ifdef _RINGBACK_
	switch_core_session_t *caller_session  = NULL;
	switch_channel_t *caller_channel = NULL;
	const char *ringback_data = NULL;
	switch_frame_t write_frame = { 0 };
	switch_codec_t write_codec = { 0 };
	switch_codec_t *read_codec;
	uint8_t pass = 0;
	ringback_t ringback = { 0 };
#endif
	
	data->clean_exit = 0;

	session_a = data->session;
	if (!(session_b = switch_core_session_locate(data->b_uuid))) {
		return NULL;
	}

	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_timeout = 0;
		} else {
			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 (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"))) {
		switch_codec_implementation_t read_impl = { 0 };
		switch_core_session_get_read_impl(session_a, &read_impl);

		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,
									   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"));

#ifdef _RINGBACK_
	if (switch_channel_direction(chan_a) == SWITCH_CALL_DIRECTION_OUTBOUND)
	{
		caller_channel  = chan_b;
		caller_session  = session_b;

		ringback_data = switch_channel_get_variable(caller_channel, "ringback");
		if (ringback_data != NULL)
		{
			switch_zmalloc(write_frame.data, SWITCH_RECOMMENDED_BUFFER_SIZE);
			write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;

			read_codec = switch_core_session_get_read_codec(caller_session);
		}

		if (read_codec && ringback_data)
		{
			if (switch_is_file_path(ringback_data))
			{
				if (!(strrchr(ringback_data, '.') || strstr(ringback_data, SWITCH_URL_SEPARATOR)))
				{
					ringback.asis++;
				}
			}

			if (!ringback.asis)
			{
				if ((pass = (uint8_t) switch_test_flag(read_codec, SWITCH_CODEC_FLAG_PASSTHROUGH)))
				{
					ringback_data  = NULL;
				}
				else
				{
					if (switch_core_codec_init(&write_codec,
											   "L16",
											   NULL,
											   read_codec->implementation->actual_samples_per_second,
											   read_codec->implementation->microseconds_per_packet / 1000,
											   read_codec->implementation->number_of_channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
											   switch_core_session_get_pool(caller_session)) != SWITCH_STATUS_SUCCESS)
					{
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_ERROR, "Codec Error!\n");
						if (caller_channel)
						{
							switch_channel_hangup(caller_channel, SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL);
						}

						read_codec = NULL;
						
						ringback_uninit(ringback, write_frame, write_codec);
						ringback_data = NULL;
					}
					else
					{
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_DEBUG,
										  "Raw Codec Activation Success L16@%uhz 1 channel %dms\n",
										  read_codec->implementation->actual_samples_per_second, read_codec->implementation->microseconds_per_packet / 1000);

						write_frame.codec = &write_codec;
						write_frame.datalen = read_codec->implementation->decoded_bytes_per_packet;
						write_frame.samples = write_frame.datalen / 2;
						memset(write_frame.data, 255, write_frame.datalen);
					}
				}
			}

			if (switch_channel_test_flag(caller_channel, CF_DISABLE_RINGBACK))
			{
				ringback_data = NULL;
			}

			if (ringback_data)
			{
				char *tmp_data = NULL;

				if (switch_is_file_path(ringback_data))
				{
					char *ext;

					if (ringback.asis) {
						write_frame.codec = read_codec;
						ext = read_codec->implementation->iananame;
						tmp_data = switch_mprintf("%s.%s", ringback_data, ext);
						ringback_data = tmp_data;
					}

					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_DEBUG, "Play Ringback File [%s]\n", ringback_data);

					ringback.fhb.channels = read_codec->implementation->number_of_channels;
					ringback.fhb.samplerate = read_codec->implementation->actual_samples_per_second;

					if (switch_core_file_open(&ringback.fhb,
											  ringback_data,
											  read_codec->implementation->number_of_channels,
											  read_codec->implementation->actual_samples_per_second,
											  SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS)
					{
						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_ERROR, "Error Playing File\n");
						switch_safe_free(tmp_data);
						
						ringback_uninit(ringback, write_frame, write_codec);
						ringback_data = NULL;
					}
					else
					{
						ringback.fh = &ringback.fhb;
					}
				}
				else
				{
					if (!strncasecmp(ringback_data, "silence", 7))
					{
						ringback_data = NULL;
					}
					else
					{
						switch_buffer_create_dynamic(&ringback.audio_buffer, 512, 1024, 0);
						switch_buffer_set_loops(ringback.audio_buffer, -1);

						teletone_init_session(&ringback.ts, 0, teletone_handler, &ringback);
						ringback.ts.rate = read_codec->implementation->actual_samples_per_second;
						ringback.channels = read_codec->implementation->number_of_channels;

						switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_DEBUG, "Play Ringback Tone [%s]\n", ringback_data);

						if (teletone_run(&ringback.ts, ringback_data))
						{
							switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(caller_session), SWITCH_LOG_ERROR, "Error Playing Tone\n");
							teletone_destroy_session(&ringback.ts);
							switch_buffer_destroy(&ringback.audio_buffer);

							ringback_data = NULL;
						}
					}
				}

				switch_safe_free(tmp_data);
			}
		}
	}
#endif

	for (;;) {
		switch_channel_state_t b_state;
		switch_status_t status;
		switch_event_t *event;

		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;
		}
#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;
			vid_thread = launch_video(&vh);
		}
#endif

		if (read_frame_count >= DEFAULT_LEAD_FRAMES && switch_channel_media_ack(chan_a)) {

			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)) {
				switch_ivr_nomedia(switch_core_session_get_uuid(session_a), SMF_REBRIDGE);
				bypass_media_after_bridge = 0;
				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) {
				status = 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

		/* 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++;

#ifdef _RINGBACK_
			if ((switch_channel_direction(chan_a) == SWITCH_CALL_DIRECTION_OUTBOUND) && (switch_channel_get_callstate(chan_a) == CCS_EARLY))
			{
				//在被叫183振铃状态下,如果没有收到被叫的rtp数据则播放本地回铃,如果已经收到过被叫rtp数据则不再播放本地回铃
				if (switch_test_flag(read_frame, SFF_CNG))
				{
					if (ringback_data != NULL)
					{
						switch_bool_t  ringback_status  = SWITCH_TRUE;

						if (ringback.fh)
						{
							switch_size_t mlen, olen;
							unsigned int pos = 0;

							if (ringback.asis)
							{
								mlen = write_frame.codec->implementation->encoded_bytes_per_packet;
							}
							else
							{
								mlen = write_frame.codec->implementation->samples_per_packet;
							}

							olen = mlen;
							//if (ringback.fh->resampler && ringback.fh->resampler->rfactor > 1) {
							//olen = (switch_size_t) (olen * ringback.fh->resampler->rfactor);
							//}
							switch_core_file_read(ringback.fh, write_frame.data, &olen);

							if (olen == 0)
							{
								olen = mlen;
								ringback.fh->speed = 0;
								switch_core_file_seek(ringback.fh, &pos, 0, SEEK_SET);
								switch_core_file_read(ringback.fh, write_frame.data, &olen);

								if (olen == 0)
								{
									switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(caller_channel), SWITCH_LOG_ERROR, 
													  "Failure to read or re-read after seeking to beginning on file [%s]\n", ringback.fh->file_path);

									ringback_status  = SWITCH_FALSE;
								}
							}

							write_frame.datalen = (uint32_t) (ringback.asis ? olen : olen * 2 * ringback.fh->channels);
						}
						else if (ringback.audio_buffer)
						{
							if ((write_frame.datalen = (uint32_t) switch_buffer_read_loop(ringback.audio_buffer,
																						  write_frame.data,
																						  write_frame.codec->implementation->decoded_bytes_per_packet)) <= 0)
							{
								ringback_status  = SWITCH_FALSE;
							}
						}
						else
						{
							ringback_status  = SWITCH_FALSE;
						}

						if ((ringback.fh || ringback.audio_buffer) && write_frame.codec && write_frame.datalen && (ringback_status == SWITCH_TRUE))
						{
							read_frame  = &write_frame;

							switch_clear_flag(read_frame, SFF_CNG);
						}
					}
				}
				else
				{
					if (ringback_data != NULL)
					{
					    //已经收到过被叫的rtp数据,不再播放本地回铃
					    ringback_uninit(ringback, write_frame, write_codec);

					    ringback_data  = NULL;
					}
				}
			}
#endif

			if (switch_test_flag(read_frame, SFF_CNG)) {
				if (silence_val) {
					switch_generate_sln_silence((int16_t *) silence_frame.data, silence_frame.samples, 
												read_frame->codec->implementation->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)) {
				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:

#ifdef SWITCH_VIDEO_IN_THREADS
	if (vid_thread) {
		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

#ifdef _RINGBACK_
	if (ringback_data != NULL)
	{
		ringback_uninit(ringback, write_frame, write_codec);
	}
#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)) {
		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:

#ifdef SWITCH_VIDEO_IN_THREADS
	if (vid_thread) {
		switch_status_t st;

		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_thread_join(&st, vid_thread);
		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) {
			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_core_session_kill_channel(session_b, SWITCH_SIG_BREAK);
	data->done = 1;
	switch_core_session_rwunlock(session_b);
	return NULL;
}
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值