Wed Feb 11 12:00:31 2009

Asterisk developer's documentation


res_musiconhold.c File Reference

Routines implementing music on hold. More...

#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
#include "asterisk/astobj2.h"
#include "asterisk/dahdi_compat.h"

Go to the source code of this file.

Data Structures

struct  moh_files_state
struct  mohclass
struct  mohdata

Defines

#define INITIAL_NUM_FILES   8
#define LOCAL_MPG_123   "/usr/local/bin/mpg123"
#define MAX_MP3S   256
#define MOH_CUSTOM   (1 << 2)
#define MOH_MS_INTERVAL   100
#define MOH_QUIET   (1 << 0)
#define MOH_RANDOMIZE   (1 << 3)
#define MOH_SINGLE   (1 << 1)
#define mohclass_ref(class)   (ao2_ref((class), +1), class)
#define mohclass_unref(class)   (ao2_ref((class), -1), (struct mohclass *) NULL)
#define MPG_123   "/usr/bin/mpg123"

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void ast_moh_destroy (void)
static int ast_moh_files_next (struct ast_channel *chan)
static int cli_files_show (int fd, int argc, char *argv[])
static struct mohclassget_mohbyname (const char *name, int warn)
static int init_app_class (struct mohclass *class)
static int init_files_class (struct mohclass *class)
static int load_module (void)
static int load_moh_classes (int reload)
static void local_ast_moh_cleanup (struct ast_channel *chan)
static int local_ast_moh_start (struct ast_channel *chan, const char *mclass, const char *interpclass)
static void local_ast_moh_stop (struct ast_channel *chan)
static int moh0_exec (struct ast_channel *chan, void *data)
static int moh1_exec (struct ast_channel *chan, void *data)
static int moh2_exec (struct ast_channel *chan, void *data)
static int moh3_exec (struct ast_channel *chan, void *data)
static int moh4_exec (struct ast_channel *chan, void *data)
static int moh_add_file (struct mohclass *class, const char *filepath)
static void * moh_alloc (struct ast_channel *chan, void *params)
static int moh_class_cmp (void *obj, void *arg, int flags)
static void moh_class_destructor (void *obj)
static int moh_class_hash (const void *obj, const int flags)
static int moh_class_inuse (void *obj, void *arg, int flags)
static struct mohclassmoh_class_malloc (void)
static int moh_class_mark (void *obj, void *arg, int flags)
static int moh_classes_delete_marked (void *obj, void *arg, int flags)
static int moh_classes_show (int fd, int argc, char *argv[])
static int moh_cli (int fd, int argc, char *argv[])
static void * moh_files_alloc (struct ast_channel *chan, void *params)
static int moh_files_generator (struct ast_channel *chan, void *data, int len, int samples)
static struct ast_framemoh_files_readframe (struct ast_channel *chan)
static void moh_files_release (struct ast_channel *chan, void *data)
static int moh_generate (struct ast_channel *chan, void *data, int len, int samples)
static int moh_register (struct mohclass *moh, int reload)
static void moh_release (struct ast_channel *chan, void *data)
static int moh_scan_files (struct mohclass *class)
static struct mohdatamohalloc (struct mohclass *cl)
static void * monmp3thread (void *data)
static int reload (void)
static int spawn_mp3 (struct mohclass *class)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_BUILDSUM, .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app0 = "MusicOnHold"
static char * app1 = "WaitMusicOnHold"
static char * app2 = "SetMusicOnHold"
static char * app3 = "StartMusicOnHold"
static char * app4 = "StopMusicOnHold"
static const struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_moh []
static struct ast_cli_entry cli_moh_classes_show_deprecated
static struct ast_cli_entry cli_moh_files_show_deprecated
static char * descrip0
static char * descrip1
static char * descrip2
static char * descrip3
static char * descrip4
static struct ast_generator moh_file_stream
static struct ao2_containermohclasses
static struct ast_generator mohgen
static int respawn_time = 20
static char * synopsis0 = "Play Music On Hold indefinitely"
static char * synopsis1 = "Wait, playing Music On Hold"
static char * synopsis2 = "Set default Music On Hold class"
static char * synopsis3 = "Play Music On Hold"
static char * synopsis4 = "Stop Playing Music On Hold"


Detailed Description

Routines implementing music on hold.

Author:
Mark Spencer <markster@digium.com>

Definition in file res_musiconhold.c.


Define Documentation

#define INITIAL_NUM_FILES   8

Definition at line 74 of file res_musiconhold.c.

Referenced by moh_add_file().

#define LOCAL_MPG_123   "/usr/local/bin/mpg123"

Definition at line 167 of file res_musiconhold.c.

#define MAX_MP3S   256

Definition at line 169 of file res_musiconhold.c.

Referenced by spawn_mp3().

#define MOH_CUSTOM   (1 << 2)

Definition at line 127 of file res_musiconhold.c.

Referenced by init_app_class(), moh_classes_show(), and spawn_mp3().

#define MOH_MS_INTERVAL   100

Referenced by monmp3thread().

#define MOH_QUIET   (1 << 0)

Definition at line 125 of file res_musiconhold.c.

Referenced by init_app_class(), and spawn_mp3().

#define MOH_RANDOMIZE   (1 << 3)

Definition at line 128 of file res_musiconhold.c.

Referenced by ast_moh_files_next(), init_files_class(), and moh_files_alloc().

#define MOH_SINGLE   (1 << 1)

Definition at line 126 of file res_musiconhold.c.

Referenced by init_app_class(), and spawn_mp3().

#define mohclass_ref ( class   )     (ao2_ref((class), +1), class)

Definition at line 173 of file res_musiconhold.c.

Referenced by moh_files_alloc(), and mohalloc().

#define mohclass_unref ( class   )     (ao2_ref((class), -1), (struct mohclass *) NULL)

Definition at line 174 of file res_musiconhold.c.

Referenced by cli_files_show(), local_ast_moh_start(), moh_classes_show(), moh_files_alloc(), moh_files_release(), moh_register(), moh_release(), and unload_module().

#define MPG_123   "/usr/bin/mpg123"

Definition at line 168 of file res_musiconhold.c.


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1480 of file res_musiconhold.c.

static void __unreg_module ( void   )  [static]

Definition at line 1480 of file res_musiconhold.c.

static void ast_moh_destroy ( void   )  [static]

Definition at line 1297 of file res_musiconhold.c.

References ao2_callback(), ast_verbose(), option_verbose, and VERBOSE_PREFIX_2.

Referenced by load_module(), and unload_module().

01298 {
01299    if (option_verbose > 1) {
01300       ast_verbose(VERBOSE_PREFIX_2 "Destroying musiconhold processes\n");
01301    }
01302 
01303    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01304 }

static int ast_moh_files_next ( struct ast_channel chan  )  [static]

Definition at line 205 of file res_musiconhold.c.

References ast_closestream(), ast_fileexists(), ast_log(), ast_openstream_full(), ast_random(), ast_test_flag, moh_files_state::class, errno, mohclass::filearray, ast_channel::language, LOG_WARNING, MOH_RANDOMIZE, ast_channel::music_state, mohclass::name, moh_files_state::pos, moh_files_state::samples, moh_files_state::save_pos, moh_files_state::save_pos_filename, ast_channel::stream, and mohclass::total_files.

Referenced by moh_files_readframe().

