From 304aac9d8f68a374d9cd5d2b2204f8c26f10a948 Mon Sep 17 00:00:00 2001 From: kpfleming Date: Mon, 22 Aug 2005 19:29:29 +0000 Subject: support new format for musiconhold.conf (issue #4908) support non-SLINEAR moh streams (issue #4908) add external app to feed TCP stream into Asterisk for moh (issue #4908) git-svn-id: http://svn.digium.com/svn/asterisk/trunk@6353 f38db490-d61c-443f-a65b-d21fe96a405b --- res/res_musiconhold.c | 231 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 178 insertions(+), 53 deletions(-) (limited to 'res') diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index e2a291c94..c2db43d74 100755 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -108,12 +108,14 @@ struct moh_files_state { #define MOH_RANDOMIZE (1 << 3) struct mohclass { - char class[80]; + char name[MAX_MUSICCLASS]; char dir[256]; - char miscargs[256]; + char args[256]; + char mode[80]; char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN]; unsigned int flags; int total_files; + int format; int pid; /* PID of mpg123 */ time_t start; pthread_t thread; @@ -212,8 +214,8 @@ static int ast_moh_files_next(struct ast_channel *chan) return -1; } - if (option_verbose > 2) - ast_log(LOG_NOTICE, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); + if (option_debug) + ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]); if (state->samples) ast_seekstream(chan->stream, state->samples, SEEK_SET); @@ -285,7 +287,7 @@ static void *moh_files_alloc(struct ast_channel *chan, void *params) chan->music_state = NULL; } else { if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->class, chan->name); + ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name); } } @@ -343,7 +345,7 @@ static int spawn_mp3(struct mohclass *class) argv[argc++] = "8192"; /* Look for extra arguments and add them to the list */ - strncpy(xargs, class->miscargs, sizeof(xargs) - 1); + strncpy(xargs, class->args, sizeof(xargs) - 1); argptr = xargs; while (argptr && !ast_strlen_zero(argptr)) { argv[argc++] = argptr; @@ -355,7 +357,7 @@ static int spawn_mp3(struct mohclass *class) } } else { /* Format arguments for argv vector */ - strncpy(xargs, class->miscargs, sizeof(xargs) - 1); + strncpy(xargs, class->args, sizeof(xargs) - 1); argptr = xargs; while (argptr && !ast_strlen_zero(argptr)) { argv[argc++] = argptr; @@ -460,6 +462,7 @@ static void *monmp3thread(void *data) char buf[8192]; short sbuf[8192]; int res, res2; + int len; struct timeval tv, tv_tmp; tv.tv_sec = 0; @@ -495,7 +498,9 @@ static void *monmp3thread(void *data) if (!class->members) continue; /* Read mp3 audio */ - if ((res2 = read(class->srcfd, sbuf, res * 2)) != res * 2) { + len = ast_codec_get_len(class->format, res); + + if ((res2 = read(class->srcfd, sbuf, len)) != len) { if (!res2) { close(class->srcfd); class->srcfd = -1; @@ -504,7 +509,7 @@ static void *monmp3thread(void *data) class->pid = 0; } } else - ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, res * 2); + ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len); continue; } ast_mutex_lock(&moh_lock); @@ -581,7 +586,7 @@ static struct mohclass *get_mohbyname(char *name) struct mohclass *moh; moh = mohclasses; while (moh) { - if (!strcasecmp(name, moh->class)) + if (!strcasecmp(name, moh->name)) return moh; moh = moh->next; } @@ -647,19 +652,18 @@ static void moh_release(struct ast_channel *chan, void *data) static void *moh_alloc(struct ast_channel *chan, void *params) { struct mohdata *res; - struct mohclass *class; - class = params; + struct mohclass *class = params; res = mohalloc(class); if (res) { res->origwfmt = chan->writeformat; - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { - ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format\n", chan->name); + if (ast_set_write_format(chan, class->format)) { + ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format)); moh_release(NULL, res); res = NULL; } if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->class, chan->name); + ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name); } return res; } @@ -674,7 +678,8 @@ static int moh_generate(struct ast_channel *chan, void *data, int len, int sampl if (!moh->parent->pid) return -1; - len = samples * 2; + len = ast_codec_get_len(moh->parent->format, samples); + if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) { ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name); len = sizeof(buf) - AST_FRIENDLY_OFFSET; @@ -689,17 +694,20 @@ static int moh_generate(struct ast_channel *chan, void *data, int len, int sampl return 0; memset(&f, 0, sizeof(f)); + f.frametype = AST_FRAME_VOICE; - f.subclass = AST_FORMAT_SLINEAR; + f.subclass = moh->parent->format; f.mallocd = 0; f.datalen = res; - f.samples = res / 2; f.data = buf + AST_FRIENDLY_OFFSET / 2; f.offset = AST_FRIENDLY_OFFSET; + f.samples = ast_codec_get_samples(&f); + if (ast_write(chan, &f) < 0) { ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); return -1; } + return 0; } @@ -763,53 +771,42 @@ static int moh_scan_files(struct mohclass *class) { return class->total_files; } -static int moh_register(char *classname, char *mode, char *param, char *miscargs) +static int moh_register(struct mohclass *moh) { - struct mohclass *moh; #ifdef ZAPATA_MOH int x; #endif ast_mutex_lock(&moh_lock); - moh = get_mohbyname(classname); - ast_mutex_unlock(&moh_lock); - if (moh) { - ast_log(LOG_WARNING, "Music on Hold '%s' already exists\n", classname); + if (get_mohbyname(moh->name)) { + ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name); + free(moh); + ast_mutex_unlock(&moh_lock); return -1; } - moh = malloc(sizeof(struct mohclass)); - if (!moh) - return -1; - memset(moh, 0, sizeof(struct mohclass)); + ast_mutex_unlock(&moh_lock); + time(&moh->start); moh->start -= respawn_time; - strncpy(moh->class, classname, sizeof(moh->class) - 1); - if (miscargs) { - strncpy(moh->miscargs, miscargs, sizeof(moh->miscargs) - 1); - if (strchr(miscargs,'r')) - ast_set_flag(moh, MOH_RANDOMIZE); - } - if (!strcasecmp(mode, "files")) { - if (param) - strncpy(moh->dir, param, sizeof(moh->dir) - 1); + + if (!strcasecmp(moh->mode, "files")) { if (!moh_scan_files(moh)) { ast_moh_free_class(&moh); return -1; } - } else if (!strcasecmp(mode, "mp3") || !strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb") || !strcasecmp(mode, "httpmp3") || !strcasecmp(mode, "custom")) { - - if (param) - strncpy(moh->dir, param, sizeof(moh->dir) - 1); + if (strchr(moh->args, 'r')) + ast_set_flag(moh, MOH_RANDOMIZE); + } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) { - if (!strcasecmp(mode, "custom")) + if (!strcasecmp(moh->mode, "custom")) ast_set_flag(moh, MOH_CUSTOM); - else if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb")) + else if (!strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3nb")) ast_set_flag(moh, MOH_SINGLE); - else if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb")) + else if (!strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb")) ast_set_flag(moh, MOH_QUIET); moh->srcfd = -1; #ifdef ZAPATA_MOH - /* It's an MP3 Moh -- Open /dev/zap/pseudo for timing... Is + /* Open /dev/zap/pseudo for timing... Is there a better, yet reliable way to do this? */ moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY); if (moh->pseudofd < 0) { @@ -829,7 +826,7 @@ static int moh_register(char *classname, char *mode, char *param, char *miscargs return -1; } } else { - ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mode); + ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode); ast_moh_free_class(&moh); return -1; } @@ -885,21 +882,100 @@ static void local_ast_moh_stop(struct ast_channel *chan) } } +static struct mohclass *moh_class_malloc(void) +{ + struct mohclass *class; + + class = malloc(sizeof(struct mohclass)); + + if (!class) + return NULL; + + memset(class, 0, sizeof(struct mohclass)); + + class->format = AST_FORMAT_SLINEAR; + + return class; +} + static int load_moh_classes(void) { struct ast_config *cfg; struct ast_variable *var; + struct mohclass *class; char *data; char *args; - int x = 0; + char *cat; + int numclasses = 0; + static int dep_warning = 0; cfg = ast_config_load("musiconhold.conf"); if (!cfg) return 0; + cat = ast_category_browse(cfg, NULL); + for (; cat; cat = ast_category_browse(cfg, cat)) { + if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) { + class = moh_class_malloc(); + if (!class) { + ast_log(LOG_WARNING, "Out of memory!\n"); + break; + } + ast_copy_string(class->name, cat, sizeof(class->name)); + var = ast_variable_browse(cfg, cat); + while (var) { + if (!strcasecmp(var->name, "mode")) + ast_copy_string(class->mode, var->value, sizeof(class->name)); + else if (!strcasecmp(var->name, "directory")) + ast_copy_string(class->dir, var->value, sizeof(class->dir)); + else if (!strcasecmp(var->name, "application")) + ast_copy_string(class->args, var->value, sizeof(class->args)); + else if (!strcasecmp(var->name, "random")) + ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE); + else if (!strcasecmp(var->name, "format")) { + class->format = ast_getformatbyname(var->value); + if (!class->format) { + ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value); + class->format = AST_FORMAT_SLINEAR; + } + } + var = var->next; + } + + if (ast_strlen_zero(class->dir)) { + if (!strcasecmp(class->mode, "custom")) { + strcpy(class->dir, "nodir"); + } else { + ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name); + free(class); + continue; + } + } + if (ast_strlen_zero(class->mode)) { + ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name); + free(class); + continue; + } + if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) { + ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name); + free(class); + continue; + } + + moh_register(class); + numclasses++; + } + } + + + /* Deprecated Old-School Configuration */ var = ast_variable_browse(cfg, "classes"); while (var) { + if (!dep_warning) { + ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n"); + dep_warning = 1; + } data = strchr(var->value, ':'); if (data) { *data++ = '\0'; @@ -907,26 +983,55 @@ static int load_moh_classes(void) if (args) *args++ = '\0'; if (!(get_mohbyname(var->name))) { - moh_register(var->name, var->value, data, args); - x++; + class = moh_class_malloc(); + if (!class) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return numclasses; + } + + ast_copy_string(class->name, var->name, sizeof(class->name)); + ast_copy_string(class->dir, data, sizeof(class->dir)); + ast_copy_string(class->mode, var->value, sizeof(class->mode)); + if (args) + ast_copy_string(class->args, args, sizeof(class->args)); + + moh_register(class); + numclasses++; } } var = var->next; } var = ast_variable_browse(cfg, "moh_files"); while (var) { + if (!dep_warning) { + ast_log(LOG_WARNING, "The old musiconhold.conf syntax has been deprecated! Please refer to the sample configuration for information on the new syntax.\n"); + dep_warning = 1; + } if (!(get_mohbyname(var->name))) { args = strchr(var->value, ','); if (args) *args++ = '\0'; - moh_register(var->name, "files", var->value, args); - x++; + class = moh_class_malloc(); + if (!class) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return numclasses; + } + + ast_copy_string(class->name, var->name, sizeof(class->name)); + ast_copy_string(class->dir, var->value, sizeof(class->dir)); + strcpy(class->mode, "files"); + if (args) + ast_copy_string(class->args, args, sizeof(class->args)); + + moh_register(class); + numclasses++; } var = var->next; } ast_config_destroy(cfg); - return x; + + return numclasses; } static void ast_moh_destroy(void) @@ -998,7 +1103,7 @@ static int cli_files_show(int fd, int argc, char *argv[]) if (!class->total_files) continue; - ast_cli(fd, "Class: %s\n", class->class); + ast_cli(fd, "Class: %s\n", class->name); for (i = 0; i < class->total_files; i++) ast_cli(fd, "\tFile: %s\n", class->filearray[i]); } @@ -1007,8 +1112,27 @@ static int cli_files_show(int fd, int argc, char *argv[]) return 0; } +static int moh_classes_show(int fd, int argc, char *argv[]) +{ + struct mohclass *class; + + ast_mutex_lock(&moh_lock); + for (class = mohclasses; class; class = class->next) { + ast_cli(fd, "Class: %s\n", class->name); + ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "" : class->dir); + if (ast_test_flag(class, MOH_CUSTOM)) + ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "" : class->args); + ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format)); + } + ast_mutex_unlock(&moh_lock); + + return 0; +} + static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL}; +static struct ast_cli_entry cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL}; + static struct ast_cli_entry cli_moh_files_show = { { "moh", "files", "show"}, cli_files_show, "List MOH file-based classes", "Lists all loaded file-based MOH classes and their files", NULL}; static void init_classes(void) @@ -1033,6 +1157,7 @@ int load_module(void) ast_register_atexit(ast_moh_destroy); ast_cli_register(&cli_moh); ast_cli_register(&cli_moh_files_show); + ast_cli_register(&cli_moh_classes_show); if (!res) res = ast_register_application(app1, moh1_exec, synopsis1, descrip1); if (!res) -- cgit v1.2.3