目录
一、概要
二、解析配置文件
三、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;
}
使用方法:
当在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;
}
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");
}