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應用

使用方法:

當在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函數主要執行

這裏有兩個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函數指針。

此時已經調用了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的用法:

當在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");
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章