00206 {
00207    struct moh_files_state *state = chan->music_state;
00208    int tries;
00209 
00210    /* Discontinue a stream if it is running already */
00211    if (chan->stream) {
00212       ast_closestream(chan->stream);
00213       chan->stream = NULL;
00214    }
00215 
00216    if (!state->class->total_files) {
00217       ast_log(LOG_WARNING, "No files available for class '%s'\n", state->class->name);
00218       return -1;
00219    }
00220 
00221    /* If a specific file has been saved confirm it still exists and that it is still valid */
00222    if (state->save_pos >= 0 && state->save_pos < state->class->total_files && state->class->filearray[state->save_pos] == state->save_pos_filename) {
00223       state->pos = state->save_pos;
00224       state->save_pos = -1;
00225    } else if (ast_test_flag(state->class, MOH_RANDOMIZE)) {
00226       /* Get a random file and ensure we can open it */
00227       for (tries = 0; tries < 20; tries++) {
00228          state->pos = ast_random() % state->class->total_files;
00229          if (ast_fileexists(state->class->filearray[state->pos], NULL, NULL) > 0)
00230             break;
00231       }
00232       state->save_pos = -1;
00233       state->samples = 0;
00234    } else {
00235       /* This is easy, just increment our position and make sure we don't exceed the total file count */
00236       state->pos++;
00237       state->pos %= state->class->total_files;
00238       state->save_pos = -1;
00239       state->samples = 0;
00240    }
00241 
00242    if (!ast_openstream_full(chan, state->class->filearray[state->pos], chan->language, 1)) {
00243       ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", state->class->filearray[state->pos], strerror(errno));
00244       state->pos++;
00245       state->pos %= state->class->total_files;
00246       return -1;
00247    }
00248 
00249    /* Record the pointer to the filename for position resuming later */
00250    state->save_pos_filename = state->class->filearray[state->pos];
00251 
00252    if (option_debug)
00253       ast_log(LOG_DEBUG, "%s Opened file %d '%s'\n", chan->name, state->pos, state->class->filearray[state->pos]);
00254 
00255    if (state->samples)
00256       ast_seekstream(chan->stream, state->samples, SEEK_SET);
00257 
00258    return 0;
00259 }

static int cli_files_show ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1312 of file res_musiconhold.c.

References ao2_iterator_init(), ao2_iterator_next(), ast_cli(), and mohclass_unref.

01313 {
01314    struct mohclass *class;
01315    struct ao2_iterator i;
01316 
01317    i = ao2_iterator_init(mohclasses, 0);
01318 
01319    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01320       int x;
01321 
01322       if (!class->total_files) {
01323          continue;
01324       }
01325 
01326       ast_cli(fd, "Class: %s\n", class->name);
01327 
01328       for (x = 0; x < class->total_files; x++) {
01329          ast_cli(fd, "\tFile: %s\n", class->filearray[x]);
01330       }
01331    }
01332 
01333    return 0;
01334 }

static struct mohclass* get_mohbyname ( const char *  name,
int  warn 
) [static]

Definition at line 640 of file res_musiconhold.c.

References ao2_find(), ast_copy_string(), ast_log(), mohclass::flags, LOG_WARNING, moh, and mohclass::name.

Referenced by local_ast_moh_start(), and moh_register().

00641 {
00642    struct mohclass *moh = NULL;
00643    struct mohclass tmp_class = {
00644       .flags = 0,
00645    };
00646 
00647    ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
00648 
00649    moh = ao2_find(mohclasses, &tmp_class, 0);
00650 
00651    if (!moh && warn) {
00652       ast_log(LOG_WARNING, "Music on Hold class '%s' not found\n", name);
00653    }
00654 
00655    return moh;
00656 }

static int init_app_class ( struct mohclass class  )  [static]

Definition at line 896 of file res_musiconhold.c.

References ast_log(), ast_pthread_create_background, ast_set_flag, DAHDI_FILE_PSEUDO, LOG_WARNING, mohclass::mode, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, monmp3thread(), mohclass::pseudofd, and mohclass::thread.

Referenced by moh_register().

00897 {
00898 #ifdef HAVE_DAHDI
00899    int x;
00900 #endif
00901 
00902    if (!strcasecmp(class->mode, "custom")) {
00903       ast_set_flag(class, MOH_CUSTOM);
00904    } else if (!strcasecmp(class->mode, "mp3nb")) {
00905       ast_set_flag(class, MOH_SINGLE);
00906    } else if (!strcasecmp(class->mode, "quietmp3nb")) {
00907       ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
00908    } else if (!strcasecmp(class->mode, "quietmp3")) {
00909       ast_set_flag(class, MOH_QUIET);
00910    }
00911       
00912    class->srcfd = -1;
00913    class->pseudofd = -1;
00914 
00915 #ifdef HAVE_DAHDI
00916    /* Open /dev/zap/pseudo for timing...  Is
00917       there a better, yet reliable way to do this? */
00918    class->pseudofd = open(DAHDI_FILE_PSEUDO, O_RDONLY);
00919    if (class->pseudofd < 0) {
00920       ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
00921    } else {
00922       x = 320;
00923       ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
00924    }
00925 #endif
00926 
00927    if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
00928       ast_log(LOG_WARNING, "Unable to create moh thread...\n");
00929       if (class->pseudofd > -1) {
00930          close(class->pseudofd);
00931          class->pseudofd = -1;
00932       }
00933       return -1;
00934    }
00935 
00936    return 0;
00937 }

static int init_files_class ( struct mohclass class  )  [static]

Definition at line 871 of file res_musiconhold.c.

References mohclass::args, ast_set_flag, ast_verbose(), mohclass::dir, MOH_RANDOMIZE, moh_scan_files(), mohclass::name, option_verbose, and VERBOSE_PREFIX_3.

Referenced by moh_register().

00872 {
00873    int res;
00874 
00875    res = moh_scan_files(class);
00876 
00877    if (res < 0) {
00878       return -1;
00879    }
00880 
00881    if (!res) {
00882       if (option_verbose > 2) {
00883          ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
00884                class->dir, class->name);
00885       }
00886       return -1;
00887    }
00888 
00889    if (strchr(class->args, 'r')) {
00890       ast_set_flag(class, MOH_RANDOMIZE);
00891    }
00892 
00893    return 0;
00894 }

static int load_module ( void   )  [static]

Definition at line 1396 of file res_musiconhold.c.

References ao2_container_alloc(), ARRAY_LEN, ast_cli_register_multiple(), ast_install_music_functions(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_moh_destroy(), ast_register_application(), ast_register_atexit(), cli_moh, load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), local_ast_moh_stop(), LOG_WARNING, moh0_exec(), moh1_exec(), moh2_exec(), moh3_exec(), moh4_exec(), moh_class_cmp(), and moh_class_hash().

01397 {
01398    int res;
01399 
01400    if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
01401       return AST_MODULE_LOAD_DECLINE;
01402    }
01403 
01404    if (!load_moh_classes(0)) {   /* No music classes configured, so skip it */
01405       ast_log(LOG_WARNING, "No music on hold classes configured, "
01406             "disabling music on hold.\n");
01407    } else {
01408       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01409             local_ast_moh_cleanup);
01410    }
01411 
01412    res = ast_register_application(app0, moh0_exec, synopsis0, descrip0);
01413    ast_register_atexit(ast_moh_destroy);
01414    ast_cli_register_multiple(cli_moh, ARRAY_LEN(cli_moh));
01415    if (!res)
01416       res = ast_register_application(app1, moh1_exec, synopsis1, descrip1);
01417    if (!res)
01418       res = ast_register_application(app2, moh2_exec, synopsis2, descrip2);
01419    if (!res)
01420       res = ast_register_application(app3, moh3_exec, synopsis3, descrip3);
01421    if (!res)
01422       res = ast_register_application(app4, moh4_exec, synopsis4, descrip4);
01423 
01424    return AST_MODULE_LOAD_SUCCESS;
01425 }

