Newer
Older
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
{
s->owner = chan;
return 0;
}
int ast_playstream(struct ast_filestream *s)
{
if (ast_format_get_type(s->fmt->format) == AST_MEDIA_TYPE_AUDIO)
res = ast_readaudio_callback(s);
res = ast_readvideo_callback(s);
return (res == FSREAD_FAILURE) ? -1 : 0;
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
{
return fs->fmt->seek(fs, sample_offset, whence);
}
int ast_truncstream(struct ast_filestream *fs)
{
return fs->fmt->trunc(fs);
}
off_t ast_tellstream(struct ast_filestream *fs)
int ast_ratestream(struct ast_filestream *fs)
{
return ast_format_get_sample_rate(fs->fmt->format);
}
int ast_stream_fastforward(struct ast_filestream *fs, off_t ms)
return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
}
int ast_closestream(struct ast_filestream *f)
{
/* This used to destroy the filestream, but it now just decrements a refcount.
Matthew Jordan
committed
* We close the stream in order to quit queuing frames now, because we might
* change the writeformat, which could result in a subsequent write error, if
* the format is different. */
Matthew Jordan
committed
filestream_close(f);
/*
* Look the various language-specific places where a file could exist.
*/
Mark Spencer
committed
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
char *buf;
int buflen;
if (preflang == NULL)
preflang = "";
buflen = strlen(preflang) + strlen(filename) + 4; /* room for everything */
return fileexists_core(filename, fmt, preflang, buf, buflen, NULL) ? 1 : 0;
Mark Spencer
committed
int ast_filedelete(const char *filename, const char *fmt)
return filehelper(filename, NULL, fmt, ACTION_DELETE);
Mark Spencer
committed
int ast_filerename(const char *filename, const char *filename2, const char *fmt)
return filehelper(filename, filename2, fmt, ACTION_RENAME);
Mark Spencer
committed
int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
return filehelper(filename, filename2, fmt, ACTION_COPY);
static int __ast_file_read_dirs(const char *path, ast_file_on_file on_file,
void *obj, int max_depth)
{
DIR *dir;
struct dirent *entry;
int res;
if (!(dir = opendir(path))) {
ast_log(LOG_ERROR, "Error opening directory - %s: %s\n",
path, strerror(errno));
return -1;
}
--max_depth;
res = 0;
while ((entry = readdir(dir)) != NULL && !errno) {
int is_file = 0;
int is_dir = 0;
RAII_VAR(char *, full_path, NULL, ast_free);
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
/*
* If the dirent structure has a d_type use it to determine if we are dealing with
* a file or directory. Unfortunately if it doesn't have it, or if the type is
* unknown, or a link then we'll need to use the stat function instead.
*/
#ifdef _DIRENT_HAVE_D_TYPE
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
is_file = entry->d_type == DT_REG;
is_dir = entry->d_type == DT_DIR;
} else
#endif
{
struct stat statbuf;
/*
* Don't use alloca or we risk blowing out the stack if recursing
* into subdirectories.
*/
full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
if (!full_path) {
return -1;
}
sprintf(full_path, "%s/%s", path, entry->d_name);
if (stat(full_path, &statbuf)) {
ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
full_path, strerror(errno));
/*
* Output an error, but keep going. It could just be
* a broken link and other files could be fine.
*/
continue;
}
is_file = S_ISREG(statbuf.st_mode);
is_dir = S_ISDIR(statbuf.st_mode);
}
if (is_file) {
/* If the handler returns non-zero then stop */
if ((res = on_file(path, entry->d_name, obj))) {
break;
}
/* Otherwise move on to next item in directory */
continue;
}
if (!is_dir) {
ast_debug(5, "Skipping %s: not a regular file or directory\n", full_path);
continue;
}
/* Only re-curse into sub-directories if not at the max depth */
if (max_depth != 0) {
if (!full_path) {
/* Don't use alloca. See note above. */
full_path = ast_malloc(strlen(path) + strlen(entry->d_name) + 2);
if (!full_path) {
return -1;
}
sprintf(full_path, "%s/%s", path, entry->d_name);
}
if ((res = __ast_file_read_dirs(full_path, on_file, obj, max_depth))) {
break;
}
}
}
closedir(dir);
if (!res && errno) {
ast_log(LOG_ERROR, "Error while reading directories - %s: %s\n",
path, strerror(errno));
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
res = -1;
}
return res;
}
#if !defined(__GLIBC__)
/*!
* \brief Lock to hold when iterating over directories.
*
* Currently, 'readdir' is not required to be thread-safe. In most modern implementations
* it should be safe to make concurrent calls into 'readdir' that specify different directory
* streams (glibc would be one of these). However, since it is potentially unsafe for some
* implementations we'll use our own locking in order to achieve synchronization for those.
*/
AST_MUTEX_DEFINE_STATIC(read_dirs_lock);
#endif
int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth)
{
int res;
errno = 0;
#if !defined(__GLIBC__)
ast_mutex_lock(&read_dirs_lock);
#endif
res = __ast_file_read_dirs(dir_name, on_file, obj, max_depth);
#if !defined(__GLIBC__)
ast_mutex_unlock(&read_dirs_lock);
#endif
return res;
}
Mark Spencer
committed
int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
struct ast_filestream *vfs=NULL;
fs = ast_openstream(chan, filename, preflang);
if (!fs) {
Alexander Traud
committed
struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
ast_channel_lock(chan);
ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n",
filename, ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf), strerror(errno));
ast_channel_unlock(chan);
return -1;
}
/* check to see if there is any data present (not a zero length file),
* done this way because there is no where for ast_openstream_full to
* return the file had no data. */
pos = ftello(fs->f);
seekattempt = fseeko(fs->f, -1, SEEK_END);
if (seekattempt) {
if (errno == EINVAL) {
/* Zero-length file, as opposed to a pipe */
return 0;
} else {
ast_seekstream(fs, 0, SEEK_SET);
}
fseeko(fs->f, pos, SEEK_SET);
vfs = ast_openvstream(chan, filename, preflang);
if (vfs) {
ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_format_get_name(vfs->fmt->format));
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_MASQ_NOSTREAM))
fs->orig_chan_name = ast_strdup(ast_channel_name(chan));
if (ast_applystream(chan, fs))
return -1;
if (vfs && ast_applystream(chan, vfs))
return -1;
ast_test_suite_event_notify("PLAYBACK", "Message: %s\r\nChannel: %s", filename, ast_channel_name(chan));
res = ast_playstream(fs);
if (!res && vfs)
res = ast_playstream(vfs);
if (VERBOSITY_ATLEAST(3)) {
ast_channel_lock(chan);
ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", ast_channel_name(chan), filename, ast_format_get_name(ast_channel_writeformat(chan)), preflang ? preflang : "default");
ast_channel_unlock(chan);
}
return res;
Mark Spencer
committed
struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Martin Pycko
committed
{
struct ast_format_def *f;
struct ast_filestream *fs = NULL;
Martin Pycko
committed
char *fn;
AST_RWLIST_RDLOCK(&formats);
AST_RWLIST_TRAVERSE(&formats, f, list) {
if (!exts_compare(f->exts, type))
continue;
Joshua Colp
committed
format_found = 1;
fn = build_filename(filename, type);
if (!fn) {
continue;
}
bfile = fopen(fn, "r");
Joshua Colp
committed
if (!bfile || (fs = get_filestream(f, bfile)) == NULL || open_wrapper(fs) ) {
ast_log(LOG_WARNING, "Unable to open %s\n", fn);
if (fs) {
ast_closestream(fs);
}
Tilghman Lesher
committed
ast_free(fn);
}
/* found it */
fs->trans = NULL;
fs->fmt = f;
fs->flags = flags;
fs->mode = mode;
fs->filename = ast_strdup(filename);
Martin Pycko
committed
}
AST_RWLIST_UNLOCK(&formats);
Joshua Colp
committed
if (!format_found)
Martin Pycko
committed
ast_log(LOG_WARNING, "No such format '%s'\n", type);
Martin Pycko
committed
return fs;
}
Mark Spencer
committed
struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
int fd, myflags = 0;
/* compiler claims this variable can be used before initialization... */
FILE *bfile = NULL;
struct ast_format_def *f;
struct ast_filestream *fs = NULL;
char *buf = NULL;
AST_RWLIST_RDLOCK(&formats);
/* set the O_TRUNC flag if and only if there is no O_APPEND specified */
/* We really can't use O_APPEND as it will break WAV header updates */
Martin Pycko
committed
myflags = O_TRUNC;
Martin Pycko
committed
myflags |= O_WRONLY | O_CREAT;
/* XXX need to fix this - we should just do the fopen,
* not open followed by fdopen()
*/
AST_RWLIST_TRAVERSE(&formats, f, list) {
char *fn, *orig_fn = NULL;
if (fs)
break;
if (!exts_compare(f->exts, type))
continue;
else
format_found = 1;
fn = build_filename(filename, type);
if (!fn) {
continue;
}
fd = open(fn, flags | myflags, mode);
if (fd > -1) {
/* fdopen() the resulting file stream */
bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
if (!bfile) {
ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
close(fd);
fd = -1;
}
}
Russell Bryant
committed
if (ast_opt_cache_record_files && (fd > -1)) {
char *c;
fclose(bfile); /* this also closes fd */
/*
We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
*/
orig_fn = ast_strdupa(fn);
for (c = fn; *c; c++)
if (*c == '/')
*c = '_';
size = strlen(fn) + strlen(record_cache_dir) + 2;
strcpy(buf, record_cache_dir);
strcat(buf, "/");
strcat(buf, fn);
Tilghman Lesher
committed
ast_free(fn);
fn = buf;
Martin Pycko
committed
fd = open(fn, flags | myflags, mode);
if (fd > -1) {
/* fdopen() the resulting file stream */
bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
if (!bfile) {
ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
close(fd);
fd = -1;
}
}
errno = 0;
fs = get_filestream(f, bfile);
if (fs) {
if ((fs->write_buffer = ast_malloc(32768))) {
setvbuf(fs->f, fs->write_buffer, _IOFBF, 32768);
}
}
if (!fs || rewrite_wrapper(fs, comment)) {
ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
close(fd);
if (orig_fn) {
unlink(fn);
if (fs) {
ast_closestream(fs);
fs = NULL;
}
if (!buf) {
ast_free(fn);
}
}
fs->trans = NULL;
fs->fmt = f;
fs->flags = flags;
fs->mode = mode;
if (orig_fn) {
fs->realfilename = ast_strdup(orig_fn);
fs->filename = ast_strdup(fn);
} else {
fs->realfilename = NULL;
fs->filename = ast_strdup(filename);
fs->vfs = NULL;
/* If truncated, we'll be at the beginning; if not truncated, then append */
f->seek(fs, 0, SEEK_END);
} else if (errno != EEXIST) {
ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
if (orig_fn)
unlink(orig_fn);
/* if buf != NULL then fn is already free and pointing to it */
if (!buf)
Tilghman Lesher
committed
ast_free(fn);
AST_RWLIST_UNLOCK(&formats);
ast_log(LOG_WARNING, "No such format '%s'\n", type);
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
static void waitstream_control(struct ast_channel *c,
enum ast_waitstream_fr_cb_values type,
ast_waitstream_fr_cb cb,
int skip_ms)
{
switch (type)
{
case AST_WAITSTREAM_CB_FASTFORWARD:
{
int eoftest;
ast_stream_fastforward(ast_channel_stream(c), skip_ms);
eoftest = fgetc(ast_channel_stream(c)->f);
if (feof(ast_channel_stream(c)->f)) {
ast_stream_rewind(ast_channel_stream(c), skip_ms);
} else {
ungetc(eoftest, ast_channel_stream(c)->f);
}
}
break;
case AST_WAITSTREAM_CB_REWIND:
ast_stream_rewind(ast_channel_stream(c), skip_ms);
break;
default:
break;
}
if (cb) {
long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000);
cb(c, ms_len, type);
}
ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
"Control: %s\r\n"
"SkipMs: %d\r\n",
ast_channel_name(c),
(type == AST_WAITSTREAM_CB_FASTFORWARD) ? "FastForward" : "Rewind",
skip_ms);
}
/*!
* \brief the core of all waitstream() functions
*/
static int waitstream_core(struct ast_channel *c,
const char *breakon,
const char *forward,
const char *reverse,
int skip_ms,
int audiofd,
int cmdfd,
const char *context,
ast_waitstream_fr_cb cb)
const char *orig_chan_name = NULL;
Josh Roberson
committed
if (!breakon)
Josh Roberson
committed
if (!forward)
if (!reverse)
reverse = "";
/* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
ast_channel_set_flag(c, AST_FLAG_END_DTMF_ONLY);
if (ast_test_flag(ast_channel_flags(c), AST_FLAG_MASQ_NOSTREAM))
orig_chan_name = ast_strdupa(ast_channel_name(c));
if (ast_channel_stream(c) && cb) {
long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_get_sample_rate(ast_channel_stream(c)->fmt->format) / 1000);
cb(c, ms_len, AST_WAITSTREAM_CB_START);
}
while (ast_channel_stream(c)) {
if (orig_chan_name && strcasecmp(orig_chan_name, ast_channel_name(c))) {
ast_stopstream(c);
err = 1;
break;
}
ms = ast_sched_wait(ast_channel_sched(c));
if (ms < 0 && !ast_channel_timingfunc(c)) {
Mark Spencer
committed
ast_stopstream(c);
res = ast_waitfor(c, ms);
if (res < 0) {
ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return res;
}
} else {
int outfd;
struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
if (!rchan && (outfd < 0) && (ms)) {
/* Continue */
if (errno == EINTR)
continue;
ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
} else if (outfd > -1) { /* this requires cmdfd set */
/* The FD we were watching has something waiting */
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
/* if rchan is set, it is 'c' */
res = rchan ? 1 : 0; /* map into 'res' values */
}
if (res > 0) {
struct ast_frame *fr = ast_read(c);
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
Joshua Colp
committed
switch (fr->frametype) {
case AST_FRAME_DTMF_END:
const char exten[2] = { fr->subclass.integer, '\0' };
if (ast_exists_extension(c, context, exten, 1,
S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
res = fr->subclass.integer;
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return res;
}
} else {
res = fr->subclass.integer;
waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
} else if (strchr(reverse, res)) {
waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
} else if (strchr(breakon, res)) {
ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
"Control: %s\r\n",
ast_channel_name(c),
"Break");
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
}
break;
case AST_FRAME_CONTROL:
switch (fr->subclass.integer) {
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_RESTART:
/* Fall-through and break out */
ast_test_suite_event_notify("PLAYBACK","Channel: %s\r\n"
"Control: %s\r\n",
ast_channel_name(c),
"Break");
res = fr->subclass.integer;
ast_frfree(fr);
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return res;
case AST_CONTROL_STREAM_REVERSE:
if (!skip_ms) {
skip_ms = 3000;
}
waitstream_control(c, AST_WAITSTREAM_CB_REWIND, cb, skip_ms);
break;
case AST_CONTROL_STREAM_FORWARD:
if (!skip_ms) {
skip_ms = 3000;
}
waitstream_control(c, AST_WAITSTREAM_CB_FASTFORWARD, cb, skip_ms);
break;
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return -1;
case AST_CONTROL_RINGING:
case AST_CONTROL_ANSWER:
case AST_CONTROL_HOLD:
case AST_CONTROL_UNHOLD:
case AST_CONTROL_CONNECTED_LINE:
case AST_CONTROL_REDIRECTING:
case AST_CONTROL_AOC:
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_PVT_CAUSE_CODE:
/* Unimportant */
break;
default:
ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass.integer);
case AST_FRAME_VOICE:
/* Write audio if appropriate */
Kevin P. Fleming
committed
if (audiofd > -1) {
if (write(audiofd, fr->data.ptr, fr->datalen) < 0) {
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
}
}
default:
/* Ignore all others */
break;
ast_sched_runq(ast_channel_sched(c));
ast_channel_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return (err || ast_channel_softhangup_internal_flag(c)) ? -1 : 0;
Mark Spencer
committed
int ast_waitstream_fr_w_cb(struct ast_channel *c,
const char *breakon,
const char *forward,
const char *reverse,
int ms,
ast_waitstream_fr_cb cb)
{
return waitstream_core(c, breakon, forward, reverse, ms,
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, cb);
}
int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *reverse, int ms)
return waitstream_core(c, breakon, forward, reverse, ms,
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, NULL /* no callback */);
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
/*! \internal
* \brief Clean up the return value of a waitstream call
*
* It's possible for a control frame to come in from an external source and break the
* playback. From a consumer of most ast_waitstream_* function callers, this should
* appear like normal playback termination, i.e., return 0 and not the value of the
* control frame.
*/
static int sanitize_waitstream_return(int return_value)
{
switch (return_value) {
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_RESTART:
/* Fall through and set return_value to 0 */
return_value = 0;
break;
default:
/* Do nothing */
break;
}
return return_value;
}
int ast_waitstream(struct ast_channel *c, const char *breakon)
{
int res;
res = waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL, NULL /* no callback */);
return sanitize_waitstream_return(res);
}
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
{
int res;
res = waitstream_core(c, breakon, NULL, NULL, 0,
audiofd, cmdfd, NULL /* no context */, NULL /* no callback */);
return sanitize_waitstream_return(res);
Kevin P. Fleming
committed
int ast_waitstream_exten(struct ast_channel *c, const char *context)
{
Kevin P. Fleming
committed
/* Waitstream, with return in the case of a valid 1 digit extension */
/* in the current or specified context being pressed */
context = ast_channel_context(c);
res = waitstream_core(c, NULL, NULL, NULL, 0,
-1, -1, context, NULL /* no callback */);
return sanitize_waitstream_return(res);
Kevin P. Fleming
committed
}
/*
* if the file name is non-empty, try to play it.
* Return 0 if success, -1 if error, digit if interrupted by a digit.
* If digits == "" then we can simply check for non-zero.
*/
Steve Murphy
committed
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
int res = 0;
if (!ast_strlen_zero(file)) {
res = ast_streamfile(chan, file, ast_channel_language(chan));
if (!res) {
res = ast_waitstream(chan, digits);
}
}
return res;
char *ast_format_str_reduce(char *fmts)
{
struct ast_format_def *f;
struct ast_format_def *fmts_ptr[AST_MAX_FORMATS];
char *fmts_str[AST_MAX_FORMATS];
char *stringp, *type;
char *orig = fmts;
int i, j, x, first, found = 0;
int len = strlen(fmts) + 1;
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
if (AST_RWLIST_RDLOCK(&formats)) {
ast_log(LOG_WARNING, "Unable to lock format list\n");
return NULL;
}
stringp = ast_strdupa(fmts);
for (x = 0; (type = strsep(&stringp, "|")) && x < AST_MAX_FORMATS; x++) {
AST_RWLIST_TRAVERSE(&formats, f, list) {
if (exts_compare(f->exts, type)) {
found = 1;
break;
}
}
fmts_str[x] = type;
if (found) {
fmts_ptr[x] = f;
} else {
fmts_ptr[x] = NULL;
}
}
AST_RWLIST_UNLOCK(&formats);
for (i = 0; i < x; i++) {
/* ignore invalid entries */
if (!fmts_ptr[i]) {
ast_log(LOG_WARNING, "ignoring unknown format '%s'\n", fmts_str[i]);
continue;
}
/* special handling for the first entry */
res = snprintf(fmts, len, "%s", fmts_str[i]);
fmts += res;
len -= res;
continue;
}
found = 0;
for (j = 0; j < i; j++) {
/* this is a duplicate */
if (fmts_ptr[j] == fmts_ptr[i]) {
found = 1;
break;
}
}
if (!found) {
res = snprintf(fmts, len, "|%s", fmts_str[i]);
fmts += res;
len -= res;
if (first) {
ast_log(LOG_WARNING, "no known formats found in format list (%s)\n", orig);
return NULL;
}
static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Mark Spencer
committed
{
#define FORMAT "%-10s %-10s %-20s\n"
#define FORMAT2 "%-10s %-10s %-20s\n"
struct ast_format_def *f;
switch (cmd) {
case CLI_INIT:
e->command = "core show file formats";
e->usage =
"Usage: core show file formats\n"
" Displays currently registered file formats (if any).\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
ast_cli(a->fd, FORMAT, "Format", "Name", "Extensions");
ast_cli(a->fd, FORMAT, "------", "----", "----------");
Mark Spencer
committed
AST_RWLIST_RDLOCK(&formats);
AST_RWLIST_TRAVERSE(&formats, f, list) {
ast_cli(a->fd, FORMAT2, ast_format_get_name(f->format), f->name, f->exts);
AST_RWLIST_UNLOCK(&formats);
ast_cli(a->fd, "%d file formats registered.\n", count_fmt);
return CLI_SUCCESS;
#undef FORMAT
#undef FORMAT2
Mark Spencer
committed
}
struct ast_format *ast_get_format_for_file_ext(const char *file_ext)
{
struct ast_format_def *f;
SCOPED_RDLOCK(lock, &formats.lock);
AST_RWLIST_TRAVERSE(&formats, f, list) {
if (exts_compare(f->exts, file_ext)) {
return f->format;
}
}
return NULL;
}
static struct ast_cli_entry cli_file[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
Mark Spencer
committed
};
static void file_shutdown(void)
{
ast_cli_unregister_multiple(cli_file, ARRAY_LEN(cli_file));
STASIS_MESSAGE_TYPE_CLEANUP(ast_format_register_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_format_unregister_type);
Mark Spencer
committed
int ast_file_init(void)
{
STASIS_MESSAGE_TYPE_INIT(ast_format_register_type);
STASIS_MESSAGE_TYPE_INIT(ast_format_unregister_type);
ast_cli_register_multiple(cli_file, ARRAY_LEN(cli_file));
ast_register_cleanup(file_shutdown);