Asterisk16中的res_musiconhold.c音乐等待MOH代码简析

目录

一、概要

二、解析配置文件

三、StartMusicOnHold和StopMusicOnHold Application分析

1、StartMusicOnHold应用

2、StopMusicOnHold应用


一、概要

res_musiconhold.c主要实现音乐等待功能,提供了MusicOnHold、StartMusicOnHold、StopMusicOnHold和CLI显示musiconhold.conf中的配置和MusicOnHoldStart/MusicOnHoldStop的AMI Event。

二、解析配置文件

版权声明:本文为博主(宽简厚重,Yuesichiu)原创文章,未经博主允许不得转载。
https://blog.csdn.net/yuesichiu/article/details/106015883

static int load_module(void)
{
	int res;
        //创建一个哈希Hash Container
	if (!(mohclasses = ao2_t_container_alloc(53, moh_class_hash, moh_class_cmp, "Moh class container"))) {
		return AST_MODULE_LOAD_DECLINE;
	}

        //解析配置文件musiconhold.conf和解析是否开启实时修改后生效标志
	if (!load_moh_classes(0) && ast_check_realtime("musiconhold") == 0) { 	/* No music classes configured, so skip it */
		ast_log(LOG_WARNING, "No music on hold classes configured, "
				"disabling music on hold.\n");
	} else {
                //注册music on hold start/stop/cleanup的回调函数
		ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
				local_ast_moh_cleanup);
	}

        版权声明:本文为博主(宽简厚重,Yuesichiu)原创文章,未经博主允许不得转载。
        https://blog.csdn.net/yuesichiu/article/details/106015883

        //注册MusicOnHold
	res = ast_register_application_xml(play_moh, play_moh_exec);
	ast_register_atexit(ast_moh_destroy);
        //注册moh CLI接口
	ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
	if (!res)
                //注册StartMusicOnHold App
		res = ast_register_application_xml(start_moh, start_moh_exec);
	if (!res)
                //注册StopMusicOnHold App
		res = ast_register_application_xml(stop_moh, stop_moh_exec);
	
	return AST_MODULE_LOAD_SUCCESS;
}

三、StartMusicOnHold和StopMusicOnHold Application分析

1、StartMusicOnHold应用

使用方法:

Asterisk16中的res_musiconhold.c音乐等待MOH代码简析_第1张图片

当在Asterisk的拨号方案Dialplan中使用StartMusicOnHold(default)应用就开始ast_moh_start_ptr(channel.c),注册时:

1. channel.c

void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, const char *, const char *),
				 void (*stop_ptr)(struct ast_channel *),
				 void (*cleanup_ptr)(struct ast_channel *))
{
	ast_moh_start_ptr = start_ptr;
	ast_moh_stop_ptr = stop_ptr;
	ast_moh_cleanup_ptr = cleanup_ptr;
}

2. res_musiconhold.c
   ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
				local_ast_moh_cleanup);

local_ast_moh_start函数主要执行

Asterisk16中的res_musiconhold.c音乐等待MOH代码简析_第2张图片

这里有两个MOH生成器moh_file_stream和mohgen。

1. moh_file_stream结构体
static struct ast_generator moh_file_stream = {
	.alloc    = moh_files_alloc,
	.release  = moh_files_release,
	.generate = moh_files_generator,
	.digit    = moh_handle_digit,
	.write_format_change = moh_files_write_format_change,
};

2. mohgen结构体
static struct ast_generator mohgen = {
	.alloc    = moh_alloc,
	.release  = moh_release,
	.generate = moh_generate,
	.digit    = moh_handle_digit,
};

这里以moh_file_stream进行分析。进一步查看 ast_activate_generator函数(channel.c)的实现。

int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
{
	int res = 0;
	void *generatordata = NULL;

	ast_channel_lock(chan);
	if (ast_channel_generatordata(chan)) {
                //如果已经设置过MOH了就需要先停止旧的MOH。
		struct ast_generator *generator_old = ast_channel_generator(chan);

		if (generator_old && generator_old->release) {
			generator_old->release(chan, ast_channel_generatordata(chan));
		}
	}
        //调用moh_file_stream的moh_files_alloc函数
	if (gen->alloc && !(generatordata = gen->alloc(chan, params))) {
		res = -1;
	}
        //设置该Channel的generator data。
	ast_channel_generatordata_set(chan, generatordata);
	if (!res) {
                //开启或者关闭通道的定时器ticks,速率是50次/秒,generator_force是回调函数
		ast_settimeout(chan, 50, generator_force, chan);
                //设置该channel的generator
		ast_channel_generator_set(chan, gen);
	}
	ast_channel_unlock(chan);

	ast_prod(chan);

	return res;
}