static int load_moh_classes ( int  reload  )  [static]

Definition at line 1135 of file res_musiconhold.c.

References ao2_callback(), mohclass::args, ast_category_browse(), ast_config_load(), ast_copy_string(), ast_variable_browse(), moh_class_malloc(), moh_class_mark(), and var.

Referenced by load_module(), and reload().

01136 {
01137    struct ast_config *cfg;
01138    struct ast_variable *var;
01139    struct mohclass *class; 
01140    char *data;
01141    char *args;
01142    char *cat;
01143    int numclasses = 0;
01144    static int dep_warning = 0;
01145 
01146    cfg = ast_config_load("musiconhold.conf");
01147 
01148    if (!cfg) {
01149       return 0;
01150    }
01151 
01152    if (reload) {
01153       ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
01154    }
01155 
01156    cat = ast_category_browse(cfg, NULL);
01157    for (; cat; cat = ast_category_browse(cfg, cat)) {
01158       if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files")) {
01159          continue;
01160       }
01161 
01162       if (!(class = moh_class_malloc())) {
01163          break;
01164       }
01165 
01166       ast_copy_string(class->name, cat, sizeof(class->name));  
01167 
01168       for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
01169          if (!strcasecmp(var->name, "mode")) {
01170             ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
01171          } else if (!strcasecmp(var->name, "directory")) {
01172             ast_copy_string(class->dir, var->value, sizeof(class->dir));
01173          } else if (!strcasecmp(var->name, "application")) {
01174             ast_copy_string(class->args, var->value, sizeof(class->args));
01175          } else if (!strcasecmp(var->name, "random")) {
01176             ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
01177          } else if (!strcasecmp(var->name, "format")) {
01178             class->format = ast_getformatbyname(var->value);
01179             if (!class->format) {
01180                ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
01181                class->format = AST_FORMAT_SLINEAR;
01182             }
01183          }
01184       }
01185 
01186       if (ast_strlen_zero(class->dir)) {
01187          if (!strcasecmp(class->mode, "custom")) {
01188             ast_copy_string(class->dir, "nodir", sizeof(class->dir));
01189          } else {
01190             ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
01191             class = mohclass_unref(class);
01192             continue;
01193          }
01194       }
01195 
01196       if (ast_strlen_zero(class->mode)) {
01197          ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
01198          class = mohclass_unref(class);
01199          continue;
01200       }
01201 
01202       if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
01203          ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
01204          class = mohclass_unref(class);
01205          continue;
01206       }
01207 
01208       /* Don't leak a class when it's already registered */
01209       moh_register(class, reload);
01210 
01211       numclasses++;
01212    }
01213    
01214 
01215    /* Deprecated Old-School Configuration */
01216    for (var = ast_variable_browse(cfg, "classes"); var; var = var->next) {
01217       struct mohclass *tmp_class;
01218 
01219       if (!dep_warning) {
01220          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");
01221          dep_warning = 1;
01222       }
01223 
01224       if (!(data = strchr(var->value, ':'))) {
01225          continue;
01226       }
01227       *data++ = '\0';
01228 
01229       if ((args = strchr(data, ','))) {
01230          *args++ = '\0';
01231       }
01232 
01233       if ((tmp_class = get_mohbyname(var->name, 0))) {
01234          tmp_class = mohclass_unref(tmp_class);
01235          continue;
01236       }
01237 
01238       if (!(class = moh_class_malloc())) {
01239          break;
01240       }
01241             
01242       ast_copy_string(class->name, var->name, sizeof(class->name));
01243       ast_copy_string(class->dir, data, sizeof(class->dir));
01244       ast_copy_string(class->mode, var->value, sizeof(class->mode));
01245       if (args) {
01246          ast_copy_string(class->args, args, sizeof(class->args));
01247       }
01248             
01249       moh_register(class, reload);
01250       class = NULL;
01251 
01252       numclasses++;
01253    }
01254 
01255    for (var = ast_variable_browse(cfg, "moh_files"); var; var = var->next) {
01256       struct mohclass *tmp_class;
01257 
01258       if (!dep_warning) {
01259          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");
01260          dep_warning = 1;
01261       }
01262 
01263       if ((tmp_class = get_mohbyname(var->name, 0))) {
01264          tmp_class = mohclass_unref(tmp_class);
01265          continue;
01266       }
01267 
01268       if ((args = strchr(var->value, ','))) {
01269          *args++ = '\0';
01270       }
01271 
01272       if (!(class = moh_class_malloc())) {
01273          break;
01274       }
01275          
01276       ast_copy_string(class->name, var->name, sizeof(class->name));
01277       ast_copy_string(class->dir, var->value, sizeof(class->dir));
01278       ast_copy_string(class->mode, "files", sizeof(class->mode));
01279       if (args) {
01280          ast_copy_string(class->args, args, sizeof(class->args));
01281       }
01282          
01283       moh_register(class, reload);
01284       class = NULL;
01285 
01286       numclasses++;
01287    }
01288 
01289    ast_config_destroy(cfg);
01290 
01291    ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
01292          moh_classes_delete_marked, NULL);
01293 
01294    return numclasses;
01295 }

static void local_ast_moh_cleanup ( struct ast_channel chan  )  [static]

Definition at line 984 of file res_musiconhold.c.

References free, and ast_channel::music_state.

Referenced by load_module(), and reload().

00985 {
00986    if (chan->music_state) {
00987       free(chan->music_state);
00988       chan->music_state = NULL;
00989    }
00990 }

static int local_ast_moh_start ( struct ast_channel chan,
const char *  mclass,
const char *  interpclass 
) [static]

Definition at line 992 of file res_musiconhold.c.

References ast_activate_generator(), AST_FLAG_MOH, ast_set_flag, ast_strlen_zero(), get_mohbyname(), moh_file_stream, mohclass_unref, mohgen, ast_channel::musicclass, and mohclass::total_files.

Referenced by load_module(), and reload().

00993 {
00994    struct mohclass *mohclass = NULL;
00995    int res;
00996 
00997    /* The following is the order of preference for which class to use:
00998     * 1) The channels explicitly set musicclass, which should *only* be
00999     *    set by a call to Set(CHANNEL(musicclass)=whatever) in the dialplan.
01000     * 2) The mclass argument. If a channel is calling ast_moh_start() as the
01001     *    result of receiving a HOLD control frame, this should be the
01002     *    payload that came with the frame.
01003     * 3) The interpclass argument. This would be from the mohinterpret
01004     *    option from channel drivers. This is the same as the old musicclass
01005     *    option.
01006     * 4) The default class.
01007     */
01008    if (!ast_strlen_zero(chan->musicclass)) {
01009       mohclass = get_mohbyname(chan->musicclass, 1);
01010    }
01011    if (!mohclass && !ast_strlen_zero(mclass)) {
01012       mohclass = get_mohbyname(mclass, 1);
01013    }
01014    if (!mohclass && !ast_strlen_zero(interpclass)) {
01015       mohclass = get_mohbyname(interpclass, 1);
01016    }
01017    if (!mohclass) {
01018       mohclass = get_mohbyname("default", 1);
01019    }
01020 
01021    if (!mohclass) {
01022       return -1;
01023    }
01024 
01025    ast_set_flag(chan, AST_FLAG_MOH);
01026 
01027    if (mohclass->total_files) {
01028       res = ast_activate_generator(chan, &moh_file_stream, mohclass);
01029    } else {
01030       res = ast_activate_generator(chan, &mohgen, mohclass);
01031    }
01032 
01033    mohclass = mohclass_unref(mohclass);
01034 
01035    return res;
01036 }

