目錄
三、StartMusicOnHold和StopMusicOnHold Application分析
一、概要
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");
}