aboutsummaryrefslogtreecommitdiffstats
path: root/res/res_musiconhold.c
diff options
context:
space:
mode:
authormarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2004-12-24 01:40:07 +0000
committermarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2004-12-24 01:40:07 +0000
commit05c1466c552ff0085a191038e034030a3681d53e (patch)
tree5b80a7b0d9f29485a99055442adc6b277aa4357f /res/res_musiconhold.c
parent461f9cdb357b9dd51ed1072b61310424d0db22d6 (diff)
Merge anthm's native MOH patch (bug #2639) he promises me he'll rid it of ast_flag_moh...
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@4552 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'res/res_musiconhold.c')
-rwxr-xr-xres/res_musiconhold.c413
1 files changed, 366 insertions, 47 deletions
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index 53d25f3da..470f7ffc4 100755
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -16,6 +16,7 @@
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
+#include <../astconf.h>
#include <asterisk/options.h>
#include <asterisk/module.h>
#include <asterisk/translate.h>
@@ -25,6 +26,7 @@
#include <asterisk/config.h>
#include <asterisk/utils.h>
#include <stdlib.h>
+#include <asterisk/cli.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
@@ -45,6 +47,8 @@
#endif
#include <unistd.h>
#include <sys/ioctl.h>
+#define MAX_MOHFILES 512
+#define MAX_MOHFILE_LEN 128
static char *app0 = "MusicOnHold";
static char *app1 = "WaitMusicOnHold";
@@ -73,15 +77,28 @@ static char *descrip2 = "SetMusicOnHold(class): "
static int respawn_time = 20;
+struct moh_files_state {
+ struct mohclass *class;
+ struct ast_filestream *stream;
+ int origwfmt;
+ int samples;
+ int sample_queue;
+ unsigned char pos;
+ unsigned char save_pos;
+};
+
struct mohclass {
char class[80];
char dir[256];
char miscargs[256];
+ char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
+ int total_files;
int destroyme;
int pid; /* PID of mpg123 */
int quiet;
int single;
int custom;
+ int randomize;
time_t start;
pthread_t thread;
struct mohdata *members;
@@ -101,17 +118,154 @@ struct mohdata {
static struct mohclass *mohclasses;
-
AST_MUTEX_DEFINE_STATIC(moh_lock);
#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
#define MPG_123 "/usr/bin/mpg123"
#define MAX_MP3S 256
+static void moh_files_release(struct ast_channel *chan, void *data)
+{
+ struct moh_files_state *state = chan->music_state;
+
+ if (chan && state) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
+
+ if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
+ }
+ state->save_pos = state->pos + 1;
+ }
+}
+
+
+static int ast_moh_files_next(struct ast_channel *chan) {
+ struct moh_files_state *state = chan->music_state;
+
+ if(state->save_pos) {
+ state->pos = state->save_pos - 1;
+ state->save_pos = 0;
+ } else {
+ state->samples = 0;
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ state->pos++;
+ }
+
+ if (state->class->randomize) {
+ srand(time(NULL)+getpid()+strlen(chan->name)-state->class->total_files);
+ state->pos = rand();
+ }
+ }
+
+ state->pos = state->pos % state->class->total_files;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
+ return -1;
+ }
+ if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
+ ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
+ state->pos++;
+ 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 (state->samples)
+ ast_seekstream(chan->stream, state->samples, SEEK_SET);
+
+ return state->pos;
+}
+
+
+static struct ast_frame *moh_files_readframe(struct ast_channel *chan) {
+ struct ast_frame *f = NULL;
+
+ if (!chan->stream || !(f = ast_readframe(chan->stream))) {
+ if (ast_moh_files_next(chan) > -1)
+ f = ast_readframe(chan->stream);
+ }
+
+ return f;
+}
+
+static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct moh_files_state *state = chan->music_state;
+ struct ast_frame *f = NULL;
+ int res = 0;
+ state->sample_queue += samples;
+
+ while(state->sample_queue > 0) {
+ if ((f = moh_files_readframe(chan))) {
+ state->samples += f->samples;
+ res = ast_write(chan, f);
+ ast_frfree(f);
+ if(res < 0) {
+ ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
+ return -1;
+ }
+ state->sample_queue -= f->samples;
+ } else
+ return -1;
+ }
+ return res;
+}
+
+
+static void *moh_files_alloc(struct ast_channel *chan, void *params)
+{
+ struct moh_files_state *state;
+ struct mohclass *class = params;
+ int allocated = 0;
+
+ if ((!chan->music_state) && ((state = malloc(sizeof(struct moh_files_state))))) {
+ chan->music_state = state;
+ allocated = 1;
+ } else
+ state = chan->music_state;
+
+
+ if(state) {
+ if (allocated || state->class != class) {
+ /* initialize */
+ memset(state, 0, sizeof(struct moh_files_state));
+ state->class = class;
+ }
+
+ state->origwfmt = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
+ free(chan->music_state);
+ chan->music_state = NULL;
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name);
+ }
+
+
+ }
+
+ return chan->music_state;
+}
+
+static struct ast_generator moh_file_stream =
+{
+ alloc: moh_files_alloc,
+ release: moh_files_release,
+ generate: moh_files_generator,
+};
+
static int spawn_mp3(struct mohclass *class)
{
int fds[2];
- int files;
+ int files=0;
char fns[MAX_MP3S][80];
char *argv[MAX_MP3S + 50];
char xargs[256];
@@ -119,11 +273,13 @@ static int spawn_mp3(struct mohclass *class)
int argc = 0;
DIR *dir;
struct dirent *de;
+
+
dir = opendir(class->dir);
if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
return -1;
- }
+ }
if (!class->custom) {
argv[argc++] = "mpg123";
@@ -137,14 +293,14 @@ static int spawn_mp3(struct mohclass *class)
argv[argc++] = "-b";
argv[argc++] = "2048";
}
-
+
argv[argc++] = "-f";
-
+
if (class->quiet)
argv[argc++] = "4096";
else
argv[argc++] = "8192";
-
+
/* Look for extra arguments and add them to the list */
strncpy(xargs, class->miscargs, sizeof(xargs) - 1);
argptr = xargs;
@@ -156,7 +312,7 @@ static int spawn_mp3(struct mohclass *class)
argptr++;
}
}
- } else {
+ } else {
/* Format arguments for argv vector */
strncpy(xargs, class->miscargs, sizeof(xargs) - 1);
argptr = xargs;
@@ -171,16 +327,17 @@ static int spawn_mp3(struct mohclass *class)
}
files = 0;
- if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://"))
- {
+ if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
strncpy(fns[files], class->dir, sizeof(fns[files]) - 1);
argv[argc++] = fns[files];
files++;
- }
- else
- {
+ } else {
while((de = readdir(dir)) && (files < MAX_MP3S)) {
- if ((strlen(de->d_name) > 3) && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3")) {
+ if ((strlen(de->d_name) > 3) &&
+ ((class->custom &&
+ (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") ||
+ !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln")))
+ || !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1);
argv[argc++] = fns[files];
files++;
@@ -189,6 +346,7 @@ static int spawn_mp3(struct mohclass *class)
}
argv[argc] = NULL;
closedir(dir);
+
if (pipe(fds)) {
ast_log(LOG_WARNING, "Pipe failed\n");
return -1;
@@ -229,18 +387,19 @@ static int spawn_mp3(struct mohclass *class)
close(x);
}
}
- /* Child */
- chdir(class->dir);
- if(class->custom) {
+ /* Child */
+
+ if(class->custom) {
+ chdir(class->dir);
execv(argv[0], argv);
- } else {
- /* Default install is /usr/local/bin */
- execv(LOCAL_MPG_123, argv);
- /* Many places have it in /usr/bin */
- execv(MPG_123, argv);
- /* Check PATH as a last-ditch effort */
- execvp("mpg123", argv);
- }
+ } else {
+ /* Default install is /usr/local/bin */
+ execv(LOCAL_MPG_123, argv);
+ /* Many places have it in /usr/bin */
+ execv(MPG_123, argv);
+ /* Check PATH as a last-ditch effort */
+ execvp("mpg123", argv);
+ }
ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
close(fds[1]);
exit(1);
@@ -456,8 +615,7 @@ static void *moh_alloc(struct ast_channel *chan, void *params)
{
struct mohdata *res;
struct mohclass *class;
- ast_mutex_lock(&moh_lock);
- class = get_mohbyname(params);
+ class = params;
if (class)
res = mohalloc(class);
else {
@@ -465,7 +623,6 @@ static void *moh_alloc(struct ast_channel *chan, void *params)
ast_log(LOG_WARNING, "No class: %s\n", (char *)params);
res = NULL;
}
- ast_mutex_unlock(&moh_lock);
if (res) {
res->origwfmt = chan->writeformat;
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
@@ -473,10 +630,6 @@ static void *moh_alloc(struct ast_channel *chan, void *params)
moh_release(NULL, res);
res = NULL;
}
-#if 0
- /* Allow writes to interrupt */
- chan->writeinterrupt = 1;
-#endif
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", (char *)params, chan->name);
}
@@ -489,7 +642,8 @@ static int moh_generate(struct ast_channel *chan, void *data, int len, int sampl
struct mohdata *moh = data;
short buf[1280 + AST_FRIENDLY_OFFSET / 2];
int res;
-
+ if(!moh->parent->pid)
+ return - 1;
len = samples * 2;
if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), (int)len, chan->name);
@@ -525,6 +679,58 @@ static struct ast_generator mohgen =
generate: moh_generate,
};
+static int moh_scan_files(struct mohclass *class) {
+
+ DIR *files_DIR;
+ struct dirent *files_dirent;
+ char path[512];
+ char filepath[MAX_MOHFILE_LEN];
+ char *scan;
+ struct stat statbuf;
+ int dirnamelen;
+ int i;
+
+ files_DIR = opendir(class->dir);
+ if (!files_DIR) {
+ ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
+ return -1;
+ }
+
+ class->total_files = 0;
+ dirnamelen = strlen(class->dir) + 2;
+ getcwd(path, 512);
+ chdir(class->dir);
+ memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
+
+ while ((files_dirent = readdir(files_DIR))) {
+ if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
+ continue;
+
+ snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
+
+ if (stat(filepath, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode))
+ continue;
+
+ if ((scan = strrchr(filepath, '.')))
+ *scan = '\0';
+
+ /* if the file is present in multiple formats, ensure we only put it into the list once */
+ for (i = 0; i < class->total_files; i++)
+ if (!strcmp(filepath, class->filearray[i]))
+ break;
+
+ if (i == class->total_files)
+ strcpy(class->filearray[class->total_files++], filepath);
+ }
+
+ closedir(files_DIR);
+ chdir(path);
+ return class->total_files;
+}
+
static int moh_register(char *classname, char *mode, char *param, char *miscargs)
{
struct mohclass *moh;
@@ -545,16 +751,30 @@ static int moh_register(char *classname, char *mode, char *param, char *miscargs
time(&moh->start);
moh->start -= respawn_time;
strncpy(moh->class, classname, sizeof(moh->class) - 1);
- if (miscargs)
+ if (miscargs) {
strncpy(moh->miscargs, miscargs, sizeof(moh->miscargs) - 1);
- if (!strcasecmp(mode, "mp3") || !strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb") || !strcasecmp(mode, "httpmp3") || !strcasecmp(mode, "custom")) {
+ if (strchr(miscargs,'r'))
+ moh->randomize=1;
+ }
+ if (!strcasecmp(mode, "files")) {
+ if (param)
+ strncpy(moh->dir, param, sizeof(moh->dir) - 1);
+ if (!moh_scan_files(moh)) {
+ free(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 (!strcasecmp(mode, "custom"))
moh->custom = 1;
- if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb"))
+ else if (!strcasecmp(mode, "mp3nb") || !strcasecmp(mode, "quietmp3nb"))
moh->single = 1;
- if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb"))
+ else if (!strcasecmp(mode, "quietmp3") || !strcasecmp(mode, "quietmp3nb"))
moh->quiet = 1;
- strncpy(moh->dir, param, sizeof(moh->dir) - 1);
+
moh->srcfd = -1;
#ifdef ZAPATA_MOH
/* It's an MP3 Moh -- Open /dev/zap/pseudo for timing... Is
@@ -588,26 +808,58 @@ static int moh_register(char *classname, char *mode, char *param, char *miscargs
return 0;
}
+static void local_ast_moh_cleanup(struct ast_channel *chan)
+{
+ if(chan->music_state) {
+ free(chan->music_state);
+ chan->music_state = NULL;
+ }
+}
+
static int local_ast_moh_start(struct ast_channel *chan, char *class)
{
+ struct mohclass *mohclass;
+
if (!class || ast_strlen_zero(class))
class = chan->musicclass;
if (!class || ast_strlen_zero(class))
class = "default";
- return ast_activate_generator(chan, &mohgen, class);
+ ast_mutex_lock(&moh_lock);
+ mohclass = get_mohbyname(class);
+ ast_mutex_unlock(&moh_lock);
+
+ if (!mohclass) {
+ ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
+ return -1;
+ }
+
+ ast_set_flag(chan, AST_FLAG_MOH);
+ if (mohclass->total_files) {
+ return ast_activate_generator(chan, &moh_file_stream, mohclass);
+ } else
+ return ast_activate_generator(chan, &mohgen, mohclass);
}
static void local_ast_moh_stop(struct ast_channel *chan)
{
+ ast_clear_flag(chan, AST_FLAG_MOH);
ast_deactivate_generator(chan);
+
+ if(chan->music_state) {
+ if(chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+ }
}
-static void load_moh_classes(void)
+static int load_moh_classes(void)
{
struct ast_config *cfg;
struct ast_variable *var;
char *data;
char *args;
+ int x = 0;
cfg = ast_load("musiconhold.conf");
if (cfg) {
var = ast_variable_browse(cfg, "classes");
@@ -621,54 +873,121 @@ static void load_moh_classes(void)
*args = '\0';
args++;
}
- moh_register(var->name, var->value, data,args);
+ if(!(get_mohbyname(var->name))) {
+ moh_register(var->name, var->value, data, args);
+ x++;
+ }
}
var = var->next;
}
+ var = ast_variable_browse(cfg, "moh_files");
+ while(var) {
+ if(!(get_mohbyname(var->name))) {
+ args = strchr(var->value, ',');
+ if (args) {
+ *args = '\0';
+ args++;
+ }
+ moh_register(var->name, "files", var->value, args);
+ x++;
+ }
+ var = var->next;
+ }
+
ast_destroy(cfg);
}
+ return x;
}
static void ast_moh_destroy(void)
{
- struct mohclass *moh;
+ struct mohclass *moh,*tmp;
char buff[8192];
- int bytes, tbytes=0, stime = 0;
+ int bytes, tbytes=0, stime = 0, pid = 0;
if (option_verbose > 1)
- ast_verbose(VERBOSE_PREFIX_2 "Destroying any remaining musiconhold processes\n");
+ ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
ast_mutex_lock(&moh_lock);
moh = mohclasses;
+
while(moh) {
if (moh->pid) {
ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
stime = time(NULL);
- kill(moh->pid, SIGKILL);
+ pid = moh->pid;
+ moh->pid = 0;
+ kill(pid, SIGKILL);
while ((bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime + 5) {
tbytes = tbytes + bytes;
}
- ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", moh->pid, tbytes);
+ ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
close(moh->srcfd);
- moh->pid = 0;
}
+ tmp = moh;
moh = moh->next;
+ free(tmp);
}
+ mohclasses = NULL;
ast_mutex_unlock(&moh_lock);
}
+
+static void moh_on_off(int on) {
+ struct ast_channel *chan = ast_channel_walk_locked(NULL);
+ while(chan) {
+ if(ast_test_flag(chan, AST_FLAG_MOH)) {
+ if(on)
+ local_ast_moh_start(chan,NULL);
+ else
+ ast_deactivate_generator(chan);
+ }
+ ast_mutex_unlock(&chan->lock);
+ chan = ast_channel_walk_locked(chan);
+ }
+}
+
+static int moh_cli(int fd, int argc, char *argv[])
+{
+ int x = 0;
+ moh_on_off(0);
+ ast_moh_destroy();
+ x = load_moh_classes();
+ moh_on_off(1);
+ ast_cli(fd,"\n%d class%s reloaded.\n",x,x == 1 ? "" : "es");
+ return 0;
+}
+
+static struct ast_cli_entry cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
+
+
int load_module(void)
{
int res;
load_moh_classes();
- ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop);
+ ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
ast_register_atexit(ast_moh_destroy);
+ ast_cli_register(&cli_moh);
if (!res)
res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
if (!res)
res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
+
return res;
}
+int reload(void)
+{
+ struct mohclass *moh = mohclasses;
+ load_moh_classes();
+ while(moh) {
+ if (moh->total_files)
+ moh_scan_files(moh);
+ moh = moh->next;
+ }
+ return 0;
+}
+
+
int unload_module(void)
{
return -1;