static void local_ast_moh_stop ( struct ast_channel chan  )  [static]

Definition at line 1038 of file res_musiconhold.c.

References ast_clear_flag, ast_closestream(), ast_deactivate_generator(), AST_FLAG_MOH, ast_channel::music_state, and ast_channel::stream.

Referenced by load_module(), and reload().

01039 {
01040    ast_clear_flag(chan, AST_FLAG_MOH);
01041    ast_deactivate_generator(chan);
01042 
01043    if (chan->music_state) {
01044       if (chan->stream) {
01045          ast_closestream(chan->stream);
01046          chan->stream = NULL;
01047       }
01048    }
01049 }

static int moh0_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 585 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), LOG_WARNING, and ast_channel::name.

Referenced by load_module().

00586 {
00587    if (ast_moh_start(chan, data, NULL)) {
00588       ast_log(LOG_WARNING, "Unable to start music on hold (class '%s') on channel %s\n", (char *)data, chan->name);
00589       return 0;
00590    }
00591    while (!ast_safe_sleep(chan, 10000));
00592    ast_moh_stop(chan);
00593    return -1;
00594 }

static int moh1_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 596 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), ast_moh_stop(), ast_safe_sleep(), LOG_WARNING, and ast_channel::name.

Referenced by load_module().

00597 {
00598    int res;
00599    if (!data || !atoi(data)) {
00600       ast_log(LOG_WARNING, "WaitMusicOnHold requires an argument (number of seconds to wait)\n");
00601       return -1;
00602    }
00603    if (ast_moh_start(chan, NULL, NULL)) {
00604       ast_log(LOG_WARNING, "Unable to start music on hold for %d seconds on channel %s\n", atoi(data), chan->name);
00605       return 0;
00606    }
00607    res = ast_safe_sleep(chan, atoi(data) * 1000);
00608    ast_moh_stop(chan);
00609    return res;
00610 }

static int moh2_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 612 of file res_musiconhold.c.

References ast_log(), ast_string_field_set, ast_strlen_zero(), LOG_WARNING, and musicclass.

Referenced by load_module().

00613 {
00614    if (ast_strlen_zero(data)) {
00615       ast_log(LOG_WARNING, "SetMusicOnHold requires an argument (class)\n");
00616       return -1;
00617    }
00618    ast_string_field_set(chan, musicclass, data);
00619    return 0;
00620 }

static int moh3_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 622 of file res_musiconhold.c.

References ast_log(), ast_moh_start(), and LOG_NOTICE.

Referenced by load_module().

00623 {
00624    char *class = NULL;
00625    if (data && strlen(data))
00626       class = data;
00627    if (ast_moh_start(chan, class, NULL)) 
00628       ast_log(LOG_NOTICE, "Unable to start music on hold class '%s' on channel %s\n", class ? class : "default", chan->name);
00629 
00630    return 0;
00631 }

static int moh4_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 633 of file res_musiconhold.c.

References ast_moh_stop().

Referenced by load_module().

00634 {
00635    ast_moh_stop(chan);
00636 
00637    return 0;
00638 }

static int moh_add_file ( struct mohclass class,
const char *  filepath 
) [static]

Definition at line 773 of file res_musiconhold.c.

References mohclass::allowed_files, ast_calloc, ast_realloc, ast_strdup, mohclass::filearray, INITIAL_NUM_FILES, and mohclass::total_files.

00774 {
00775    if (!class->allowed_files) {
00776       if (!(class->filearray = ast_calloc(1, INITIAL_NUM_FILES * sizeof(*class->filearray))))
00777          return -1;
00778       class->allowed_files = INITIAL_NUM_FILES;
00779    } else if (class->total_files == class->allowed_files) {
00780       if (!(class->filearray = ast_realloc(class->filearray, class->allowed_files * sizeof(*class->filearray) * 2))) {
00781          class->allowed_files = 0;
00782          class->total_files = 0;
00783          return -1;
00784       }
00785       class->allowed_files *= 2;
00786    }
00787 
00788    if (!(class->filearray[class->total_files] = ast_strdup(filepath)))
00789       return -1;
00790 
00791    class->total_files++;
00792 
00793    return 0;
00794 }

static void* moh_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 721 of file res_musiconhold.c.

References ast_codec2str(), ast_log(), ast_set_write_format(), ast_verbose(), LOG_WARNING, moh_release(), mohalloc(), ast_channel::name, option_verbose, mohdata::origwfmt, VERBOSE_PREFIX_3, and ast_channel::writeformat.

00722 {
00723    struct mohdata *res;
00724    struct mohclass *class = params;
00725 
00726    if ((res = mohalloc(class))) {
00727       res->origwfmt = chan->writeformat;
00728       if (ast_set_write_format(chan, class->format)) {
00729          ast_log(LOG_WARNING, "Unable to set channel '%s' to format '%s'\n", chan->name, ast_codec2str(class->format));
00730          moh_release(NULL, res);
00731          res = NULL;
00732       }
00733       if (option_verbose > 2)
00734          ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on channel '%s'\n", class->name, chan->name);
00735    }
00736    return res;
00737 }

static int moh_class_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1389 of file res_musiconhold.c.

Referenced by load_module().

01390 {
01391    struct mohclass *class = obj, *class2 = arg;
01392 
01393    return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
01394 }

static void moh_class_destructor ( void *  obj  )  [static]

Definition at line 1051 of file res_musiconhold.c.

References AST_LIST_REMOVE_HEAD, ast_log(), AST_PTHREADT_NULL, ast_wait_for_input(), free, mohdata::list, LOG_DEBUG, and option_debug.

Referenced by moh_class_malloc().

01052 {
01053    struct mohclass *class = obj;
01054    struct mohdata *member;
01055 
01056    if (option_debug) {
01057       ast_log(LOG_DEBUG, "Destroying MOH class '%s'\n", class->name);
01058    }
01059 
01060    if (class->pid > 1) {
01061       char buff[8192];
01062       int bytes, tbytes = 0, stime = 0, pid = 0;
01063 
01064       ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
01065 
01066       stime = time(NULL) + 2;
01067       pid = class->pid;
01068       class->pid = 0;
01069 
01070       /* Back when this was just mpg123, SIGKILL was fine.  Now we need
01071        * to give the process a reason and time enough to kill off its
01072        * children. */
01073       killpg(pid, SIGHUP);
01074       usleep(100000);
01075       killpg(pid, SIGTERM);
01076       usleep(100000);
01077       killpg(pid, SIGKILL);
01078 
01079       while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
01080             (bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
01081          tbytes = tbytes + bytes;
01082       }
01083 
01084       ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
01085 
01086       close(class->srcfd);
01087    }
01088 
01089    while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
01090       free(member);
01091    }
01092    
01093    if (class->thread) {
01094       pthread_cancel(class->thread);
01095       class->thread = AST_PTHREADT_NULL;
01096    }
01097 
01098    if (class->filearray) {
01099       int i;
01100       for (i = 0; i < class->total_files; i++) {
01101          free(class->filearray[i]);
01102       }
01103       free(class->filearray);
01104       class->filearray = NULL;
01105    }
01106 }