moh_files_alloc函数主要是设置相关参数和发布AMI StartMusicOnHold事件

generator_force函数就会调用moh_file_stream中的generate函数指针。

Asterisk16中的res_musiconhold.c音乐等待MOH代码简析_第3张图片

此时已经调用了moh_files_generator函数。

static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
{
	struct moh_files_state *state = ast_channel_music_state(chan);
	struct ast_frame *f = NULL;
	int res = 0;

	state->sample_queue += samples;

	while (state->sample_queue > 0) {
		ast_channel_lock(chan);
                //读取
		f = moh_files_readframe(chan);

		/* We need to be sure that we unlock
		 * the channel prior to calling
		 * ast_write. Otherwise, the recursive locking
		 * that occurs can cause deadlocks when using
		 * indirect channels, like local channels
		 */
		ast_channel_unlock(chan);
		if (!f) {
			return -1;
		}

                //更新samples,记录总共读取的samples
		state->samples += f->samples;
                //更新samples_queue,减去每次读一次的samples
		state->sample_queue -= f->samples;
		if (ast_format_cmp(f->subclass.format, state->mohwfmt) == AST_FORMAT_CMP_NOT_EQUAL) {
			ao2_replace(state->mohwfmt, f->subclass.format);
		}
                //往该channel上写数据帧
		res = ast_write(chan, f);
		ast_frfree(f);
		if (res < 0) {
			ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", ast_channel_name(chan), strerror(errno));
			return -1;
		}
	}
	return res;
}

2、StopMusicOnHold应用

StopMusicOnHold的用法:

Asterisk16中的res_musiconhold.c音乐等待MOH代码简析_第4张图片

当在Asterisk的拨号方案Dialplan中使用StopMusicOnHold()应用就开始ast_moh_stop_ptr(channel.c),注册时设置为local_ast_moh_stop函数。
版权声明:本文为博主(宽简厚重,Yuesichiu)原创文章,未经博主允许不得转载。
https://blog.csdn.net/yuesichiu/article/details/106015883

static void local_ast_moh_stop(struct ast_channel *chan)
{
        //关闭generator
	ast_deactivate_generator(chan);

	ast_channel_lock(chan);
        //清除MOH标志
	ast_clear_flag(ast_channel_flags(chan), AST_FLAG_MOH);
	if (ast_channel_music_state(chan)) {
            //如果是设置了music 状态就关闭该通道的moh流
		if (ast_channel_stream(chan)) {
			ast_closestream(ast_channel_stream(chan));
			ast_channel_stream_set(chan, NULL);
		}
	}
	ast_channel_unlock(chan);
}


void ast_deactivate_generator(struct ast_channel *chan)
{
	ast_channel_lock(chan);
	deactivate_generator_nolock(chan);
	ast_channel_unlock(chan);
}

接着看deactivate_generator_nolock函数。

static void deactivate_generator_nolock(struct ast_channel *chan)
{
        //如果设置了generator data,代表之前开启音乐等待功能,就需要关闭它。
	if (ast_channel_generatordata(chan)) {
		struct ast_generator *generator = ast_channel_generator(chan);

		if (generator && generator->release) {
                        //执行release方式
			generator->release(chan, ast_channel_generatordata(chan));
		}
		ast_channel_generatordata_set(chan, NULL);
		ast_channel_generator_set(chan, NULL);
		ast_channel_set_fd(chan, AST_GENERATOR_FD, -1);
		ast_clear_flag(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
		ast_settimeout(chan, 0, NULL, NULL);
	}
}

moh_file_stream的release为moh_files_release。

static void moh_files_release(struct ast_channel *chan, void *data)
{
	struct moh_files_state *state;

	if (!chan || !ast_channel_music_state(chan)) {
		return;
	}

	state = ast_channel_music_state(chan);

	if (ast_channel_stream(chan)) {
                关闭该通道的moh流数据
		ast_closestream(ast_channel_stream(chan));
		ast_channel_stream_set(chan, NULL);
	}

        //发布AMI StopMusicOnHold事件
	moh_post_stop(chan);

	ao2_ref(state->mohwfmt, -1);
	state->mohwfmt = NULL; /* make sure to clear this format before restoring the original format */
	if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
		ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%s'\n", ast_channel_name(chan),
			ast_format_get_name(state->origwfmt));
	}
	ao2_cleanup(state->origwfmt);
	state->origwfmt = NULL;

	state->save_pos = state->pos;
	state->announcement = 0;

	state->class = mohclass_unref(state->class, "Unreffing channel's music class upon deactivation of generator");
}

 

你可能感兴趣的:(Asterisk)