[Asterisk-cvs] asterisk/res res_musiconhold.c,1.41,1.42

markster at lists.digium.com markster at lists.digium.com
Thu Dec 23 20:44:32 CST 2004


Update of /usr/cvsroot/asterisk/res
In directory mongoose.digium.com:/tmp/cvs-serv2815/res

Modified Files:
	res_musiconhold.c 
Log Message:
Merge anthm's native MOH patch (bug #2639) he promises me he'll rid it of ast_flag_moh...


Index: res_musiconhold.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_musiconhold.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -d -r1.41 -r1.42
--- res_musiconhold.c	9 Dec 2004 19:55:01 -0000	1.41
+++ res_musiconhold.c	24 Dec 2004 01:40:07 -0000	1.42
@@ -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 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 @@
 
 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 @@
 	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 @@
 			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 @@
 				argptr++;
 			}
 		}
-	} else {
+	} else  {
 		/* Format arguments for argv vector */
 		strncpy(xargs, class->miscargs, sizeof(xargs) - 1);
 		argptr = xargs;
@@ -171,16 +327,17 @@
 	}
 
 	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 @@
 	}
 	argv[argc] = NULL;
 	closedir(dir);
+	
 	if (pipe(fds)) {	
 		ast_log(LOG_WARNING, "Pipe failed\n");
 		return -1;
@@ -229,18 +387,19 @@
 				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 @@
 {
 	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 @@
 			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 @@
 			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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 					*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;




More information about the svn-commits mailing list