static int moh_class_hash ( const void *  obj,
const int  flags 
) [static]

Definition at line 1382 of file res_musiconhold.c.

References ast_str_case_hash().

Referenced by load_module().

01383 {
01384    const struct mohclass *class = obj;
01385 
01386    return ast_str_case_hash(class->name);
01387 }

static int moh_class_inuse ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1437 of file res_musiconhold.c.

References AST_LIST_EMPTY.

Referenced by unload_module().

01438 {
01439    struct mohclass *class = obj;
01440 
01441    return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
01442 }

static struct mohclass* moh_class_malloc ( void   )  [static]

Definition at line 1108 of file res_musiconhold.c.

References ao2_alloc(), AST_FORMAT_SLINEAR, mohclass::format, and moh_class_destructor().

Referenced by load_moh_classes().

01109 {
01110    struct mohclass *class;
01111 
01112    if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
01113       class->format = AST_FORMAT_SLINEAR;
01114    }
01115 
01116    return class;
01117 }

static int moh_class_mark ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1119 of file res_musiconhold.c.

Referenced by load_moh_classes().

01120 {
01121    struct mohclass *class = obj;
01122 
01123    class->delete = 1;
01124 
01125    return 0;
01126 }

static int moh_classes_delete_marked ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1128 of file res_musiconhold.c.

01129 {
01130    struct mohclass *class = obj;
01131 
01132    return class->delete ? CMP_MATCH : 0;
01133 }

static int moh_classes_show ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1336 of file res_musiconhold.c.

References ao2_iterator_init(), ao2_iterator_next(), ast_cli(), ast_getformatname(), ast_test_flag, MOH_CUSTOM, mohclass_unref, and S_OR.

01337 {
01338    struct mohclass *class;
01339    struct ao2_iterator i;
01340 
01341    i = ao2_iterator_init(mohclasses, 0);
01342 
01343    for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
01344       ast_cli(fd, "Class: %s\n", class->name);
01345       ast_cli(fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
01346       ast_cli(fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
01347       if (ast_test_flag(class, MOH_CUSTOM)) {
01348          ast_cli(fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
01349       }
01350       if (strcasecmp(class->mode, "files")) {
01351          ast_cli(fd, "\tFormat: %s\n", ast_getformatname(class->format));
01352       }
01353    }
01354 
01355    return 0;
01356 }

static int moh_cli ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 1306 of file res_musiconhold.c.

References reload().

01307 {
01308    reload();
01309    return 0;
01310 }

static void* moh_files_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 299 of file res_musiconhold.c.

References ast_calloc, ast_random(), ast_test_flag, ast_verbose(), MOH_RANDOMIZE, mohclass_ref, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, VERBOSE_PREFIX_3, and ast_channel::writeformat.

00300 {
00301    struct moh_files_state *state;
00302    struct mohclass *class = params;
00303 
00304    if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
00305       chan->music_state = state;
00306       state->class = mohclass_ref(class);
00307       state->save_pos = -1;
00308    } else {
00309       state = chan->music_state;
00310    }
00311 
00312    if (!state) {
00313       return NULL;
00314    }
00315 
00316    if (state->class != class) {
00317       /* (re-)initialize */
00318       if (state->class) {
00319          state->class = mohclass_unref(state->class);
00320       }
00321       memset(state, 0, sizeof(*state));
00322       state->class = mohclass_ref(class);
00323       if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files) {
00324          state->pos = ast_random() % class->total_files;
00325       }
00326    }
00327 
00328    state->origwfmt = chan->writeformat;
00329 
00330    if (option_verbose > 2) {
00331       ast_verbose(VERBOSE_PREFIX_3 "Started music on hold, class '%s', on %s\n", 
00332             class->name, chan->name);
00333    }
00334    
00335    return chan->music_state;
00336 }

static int moh_files_generator ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 274 of file res_musiconhold.c.

References ast_frfree, ast_log(), ast_write(), errno, f, LOG_WARNING, moh_files_readframe(), ast_channel::music_state, ast_channel::name, moh_files_state::sample_queue, and moh_files_state::samples.

00275 {
00276    struct moh_files_state *state = chan->music_state;
00277    struct ast_frame *f = NULL;
00278    int res = 0;
00279 
00280    state->sample_queue += samples;
00281 
00282    while (state->sample_queue > 0) {
00283       if ((f = moh_files_readframe(chan))) {
00284          state->samples += f->samples;
00285          state->sample_queue -= f->samples;
00286          res = ast_write(chan, f);
00287          ast_frfree(f);
00288          if (res < 0) {
00289             ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00290             return -1;
00291          }
00292       } else
00293          return -1;  
00294    }
00295    return res;
00296 }

static struct ast_frame* moh_files_readframe ( struct ast_channel chan  )  [static]

Definition at line 262 of file res_musiconhold.c.

References ast_moh_files_next(), ast_readframe(), f, and ast_channel::stream.

Referenced by moh_files_generator().

00263 {
00264    struct ast_frame *f = NULL;
00265    
00266    if (!(chan->stream && (f = ast_readframe(chan->stream)))) {
00267       if (!ast_moh_files_next(chan))
00268          f = ast_readframe(chan->stream);
00269    }
00270 
00271    return f;
00272 }

static void moh_files_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 176 of file res_musiconhold.c.

References ast_closestream(), ast_log(), ast_set_write_format(), ast_verbose(), moh_files_state::class, LOG_WARNING, mohclass_unref, ast_channel::music_state, ast_channel::name, option_verbose, moh_files_state::origwfmt, moh_files_state::pos, moh_files_state::save_pos, ast_channel::stream, and VERBOSE_PREFIX_3.

00177 {
00178    struct moh_files_state *state;
00179 
00180    if (!chan || !chan->music_state) {
00181       return;
00182    }
00183 
00184    state = chan->music_state;
00185 
00186    if (chan->stream) {
00187       ast_closestream(chan->stream);
00188       chan->stream = NULL;
00189    }
00190    
00191    if (option_verbose > 2) {
00192       ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00193    }
00194 
00195    if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
00196       ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
00197    }
00198 
00199    state->save_pos = state->pos;
00200 
00201    state->class = mohclass_unref(state->class);
00202 }

static int moh_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 739 of file res_musiconhold.c.

References ast_codec_get_len(), ast_codec_get_samples(), AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, LOG_WARNING, moh, and ast_channel::name.

00740 {
00741    struct mohdata *moh = data;
00742    short buf[1280 + AST_FRIENDLY_OFFSET / 2];
00743    int res;
00744 
00745    len = ast_codec_get_len(moh->parent->format, samples);
00746 
00747    if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
00748       ast_log(LOG_WARNING, "Only doing %d of %d requested bytes on %s\n", (int)sizeof(buf), len, chan->name);
00749       len = sizeof(buf) - AST_FRIENDLY_OFFSET;
00750    }
00751    res = read(moh->pipe[0], buf + AST_FRIENDLY_OFFSET/2, len);
00752    if (res <= 0)
00753       return 0;
00754 
00755    moh->f.datalen = res;
00756    moh->f.data = buf + AST_FRIENDLY_OFFSET / 2;
00757    moh->f.samples = ast_codec_get_samples(&moh->f);
00758 
00759    if (ast_write(chan, &moh->f) < 0) {
00760       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00761       return -1;
00762    }
00763 
00764    return 0;
00765 }

