summaryrefslogtreecommitdiff
path: root/main/file.c
diff options
context:
space:
mode:
authorDavid Vossel <dvossel@digium.com>2011-02-03 16:22:10 +0000
committerDavid Vossel <dvossel@digium.com>2011-02-03 16:22:10 +0000
commitc26c190711a1bbe3b5fff1a93facae333757c56e (patch)
tree00da0caa5a07b7b25729f089dbcafb08129fa9be /main/file.c
parent652fb64a01c7a8656697d07e606620ee0ced6929 (diff)
Asterisk media architecture conversion - no more format bitfields
This patch is the foundation of an entire new way of looking at media in Asterisk. The code present in this patch is everything required to complete phase1 of my Media Architecture proposal. For more information about this project visit the link below. https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal The primary function of this patch is to convert all the usages of format bitfields in Asterisk to use the new format and format_cap APIs. Functionally no change in behavior should be present in this patch. Thanks to twilson and russell for all the time they spent reviewing these changes. Review: https://reviewboard.asterisk.org/r/1083/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@306010 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/file.c')
-rw-r--r--main/file.c214
1 files changed, 135 insertions, 79 deletions
diff --git a/main/file.c b/main/file.c
index c2ab096cd..57bf1570c 100644
--- a/main/file.c
+++ b/main/file.c
@@ -59,11 +59,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
*/
int ast_language_is_prefix = 1;
-static AST_RWLIST_HEAD_STATIC(formats, ast_format);
+static AST_RWLIST_HEAD_STATIC(formats, ast_format_def);
-int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
+int __ast_format_def_register(const struct ast_format_def *f, struct ast_module *mod)
{
- struct ast_format *tmp;
+ struct ast_format_def *tmp;
AST_RWLIST_WRLOCK(&formats);
AST_RWLIST_TRAVERSE(&formats, tmp, list) {
@@ -98,9 +98,9 @@ int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
return 0;
}
-int ast_format_unregister(const char *name)
+int ast_format_def_unregister(const char *name)
{
- struct ast_format *tmp;
+ struct ast_format_def *tmp;
int res = -1;
AST_RWLIST_WRLOCK(&formats);
@@ -130,8 +130,8 @@ int ast_stopstream(struct ast_channel *tmp)
if (tmp->stream) {
ast_closestream(tmp->stream);
tmp->stream = NULL;
- if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
- ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_getformatname(tmp->oldwriteformat));
+ if (tmp->oldwriteformat.id && ast_set_write_format(tmp, &tmp->oldwriteformat))
+ ast_log(LOG_WARNING, "Unable to restore format back to %s\n", ast_getformatname(&tmp->oldwriteformat));
}
/* Stop the video stream too */
if (tmp->vstream != NULL) {
@@ -149,10 +149,10 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
int res = -1;
int alt = 0;
if (f->frametype == AST_FRAME_VIDEO) {
- if (fs->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ if (AST_FORMAT_GET_TYPE(fs->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) {
/* This is the audio portion. Call the video one... */
if (!fs->vfs && fs->filename) {
- const char *type = ast_getformatname(f->subclass.codec & ~0x1);
+ const char *type = ast_getformatname(&f->subclass.format);
fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
ast_debug(1, "Opened video output file\n");
}
@@ -168,7 +168,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
return -1;
}
- if (((fs->fmt->format | alt) & f->subclass.codec) == f->subclass.codec) {
+ if (ast_format_cmp(&f->subclass.format, &fs->fmt->format) != AST_FORMAT_CMP_NOT_EQUAL) {
res = fs->fmt->write(fs, f);
if (res < 0)
ast_log(LOG_WARNING, "Natural write failed\n");
@@ -177,18 +177,18 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
} else {
/* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
the one we've setup a translator for, we do the "wrong thing" XXX */
- if (fs->trans && f->subclass.codec != fs->lastwriteformat) {
+ if (fs->trans && (ast_format_cmp(&f->subclass.format, &fs->lastwriteformat) != AST_FORMAT_CMP_EQUAL)) {
ast_translator_free_path(fs->trans);
fs->trans = NULL;
}
if (!fs->trans)
- fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass.codec);
+ fs->trans = ast_translator_build_path(&fs->fmt->format, &f->subclass.format);
if (!fs->trans)
ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n",
- fs->fmt->name, ast_getformatname(f->subclass.codec));
+ fs->fmt->name, ast_getformatname(&f->subclass.format));
else {
struct ast_frame *trf;
- fs->lastwriteformat = f->subclass.codec;
+ ast_format_copy(&fs->lastwriteformat, &f->subclass.format);
/* Get the translated frame but don't consume the original in case they're using it on another stream */
if ((trf = ast_translate(fs->trans, f, 0))) {
struct ast_frame *cur;
@@ -296,7 +296,7 @@ static void filestream_destructor(void *arg)
/* Stop a running stream if there is one */
if (f->owner) {
- if (f->fmt->format < AST_FORMAT_AUDIO_MASK) {
+ if (AST_FORMAT_GET_TYPE(f->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) {
f->owner->stream = NULL;
AST_SCHED_DEL(f->owner->sched, f->owner->streamid);
ast_settimeout(f->owner, 0, NULL, NULL);
@@ -335,7 +335,7 @@ static void filestream_destructor(void *arg)
ast_module_unref(f->fmt->module);
}
-static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile)
+static struct ast_filestream *get_filestream(struct ast_format_def *fmt, FILE *bfile)
{
struct ast_filestream *s;
@@ -361,7 +361,7 @@ enum wrap_fn { WRAP_OPEN, WRAP_REWRITE };
static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode)
{
- struct ast_format *f = s->fmt;
+ struct ast_format_def *f = s->fmt;
int ret = -1;
int (*openfn)(struct ast_filestream *s);
@@ -396,18 +396,20 @@ enum file_action {
};
/*!
+ * \internal
* \brief perform various actions on a file. Second argument
- * arg2 depends on the command:
- * unused for EXISTS and DELETE
+ * \note arg2 depends on the command:
+ * unused for DELETE
+ * optional ast_format_cap holding all the formats found for a file, for EXISTS.
* destination file name (const char *) for COPY and RENAME
* struct ast_channel * for OPEN
* if fmt is NULL, OPEN will return the first matching entry,
* whereas other functions will run on all matching entries.
*/
-static format_t ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action)
+static int filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action)
{
- struct ast_format *f;
- format_t res = (action == ACTION_EXISTS) ? 0 : -1;
+ struct ast_format_def *f;
+ int res = (action == ACTION_EXISTS) ? 0 : -1;
AST_RWLIST_RDLOCK(&formats);
/* Check for a specific format */
@@ -441,9 +443,9 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha
FILE *bfile;
struct ast_filestream *s;
- if ( !(chan->writeformat & f->format) &&
- !((f->format & AST_FORMAT_AUDIO_MASK && fmt) ||
- (f->format & AST_FORMAT_VIDEO_MASK && fmt))) {
+ if ((ast_format_cmp(&chan->writeformat, &f->format) == AST_FORMAT_CMP_NOT_EQUAL) &&
+ !(((AST_FORMAT_GET_TYPE(f->format.id) == AST_FORMAT_TYPE_AUDIO) && fmt) ||
+ ((AST_FORMAT_GET_TYPE(f->format.id) == AST_FORMAT_TYPE_VIDEO) && fmt))) {
ast_free(fn);
continue; /* not a supported format */
}
@@ -471,7 +473,7 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha
s->fmt = f;
s->trans = NULL;
s->filename = NULL;
- if (s->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) {
if (chan->stream)
ast_closestream(chan->stream);
chan->stream = s;
@@ -488,7 +490,12 @@ static format_t ast_filehelper(const char *filename, const void *arg2, const cha
break; /* will never get here */
case ACTION_EXISTS: /* return the matching format */
- res |= f->format;
+ /* if arg2 is present, it is a format capabilities structure.
+ * Add this format to the set of formats this file can be played in */
+ if (arg2) {
+ ast_format_cap_add((struct ast_format_cap *) arg2, &f->format);
+ }
+ res = 1; /* file does exist and format it exists in is returned in arg2 */
break;
case ACTION_DELETE:
@@ -527,11 +534,17 @@ static int is_absolute_path(const char *filename)
return filename[0] == '/';
}
-static format_t fileexists_test(const char *filename, const char *fmt, const char *lang,
- char *buf, int buflen)
+/*!
+ * \brief test if a file exists for a given format.
+ * \note result_cap is OPTIONAL
+ * \retval 1, true and result_cap represents format capabilities file exists in.
+ * \retval 0, false
+ */
+static int fileexists_test(const char *filename, const char *fmt, const char *lang,
+ char *buf, int buflen, struct ast_format_cap *result_cap)
{
if (buf == NULL) {
- return -1;
+ return 0;
}
if (ast_language_is_prefix && !is_absolute_path(filename)) { /* new layout */
@@ -550,25 +563,36 @@ static format_t fileexists_test(const char *filename, const char *fmt, const cha
}
}
- return ast_filehelper(buf, NULL, fmt, ACTION_EXISTS);
+ return filehelper(buf, result_cap, fmt, ACTION_EXISTS);
}
/*!
* \brief helper routine to locate a file with a given format
* and language preference.
- * Try preflang, preflang with stripped '_' suffices, or NULL.
+ *
+ * \note Try preflang, preflang with stripped '_' suffices, or NULL.
*
- * The last parameter(s) point to a buffer of sufficient size,
+ * \note The last parameter(s) point to a buffer of sufficient size,
* which on success is filled with the matching filename.
+ *
+ * \param filename, name of the file.
+ * \param fmt, format to look for the file in. OPTIONAL
+ * \param preflang, the perfered language
+ * \param buf, returns the matching filename
+ * \param buflen, size of the buf
+ * \param result_cap, OPTIONAL format capabilities result structure
+ * returns what formats the file was found in.
+ *
+ * \retval 1, true. file exists and result format is set
+ * \retval 0, false. file does not exist.
*/
-static format_t fileexists_core(const char *filename, const char *fmt, const char *preflang,
- char *buf, int buflen)
+static int fileexists_core(const char *filename, const char *fmt, const char *preflang,
+ char *buf, int buflen, struct ast_format_cap *result_cap)
{
- format_t res = -1;
char *lang;
if (buf == NULL) {
- return -1;
+ return 0;
}
/* We try languages in the following order:
@@ -584,8 +608,8 @@ static format_t fileexists_core(const char *filename, const char *fmt, const cha
while (!ast_strlen_zero(lang)) {
char *end;
- if ((res = fileexists_test(filename, fmt, lang, buf, buflen)) > 0) {
- return res;
+ if (fileexists_test(filename, fmt, lang, buf, buflen, result_cap)) {
+ return 1;
}
if ((end = strrchr(lang, '_')) != NULL) {
@@ -597,14 +621,14 @@ static format_t fileexists_core(const char *filename, const char *fmt, const cha
}
/* Try without any language */
- if ((res = fileexists_test(filename, fmt, NULL, buf, buflen)) > 0) {
- return res;
+ if (fileexists_test(filename, fmt, NULL, buf, buflen, result_cap)) {
+ return 1;
}
/* Finally try the default language unless it was already tried before */
if ((ast_strlen_zero(preflang) || strcmp(preflang, DEFAULT_LANGUAGE)) && (ast_strlen_zero(lang) || strcmp(lang, DEFAULT_LANGUAGE))) {
- if ((res = fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen)) > 0) {
- return res;
+ if ((fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen, result_cap)) > 0) {
+ return 1;
}
}
@@ -623,7 +647,8 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
* language and format, set up a suitable translator,
* and open the stream.
*/
- format_t fmts, res;
+ struct ast_format_cap *file_fmt_cap;
+ int res;
int buflen;
char *buf;
@@ -639,20 +664,29 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
buf = alloca(buflen);
if (buf == NULL)
return NULL;
- fmts = fileexists_core(filename, NULL, preflang, buf, buflen);
- if (fmts > 0)
- fmts &= AST_FORMAT_AUDIO_MASK;
- if (fmts < 1) {
+
+ if (!(file_fmt_cap = ast_format_cap_alloc_nolock())) {
+ return NULL;
+ }
+ if (!fileexists_core(filename, NULL, preflang, buf, buflen, file_fmt_cap) ||
+ !ast_format_cap_has_type(file_fmt_cap, AST_FORMAT_TYPE_AUDIO)) {
+
ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
+ file_fmt_cap = ast_format_cap_destroy(file_fmt_cap);
return NULL;
}
- chan->oldwriteformat = chan->writeformat;
- /* Set the channel to a format we can work with */
- res = ast_set_write_format(chan, fmts);
+
+ /* Set the channel to a format we can work with and save off the previous format. */
+ ast_format_copy(&chan->oldwriteformat, &chan->writeformat);
+ /* Set the channel to the best format that exists for the file. */
+ res = ast_set_write_format_from_cap(chan, file_fmt_cap);
+ /* don't need this anymore now that the channel's write format is set. */
+ file_fmt_cap = ast_format_cap_destroy(file_fmt_cap);
+
if (res == -1) { /* No format available that works with this channel */
return NULL;
}
- res = ast_filehelper(buf, chan, NULL, ACTION_OPEN);
+ res = filehelper(buf, chan, NULL, ACTION_OPEN);
if (res >= 0)
return chan->stream;
return NULL;
@@ -663,9 +697,12 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil
/* As above, but for video. But here we don't have translators
* so we must enforce a format.
*/
- format_t format;
+ struct ast_format tmp_fmt;
+ struct ast_format_cap *tmp_cap;
char *buf;
int buflen;
+ const char *fmt;
+ int fd;
if (preflang == NULL)
preflang = "";
@@ -674,20 +711,39 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil
if (buf == NULL)
return NULL;
- for (format = AST_FORMAT_AUDIO_MASK + 1; format <= AST_FORMAT_VIDEO_MASK; format = format << 1) {
- int fd;
- const char *fmt;
+ /* is the channel capable of video without translation ?*/
+ if (!ast_format_cap_has_type(chan->nativeformats, AST_FORMAT_TYPE_VIDEO)) {
+ return NULL;
+ }
+ if (!(tmp_cap = ast_format_cap_alloc_nolock())) {
+ return NULL;
+ }
+ /* Video is supported, so see what video formats exist for this file */
+ if (!fileexists_core(filename, NULL, preflang, buf, buflen, tmp_cap)) {
+ tmp_cap = ast_format_cap_destroy(tmp_cap);
+ return NULL;
+ }
- if (!(chan->nativeformats & format))
+ /* iterate over file formats and pick the first one compatible with the channel's native formats */
+ ast_format_cap_iter_start(tmp_cap);
+ while (!ast_format_cap_iter_next(tmp_cap, &tmp_fmt)) {
+ fmt = ast_getformatname(&tmp_fmt);
+ if ((AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_VIDEO) ||
+ !ast_format_cap_iscompatible(chan->nativeformats, &tmp_fmt)) {
continue;
- fmt = ast_getformatname(format);
- if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */
- continue;
- fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN);
- if (fd >= 0)
+ }
+
+ fd = filehelper(buf, chan, fmt, ACTION_OPEN);
+ if (fd >= 0) {
+ ast_format_cap_iter_end(tmp_cap);
+ tmp_cap = ast_format_cap_destroy(tmp_cap);
return chan->vstream;
+ }
ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
}
+ ast_format_cap_iter_end(tmp_cap);
+ tmp_cap = ast_format_cap_destroy(tmp_cap);
+
return NULL;
}
@@ -759,7 +815,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
if (whennext != s->lasttimeout) {
if (s->owner->timingfd > -1) {
- float samp_rate = (float) ast_format_rate(s->fmt->format);
+ float samp_rate = (float) ast_format_rate(&s->fmt->format);
unsigned int rate;
rate = (unsigned int) roundf(samp_rate / ((float) whennext));
@@ -767,7 +823,7 @@ static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
ast_settimeout(s->owner, rate, ast_fsread_audio, s);
} else {
s->owner->streamid = ast_sched_add(s->owner->sched,
- whennext / (ast_format_rate(s->fmt->format) / 1000), ast_fsread_audio, s);
+ whennext / (ast_format_rate(&s->fmt->format) / 1000), ast_fsread_audio, s);
}
s->lasttimeout = whennext;
return FSREAD_SUCCESS_NOSCHED;
@@ -818,7 +874,7 @@ static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
if (whennext != s->lasttimeout) {
s->owner->vstreamid = ast_sched_add(s->owner->sched,
- whennext / (ast_format_rate(s->fmt->format) / 1000),
+ whennext / (ast_format_rate(&s->fmt->format) / 1000),
ast_fsread_video, s);
s->lasttimeout = whennext;
return FSREAD_SUCCESS_NOSCHED;
@@ -850,7 +906,7 @@ int ast_playstream(struct ast_filestream *s)
{
enum fsread_res res;
- if (s->fmt->format & AST_FORMAT_AUDIO_MASK)
+ if (AST_FORMAT_GET_TYPE(s->fmt->format.id) == AST_FORMAT_TYPE_AUDIO)
res = ast_readaudio_callback(s);
else
res = ast_readvideo_callback(s);
@@ -892,7 +948,7 @@ int ast_closestream(struct ast_filestream *f)
/* Stop a running stream if there is one */
if (f->owner) {
- if (f->fmt->format < AST_FORMAT_AUDIO_MASK) {
+ if (AST_FORMAT_GET_TYPE(f->fmt->format.id) == AST_FORMAT_TYPE_AUDIO) {
f->owner->stream = NULL;
AST_SCHED_DEL(f->owner->sched, f->owner->streamid);
ast_settimeout(f->owner, 0, NULL, NULL);
@@ -921,22 +977,22 @@ int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
buf = alloca(buflen);
if (buf == NULL)
return 0;
- return fileexists_core(filename, fmt, preflang, buf, buflen);
+ return fileexists_core(filename, fmt, preflang, buf, buflen, NULL) ? 1 : 0;
}
int ast_filedelete(const char *filename, const char *fmt)
{
- return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
+ return filehelper(filename, NULL, fmt, ACTION_DELETE);
}
int ast_filerename(const char *filename, const char *filename2, const char *fmt)
{
- return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
+ return filehelper(filename, filename2, fmt, ACTION_RENAME);
}
int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
{
- return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
+ return filehelper(filename, filename2, fmt, ACTION_COPY);
}
int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
@@ -966,7 +1022,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
vfs = ast_openvstream(chan, filename, preflang);
if (vfs) {
- ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format));
+ ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(&vfs->fmt->format));
}
if (ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM))
@@ -978,7 +1034,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
res = ast_playstream(fs);
if (!res && vfs)
res = ast_playstream(vfs);
- ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(chan->writeformat), preflang ? preflang : "default");
+ ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(&chan->writeformat), preflang ? preflang : "default");
return res;
}
@@ -986,7 +1042,7 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
{
FILE *bfile;
- struct ast_format *f;
+ struct ast_format_def *f;
struct ast_filestream *fs = NULL;
char *fn;
int format_found = 0;
@@ -1036,7 +1092,7 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con
int fd, myflags = 0;
/* compiler claims this variable can be used before initialization... */
FILE *bfile = NULL;
- struct ast_format *f;
+ struct ast_format_def *f;
struct ast_filestream *fs = NULL;
char *buf = NULL;
size_t size = 0;
@@ -1359,8 +1415,8 @@ int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *
char *ast_format_str_reduce(char *fmts)
{
- struct ast_format *f;
- struct ast_format *fmts_ptr[AST_MAX_FORMATS];
+ 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;
@@ -1437,7 +1493,7 @@ static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd,
{
#define FORMAT "%-10s %-10s %-20s\n"
#define FORMAT2 "%-10s %-10s %-20s\n"
- struct ast_format *f;
+ struct ast_format_def *f;
int count_fmt = 0;
switch (cmd) {
@@ -1459,7 +1515,7 @@ static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd,
AST_RWLIST_RDLOCK(&formats);
AST_RWLIST_TRAVERSE(&formats, f, list) {
- ast_cli(a->fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
+ ast_cli(a->fd, FORMAT2, ast_getformatname(&f->format), f->name, f->exts);
count_fmt++;
}
AST_RWLIST_UNLOCK(&formats);