Mon Mar 20 08:20:13 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

res_musiconhold.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Routines implementing music on hold
00022  *
00023  * \arg See also \ref Config_moh
00024  * 
00025  */
00026 
00027 #include <stdlib.h>
00028 #include <errno.h>
00029 #include <unistd.h>
00030 #include <string.h>
00031 #include <signal.h>
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <sys/time.h>
00035 #include <sys/signal.h>
00036 #include <netinet/in.h>
00037 #include <sys/stat.h>
00038 #include <dirent.h>
00039 #ifdef ZAPATA_MOH
00040 #ifdef __linux__
00041 #include <linux/zaptel.h>
00042 #else
00043 #include <zaptel.h>
00044 #endif /* __linux__ */
00045 #endif
00046 #include <unistd.h>
00047 #include <sys/ioctl.h>
00048 
00049 #include "asterisk.h"
00050 
00051 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
00052 
00053 #include "asterisk/lock.h"
00054 #include "asterisk/file.h"
00055 #include "asterisk/logger.h"
00056 #include "asterisk/channel.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/options.h"
00059 #include "asterisk/module.h"
00060 #include "asterisk/translate.h"
00061 #include "asterisk/say.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/config.h"
00064 #include "asterisk/utils.h"
00065 #include "asterisk/cli.h"
00066 
00067 #define MAX_MOHFILES 512
00068 #define MAX_MOHFILE_LEN 128
00069 
00070 static char *app0 = "MusicOnHold";
00071 static char *app1 = "WaitMusicOnHold";
00072 static char *app2 = "SetMusicOnHold";
00073 static char *app3 = "StartMusicOnHold";
00074 static char *app4 = "StopMusicOnHold";
00075 
00076 static char *synopsis0 = "Play Music On Hold indefinitely";
00077 static char *synopsis1 = "Wait, playing Music On Hold";
00078 static char *synopsis2 = "Set default Music On Hold class";
00079 static char *synopsis3 = "Play Music On Hold";
00080 static char *synopsis4 = "Stop Playing Music On Hold";
00081 
00082 static char *descrip0 = "MusicOnHold(class): "
00083 "Plays hold music specified by class.  If omitted, the default\n"
00084 "music source for the channel will be used. Set the default \n"
00085 "class with the SetMusicOnHold() application.\n"
00086 "Returns -1 on hangup.\n"
00087 "Never returns otherwise.\n";
00088 
00089 static char *descrip1 = "WaitMusicOnHold(delay): "
00090 "Plays hold music specified number of seconds.  Returns 0 when\n"
00091 "done, or -1 on hangup.  If no hold music is available, the delay will\n"
00092 "still occur with no sound.\n";
00093 
00094 static char *descrip2 = "SetMusicOnHold(class): "
00095 "Sets the default class for music on hold for a given channel.  When\n"
00096 "music on hold is activated, this class will be used to select which\n"
00097 "music is played.\n";
00098 
00099 static char *descrip3 = "StartMusicOnHold(class): "
00100 "Starts playing music on hold, uses default music class for channel.\n"
00101 "Starts playing music specified by class.  If omitted, the default\n"
00102 "music source for the channel will be used.  Always returns 0.\n";
00103 
00104 static char *descrip4 = "StopMusicOnHold: "
00105 "Stops playing music on hold.\n";
00106 
00107 static int respawn_time = 20;
00108 
00109 struct moh_files_state {
00110    struct mohclass *class;
00111    int origwfmt;
00112    int samples;
00113    int sample_queue;
00114    unsigned char pos;
00115    unsigned char save_pos;
00116 };
00117 
00118 #define MOH_QUIET    (1 << 0)
00119 #define MOH_SINGLE      (1 << 1)
00120 #define MOH_CUSTOM      (1 << 2)
00121 #define MOH_RANDOMIZE      (1 << 3)
00122 
00123 struct mohclass {
00124    char name[MAX_MUSICCLASS];
00125    char dir[256];
00126    char args[256];
00127    char mode[80];
00128    char filearray[MAX_MOHFILES][MAX_MOHFILE_LEN];
00129    unsigned int flags;
00130    int total_files;
00131    int format;
00132    int pid;    /* PID of mpg123 */
00133    time_t start;
00134    pthread_t thread;
00135    struct mohdata *members;
00136    /* Source of audio */
00137    int srcfd;
00138    /* FD for timing source */
00139    int pseudofd;
00140    struct mohclass *next;
00141 };
00142 
00143 struct mohdata {
00144    int pipe[2];
00145    int origwfmt;
00146    struct mohclass *parent;
00147    struct mohdata *next;
00148 };
00149 
00150 static struct mohclass *mohclasses;
00151 
00152 AST_MUTEX_DEFINE_STATIC(moh_lock);
00153 
00154 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
00155 #define MPG_123 "/usr/bin/mpg123"
00156 #define MAX_MP3S 256
00157 
00158 
00159 static void ast_moh_free_class(struct mohclass **class) 
00160 {
00161    struct mohdata *members, *mtmp;
00162    
00163    members = (*class)->members;
00164    while(members) {
00165       mtmp = members;
00166       members = members->next;
00167       free(mtmp);
00168    }
00169    free(*class);
00170    *class = NULL;
00171 }
00172 
00173 
00174 static void moh_files_release(struct ast_channel *chan, void *data)
00175 {
00176    struct moh_files_state *state = chan->music_state;
00177 
00178    if (chan && state) {
00179       if (option_verbose > 2)
00180          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00181 
00182       if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00183          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00184       }
00185       state->save_pos = state->pos + 1;
00186    }
00187 }
00188 
00189 
00190 static int ast_moh_files_next(struct ast_channel *chan) 
00191 {
00192    struct moh_files_state *state = chan->music_state;
00193    int tries;
00194 
00195    if (state->save_pos) {
00196       state->pos = state->save_pos - 1;
00197       state->save_pos = 0;
00198    } else {
00199       /* Try 20 times to find something good */
00200       for (tries=0;tries < 20;tries++) {
00201          state->samples = 0;
00202          if (chan->stream) {
00203             ast_closestream(chan->stream);
00204             chan->stream = NULL;
00205             state->pos++;
00206          }
00207 
00208          if (ast_test_flag(state->class, MOH_RANDOMIZE))
00209             state->pos = rand();
00210 
00211          /* check to see if this file's format can be opened */
00212          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) != -1)
00213             break;
00214 
00215       }
00216    }
00217 
00218    state->pos = state->pos % state->class->total_files;
00219    
00220    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00221       ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
00222       return -1;
00223    }
00224    if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00225       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00226       state->pos++;
00227       return -1;
00228    }
00229 
00230    if (option_debug)
00231       ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00232 
00233    if (state->samples)
00234       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00235 
00236    return 0;
00237 }
00238 
00239 
00240 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
00241 {
00242    struct ast_frame *f = NULL;
00243    
00244    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00245       if (!ast_moh_files_next(chan))
00246          f = ast_readframe(chan->stream);
00247    }
00248 
00249    return f;
00250 }
00251 
00252 static int moh_files_generator(struct ast_channel *chan, void *data, int len, int samples)
00253 {
00254    struct moh_files_state *state = chan->music_state;
00255    struct ast_frame *f = NULL;
00256    int res = 0;
00257 
00258    state->sample_queue += samples;
00259 
00260    while (state->sample_queue > 0) {
00261       if ((f = moh_files_readframe(chan))) {
00262          state->samples += f->samples;
00263          res = ast_write(chan, f);
00264          state->sample_queue -= f->samples;
00265          ast_frfree(f);
00266          if (res < 0) {
00267             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00268             return -1;
00269          }
00270       } else
00271          return -1;  
00272    }
00273    return res;
00274 }
00275 
00276 
00277 static void *moh_files_alloc(struct ast_channel *chan, void *params)
00278 {
00279    struct moh_files_state *state;
00280    struct mohclass *class = params;
00281    int allocated = 0;
00282 
00283    if (!chan->music_state && (state = malloc(sizeof(struct moh_files_state)))) {
00284       chan->music_state = state;
00285       allocated = 1;
00286    } else 
00287       state = chan->music_state;
00288 
00289    if (state) {
00290       if (allocated || state->class != class) {
00291          /* initialize */
00292          memset(state, 0, sizeof(struct moh_files_state));
00293          state->class = class;
00294       }
00295 
00296       state->origwfmt = chan->writeformat;
00297 
00298       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00299          ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
00300          free(chan->music_state);
00301          chan->music_state = NULL;
00302       } else {
00303          if (option_verbose > 2)
00304             ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", class->name, chan->name);
00305       }
00306    }
00307    
00308    return chan->music_state;
00309 }
00310 
00311 static struct ast_generator moh_file_stream = 
00312 {
00313    alloc: moh_files_alloc,
00314    release: moh_files_release,
00315    generate: moh_files_generator,
00316 };
00317 
00318 static int spawn_mp3(struct mohclass *class)
00319 {
00320    int fds[2];
00321    int files = 0;
00322    char fns[MAX_MP3S][80];
00323    char *argv[MAX_MP3S + 50];
00324    char xargs[256];
00325    char *argptr;
00326    int argc = 0;
00327    DIR *dir = NULL;
00328    struct dirent *de;
00329 
00330    
00331    if (!strcasecmp(class->dir, "nodir")) {
00332       files = 1;
00333    } else {
00334       dir = opendir(class->dir);
00335       if (!dir && !strstr(class->dir,"http://") && !strstr(class->dir,"HTTP://")) {
00336          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00337          return -1;
00338       }
00339    }
00340 
00341    if (!ast_test_flag(class, MOH_CUSTOM)) {
00342       argv[argc++] = "mpg123";
00343       argv[argc++] = "-q";
00344       argv[argc++] = "-s";
00345       argv[argc++] = "--mono";
00346       argv[argc++] = "-r";
00347       argv[argc++] = "8000";
00348       
00349       if (!ast_test_flag(class, MOH_SINGLE)) {
00350          argv[argc++] = "-b";
00351          argv[argc++] = "2048";
00352       }
00353       
00354       argv[argc++] = "-f";
00355       
00356       if (ast_test_flag(class, MOH_QUIET))
00357          argv[argc++] = "4096";
00358       else
00359          argv[argc++] = "8192";
00360       
00361       /* Look for extra arguments and add them to the list */
00362       strncpy(xargs, class->args, sizeof(xargs) - 1);
00363       argptr = xargs;
00364       while (!ast_strlen_zero(argptr)) {
00365          argv[argc++] = argptr;
00366          argptr = strchr(argptr, ',');
00367          if (argptr) {
00368             *argptr = '\0';
00369             argptr++;
00370          }
00371       }
00372    } else  {
00373       /* Format arguments for argv vector */
00374       strncpy(xargs, class->args, sizeof(xargs) - 1);
00375       argptr = xargs;
00376       while (!ast_strlen_zero(argptr)) {
00377          argv[argc++] = argptr;
00378          argptr = strchr(argptr, ' ');
00379          if (argptr) {
00380             *argptr = '\0';
00381             argptr++;
00382          }
00383       }
00384    }
00385 
00386 
00387    if (strstr(class->dir,"http://") || strstr(class->dir,"HTTP://")) {
00388       strncpy(fns[files], class->dir, sizeof(fns[files]) - 1);
00389       argv[argc++] = fns[files];
00390       files++;
00391    } else if (dir) {
00392       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00393          if ((strlen(de->d_name) > 3) && 
00394              ((ast_test_flag(class, MOH_CUSTOM) && 
00395                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00396                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00397               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00398             strncpy(fns[files], de->d_name, sizeof(fns[files]) - 1);
00399             argv[argc++] = fns[files];
00400             files++;
00401          }
00402       }
00403    }
00404    argv[argc] = NULL;
00405    if (dir) {
00406       closedir(dir);
00407    }
00408    if (pipe(fds)) {  
00409       ast_log(LOG_WARNING, "Pipe failed\n");
00410       return -1;
00411    }
00412 #if 0
00413    printf("%d files total, %d args total\n", files, argc);
00414    {
00415       int x;
00416       for (x=0;argv[x];x++)
00417          printf("arg%d: %s\n", x, argv[x]);
00418    }
00419 #endif   
00420    if (!files) {
00421       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00422       close(fds[0]);
00423       close(fds[1]);
00424       return -1;
00425    }
00426    if (time(NULL) - class->start < respawn_time) {
00427       sleep(respawn_time - (time(NULL) - class->start));
00428    }
00429    time(&class->start);
00430    class->pid = fork();
00431    if (class->pid < 0) {
00432       close(fds[0]);
00433       close(fds[1]);
00434       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00435       return -1;
00436    }
00437    if (!class->pid) {
00438       int x;
00439       close(fds[0]);
00440       /* Stdout goes to pipe */
00441       dup2(fds[1], STDOUT_FILENO);
00442       /* Close unused file descriptors */
00443       for (x=3;x<8192;x++) {
00444          if (-1 != fcntl(x, F_GETFL)) {
00445             close(x);
00446          }
00447       }
00448       /* Child */
00449       chdir(class->dir);
00450       if (ast_test_flag(class, MOH_CUSTOM)) {
00451          execv(argv[0], argv);
00452       } else {
00453          /* Default install is /usr/local/bin */
00454          execv(LOCAL_MPG_123, argv);
00455          /* Many places have it in /usr/bin */
00456          execv(MPG_123, argv);
00457          /* Check PATH as a last-ditch effort */
00458          execvp("mpg123", argv);
00459       }
00460       ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00461       close(fds[1]);
00462       exit(1);
00463    } else {
00464       /* Parent */
00465       close(fds[1]);
00466    }
00467    return fds[0];
00468 }
00469 
00470 static void *monmp3thread(void *data)
00471 {
00472 #define  MOH_MS_INTERVAL      100
00473 
00474    struct mohclass *class = data;
00475    struct mohdata *moh;
00476    char buf[8192];
00477    short sbuf[8192];
00478    int res, res2;
00479    int len;
00480    struct timeval tv, tv_tmp;
00481 
00482    tv.tv_sec = 0;
00483    tv.tv_usec = 0;
00484    for(;/* ever */;) {
00485       /* Spawn mp3 player if it's not there */
00486       if (class->srcfd < 0) {
00487          if ((class->srcfd = spawn_mp3(class)) < 0) {
00488             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00489             /* Try again later */
00490             sleep(500);
00491          }
00492       }
00493       if (class->pseudofd > -1) {
00494          /* Pause some amount of time */
00495          res = read(class->pseudofd, buf, sizeof(buf));
00496       } else {
00497          long delta;
00498          /* Reliable sleep */
00499          tv_tmp = ast_tvnow();
00500          if (ast_tvzero(tv))
00501             tv = tv_tmp;
00502          delta = ast_tvdiff_ms(tv_tmp, tv);
00503          if (delta < MOH_MS_INTERVAL) {   /* too early */
00504             tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00505             usleep(1000 * (MOH_MS_INTERVAL - delta));
00506          } else {
00507             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00508             tv = tv_tmp;
00509          }
00510          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00511       }
00512       if (!class->members)
00513          continue;
00514       /* Read mp3 audio */
00515       len = ast_codec_get_len(class->format, res);
00516       
00517       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00518          if (!res2) {
00519             close(class->srcfd);
00520             class->srcfd = -1;
00521             if (class->pid) {
00522                kill(class->pid, SIGKILL);
00523                class->pid = 0;
00524             }
00525          } else
00526             ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
00527          continue;
00528       }
00529       ast_mutex_lock(&moh_lock);
00530       moh = class->members;
00531       while (moh) {
00532          /* Write data */
00533          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) 
00534             if (option_debug)
00535                ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
00536          moh = moh->next;
00537       }
00538       ast_mutex_unlock(&moh_lock);
00539    }
00540    return NULL;
00541 }
00542 
00543 static int moh0_exec(struct ast_channel *chan, void *data)
00544 {
00545    if (ast_moh_start(chan, data)) {
00546       ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00547       return -1;
00548    }
00549    while (!ast_safe_sleep(chan, 10000));
00550    ast_moh_stop(chan);
00551    return -1;
00552 }
00553 
00554 static int moh1_exec(struct ast_channel *chan, void *data)
00555 {
00556    int res;
00557    if (!data || !atoi(data)) {
00558       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00559       return -1;
00560    }
00561    if (ast_moh_start(chan, NULL)) {
00562       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00563       return -1;
00564    }
00565    res = ast_safe_sleep(chan, atoi(data) * 1000);
00566    ast_moh_stop(chan);
00567    return res;
00568 }
00569 
00570 static int moh2_exec(struct ast_channel *chan, void *data)
00571 {
00572    if (ast_strlen_zero(data)) {
00573       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00574       return -1;
00575    }
00576    strncpy(chan->musicclass, data, sizeof(chan->musicclass) - 1);
00577    return 0;
00578 }
00579 
00580 static int moh3_exec(struct ast_channel *chan, void *data)
00581 {
00582    char *class = NULL;
00583    if (data && strlen(data))
00584       class = data;
00585    if (ast_moh_start(chan, class)) 
00586       ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00587 
00588    return 0;
00589 }
00590 
00591 static int moh4_exec(struct ast_channel *chan, void *data)
00592 {
00593    ast_moh_stop(chan);
00594 
00595    return 0;
00596 }
00597 
00598 static struct mohclass *get_mohbyname(char *name)
00599 {
00600    struct mohclass *moh;
00601    moh = mohclasses;
00602    while (moh) {
00603       if (!strcasecmp(name, moh->name))
00604          return moh;
00605       moh = moh->next;
00606    }
00607    return NULL;
00608 }
00609 
00610 static struct mohdata *mohalloc(struct mohclass *cl)
00611 {
00612    struct mohdata *moh;
00613    long flags;
00614    moh = malloc(sizeof(struct mohdata));
00615    if (!moh)
00616       return NULL;
00617    memset(moh, 0, sizeof(struct mohdata));
00618    if (pipe(moh->pipe)) {
00619       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00620       free(moh);
00621       return NULL;
00622    }
00623    /* Make entirely non-blocking */
00624    flags = fcntl(moh->pipe[0], F_GETFL);
00625    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00626    flags = fcntl(moh->pipe[1], F_GETFL);
00627    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00628    moh->parent = cl;
00629    moh->next = cl->members;
00630    cl->members = moh;
00631    return moh;
00632 }
00633 
00634 static void moh_release(struct ast_channel *chan, void *data)
00635 {
00636    struct mohdata *moh = data, *prev, *cur;
00637    int oldwfmt;
00638    ast_mutex_lock(&moh_lock);
00639    /* Unlink */
00640    prev = NULL;
00641    cur = moh->parent->members;
00642    while (cur) {
00643       if (cur == moh) {
00644          if (prev)
00645             prev->next = cur->next;
00646          else
00647             moh->parent->members = cur->next;
00648          break;
00649       }
00650       prev = cur;
00651       cur = cur->next;
00652    }
00653    ast_mutex_unlock(&moh_lock);
00654    close(moh->pipe[0]);
00655    close(moh->pipe[1]);
00656    oldwfmt = moh->origwfmt;
00657    free(moh);
00658    if (chan) {
00659       if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
00660          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
00661       if (option_verbose > 2)
00662          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00663    }
00664 }
00665 
00666 static void *moh_alloc(struct ast_channel *chan, void *params)
00667 {
00668    struct mohdata *res;
00669    struct mohclass *class = params;
00670 
00671    res = mohalloc(class);
00672    if (res) {
00673       res->origwfmt = chan->writeformat;
00674       if (ast_set_write_format(chan, class->format)) {
00675          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00676          moh_release(NULL, res);
00677          res = NULL;
00678       }
00679       if (option_verbose > 2)
00680          ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00681    }
00682    return res;
00683 }
00684 
00685 static int moh_generate(struct ast_channel *chan, void *data, int len, int samples)
00686 {
00687    struct ast_frame f;
00688    struct mohdata *moh = data;
00689    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00690    int res;
00691 
00692    if (!moh->parent->pid)
00693       return -1;
00694 
00695    len = ast_codec_get_len(moh->parent->format, samples);
00696 
00697    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00698       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00699       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00700    }
00701    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00702 #if 0
00703    if (res != len) {
00704       ast_log(LOG_WARNING, "Read only %d of %d bytes: %s\n", res, len, strerror(errno));
00705    }
00706 #endif
00707    if (res <= 0)
00708       return 0;
00709 
00710    memset(&f, 0, sizeof(f));
00711    
00712    f.frametype = AST_FRAME_VOICE;
00713    f.subclass = moh->parent->format;
00714    f.mallocd = 0;
00715    f.datalen = res;
00716    f.data = buf + AST_FRIENDLY_OFFSET / 2;
00717    f.offset = AST_FRIENDLY_OFFSET;
00718    f.samples = ast_codec_get_samples(&f);
00719 
00720    if (ast_write(chan, &f) < 0) {
00721       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00722       return -1;
00723    }
00724 
00725    return 0;
00726 }
00727 
00728 static struct ast_generator mohgen = 
00729 {
00730    alloc: moh_alloc,
00731    release: moh_release,
00732    generate: moh_generate,
00733 };
00734 
00735 static int moh_scan_files(struct mohclass *class) {
00736 
00737    DIR *files_DIR;
00738    struct dirent *files_dirent;
00739    char path[512];
00740    char filepath[MAX_MOHFILE_LEN];
00741    char *ext;
00742    struct stat statbuf;
00743    int dirnamelen;
00744    int i;
00745    
00746    files_DIR = opendir(class->dir);
00747    if (!files_DIR) {
00748       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist", class->dir);
00749       return -1;
00750    }
00751 
00752    class->total_files = 0;
00753    dirnamelen = strlen(class->dir) + 2;
00754    getcwd(path, 512);
00755    chdir(class->dir);
00756    memset(class->filearray, 0, MAX_MOHFILES*MAX_MOHFILE_LEN);
00757    while ((files_dirent = readdir(files_DIR))) {
00758       if ((strlen(files_dirent->d_name) < 4) || ((strlen(files_dirent->d_name) + dirnamelen) >= MAX_MOHFILE_LEN))
00759          continue;
00760 
00761       snprintf(filepath, MAX_MOHFILE_LEN, "%s/%s", class->dir, files_dirent->d_name);
00762 
00763       if (stat(filepath, &statbuf))
00764          continue;
00765 
00766       if (!S_ISREG(statbuf.st_mode))
00767          continue;
00768 
00769       if ((ext = strrchr(filepath, '.'))) {
00770          *ext = '\0';
00771          ext++;
00772       }
00773 
00774       /* if the file is present in multiple formats, ensure we only put it into the list once */
00775       for (i = 0; i < class->total_files; i++)
00776          if (!strcmp(filepath, class->filearray[i]))
00777             break;
00778 
00779       if (i == class->total_files)
00780          strcpy(class->filearray[class->total_files++], filepath);
00781    }
00782 
00783    closedir(files_DIR);
00784    chdir(path);
00785    return class->total_files;
00786 }
00787 
00788 static int moh_register(struct mohclass *moh, int reload)
00789 {
00790 #ifdef ZAPATA_MOH
00791    int x;
00792 #endif
00793    ast_mutex_lock(&moh_lock);
00794    if (get_mohbyname(moh->name)) {
00795       if (reload) {
00796          ast_log(LOG_DEBUG, "Music on Hold class '%s' left alone from initial load.\n", moh->name);
00797       } else {
00798          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00799       }
00800       free(moh);  
00801       ast_mutex_unlock(&moh_lock);
00802       return -1;
00803    }
00804    ast_mutex_unlock(&moh_lock);
00805 
00806    time(&moh->start);
00807    moh->start -= respawn_time;
00808    
00809    if (!strcasecmp(moh->mode, "files")) {
00810       if (!moh_scan_files(moh)) {
00811          ast_moh_free_class(&moh);
00812          return -1;
00813       }
00814       if (strchr(moh->args, 'r'))
00815          ast_set_flag(moh, MOH_RANDOMIZE);
00816    } 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")) {
00817 
00818       if (!strcasecmp(moh->mode, "custom"))
00819          ast_set_flag(moh, MOH_CUSTOM);
00820       else if (!strcasecmp(moh->mode, "mp3nb"))
00821          ast_set_flag(moh, MOH_SINGLE);
00822       else if (!strcasecmp(moh->mode, "quietmp3nb"))
00823          ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
00824       else if (!strcasecmp(moh->mode, "quietmp3"))
00825          ast_set_flag(moh, MOH_QUIET);
00826       
00827       moh->srcfd = -1;
00828 #ifdef ZAPATA_MOH
00829       /* Open /dev/zap/pseudo for timing...  Is
00830          there a better, yet reliable way to do this? */
00831       moh->pseudofd = open("/dev/zap/pseudo", O_RDONLY);
00832       if (moh->pseudofd < 0) {
00833          ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00834       } else {
00835          x = 320;
00836          ioctl(moh->pseudofd, ZT_SET_BLOCKSIZE, &x);
00837       }
00838 #else
00839       moh->pseudofd = -1;
00840 #endif
00841       if (ast_pthread_create(&moh->thread, NULL, monmp3thread, moh)) {
00842          ast_log(LOG_WARNING, "Unable to create moh...\n");
00843          if (moh->pseudofd > -1)
00844             close(moh->pseudofd);
00845          ast_moh_free_class(&moh);
00846          return -1;
00847       }
00848    } else {
00849       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00850       ast_moh_free_class(&moh);
00851       return -1;
00852    }
00853    ast_mutex_lock(&moh_lock);
00854    moh->next = mohclasses;
00855    mohclasses = moh;
00856    ast_mutex_unlock(&moh_lock);
00857    return 0;
00858 }
00859 
00860 static void local_ast_moh_cleanup(struct ast_channel *chan)
00861 {
00862    if (chan->music_state) {
00863       free(chan->music_state);
00864       chan->music_state = NULL;
00865    }
00866 }
00867 
00868 static int local_ast_moh_start(struct ast_channel *chan, char *class)
00869 {
00870    struct mohclass *mohclass;
00871 
00872    if (ast_strlen_zero(class))
00873       class = chan->musicclass;
00874    if (ast_strlen_zero(class))
00875       class = "default";
00876    ast_mutex_lock(&moh_lock);
00877    mohclass = get_mohbyname(class);
00878    ast_mutex_unlock(&moh_lock);
00879 
00880    if (!mohclass) {
00881       ast_log(LOG_WARNING, "No class: %s\n", (char *)class);
00882       return -1;
00883    }
00884 
00885    ast_set_flag(chan, AST_FLAG_MOH);
00886    if (mohclass->total_files) {
00887       return ast_activate_generator(chan, &moh_file_stream, mohclass);
00888    } else
00889       return ast_activate_generator(chan, &mohgen, mohclass);
00890 }
00891 
00892 static void local_ast_moh_stop(struct ast_channel *chan)
00893 {
00894    ast_clear_flag(chan, AST_FLAG_MOH);
00895    ast_deactivate_generator(chan);
00896 
00897    if (chan->music_state) {
00898       if (chan->stream) {
00899          ast_closestream(chan->stream);
00900          chan->stream = NULL;
00901       }
00902    }
00903 }
00904 
00905 static struct mohclass *moh_class_malloc(void)
00906 {
00907    struct mohclass *class;
00908 
00909    class = malloc(sizeof(struct mohclass));
00910 
00911    if (!class)
00912       return NULL;
00913 
00914    memset(class, 0, sizeof(struct mohclass));
00915 
00916    class->format = AST_FORMAT_SLINEAR;
00917 
00918    return class;
00919 }
00920 
00921 static int load_moh_classes(int reload)
00922 {
00923    struct ast_config *cfg;
00924    struct ast_variable *var;
00925    struct mohclass *class; 
00926    char *data;
00927    char *args;
00928    char *cat;
00929    int numclasses = 0;
00930    static int dep_warning = 0;
00931 
00932    cfg = ast_config_load("musiconhold.conf");
00933 
00934    if (!cfg)
00935       return 0;
00936 
00937    cat = ast_category_browse(cfg, NULL);
00938    for (; cat; cat = ast_category_browse(cfg, cat)) {
00939       if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files")) {
00940          class = moh_class_malloc();
00941          if (!class) {
00942             ast_log(LOG_WARNING, "Out of memory!\n");
00943             break;
00944          }           
00945          ast_copy_string(class->name, cat, sizeof(class->name));  
00946          var = ast_variable_browse(cfg, cat);
00947          while (var) {
00948             if (!strcasecmp(var->name, "mode"))
00949                ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
00950             else if (!strcasecmp(var->name, "directory"))
00951                ast_copy_string(class->dir, var->value, sizeof(class->dir));
00952             else if (!strcasecmp(var->name, "application"))
00953                ast_copy_string(class->args, var->value, sizeof(class->args));
00954             else if (!strcasecmp(var->name, "random"))
00955                ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
00956             else if (!strcasecmp(var->name, "format")) {
00957                class->format = ast_getformatbyname(var->value);
00958                if (!class->format) {
00959                   ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
00960                   class->format = AST_FORMAT_SLINEAR;
00961                }
00962             }
00963                var = var->next;
00964          }
00965 
00966          if (ast_strlen_zero(class->dir)) {
00967             if (!strcasecmp(class->mode, "custom")) {
00968                strcpy(class->dir, "nodir");
00969             } else {
00970                ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
00971                free(class);
00972                continue;
00973             }
00974          }
00975          if (ast_strlen_zero(class->mode)) {
00976             ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
00977             free(class);
00978             continue;
00979          }
00980          if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
00981             ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
00982             free(class);
00983             continue;
00984          }
00985 
00986          /* Don't leak a class when it's already registered */
00987          moh_register(class, reload);
00988 
00989          numclasses++;
00990       }
00991    }
00992    
00993 
00994    /* Deprecated Old-School Configuration */
00995    var = ast_variable_browse(cfg, "classes");
00996    while (var) {
00997       if (!dep_warning) {
00998          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");
00999          dep_warning = 1;
01000       }
01001       data = strchr(var->value, ':');
01002       if (data) {
01003          *data++ = '\0';
01004          args = strchr(data, ',');
01005          if (args)
01006             *args++ = '\0';
01007          if (!(get_mohbyname(var->name))) {
01008             class = moh_class_malloc();
01009             if (!class) {
01010                ast_log(LOG_WARNING, "Out of memory!\n");
01011                return numclasses;
01012             }
01013             
01014             ast_copy_string(class->name, var->name, sizeof(class->name));
01015             ast_copy_string(class->dir, data, sizeof(class->dir));
01016             ast_copy_string(class->mode, var->value, sizeof(class->mode));
01017             if (args)
01018                ast_copy_string(class->args, args, sizeof(class->args));
01019             
01020             moh_register(class, reload);
01021             numclasses++;
01022          }
01023       }
01024       var = var->next;
01025    }
01026    var = ast_variable_browse(cfg, "moh_files");
01027    while (var) {
01028       if (!dep_warning) {
01029          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");
01030          dep_warning = 1;
01031       }
01032       if (!(get_mohbyname(var->name))) {
01033          args = strchr(var->value, ',');
01034          if (args)
01035             *args++ = '\0';
01036          class = moh_class_malloc();
01037          if (!class) {
01038             ast_log(LOG_WARNING, "Out of memory!\n");
01039             return numclasses;
01040          }
01041          
01042          ast_copy_string(class->name, var->name, sizeof(class->name));
01043          ast_copy_string(class->dir, var->value, sizeof(class->dir));
01044          strcpy(class->mode, "files");
01045          if (args)   
01046             ast_copy_string(class->args, args, sizeof(class->args));
01047          
01048          moh_register(class, reload);
01049          numclasses++;
01050       }
01051       var = var->next;
01052    }
01053 
01054    ast_config_destroy(cfg);
01055 
01056    return numclasses;
01057 }
01058 
01059 static void ast_moh_destroy(void)
01060 {
01061    struct mohclass *moh, *tmp;
01062    char buff[8192];
01063    int bytes, tbytes=0, stime = 0, pid = 0;
01064 
01065    if (option_verbose > 1)
01066       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01067    ast_mutex_lock(&moh_lock);
01068    moh = mohclasses;
01069 
01070    while (moh) {
01071       if (moh->pid) {
01072          ast_log(LOG_DEBUG, "killing %d!\n", moh->pid);
01073          stime = time(NULL) + 2;
01074          pid = moh->pid;
01075          moh->pid = 0;
01076          kill(pid, SIGKILL);
01077          while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime) {
01078             tbytes = tbytes + bytes;
01079          }
01080          ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01081          close(moh->srcfd);
01082       }
01083       tmp = moh;
01084       moh = moh->next;
01085       ast_moh_free_class(&tmp);
01086    }
01087    mohclasses = NULL;
01088    ast_mutex_unlock(&moh_lock);
01089 }
01090 
01091 static void moh_on_off(int on)
01092 {
01093    struct ast_channel *chan = NULL;
01094 
01095    while ( (chan = ast_channel_walk_locked(chan)) != NULL) {
01096       if (ast_test_flag(chan, AST_FLAG_MOH)) {
01097          if (on)
01098             local_ast_moh_start(chan, NULL);
01099          else
01100             ast_deactivate_generator(chan);
01101       }
01102       ast_mutex_unlock(&chan->lock);
01103    }
01104 }
01105 
01106 static int moh_cli(int fd, int argc, char *argv[]) 
01107 {
01108    int x;
01109 
01110    moh_on_off(0);
01111    ast_moh_destroy();
01112    x = load_moh_classes(1);
01113    moh_on_off(1);
01114    ast_cli(fd, "\n%d class%s reloaded.\n", x, x == 1 ? "" : "es");
01115    return 0;
01116 }
01117 
01118 static int cli_files_show(int fd, int argc, char *argv[])
01119 {
01120    int i;
01121    struct mohclass *class;
01122 
01123    ast_mutex_lock(&moh_lock);
01124    for (class = mohclasses; class; class = class->next) {
01125       if (!class->total_files)
01126          continue;
01127 
01128       ast_cli(fd, "Class: %s\n", class->name);
01129       for (i = 0; i < class->total_files; i++)
01130          ast_cli(fd, "\tFile: %s\n", class->filearray[i]);
01131    }
01132    ast_mutex_unlock(&moh_lock);
01133 
01134    return 0;
01135 }
01136 
01137 static int moh_classes_show(int fd, int argc, char *argv[])
01138 {
01139    struct mohclass *class;
01140 
01141    ast_mutex_lock(&moh_lock);
01142    for (class = mohclasses; class; class = class->next) {
01143       ast_cli(fd, "Class: %s\n", class->name);
01144       ast_cli(fd, "\tMode: %s\n", ast_strlen_zero(class->mode) ? "<none>" : class->mode);
01145       ast_cli(fd, "\tDirectory: %s\n", ast_strlen_zero(class->dir) ? "<none>" : class->dir);
01146       if (ast_test_flag(class, MOH_CUSTOM))
01147          ast_cli(fd, "\tApplication: %s\n", ast_strlen_zero(class->args) ? "<none>" : class->args);
01148       ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01149    }
01150    ast_mutex_unlock(&moh_lock);
01151 
01152    return 0;
01153 }
01154 
01155 static struct ast_cli_entry  cli_moh = { { "moh", "reload"}, moh_cli, "Music On Hold", "Music On Hold", NULL};
01156 
01157 static struct ast_cli_entry  cli_moh_classes_show = { { "moh", "classes", "show"}, moh_classes_show, "List MOH classes", "Lists all MOH classes", NULL};
01158 
01159 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};
01160 
01161 static int init_classes(int reload) 
01162 {
01163    struct mohclass *moh;
01164     
01165    if (!load_moh_classes(reload))      /* Load classes from config */
01166       return 0;         /* Return if nothing is found */
01167    moh = mohclasses;
01168    while (moh) {
01169       if (moh->total_files)
01170          moh_scan_files(moh);
01171       moh = moh->next;
01172    }
01173    return 1;
01174 }
01175 
01176 int load_module(void)
01177 {
01178    int res;
01179 
01180    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01181    ast_register_atexit(ast_moh_destroy);
01182    ast_cli_register(&cli_moh);
01183    ast_cli_register(&cli_moh_files_show);
01184    ast_cli_register(&cli_moh_classes_show);
01185    if (!res)
01186       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01187    if (!res)
01188       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01189    if (!res)
01190       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01191    if (!res)
01192       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01193 
01194    if (!init_classes(0)) {    /* No music classes configured, so skip it */
01195       ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.");
01196    } else {
01197       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01198    }
01199 
01200    return 0;
01201 }
01202 
01203 int reload(void)
01204 {
01205    if (init_classes(1))
01206       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
01207 
01208    return 0;
01209 }
01210 
01211 int unload_module(void)
01212 {
01213    return -1;
01214 }
01215 
01216 char *description(void)
01217 {
01218    return "Music On Hold Resource";
01219 }
01220 
01221 int usecount(void)
01222 {
01223    /* Never allow Music On Hold to be unloaded
01224       unresolve needed symbols in the dialer */
01225 #if 0
01226    int res;
01227    STANDARD_USECOUNT(res);
01228    return res;
01229 #else
01230    return 1;
01231 #endif
01232 }
01233 
01234 char *key()
01235 {
01236    return ASTERISK_GPL_KEY;
01237 }

Generated on Mon Mar 20 08:20:13 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1