static int moh_register ( struct mohclass moh,
int  reload 
) [static]

Note:
This function owns the reference it gets to moh

Definition at line 942 of file res_musiconhold.c.

References ast_log(), mohclass::delete, get_mohbyname(), init_app_class(), init_files_class(), LOG_WARNING, moh, and mohclass_unref.

00943 {
00944    struct mohclass *mohclass = NULL;
00945 
00946    if ((mohclass = get_mohbyname(moh->name, 0))) {
00947       if (!mohclass->delete) {
00948          ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
00949          mohclass = mohclass_unref(mohclass);
00950          moh = mohclass_unref(moh);
00951          return -1;
00952       }
00953       mohclass = mohclass_unref(mohclass);
00954    }
00955 
00956    time(&moh->start);
00957    moh->start -= respawn_time;
00958    
00959    if (!strcasecmp(moh->mode, "files")) {
00960       if (init_files_class(moh)) {
00961          moh = mohclass_unref(moh);
00962          return -1;
00963       }
00964    } else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
00965          !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") ||
00966          !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
00967       if (init_app_class(moh)) {
00968          moh = mohclass_unref(moh);
00969          return -1;
00970       }
00971    } else {
00972       ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
00973       moh = mohclass_unref(moh);
00974       return -1;
00975    }
00976 
00977    ao2_link(mohclasses, moh);
00978 
00979    moh = mohclass_unref(moh);
00980    
00981    return 0;
00982 }

static void moh_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 691 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_getformatname(), AST_LIST_REMOVE, ast_log(), ast_set_write_format(), ast_verbose(), free, LOG_WARNING, moh, mohclass_unref, ast_channel::name, option_verbose, and VERBOSE_PREFIX_3.

Referenced by moh_alloc().

00692 {
00693    struct mohdata *moh = data;
00694    struct mohclass *class = moh->parent;
00695    int oldwfmt;
00696 
00697    ao2_lock(class);
00698    AST_LIST_REMOVE(&moh->parent->members, moh, list); 
00699    ao2_unlock(class);
00700    
00701    close(moh->pipe[0]);
00702    close(moh->pipe[1]);
00703 
00704    oldwfmt = moh->origwfmt;
00705 
00706    moh->parent = class = mohclass_unref(class);
00707 
00708    free(moh);
00709 
00710    if (chan) {
00711       if (oldwfmt && ast_set_write_format(chan, oldwfmt))  {
00712          ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", 
00713                chan->name, ast_getformatname(oldwfmt));
00714       }
00715       if (option_verbose > 2) {
00716          ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
00717       }
00718    }
00719 }

static int moh_scan_files ( struct mohclass class  )  [static]

Definition at line 796 of file res_musiconhold.c.

References ast_log(), mohclass::dir, errno, ext, mohclass::filearray, free, LOG_WARNING, and mohclass::total_files.

Referenced by init_files_class().

00796                                                   {
00797 
00798    DIR *files_DIR;
00799    struct dirent *files_dirent;
00800    char path[PATH_MAX];
00801    char filepath[PATH_MAX];
00802    char *ext;
00803    struct stat statbuf;
00804    int dirnamelen;
00805    int i;
00806    
00807    files_DIR = opendir(class->dir);
00808    if (!files_DIR) {
00809       ast_log(LOG_WARNING, "Cannot open dir %s or dir does not exist\n", class->dir);
00810       return -1;
00811    }
00812 
00813    for (i = 0; i < class->total_files; i++)
00814       free(class->filearray[i]);
00815 
00816    class->total_files = 0;
00817    dirnamelen = strlen(class->dir) + 2;
00818    if (!getcwd(path, sizeof(path))) {
00819       ast_log(LOG_WARNING, "getcwd() failed: %s\n", strerror(errno));
00820       return -1;
00821    }
00822    if (chdir(class->dir) < 0) {
00823       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00824       return -1;
00825    }
00826    while ((files_dirent = readdir(files_DIR))) {
00827       /* The file name must be at least long enough to have the file type extension */
00828       if ((strlen(files_dirent->d_name) < 4))
00829          continue;
00830 
00831       /* Skip files that starts with a dot */
00832       if (files_dirent->d_name[0] == '.')
00833          continue;
00834 
00835       /* Skip files without extensions... they are not audio */
00836       if (!strchr(files_dirent->d_name, '.'))
00837          continue;
00838 
00839       snprintf(filepath, sizeof(filepath), "%s/%s", class->dir, files_dirent->d_name);
00840 
00841       if (stat(filepath, &statbuf))
00842          continue;
00843 
00844       if (!S_ISREG(statbuf.st_mode))
00845          continue;
00846 
00847       if ((ext = strrchr(filepath, '.'))) {
00848          *ext = '\0';
00849          ext++;
00850       }
00851 
00852       /* if the file is present in multiple formats, ensure we only put it into the list once */
00853       for (i = 0; i < class->total_files; i++)
00854          if (!strcmp(filepath, class->filearray[i]))
00855             break;
00856 
00857       if (i == class->total_files) {
00858          if (moh_add_file(class, filepath))
00859             break;
00860       }
00861    }
00862 
00863    closedir(files_DIR);
00864    if (chdir(path) < 0) {
00865       ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00866       return -1;
00867    }
00868    return class->total_files;
00869 }

static struct mohdata* mohalloc ( struct mohclass cl  )  [static]

Definition at line 658 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_calloc, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, AST_LIST_INSERT_HEAD, ast_log(), errno, mohclass::format, free, mohdata::list, LOG_WARNING, mohclass::members, moh, mohclass_ref, and mohdata::pipe.

Referenced by moh_alloc().

00659 {
00660    struct mohdata *moh;
00661    long flags; 
00662    
00663    if (!(moh = ast_calloc(1, sizeof(*moh))))
00664       return NULL;
00665    
00666    if (pipe(moh->pipe)) {
00667       ast_log(LOG_WARNING, "Failed to create pipe: %s\n", strerror(errno));
00668       free(moh);
00669       return NULL;
00670    }
00671 
00672    /* Make entirely non-blocking */
00673    flags = fcntl(moh->pipe[0], F_GETFL);
00674    fcntl(moh->pipe[0], F_SETFL, flags | O_NONBLOCK);
00675    flags = fcntl(moh->pipe[1], F_GETFL);
00676    fcntl(moh->pipe[1], F_SETFL, flags | O_NONBLOCK);
00677 
00678    moh->f.frametype = AST_FRAME_VOICE;
00679    moh->f.subclass = cl->format;
00680    moh->f.offset = AST_FRIENDLY_OFFSET;
00681 
00682    moh->parent = mohclass_ref(cl);
00683 
00684    ao2_lock(cl);
00685    AST_LIST_INSERT_HEAD(&cl->members, moh, list);
00686    ao2_unlock(cl);
00687    
00688    return moh;
00689 }

static void* monmp3thread ( void *  data  )  [static]

Definition at line 498 of file res_musiconhold.c.

References ao2_lock(), ao2_unlock(), ast_codec_get_len(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_log(), ast_samp2tv(), ast_tvadd(), ast_tvdiff_ms(), ast_tvnow(), ast_tvzero(), len(), LOG_NOTICE, LOG_WARNING, moh, MOH_MS_INTERVAL, option_debug, and spawn_mp3().

Referenced by init_app_class().

00499 {
00500 #define  MOH_MS_INTERVAL      100
00501 
00502    struct mohclass *class = data;
00503    struct mohdata *moh;
00504    char buf[8192];
00505    short sbuf[8192];
00506    int res, res2;
00507    int len;
00508    struct timeval tv, tv_tmp;
00509 
00510    tv.tv_sec = 0;
00511    tv.tv_usec = 0;
00512    for(;/* ever */;) {
00513       pthread_testcancel();
00514       /* Spawn mp3 player if it's not there */
00515       if (class->srcfd < 0) {
00516          if ((class->srcfd = spawn_mp3(class)) < 0) {
00517             ast_log(LOG_WARNING, "Unable to spawn mp3player\n");
00518             /* Try again later */
00519             sleep(500);
00520             pthread_testcancel();
00521          }
00522       }
00523       if (class->pseudofd > -1) {
00524 #ifdef SOLARIS
00525          thr_yield();
00526 #endif
00527          /* Pause some amount of time */
00528          res = read(class->pseudofd, buf, sizeof(buf));
00529          pthread_testcancel();
00530       } else {
00531          long delta;
00532          /* Reliable sleep */
00533          tv_tmp = ast_tvnow();
00534          if (ast_tvzero(tv))
00535             tv = tv_tmp;
00536          delta = ast_tvdiff_ms(tv_tmp, tv);
00537          if (delta < MOH_MS_INTERVAL) {   /* too early */
00538             tv = ast_tvadd(tv, ast_samp2tv(MOH_MS_INTERVAL, 1000));  /* next deadline */
00539             usleep(1000 * (MOH_MS_INTERVAL - delta));
00540             pthread_testcancel();
00541          } else {
00542             ast_log(LOG_NOTICE, "Request to schedule in the past?!?!\n");
00543             tv = tv_tmp;
00544          }
00545          res = 8 * MOH_MS_INTERVAL; /* 8 samples per millisecond */
00546       }
00547       if (AST_LIST_EMPTY(&class->members))
00548          continue;
00549       /* Read mp3 audio */
00550       len = ast_codec_get_len(class->format, res);
00551       
00552       if ((res2 = read(class->srcfd, sbuf, len)) != len) {
00553          if (!res2) {
00554             close(class->srcfd);
00555             class->srcfd = -1;
00556             pthread_testcancel();
00557             if (class->pid > 1) {
00558                killpg(class->pid, SIGHUP);
00559                usleep(100000);
00560                killpg(class->pid, SIGTERM);
00561                usleep(100000);
00562                killpg(class->pid, SIGKILL);
00563                class->pid = 0;
00564             }
00565          } else
00566             ast_log(LOG_DEBUG, "Read %d bytes of audio while expecting %d\n", res2, len);
00567          continue;
00568       }
00569 
00570       pthread_testcancel();
00571 
00572       ao2_lock(class);
00573       AST_LIST_TRAVERSE(&class->members, moh, list) {
00574          /* Write data */
00575          if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
00576             if (option_debug)
00577                ast_log(LOG_DEBUG, "Only wrote %d of %d bytes to pipe\n", res, res2);
00578          }
00579       }
00580       ao2_unlock(class);
00581    }
00582    return NULL;
00583 }

static int reload ( void   )  [static]

Definition at line 1427 of file res_musiconhold.c.

References ast_install_music_functions(), load_moh_classes(), local_ast_moh_cleanup(), local_ast_moh_start(), and local_ast_moh_stop().

01428 {
01429    if (load_moh_classes(1)) {
01430       ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
01431             local_ast_moh_cleanup);
01432    }
01433 
01434    return 0;
01435 }

static int spawn_mp3 ( struct mohclass class  )  [static]

Definition at line 344 of file res_musiconhold.c.

References mohclass::args, ast_copy_string(), ast_log(), ast_opt_high_priority, ast_set_priority(), ast_strlen_zero(), ast_test_flag, mohclass::dir, errno, LOCAL_MPG_123, LOG_WARNING, MAX_MP3S, MOH_CUSTOM, MOH_QUIET, MOH_SINGLE, MPG_123, mohclass::pid, and mohclass::start.

Referenced by monmp3thread().

00345 {
00346    int fds[2];
00347    int files = 0;
00348    char fns[MAX_MP3S][80];
00349    char *argv[MAX_MP3S + 50];
00350    char xargs[256];
00351    char *argptr;
00352    int argc = 0;
00353    DIR *dir = NULL;
00354    struct dirent *de;
00355    sigset_t signal_set, old_set;
00356 
00357    
00358    if (!strcasecmp(class->dir, "nodir")) {
00359       files = 1;
00360    } else {
00361       dir = opendir(class->dir);
00362       if (!dir && strncasecmp(class->dir, "http://", 7)) {
00363          ast_log(LOG_WARNING, "%s is not a valid directory\n", class->dir);
00364          return -1;
00365       }
00366    }
00367 
00368    if (!ast_test_flag(class, MOH_CUSTOM)) {
00369       argv[argc++] = "mpg123";
00370       argv[argc++] = "-q";
00371       argv[argc++] = "-s";
00372       argv[argc++] = "--mono";
00373       argv[argc++] = "-r";
00374       argv[argc++] = "8000";
00375       
00376       if (!ast_test_flag(class, MOH_SINGLE)) {
00377          argv[argc++] = "-b";
00378          argv[argc++] = "2048";
00379       }
00380       
00381       argv[argc++] = "-f";
00382       
00383       if (ast_test_flag(class, MOH_QUIET))
00384          argv[argc++] = "4096";
00385       else
00386          argv[argc++] = "8192";
00387       
00388       /* Look for extra arguments and add them to the list */
00389       ast_copy_string(xargs, class->args, sizeof(xargs));
00390       argptr = xargs;
00391       while (!ast_strlen_zero(argptr)) {
00392          argv[argc++] = argptr;
00393          strsep(&argptr, ",");
00394       }
00395    } else  {
00396       /* Format arguments for argv vector */
00397       ast_copy_string(xargs, class->args, sizeof(xargs));
00398       argptr = xargs;
00399       while (!ast_strlen_zero(argptr)) {
00400          argv[argc++] = argptr;
00401          strsep(&argptr, " ");
00402       }
00403    }
00404 
00405    if (!strncasecmp(class->dir, "http://", 7)) {
00406       ast_copy_string(fns[files], class->dir, sizeof(fns[files]));
00407       argv[argc++] = fns[files];
00408       files++;
00409    } else if (dir) {
00410       while ((de = readdir(dir)) && (files < MAX_MP3S)) {
00411          if ((strlen(de->d_name) > 3) && 
00412              ((ast_test_flag(class, MOH_CUSTOM) && 
00413                (!strcasecmp(de->d_name + strlen(de->d_name) - 4, ".raw") || 
00414                 !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".sln"))) ||
00415               !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".mp3"))) {
00416             ast_copy_string(fns[files], de->d_name, sizeof(fns[files]));
00417             argv[argc++] = fns[files];
00418             files++;
00419          }
00420       }
00421    }
00422    argv[argc] = NULL;
00423    if (dir) {
00424       closedir(dir);
00425    }
00426    if (pipe(fds)) {  
00427       ast_log(LOG_WARNING, "Pipe failed\n");
00428       return -1;
00429    }
00430    if (!files) {
00431       ast_log(LOG_WARNING, "Found no files in '%s'\n", class->dir);
00432       close(fds[0]);
00433       close(fds[1]);
00434       return -1;
00435    }
00436    if (!strncasecmp(class->dir, "http://", 7) && time(NULL) - class->start < respawn_time) {
00437       sleep(respawn_time - (time(NULL) - class->start));
00438    }
00439 
00440    /* Block signals during the fork() */
00441    sigfillset(&signal_set);
00442    pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
00443 
00444    time(&class->start);
00445    class->pid = fork();
00446    if (class->pid < 0) {
00447       close(fds[0]);
00448       close(fds[1]);
00449       ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
00450       return -1;
00451    }
00452    if (!class->pid) {
00453       int x;
00454 
00455       if (ast_opt_high_priority)
00456          ast_set_priority(0);
00457 
00458       /* Reset ignored signals back to default */
00459       signal(SIGPIPE, SIG_DFL);
00460       pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
00461 
00462       close(fds[0]);
00463       /* Stdout goes to pipe */
00464       dup2(fds[1], STDOUT_FILENO);
00465       /* Close unused file descriptors */
00466       for (x=3;x<8192;x++) {
00467          if (-1 != fcntl(x, F_GETFL)) {
00468             close(x);
00469          }
00470       }
00471       /* Child */
00472       if (strcasecmp(class->dir, "nodir") && chdir(class->dir) < 0) {
00473          ast_log(LOG_WARNING, "chdir() failed: %s\n", strerror(errno));
00474          _exit(1);
00475       }
00476       setpgid(0, getpid());
00477       if (ast_test_flag(class, MOH_CUSTOM)) {
00478          execv(argv[0], argv);
00479       } else {
00480          /* Default install is /usr/local/bin */
00481          execv(LOCAL_MPG_123, argv);
00482          /* Many places have it in /usr/bin */
00483          execv(MPG_123, argv);
00484          /* Check PATH as a last-ditch effort */
00485          execvp("mpg123", argv);
00486       }
00487       ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
00488       close(fds[1]);
00489       _exit(1);
00490    } else {
00491       /* Parent */
00492       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
00493       close(fds[1]);
00494    }
00495    return fds[0];
00496 }

static int unload_module ( void   )  [static]

Definition at line 1444 of file res_musiconhold.c.

References ao2_callback(), ARRAY_LEN, ast_cli_unregister_multiple(), ast_log(), ast_moh_destroy(), ast_uninstall_music_functions(), ast_unregister_application(), cli_moh, LOG_WARNING, moh_class_inuse(), and mohclass_unref.

01445 {
01446    int res = 0;
01447    struct mohclass *class = NULL;
01448 
01449    /* XXX This check shouldn't be required if module ref counting was being used
01450     * properly ... */
01451    if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
01452       class = mohclass_unref(class);
01453       res = -1;
01454    }
01455 
01456    if (res < 0) {
01457       ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
01458       return res;
01459    }
01460 
01461    ast_uninstall_music_functions();
01462 
01463    ast_moh_destroy();
01464 
01465    res = ast_unregister_application(app0);
01466    res |= ast_unregister_application(app1);
01467    res |= ast_unregister_application(app2);
01468    res |= ast_unregister_application(app3);
01469    res |= ast_unregister_application(app4);
01470 
01471    ast_cli_unregister_multiple(cli_moh, ARRAY_LEN(cli_moh));
01472 
01473    return res;
01474 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_BUILDSUM, .description = "Music On Hold Resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1480 of file res_musiconhold.c.

char* app0 = "MusicOnHold" [static]

Definition at line 76 of file res_musiconhold.c.

char* app1 = "WaitMusicOnHold" [static]

Definition at line 77 of file res_musiconhold.c.

char* app2 = "SetMusicOnHold" [static]

Definition at line 78 of file res_musiconhold.c.

char* app3 = "StartMusicOnHold" [static]

Definition at line 79 of file res_musiconhold.c.

char* app4 = "StopMusicOnHold" [static]

Definition at line 80 of file res_musiconhold.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 1480 of file res_musiconhold.c.

struct ast_cli_entry cli_moh[] [static]

Definition at line 1368 of file res_musiconhold.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_moh_classes_show_deprecated [static]

Initial value:

 {
   { "moh", "classes", "show"},
   moh_classes_show, NULL,
   NULL }

Definition at line 1358 of file res_musiconhold.c.

struct ast_cli_entry cli_moh_files_show_deprecated [static]

Initial value:

 {
   { "moh", "files", "show"},
   cli_files_show, NULL,
   NULL }

Definition at line 1363 of file res_musiconhold.c.

char* descrip0 [static]

Definition at line 88 of file res_musiconhold.c.

char* descrip1 [static]

Initial value:

 "WaitMusicOnHold(delay): "
"Plays hold music specified number of seconds.  Returns 0 when\n"
"done, or -1 on hangup.  If no hold music is available, the delay will\n"
"still occur with no sound.\n"

Definition at line 95 of file res_musiconhold.c.

char* descrip2 [static]

Initial value:

 "SetMusicOnHold(class): "
"Sets the default class for music on hold for a given channel.  When\n"
"music on hold is activated, this class will be used to select which\n"
"music is played.\n"

Definition at line 100 of file res_musiconhold.c.

char* descrip3 [static]

Initial value:

 "StartMusicOnHold(class): "
"Starts playing music on hold, uses default music class for channel.\n"
"Starts playing music specified by class.  If omitted, the default\n"
"music source for the channel will be used.  Always returns 0.\n"

Definition at line 105 of file res_musiconhold.c.

char* descrip4 [static]

Initial value:

 "StopMusicOnHold: "
"Stops playing music on hold.\n"

Definition at line 110 of file res_musiconhold.c.

struct ast_generator moh_file_stream [static]

Initial value:

 {
   .alloc    = moh_files_alloc,
   .release  = moh_files_release,
   .generate = moh_files_generator,
}

Definition at line 338 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

struct ao2_container* mohclasses [static]

Definition at line 165 of file res_musiconhold.c.

struct ast_generator mohgen [static]

Initial value:

 {
   .alloc    = moh_alloc,
   .release  = moh_release,
   .generate = moh_generate,
}

Definition at line 767 of file res_musiconhold.c.

Referenced by local_ast_moh_start().

int respawn_time = 20 [static]

Definition at line 113 of file res_musiconhold.c.

char* synopsis0 = "Play Music On Hold indefinitely" [static]

Definition at line 82 of file res_musiconhold.c.

char* synopsis1 = "Wait, playing Music On Hold" [static]

Definition at line 83 of file res_musiconhold.c.

char* synopsis2 = "Set default Music On Hold class" [static]

Definition at line 84 of file res_musiconhold.c.

char* synopsis3 = "Play Music On Hold" [static]

Definition at line 85 of file res_musiconhold.c.

char* synopsis4 = "Stop Playing Music On Hold" [static]

Definition at line 86 of file res_musiconhold.c.


Generated on Wed Feb 11 12:00:31 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7