From 4beb6deeaa99b3908c3715c2dc70b5f0e3e21c44 Mon Sep 17 00:00:00 2001 From: Luigi Rizzo Date: Tue, 4 Apr 2006 12:59:25 +0000 Subject: Largely simplify format handlers (for file copy etc.) collecting common functions in a single place and removing them from the individual handlers. The full description is on mantis, http://bugs.digium.com/view.php?id=6375 and only the ogg_vorbis handler needs to be converted to the new structure. As a result of this change, format_au.c and format_pcm_alaw.c should go away (in a separate commit) as their functionality (trivial) has been merged in another file. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@17243 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- file.c | 1136 +++++++++++++++++++------------------------ formats/Makefile | 5 + formats/format_au.c | 154 ++---- formats/format_g723.c | 160 ++---- formats/format_g726.c | 456 +++++------------ formats/format_g729.c | 141 ++---- formats/format_gsm.c | 151 ++---- formats/format_h263.c | 167 ++----- formats/format_h264.c | 196 +++----- formats/format_ilbc.c | 141 ++---- formats/format_ogg_vorbis.c | 294 +++++------ formats/format_pcm.c | 459 ++++++++++++----- formats/format_pcm_alaw.c | 161 ++---- formats/format_sln.c | 137 +----- formats/format_vox.c | 147 ++---- formats/format_wav.c | 230 ++++----- formats/format_wav_gsm.c | 264 +++++----- include/asterisk/file.h | 115 ++++- 18 files changed, 1736 insertions(+), 2778 deletions(-) diff --git a/file.c b/file.c index 9e3db7b0c..6dbd81f82 100644 --- a/file.c +++ b/file.c @@ -51,100 +51,65 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/pbx.h" #include "asterisk/linkedlists.h" +#include "asterisk/module.h" /* ast_update_use_count() */ -struct ast_format { - /*! Name of format */ - char name[80]; - /*! Extensions (separated by | if more than one) - this format can read. First is assumed for writing (e.g. .mp3) */ - char exts[80]; - /*! Format of frames it uses/provides (one only) */ - int format; - /*! Open an input stream, and start playback */ - struct ast_filestream * (*open)(FILE * f); - /*! Open an output stream, of a given file descriptor and comment it appropriately if applicable */ - struct ast_filestream * (*rewrite)(FILE *f, const char *comment); - /*! Write a frame to a channel */ - int (*write)(struct ast_filestream *, struct ast_frame *); - /*! seek num samples into file, whence(think normal seek) */ - int (*seek)(struct ast_filestream *, off_t offset, int whence); - /*! trunc file to current position */ - int (*trunc)(struct ast_filestream *fs); - /*! tell current position */ - off_t (*tell)(struct ast_filestream *fs); - /*! Read the next frame from the filestream (if available) and report when to get next one - (in samples) */ - struct ast_frame * (*read)(struct ast_filestream *, int *whennext); - /*! Close file, and destroy filestream structure */ - void (*close)(struct ast_filestream *); - /*! Retrieve file comment */ - char * (*getcomment)(struct ast_filestream *); - /*! Link */ - AST_LIST_ENTRY(ast_format) list; -}; - -struct ast_filestream { - /*! Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */ - struct ast_format *fmt; - int flags; - mode_t mode; - char *filename; - char *realfilename; - /*! Video file stream */ - struct ast_filestream *vfs; - /*! Transparently translate from another format -- just once */ - struct ast_trans_pvt *trans; - struct ast_tranlator_pvt *tr; - int lastwriteformat; - int lasttimeout; - struct ast_channel *owner; -}; +/* + * The following variable controls the layout of localized sound files. + * If 0, use the historical layout with prefix just before the filename + * (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm), + * if 1 put the prefix at the beginning of the filename + * (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm). + * The latter permits a language to be entirely in one directory. + */ +int ast_language_is_prefix; static AST_LIST_HEAD_STATIC(formats, ast_format); -int ast_format_register(const char *name, const char *exts, int format, - struct ast_filestream * (*open)(FILE *f), - struct ast_filestream * (*rewrite)(FILE *f, const char *comment), - int (*write)(struct ast_filestream *, struct ast_frame *), - int (*seek)(struct ast_filestream *, off_t sample_offset, int whence), - int (*trunc)(struct ast_filestream *), - off_t (*tell)(struct ast_filestream *), - struct ast_frame * (*read)(struct ast_filestream *, int *whennext), - void (*close)(struct ast_filestream *), - char * (*getcomment)(struct ast_filestream *)) +int ast_format_register(const struct ast_format *f) { struct ast_format *tmp; + + if (f->lockp == NULL) { + ast_log(LOG_WARNING, "Missing lock pointer, you need to supply one\n"); + return -1; + } if (AST_LIST_LOCK(&formats)) { ast_log(LOG_WARNING, "Unable to lock format list\n"); return -1; } AST_LIST_TRAVERSE(&formats, tmp, list) { - if (!strcasecmp(name, tmp->name)) { + if (!strcasecmp(f->name, tmp->name)) { AST_LIST_UNLOCK(&formats); - ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name); + ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", f->name); return -1; } - } - if (!(tmp = ast_malloc(sizeof(*tmp)))) { + } + tmp = ast_calloc(1, sizeof(struct ast_format)); + if (!tmp) { AST_LIST_UNLOCK(&formats); return -1; } - ast_copy_string(tmp->name, name, sizeof(tmp->name)); - ast_copy_string(tmp->exts, exts, sizeof(tmp->exts)); - tmp->open = open; - tmp->rewrite = rewrite; - tmp->read = read; - tmp->write = write; - tmp->seek = seek; - tmp->trunc = trunc; - tmp->tell = tell; - tmp->close = close; - tmp->format = format; - tmp->getcomment = getcomment; + *tmp = *f; + if (tmp->buf_size) { + /* + * Align buf_size properly, rounding up to the machine-specific + * alignment for pointers. + */ + struct _test_align { void *a, *b; } p; + int align = (char *)&p.b - (char *)&p.a; + tmp->buf_size = ((f->buf_size + align - 1)/align)*align; + } + + memset(&tmp->list, 0, sizeof(tmp->list)); + if (tmp->lockp->usecnt < 0) { + ast_mutex_init(&tmp->lockp->lock); + tmp->lockp->usecnt = 0; + } + AST_LIST_INSERT_HEAD(&formats, tmp, list); AST_LIST_UNLOCK(&formats); if (option_verbose > 1) - ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts); + ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", f->name, f->exts); return 0; } @@ -169,7 +134,7 @@ int ast_format_unregister(const char *name) if (tmp) { if (option_verbose > 1) - ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name); + ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name); } else ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name); @@ -189,9 +154,8 @@ int ast_stopstream(struct ast_channel *tmp) int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) { - struct ast_frame *trf; int res = -1; - int alt=0; + int alt = 0; if (f->frametype == AST_FRAME_VIDEO) { if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) { /* This is the audio portion. Call the video one... */ @@ -202,7 +166,7 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) } if (fs->vfs) return ast_writestream(fs->vfs, f); - /* Ignore */ + /* else ignore */ return 0; } else { /* Might / might not have mark set */ @@ -216,23 +180,23 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) res = fs->fmt->write(fs, f); if (res < 0) ast_log(LOG_WARNING, "Natural write failed\n"); - if (res > 0) + else if (res > 0) ast_log(LOG_WARNING, "Huh??\n"); - return res; } 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 != fs->lastwriteformat)) { + if (fs->trans && f->subclass != fs->lastwriteformat) { ast_translator_free_path(fs->trans); fs->trans = NULL; } if (!fs->trans) fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass); if (!fs->trans) - ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass)); + ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", + fs->fmt->name, ast_getformatname(f->subclass)); else { + struct ast_frame *trf; fs->lastwriteformat = f->subclass; - res = 0; /* Get the translated frame but don't consume the original in case they're using it on another stream */ trf = ast_translate(fs->trans, f, 0); if (trf) { @@ -242,17 +206,14 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) } else res = 0; } - return res; } + return res; } static int copy(const char *infile, const char *outfile) { - int ifd; - int ofd; - int res; - int len; - char buf[4096]; + int ifd, ofd, len; + char buf[4096]; /* XXX make it lerger. */ if ((ifd = open(infile, O_RDONLY)) < 0) { ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile); @@ -263,187 +224,312 @@ static int copy(const char *infile, const char *outfile) close(ifd); return -1; } - do { - len = read(ifd, buf, sizeof(buf)); + while ( (len = read(ifd, buf, sizeof(buf)) ) ) { + int res; if (len < 0) { ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); + break; } - if (len) { - res = write(ofd, buf, len); - if (res != len) { - ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); - } + /* XXX handle partial writes */ + res = write(ofd, buf, len); + if (res != len) { + ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno)); + len = -1; /* error marker */ + break; } - } while(len); + } close(ifd); close(ofd); - return 0; + if (len < 0) { + unlink(outfile); + return -1; /* error */ + } + return 0; /* success */ } +/*! + * \brief construct a filename. Absolute pathnames are preserved, + * relative names are prefixed by the sounds/ directory. + * The wav49 suffix is replaced by 'WAV'. + * Returns a malloc'ed string to be freed by the caller. + */ static char *build_filename(const char *filename, const char *ext) { - char *fn, type[16]; - int fnsize = 0; + char *fn = NULL; - if (!strcmp(ext, "wav49")) { - ast_copy_string(type, "WAV", sizeof(type)); - } else { - ast_copy_string(type, ext, sizeof(type)); - } - - if (filename[0] == '/') { - fnsize = strlen(filename) + strlen(type) + 2; - if ((fn = ast_malloc(fnsize))) - snprintf(fn, fnsize, "%s.%s", filename, type); - } else { - char tmp[AST_CONFIG_MAX_PATH] = ""; - - snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_VAR_DIR, "sounds"); - fnsize = strlen(tmp) + strlen(filename) + strlen(type) + 3; - if ((fn = ast_malloc(fnsize))) - snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, type); - } + if (!strcmp(ext, "wav49")) + ext = "WAV"; + if (filename[0] == '/') + asprintf(&fn, "%s.%s", filename, ext); + else + asprintf(&fn, "%s/sounds/%s.%s", + ast_config_AST_VAR_DIR, filename, ext); return fn; } +/* compare type against the list 'exts' */ +/* XXX need a better algorithm */ static int exts_compare(const char *exts, const char *type) { - char *stringp = NULL, *ext; char tmp[256]; + char *stringp = tmp, *ext; ast_copy_string(tmp, exts, sizeof(tmp)); - stringp = tmp; while ((ext = strsep(&stringp, "|"))) { - if (!strcmp(ext, type)) { + if (!strcmp(ext, type)) return 1; - } } return 0; } +static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile) +{ + struct ast_filestream *s; + + int l = sizeof(*s) + fmt->buf_size + fmt->desc_size; /* total allocation size */ + if ( (s = ast_calloc(1, l)) == NULL) + return NULL; + s->fmt = fmt; + s->f = bfile; + + if (fmt->desc_size) + s->private = ((char *)(s+1)) + fmt->buf_size; + if (fmt->buf_size) + s->buf = (char *)(s+1); + s->fr.src = fmt->name; + return s; +} + +/* + * Default implementations of open and rewrite. + * Only use them if you don't have expensive stuff to do. + */ +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; + int ret = -1; + + if (mode == WRAP_OPEN && f->open && f->open(s)) + ast_log(LOG_WARNING, "Unable to open format %s\n", f->name); + else if (mode == WRAP_REWRITE && f->rewrite && f->rewrite(s, comment)) + ast_log(LOG_WARNING, "Unable to rewrite format %s\n", f->name); + else { + /* preliminary checks succeed. update usecount */ + if (ast_mutex_lock(&f->lockp->lock)) { + ast_log(LOG_WARNING, "Unable to lock format %s\n", f->name); + return -1; + } + f->lockp->usecnt++; + ast_mutex_unlock(&f->lockp->lock); + ret = 0; + ast_update_use_count(); + } + return ret; +} + +static int rewrite_wrapper(struct ast_filestream *s, const char *comment) +{ + return fn_wrapper(s, comment, WRAP_REWRITE); +} + +static int open_wrapper(struct ast_filestream *s) +{ + return fn_wrapper(s, NULL, WRAP_OPEN); +} + enum file_action { - ACTION_EXISTS = 1, - ACTION_DELETE, - ACTION_RENAME, + ACTION_EXISTS = 1, /* return matching format if file exists, 0 otherwise */ + ACTION_DELETE, /* delete file, return 0 on success, -1 on error */ + ACTION_RENAME, /* rename file. return 0 on success, -1 on error */ ACTION_OPEN, - ACTION_COPY + ACTION_COPY /* copy file. return 0 on success, -1 on error */ }; -static int ast_filehelper(const char *filename, const char *filename2, const char *fmt, const enum file_action action) +/*! + * \brief perform various actions on a file. Second argument + * arg2 depends on the command: + * unused for EXISTS and DELETE + * destination file name (const char *) for COPY and RENAME + * struct ast_channel * for OPEN + */ +static int ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action) { - struct stat st; struct ast_format *f; - struct ast_filestream *s; - int res=0, ret = 0; - char *ext=NULL, *exts, *fn, *nfn; - FILE *bfile; - struct ast_channel *chan = (struct ast_channel *)filename2; - - /* Start with negative response */ - if (action == ACTION_EXISTS) - res = 0; - else - res = -1; - if (action == ACTION_OPEN) - ret = -1; - /* Check for a specific format */ + char *ext = NULL, *fn = NULL; + int res = (action == ACTION_EXISTS) ? 0 : -1; + if (AST_LIST_LOCK(&formats)) { ast_log(LOG_WARNING, "Unable to lock format list\n"); return res; } + /* Check for a specific format */ AST_LIST_TRAVERSE(&formats, f, list) { - if (!fmt || exts_compare(f->exts, fmt)) { - char *stringp=NULL; - exts = ast_strdupa(f->exts); - /* Try each kind of extension */ - stringp=exts; - ext = strsep(&stringp, "|"); - do { - fn = build_filename(filename, ext); - if (fn) { - res = stat(fn, &st); - if (!res) { - switch(action) { - case ACTION_EXISTS: - ret |= f->format; - break; - case ACTION_DELETE: - res = unlink(fn); - if (res) - ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno)); - break; - case ACTION_RENAME: - nfn = build_filename(filename2, ext); - if (nfn) { - res = rename(fn, nfn); - if (res) - ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno)); - free(nfn); - } - break; - case ACTION_COPY: - nfn = build_filename(filename2, ext); - if (nfn) { - res = copy(fn, nfn); - if (res) - ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno)); - free(nfn); - } - break; - case ACTION_OPEN: - if ((ret < 0) && ((chan->writeformat & f->format) || - ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) { - bfile = fopen(fn, "r"); - if (bfile) { - ret = 1; - s = f->open(bfile); - if (s) { - s->lasttimeout = -1; - s->fmt = f; - s->trans = NULL; - s->filename = NULL; - if (s->fmt->format < AST_FORMAT_MAX_AUDIO) - chan->stream = s; - else - chan->vstream = s; - } else { - fclose(bfile); - ast_log(LOG_WARNING, "Unable to open file on %s\n", fn); - ret = -1; - } - } else{ - ast_log(LOG_WARNING, "Couldn't open file %s\n", fn); - ret = -1; - } - } - break; - default: - ast_log(LOG_WARNING, "Unknown helper %d\n", action); - } - /* Conveniently this logic is the same for all */ - if (res) - break; - } + char *stringp; + + if (fmt && !exts_compare(f->exts, fmt)) + continue; + + /* Look for a file matching the supported extensions. + * The file must exist, and for OPEN, must match + * one of the formats supported by the channel. + */ + stringp = ast_strdupa(f->exts); + while ( (ext = strsep(&stringp, "|")) ) { + struct stat st; + fn = build_filename(filename, ext); + if (fn == NULL) + continue; + + if ( stat(fn, &st) ) { /* file not existent */ + free(fn); + continue; + } + /* for 'OPEN' we need to be sure that the format matches + * what the channel can process + */ + if (action == ACTION_OPEN) { + struct ast_channel *chan = (struct ast_channel *)arg2; + FILE *bfile; + struct ast_filestream *s; + + if ( !(chan->writeformat & f->format) && + !(f->format >= AST_FORMAT_MAX_AUDIO && fmt)) { free(fn); + continue; /* not a supported format */ + } + if ( (bfile = fopen(fn, "r")) == NULL) { + free(fn); + continue; /* cannot open file */ + } + s = get_filestream(f, bfile); + if (!s) { + fclose(bfile); + free(fn); /* cannot allocate descriptor */ + continue; } - ext = strsep(&stringp, "|"); - } while(ext); - + if (open_wrapper(s)) { + fclose(bfile); + free(fn); + free(s); + continue; /* cannot run open on file */ + } + /* ok this is good for OPEN */ + res = 1; /* found */ + s->lasttimeout = -1; + s->fmt = f; + s->trans = NULL; + s->filename = NULL; + if (s->fmt->format < AST_FORMAT_MAX_AUDIO) + chan->stream = s; + else + chan->vstream = s; + } + break; /* found the file */ } + if (ext) + break; + } + if (ext) { /* break out on a valid 'ext', so fn is also valid */ + char *nfn; + + switch (action) { + case ACTION_EXISTS: /* return the matching format */ + res |= f->format; + break; + + case ACTION_DELETE: + if ( (res = unlink(fn)) ) + ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno)); + break; + case ACTION_RENAME: + case ACTION_COPY: + nfn = build_filename((const char *)arg2, ext); + if (!nfn) + ast_log(LOG_WARNING, "Out of memory\n"); + else { + res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn); + if (res) + ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n", + action == ACTION_COPY ? "copy" : "rename", + fn, nfn, strerror(errno)); + free(nfn); + } + break; + case ACTION_OPEN: /* all done already! */ + break; + default: + ast_log(LOG_WARNING, "Unknown helper %d\n", action); + } + free(fn); } AST_LIST_UNLOCK(&formats); - if ((action == ACTION_EXISTS) || (action == ACTION_OPEN)) - res = ret ? ret : -1; return res; } + +/*! + * \brief helper routine to locate a file with a given format + * and language preference. + * Try preflang, preflang with stripped '_' suffix, or NULL. + * In the standard asterisk, language goes just before the last component. + * In an alternative configuration, the language should be a prefix + * to the actual filename. + * + * The last parameter(s) point to a buffer of sufficient size, + * which on success is filled with the matching filename. + */ +static int fileexists_core(const char *filename, const char *fmt, const char *preflang, + char *buf, int buflen) +{ + int res = -1; + int langlen; /* length of language string */ + const char *c = strrchr(filename, '/'); + int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */ + + if (preflang == NULL) + preflang = ""; + langlen = strlen(preflang); + + if (buflen < langlen + strlen(filename) + 2) { + ast_log(LOG_WARNING, "buffer too small\n"); + buf[0] = '\0'; /* set to empty */ + buf = alloca(langlen + strlen(filename) + 2); /* room for everything */ + } + if (buf == NULL) + return 0; + buf[0] = '\0'; + for (;;) { + if (ast_language_is_prefix) { /* new layout */ + if (langlen) { + strcpy(buf, preflang); + buf[langlen] = '/'; + strcpy(buf + langlen + 1, filename); + } else + strcpy(buf, filename); /* first copy the full string */ + } else { /* old layout */ + strcpy(buf, filename); /* first copy the full string */ + if (langlen) { + /* insert the language and suffix if needed */ + strcpy(buf + offset, preflang); + sprintf(buf + offset + langlen, "/%s", filename + offset); + } + } + res = ast_filehelper(buf, NULL, fmt, ACTION_EXISTS); + if (res > 0) /* found format */ + break; + if (langlen == 0) /* no more formats */ + break; + if (preflang[langlen] == '_') /* we are on the local suffix */ + langlen = 0; /* try again with no language */ + else + langlen = (c = strchr(preflang, '_')) ? c - preflang : 0; + } + return res; +} + struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang) { return ast_openstream_full(chan, filename, preflang, 0); @@ -451,23 +537,13 @@ struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *file struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis) { - /* This is a fairly complex routine. Essentially we should do - the following: - - 1) Find which file handlers produce our type of format. - 2) Look for a filename which it can handle. - 3) If we find one, then great. - 4) If not, see what files are there - 5) See what we can actually support - 6) Choose the one with the least costly translator path and - set it up. - - */ - int fmts = -1; - char filename2[256]=""; - char filename3[256]; - char *endpart; - int res; + /* + * Use fileexists_core() to find a file in a compatible + * language and format, set up a suitable translator, + * and open the stream. + */ + int fmts, res, buflen; + char *buf; if (!asis) { /* do this first, otherwise we detect the wrong writeformat */ @@ -475,25 +551,15 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char if (chan->generator) ast_deactivate_generator(chan); } - if (!ast_strlen_zero(preflang)) { - ast_copy_string(filename3, filename, sizeof(filename3)); - endpart = strrchr(filename3, '/'); - if (endpart) { - *endpart = '\0'; - endpart++; - snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart); - } else - snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename); - fmts = ast_fileexists(filename2, NULL, NULL); - if (fmts > 0) - fmts &= AST_FORMAT_AUDIO_MASK; - } - if (fmts < 1) { - ast_copy_string(filename2, filename, sizeof(filename2)); - fmts = ast_fileexists(filename2, NULL, NULL); - if (fmts > 0) - fmts &= AST_FORMAT_AUDIO_MASK; - } + if (preflang == NULL) + preflang = ""; + buflen = strlen(preflang) + strlen(filename) + 2; + 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) { ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename); return NULL; @@ -501,8 +567,7 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char chan->oldwriteformat = chan->writeformat; /* Set the channel to a format we can work with */ res = ast_set_write_format(chan, fmts); - - res = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN); + res = ast_filehelper(buf, chan, NULL, ACTION_OPEN); if (res >= 0) return chan->stream; return NULL; @@ -510,45 +575,30 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang) { - /* This is a fairly complex routine. Essentially we should do - the following: - - 1) Find which file handlers produce our type of format. - 2) Look for a filename which it can handle. - 3) If we find one, then great. - 4) If not, see what files are there - 5) See what we can actually support - 6) Choose the one with the least costly translator path and - set it up. - - */ - int fd = -1; - int fmts = -1; + /* As above, but for video. But here we don't have translators + * so we must enforce a format. + */ unsigned int format; - char filename2[256]; - char lang2[MAX_LANGUAGE]; - const char *fmt; + char *buf; + int buflen; + + if (preflang == NULL) + preflang = ""; + buflen = strlen(preflang) + strlen(filename) + 2; + buf = alloca(buflen); + if (buf == NULL) + return NULL; + for (format = AST_FORMAT_MAX_AUDIO << 1; format <= AST_FORMAT_MAX_VIDEO; format = format << 1) { + int fd; + const char *fmt; + if (!(chan->nativeformats & format)) continue; fmt = ast_getformatname(format); - if (!ast_strlen_zero(preflang)) { - snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename); - fmts = ast_fileexists(filename2, fmt, NULL); - if (fmts < 1) { - ast_copy_string(lang2, preflang, sizeof(lang2)); - snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename); - fmts = ast_fileexists(filename2, fmt, NULL); - } - } - if (fmts < 1) { - ast_copy_string(filename2, filename, sizeof(filename2)); - fmts = ast_fileexists(filename2, fmt, NULL); - } - if (fmts < 1) { + if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */ continue; - } - fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN); + fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN); if (fd >= 0) return chan->vstream; ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename); @@ -568,22 +618,13 @@ struct ast_frame *ast_readframe(struct ast_filestream *s) static int ast_readaudio_callback(void *data) { struct ast_filestream *s = data; - struct ast_frame *fr; int whennext = 0; while(!whennext) { - fr = s->fmt->read(s, &whennext); - if (fr) { - if (ast_write(s->owner, fr)) { + struct ast_frame *fr = s->fmt->read(s, &whennext); + if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) { + if (fr) ast_log(LOG_WARNING, "Failed to write frame\n"); - s->owner->streamid = -1; -#ifdef ZAPTEL_OPTIMIZATIONS - ast_settimeout(s->owner, 0, NULL, NULL); -#endif - return 0; - } - } else { - /* Stream has finished */ s->owner->streamid = -1; #ifdef ZAPTEL_OPTIMIZATIONS ast_settimeout(s->owner, 0, NULL, NULL); @@ -607,19 +648,13 @@ static int ast_readaudio_callback(void *data) static int ast_readvideo_callback(void *data) { struct ast_filestream *s = data; - struct ast_frame *fr; int whennext = 0; - while(!whennext) { - fr = s->fmt->read(s, &whennext); - if (fr) { - if (ast_write(s->owner, fr)) { + while (!whennext) { + struct ast_frame *fr = s->fmt->read(s, &whennext); + if (!fr || ast_write(s->owner, fr)) { /* no stream or error, as above */ + if (fr) ast_log(LOG_WARNING, "Failed to write frame\n"); - s->owner->vstreamid = -1; - return 0; - } - } else { - /* Stream has finished */ s->owner->vstreamid = -1; return 0; } @@ -664,17 +699,12 @@ off_t ast_tellstream(struct ast_filestream *fs) int ast_stream_fastforward(struct ast_filestream *fs, off_t ms) { - /* I think this is right, 8000 samples per second, 1000 ms a second so 8 - * samples per ms */ - off_t samples = ms * 8; - return ast_seekstream(fs, samples, SEEK_CUR); + return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR); } int ast_stream_rewind(struct ast_filestream *fs, off_t ms) { - off_t samples = ms * 8; - samples = samples * -1; - return ast_seekstream(fs, samples, SEEK_CUR); + return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR); } int ast_closestream(struct ast_filestream *f) @@ -699,10 +729,8 @@ int ast_closestream(struct ast_filestream *f) } } /* destroy the translator on exit */ - if (f->trans) { + if (f->trans) ast_translator_free_path(f->trans); - f->trans = NULL; - } if (f->realfilename && f->filename) { size = strlen(f->filename) + strlen(f->realfilename) + 15; @@ -712,69 +740,42 @@ int ast_closestream(struct ast_filestream *f) ast_safe_system(cmd); } - if (f->filename) { + if (f->filename) free(f->filename); - f->filename = NULL; - } - if (f->realfilename) { + if (f->realfilename) free(f->realfilename); - f->realfilename = NULL; - } - if (f->vfs) { + if (f->fmt->close) + f->fmt->close(f); + fclose(f->f); + if (f->vfs) ast_closestream(f->vfs); - f->vfs = NULL; + if (ast_mutex_lock(&f->fmt->lockp->lock)) { + ast_log(LOG_WARNING, "Unable to lock format %s\n", f->fmt->name); + } else { + f->fmt->lockp->usecnt--; + ast_mutex_unlock(&f->fmt->lockp->lock); + ast_update_use_count(); } - f->fmt->close(f); + free(f); return 0; } +/* + * Look the various language-specific places where a file could exist. + */ int ast_fileexists(const char *filename, const char *fmt, const char *preflang) { - char filename2[256]; - char tmp[256]; - char *postfix; - char *prefix; - char *c; - char lang2[MAX_LANGUAGE]; - int res = -1; - if (!ast_strlen_zero(preflang)) { - /* Insert the language between the last two parts of the path */ - ast_copy_string(tmp, filename, sizeof(tmp)); - c = strrchr(tmp, '/'); - if (c) { - *c = '\0'; - postfix = c+1; - prefix = tmp; - snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix); - } else { - postfix = tmp; - prefix=""; - snprintf(filename2, sizeof(filename2), "%s/%s", preflang, postfix); - } - res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS); - if (res < 1) { - char *stringp=NULL; - ast_copy_string(lang2, preflang, sizeof(lang2)); - stringp=lang2; - strsep(&stringp, "_"); - /* If language is a specific locality of a language (like es_MX), strip the locality and try again */ - if (strcmp(lang2, preflang)) { - if (ast_strlen_zero(prefix)) { - snprintf(filename2, sizeof(filename2), "%s/%s", lang2, postfix); - } else { - snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix); - } - res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS); - } - } - } - - /* Fallback to no language (usually winds up being American English) */ - if (res < 1) { - res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS); - } - return res; + char *buf; + int buflen; + + if (preflang == NULL) + preflang = ""; + buflen = strlen(preflang) + strlen(filename) + 2; /* room for everything */ + buf = alloca(buflen); + if (buf == NULL) + return 0; + return fileexists_core(filename, fmt, preflang, buf, buflen); } int ast_filedelete(const char *filename, const char *fmt) @@ -835,33 +836,30 @@ struct ast_filestream *ast_readfile(const char *filename, const char *type, cons } AST_LIST_TRAVERSE(&formats, f, list) { - if (fs) - break; - + fs = NULL; if (!exts_compare(f->exts, type)) continue; fn = build_filename(filename, type); + errno = 0; bfile = fopen(fn, "r"); - if (bfile) { - errno = 0; - - if (!(fs = f->open(bfile))) { - ast_log(LOG_WARNING, "Unable to open %s\n", fn); - fclose(bfile); - free(fn); - continue; - } - - fs->trans = NULL; - fs->fmt = f; - fs->flags = flags; - fs->mode = mode; - fs->filename = strdup(filename); - fs->vfs = NULL; - } else if (errno != EEXIST) - ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno)); - free(fn); + if (!bfile || (fs = get_filestream(f, bfile)) == NULL || + open_wrapper(fs) ) { + ast_log(LOG_WARNING, "Unable to open %s\n", fn); + fclose(bfile); + free(fn); + if (fs) + free(fs); + continue; + } + /* found it */ + fs->trans = NULL; + fs->fmt = f; + fs->flags = flags; + fs->mode = mode; + fs->filename = strdup(filename); + fs->vfs = NULL; + break; } AST_LIST_UNLOCK(&formats); @@ -878,7 +876,6 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con FILE *bfile = NULL; struct ast_format *f; struct ast_filestream *fs = NULL; - char *fn, *orig_fn = NULL; char *buf = NULL; size_t size = 0; @@ -888,8 +885,8 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con } /* 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 */ if (flags & O_APPEND) { - /* We really can't use O_APPEND as it will break WAV header updates */ flags &= ~O_APPEND; } else { myflags = O_TRUNC; @@ -897,7 +894,11 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con myflags |= O_WRONLY | O_CREAT; + /* XXX need to fix this - we should just do the fopen, + * not open followed by fdopen() + */ AST_LIST_TRAVERSE(&formats, f, list) { + char *fn, *orig_fn = NULL; if (fs) break; @@ -919,7 +920,7 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con if (ast_opt_cache_record_files && (fd > -1)) { char *c; - fclose(bfile); + 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. @@ -949,29 +950,31 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con } if (fd > -1) { errno = 0; - if ((fs = f->rewrite(bfile, comment))) { - fs->trans = NULL; - fs->fmt = f; - fs->flags = flags; - fs->mode = mode; - if (orig_fn) { - fs->realfilename = strdup(orig_fn); - fs->filename = strdup(fn); - } else { - fs->realfilename = NULL; - fs->filename = 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 { + fs = get_filestream(f, bfile); + if (!fs || rewrite_wrapper(fs, comment)) { ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn); close(fd); if (orig_fn) { unlink(fn); unlink(orig_fn); } + if (fs) + free(fs); + } + fs->trans = NULL; + fs->fmt = f; + fs->flags = flags; + fs->mode = mode; + if (orig_fn) { + fs->realfilename = strdup(orig_fn); + fs->filename = strdup(fn); + } else { + fs->realfilename = NULL; + fs->filename = 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) @@ -989,176 +992,73 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con return fs; } -int ast_waitstream(struct ast_channel *c, const char *breakon) -{ - /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */ - int res; - struct ast_frame *fr; - if (!breakon) breakon = ""; - while(c->stream) { - res = ast_sched_wait(c->sched); - if ((res < 0) && !c->timingfunc) { - ast_stopstream(c); - break; - } - if (res < 0) - res = 1000; - res = ast_waitfor(c, res); - if (res < 0) { - ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno)); - return res; - } else if (res > 0) { - fr = ast_read(c); - if (!fr) { -#if 0 - ast_log(LOG_DEBUG, "Got hung up\n"); -#endif - return -1; - } - - switch(fr->frametype) { - case AST_FRAME_DTMF: - res = fr->subclass; - if (strchr(breakon, res)) { - ast_frfree(fr); - return res; - } - break; - case AST_FRAME_CONTROL: - switch(fr->subclass) { - case AST_CONTROL_HANGUP: - ast_frfree(fr); - return -1; - case AST_CONTROL_RINGING: - case AST_CONTROL_ANSWER: - case AST_CONTROL_VIDUPDATE: - /* Unimportant */ - break; - default: - ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass); - } - } - /* Ignore */ - ast_frfree(fr); - } - ast_sched_runq(c->sched); - } - return (c->_softhangup ? -1 : 0); -} - -int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms) +/*! + * \brief the core of all waitstream() functions + */ +static int waitstream_core(struct ast_channel *c, const char *breakon, + const char *forward, const char *rewind, int skip_ms, + int audiofd, int cmdfd, const char *context) { - int res; - struct ast_frame *fr; - if (!breakon) - breakon = ""; + breakon = ""; if (!forward) - forward = ""; + forward = ""; if (!rewind) - rewind = ""; - - while(c->stream) { - res = ast_sched_wait(c->sched); - if ((res < 0) && !c->timingfunc) { - ast_stopstream(c); - break; - } - if (res < 0) - res = 1000; - res = ast_waitfor(c, res); - if (res < 0) { - ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno)); - return res; - } else - if (res > 0) { - fr = ast_read(c); - if (!fr) { -#if 0 - ast_log(LOG_DEBUG, "Got hung up\n"); -#endif - return -1; - } - - switch(fr->frametype) { - case AST_FRAME_DTMF: - res = fr->subclass; - if (strchr(forward,res)) { - ast_stream_fastforward(c->stream, ms); - } else if (strchr(rewind,res)) { - ast_stream_rewind(c->stream, ms); - } else if (strchr(breakon, res)) { - ast_frfree(fr); - return res; - } - break; - case AST_FRAME_CONTROL: - switch(fr->subclass) { - case AST_CONTROL_HANGUP: - ast_frfree(fr); - return -1; - case AST_CONTROL_RINGING: - case AST_CONTROL_ANSWER: - /* Unimportant */ - break; - default: - ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass); - } - } - /* Ignore */ - ast_frfree(fr); - } else - ast_sched_runq(c->sched); - - - } - return (c->_softhangup ? -1 : 0); -} - -int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd) -{ - int res; - int ms; - int outfd; - struct ast_frame *fr; - struct ast_channel *rchan; - - if (!breakon) - breakon = ""; + rewind = ""; - while(c->stream) { - ms = ast_sched_wait(c->sched); - if ((ms < 0) && !c->timingfunc) { + while (c->stream) { + int res; + int ms = ast_sched_wait(c->sched); + if (ms < 0 && !c->timingfunc) { ast_stopstream(c); break; } if (ms < 0) ms = 1000; - 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)); - return -1; - } else if (outfd > -1) { - /* The FD we were watching has something waiting */ - return 1; - } else if (rchan) { - fr = ast_read(c); - if (!fr) { -#if 0 - ast_log(LOG_DEBUG, "Got hung up\n"); -#endif + if (!cmdfd) { + res = ast_waitfor(c, ms); + if (res < 0) { + ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno)); + 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)); return -1; + } else if (outfd > -1) { /* this requires cmdfd set */ + /* The FD we were watching has something waiting */ + return 1; } - + /* 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); + if (!fr) + return -1; switch(fr->frametype) { case AST_FRAME_DTMF: - res = fr->subclass; - if (strchr(breakon, res)) { - ast_frfree(fr); - return res; + if (context) { + const char exten[2] = { fr->subclass, '\0' }; + if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) { + ast_frfree(fr); + return res; + } + } else { + res = fr->subclass; + if (strchr(forward,res)) { + ast_stream_fastforward(c->stream, skip_ms); + } else if (strchr(rewind,res)) { + ast_stream_rewind(c->stream, skip_ms); + } else if (strchr(breakon, res)) { + ast_frfree(fr); + return res; + } } break; case AST_FRAME_CONTROL: @@ -1178,7 +1078,7 @@ int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, if (audiofd > -1) write(audiofd, fr->data, fr->datalen); } - /* Ignore */ + /* Ignore all others */ ast_frfree(fr); } ast_sched_runq(c->sched); @@ -1186,65 +1086,32 @@ int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, return (c->_softhangup ? -1 : 0); } +int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms) +{ + return waitstream_core(c, breakon, forward, rewind, ms, + -1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */); +} + +int ast_waitstream(struct ast_channel *c, const char *breakon) +{ + return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL); +} + +int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd) +{ + return waitstream_core(c, breakon, NULL, NULL, 0, + audiofd, cmdfd, NULL /* no context */); +} + int ast_waitstream_exten(struct ast_channel *c, const char *context) { /* Waitstream, with return in the case of a valid 1 digit extension */ /* in the current or specified context being pressed */ - /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */ - int res; - struct ast_frame *fr; - char exten[AST_MAX_EXTENSION]; - - if (!context) context = c->context; - while(c->stream) { - res = ast_sched_wait(c->sched); - if ((res < 0) && !c->timingfunc) { - ast_stopstream(c); - break; - } - if (res < 0) - res = 1000; - res = ast_waitfor(c, res); - if (res < 0) { - ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno)); - return res; - } else if (res > 0) { - fr = ast_read(c); - if (!fr) { -#if 0 - ast_log(LOG_DEBUG, "Got hung up\n"); -#endif - return -1; - } - - switch(fr->frametype) { - case AST_FRAME_DTMF: - res = fr->subclass; - snprintf(exten, sizeof(exten), "%c", res); - if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) { - ast_frfree(fr); - return res; - } - break; - case AST_FRAME_CONTROL: - switch(fr->subclass) { - case AST_CONTROL_HANGUP: - ast_frfree(fr); - return -1; - case AST_CONTROL_RINGING: - case AST_CONTROL_ANSWER: - /* Unimportant */ - break; - default: - ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass); - } - } - /* Ignore */ - ast_frfree(fr); - } - ast_sched_runq(c->sched); - } - return (c->_softhangup ? -1 : 0); + + if (!context) + context = c->context; + return waitstream_core(c, NULL, NULL, NULL, 0, + -1, -1, context); } static int show_file_formats(int fd, int argc, char *argv[]) @@ -1272,7 +1139,6 @@ static int show_file_formats(int fd, int argc, char *argv[]) return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 - } struct ast_cli_entry show_file = diff --git a/formats/Makefile b/formats/Makefile index 80143e58f..8b47f8d2d 100644 --- a/formats/Makefile +++ b/formats/Makefile @@ -13,6 +13,11 @@ MODS:=$(patsubst %.c,%.so,$(wildcard format_*.c)) +MODS:=$(filter-out format_pcm_alaw.so,$(MODS)) +MODS:=$(filter-out format_au.so,$(MODS)) + +# merged. format_pcm_alaw.so +# merged. format_au.so # # OGG/Vorbis format # diff --git a/formats/format_au.c b/formats/format_au.c index e8be324ae..06626a052 100644 --- a/formats/format_au.c +++ b/formats/format_au.c @@ -44,7 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/endian.h" -#define BUF_SIZE 160 +#define BUF_SIZE 160 /* samples and 1 byte per sample */ #define AU_HEADER_SIZE 24 #define AU_HEADER(var) u_int32_t var[6] @@ -58,26 +58,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define AU_ENC_8BIT_ULAW 1 -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_channel *owner; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - short buf[BUF_SIZE]; -}; - - -AST_MUTEX_DEFINE_STATIC(au_lock); -static int localusecnt = 0; - -static char *name = "au"; -static char *desc = "Sun Microsystems AU format (signed linear)"; -static char *exts = "au"; - - #define AU_MAGIC 0x2e736e64 #if __BYTE_ORDER == __BIG_ENDIAN #define htoll(b) (b) @@ -130,7 +110,7 @@ static int check_header(FILE *f) return -1; } sample_rate = ltohl(header[AU_HDR_SAMPLE_RATE_OFF]); - if (sample_rate != 8000) { + if (sample_rate != DEFAULT_SAMPLE_RATE) { ast_log(LOG_WARNING, "Sample rate can only be 8000 not %d\n", sample_rate); return -1; } @@ -189,7 +169,7 @@ static int write_header(FILE *f) header[AU_HDR_HDR_SIZE_OFF] = htoll(AU_HEADER_SIZE); header[AU_HDR_DATA_SIZE_OFF] = 0; header[AU_HDR_ENCODING_OFF] = htoll(AU_ENC_8BIT_ULAW); - header[AU_HDR_SAMPLE_RATE_OFF] = htoll(8000); + header[AU_HDR_SAMPLE_RATE_OFF] = htoll(DEFAULT_SAMPLE_RATE); header[AU_HDR_CHANNELS_OFF] = htoll(1); /* Write an au header, ignoring sizes which will be filled in later */ @@ -201,97 +181,36 @@ static int write_header(FILE *f) return 0; } -static struct ast_filestream *au_open(FILE *f) -{ - struct ast_filestream *tmp; - - if (!(tmp = malloc(sizeof(struct ast_filestream)))) { - ast_log(LOG_ERROR, "Out of memory\n"); - return NULL; - } - - memset(tmp, 0, sizeof(struct ast_filestream)); - if (check_header(f) < 0) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&au_lock)) { - ast_log(LOG_WARNING, "Unable to lock au count\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_ULAW; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - localusecnt++; - ast_mutex_unlock(&au_lock); - ast_update_use_count(); - return tmp; -} - -static struct ast_filestream *au_rewrite(FILE *f, const char *comment) +static int au_open(struct ast_filestream *s) { - struct ast_filestream *tmp; - - if ((tmp = malloc(sizeof(struct ast_filestream))) == NULL) { - ast_log(LOG_ERROR, "Out of memory\n"); - return NULL; - } - - memset(tmp, 0, sizeof(struct ast_filestream)); - if (write_header(f)) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&au_lock)) { - ast_log(LOG_WARNING, "Unable to lock au count\n"); - free(tmp); - return NULL; - } - tmp->f = f; - localusecnt++; - ast_mutex_unlock(&au_lock); - ast_update_use_count(); - return tmp; + if (check_header(s->f) < 0) + return -1; + return 0; } -static void au_close(struct ast_filestream *s) +static int au_rewrite(struct ast_filestream *s, const char *comment) { - if (ast_mutex_lock(&au_lock)) { - ast_log(LOG_WARNING, "Unable to lock au count\n"); - return; - } - localusecnt--; - ast_mutex_unlock(&au_lock); - ast_update_use_count(); - fclose(s->f); - free(s); + if (write_header(s->f)) + return -1; + return 0; } static struct ast_frame *au_read(struct ast_filestream *s, int *whennext) { int res; - int delay; /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_ULAW; - s->fr.offset = AST_FRIENDLY_OFFSET; s->fr.mallocd = 0; - s->fr.data = s->buf; - if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = res; + *whennext = s->fr.samples = res; s->fr.datalen = res; - delay = s->fr.samples; - *whennext = delay; return &s->fr; } @@ -308,8 +227,8 @@ static int au_write(struct ast_filestream *fs, struct ast_frame *f) return -1; } if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { - ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); - return -1; + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; } update_header(fs->f); return 0; @@ -348,44 +267,45 @@ static int au_trunc(struct ast_filestream *fs) static off_t au_tell(struct ast_filestream *fs) { - off_t offset; - - offset = ftello(fs->f); + off_t offset = ftello(fs->f); return offset - AU_HEADER_SIZE; } -static char *au_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format au_f = { + .name = "au", + .exts = "au", + .format = AST_FORMAT_ULAW, + .open = au_open, + .rewrite = au_rewrite, + .write = au_write, + .seek = au_seek, + .trunc = au_trunc, + .tell = au_tell, + .read = au_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, /* this many shorts */ + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_ULAW, - au_open, - au_rewrite, - au_write, - au_seek, - au_trunc, - au_tell, - au_read, - au_close, - au_getcomment); + return ast_format_register(&au_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(au_f.name); } int usecount() { - return localusecnt; + return me.usecnt; } char *description() { - return desc; + return "Sun Microsystems AU format (signed linear)"; } char *key() diff --git a/formats/format_g723.c b/formats/format_g723.c index 5eff15df3..7ddc79b04 100644 --- a/formats/format_g723.c +++ b/formats/format_g723.c @@ -47,81 +47,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define G723_MAX_SIZE 1024 -struct ast_filestream { - /* First entry MUST be reserved for the channel type */ - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_filestream *next; - struct ast_frame *fr; /* Frame representation of buf */ - struct timeval orig; /* Original frame time */ - char buf[G723_MAX_SIZE + AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ -}; - - -AST_MUTEX_DEFINE_STATIC(g723_lock); -static int glistcnt = 0; - -static char *name = "g723sf"; -static char *desc = "G.723.1 Simple Timestamp File Format"; -static char *exts = "g723|g723sf"; - -static struct ast_filestream *g723_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g723_lock)) { - ast_log(LOG_WARNING, "Unable to lock g723 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr = (struct ast_frame *)tmp->buf; - tmp->fr->data = tmp->buf + sizeof(struct ast_frame); - tmp->fr->frametype = AST_FRAME_VOICE; - tmp->fr->subclass = AST_FORMAT_G723_1; - /* datalen will vary for each frame */ - tmp->fr->src = name; - tmp->fr->mallocd = 0; - glistcnt++; - ast_mutex_unlock(&g723_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *g723_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g723_lock)) { - ast_log(LOG_WARNING, "Unable to lock g723 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&g723_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext) { unsigned short size; int res; int delay; /* Read the delay for the next packet, and schedule again if necessary */ + /* XXX is this ignored ? */ if (fread(&delay, 1, 4, s->f) == 4) delay = ntohl(delay); else @@ -133,7 +65,7 @@ static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext) } /* Looks like we have a frame to read from here */ size = ntohs(size); - if (size > G723_MAX_SIZE - sizeof(struct ast_frame)) { + if (size > G723_MAX_SIZE) { ast_log(LOG_WARNING, "Size %d is invalid\n", size); /* The file is apparently no longer any good, as we shouldn't ever get frames even close to this @@ -141,50 +73,24 @@ static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext) return NULL; } /* Read the data into the buffer */ - s->fr->offset = AST_FRIENDLY_OFFSET; - s->fr->datalen = size; - s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET; - if ((res = fread(s->fr->data, 1, size, s->f)) != size) { + s->fr.frametype = AST_FRAME_VOICE; + s->fr.subclass = AST_FORMAT_G723_1; + s->fr.mallocd = 0; + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, size); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != size) { ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno)); return NULL; } -#if 0 - /* Average out frames <= 50 ms */ - if (delay < 50) - s->fr->timelen = 30; - else - s->fr->timelen = delay; -#else - s->fr->samples = 240; -#endif - *whennext = s->fr->samples; - return s->fr; + *whennext = s->fr.samples = 240; + return &s->fr; } -static void g723_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&g723_lock)) { - ast_log(LOG_WARNING, "Unable to lock g723 list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&g723_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; -} - - -static int g723_write(struct ast_filestream *fs, struct ast_frame *f) +static int g723_write(struct ast_filestream *s, struct ast_frame *f) { u_int32_t delay; u_int16_t size; int res; - if (fs->fr) { - ast_log(LOG_WARNING, "Asked to write on a read stream??\n"); - return -1; - } + /* XXX there used to be a check s->fr means a read stream */ if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -198,16 +104,16 @@ static int g723_write(struct ast_filestream *fs, struct ast_frame *f) ast_log(LOG_WARNING, "Short frame ignored (%d bytes long?)\n", f->datalen); return 0; } - if ((res = fwrite(&delay, 1, 4, fs->f)) != 4) { + if ((res = fwrite(&delay, 1, 4, s->f)) != 4) { ast_log(LOG_WARNING, "Unable to write delay: res=%d (%s)\n", res, strerror(errno)); return -1; } size = htons(f->datalen); - if ((res = fwrite(&size, 1, 2, fs->f)) != 2) { + if ((res = fwrite(&size, 1, 2, s->f)) != 2) { ast_log(LOG_WARNING, "Unable to write size: res=%d (%s)\n", res, strerror(errno)); return -1; } - if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { + if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) { ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno)); return -1; } @@ -232,43 +138,41 @@ static off_t g723_tell(struct ast_filestream *fs) return -1; } -static char *g723_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format g723_1_f = { + .name = "g723sf", + .exts = "g723|g723sf", + .format = AST_FORMAT_G723_1, + .write = g723_write, + .seek = g723_seek, + .trunc = g723_trunc, + .tell = g723_tell, + .read = g723_read, + .buf_size = G723_MAX_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_G723_1, - g723_open, - g723_rewrite, - g723_write, - g723_seek, - g723_trunc, - g723_tell, - g723_read, - g723_close, - g723_getcomment); - - + return ast_format_register(&g723_1_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(g723_1_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "G.723.1 Simple Timestamp File Format"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_g726.c b/formats/format_g726.c index 70c6ac88f..01b94c07f 100644 --- a/formats/format_g726.c +++ b/formats/format_g726.c @@ -58,6 +58,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* We can only read/write chunks of FRAME_TIME ms G.726 data */ #define FRAME_TIME 10 /* 10 ms size */ +#define BUF_SIZE (5*FRAME_TIME) /* max frame size in bytes ? */ /* Frame sizes in bytes */ static int frame_size[4] = { FRAME_TIME * 5, @@ -66,293 +67,79 @@ static int frame_size[4] = { FRAME_TIME * 2 }; -struct ast_filestream { - /* Do not place anything before "reserved" */ - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Open file descriptor */ - int rate; /* RATE_* defines */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char g726[FRAME_TIME * 5]; /* G.726 encoded voice */ +struct g726_desc { + int rate; /* RATE_* defines */ }; -AST_MUTEX_DEFINE_STATIC(g726_lock); -static int glistcnt = 0; - -static char *desc = "Raw G.726 (16/24/32/40kbps) data"; -static char *name40 = "g726-40"; -static char *name32 = "g726-32"; -static char *name24 = "g726-24"; -static char *name16 = "g726-16"; -static char *exts40 = "g726-40"; -static char *exts32 = "g726-32"; -static char *exts24 = "g726-24"; -static char *exts16 = "g726-16"; - /* * Rate dependant format functions (open, rewrite) */ -static struct ast_filestream *g726_40_open(FILE *f) +static int g726_open(struct ast_filestream *tmp, int rate) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_40; - tmp->fr.data = tmp->g726; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_G726; - /* datalen will vary for each frame */ - tmp->fr.src = name40; - tmp->fr.mallocd = 0; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } - return tmp; + struct g726_desc *s = (struct g726_desc *)tmp->private; + s->rate = rate; + if (option_debug) + ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", + 40 - s->rate * 8); + return 0; } -static struct ast_filestream *g726_32_open(FILE *f) +static int g726_40_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_32; - tmp->fr.data = tmp->g726; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_G726; - /* datalen will vary for each frame */ - tmp->fr.src = name32; - tmp->fr.mallocd = 0; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } - return tmp; + return g726_open(s, RATE_40); } -static struct ast_filestream *g726_24_open(FILE *f) +static int g726_32_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_24; - tmp->fr.data = tmp->g726; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_G726; - /* datalen will vary for each frame */ - tmp->fr.src = name24; - tmp->fr.mallocd = 0; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } - return tmp; + return g726_open(s, RATE_32); } -static struct ast_filestream *g726_16_open(FILE *f) +static int g726_24_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_16; - tmp->fr.data = tmp->g726; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_G726; - /* datalen will vary for each frame */ - tmp->fr.src = name16; - tmp->fr.mallocd = 0; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } - return tmp; + return g726_open(s, RATE_24); } -static struct ast_filestream *g726_40_rewrite(FILE *f, const char *comment) +static int g726_16_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_40; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + return g726_open(s, RATE_16); } -static struct ast_filestream *g726_32_rewrite(FILE *f, const char *comment) +static int g726_40_rewrite(struct ast_filestream *s, const char *comment) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_32; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + return g726_open(s, RATE_40); } -static struct ast_filestream *g726_24_rewrite(FILE *f, const char *comment) +static int g726_32_rewrite(struct ast_filestream *s, const char *comment) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_24; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + return g726_open(s, RATE_32); } -static struct ast_filestream *g726_16_rewrite(FILE *f, const char *comment) +static int g726_24_rewrite(struct ast_filestream *s, const char *comment) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->rate = RATE_16; - glistcnt++; - if (option_debug) - ast_log(LOG_DEBUG, "Created filestream G.726-%dk.\n", - 40 - tmp->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + return g726_open(s, RATE_24); } -/* - * Rate independent format functions (close, read, write) - */ -static void g726_close(struct ast_filestream *s) +static int g726_16_rewrite(struct ast_filestream *s, const char *comment) { - if (ast_mutex_lock(&g726_lock)) { - ast_log(LOG_WARNING, "Unable to lock g726 list.\n"); - return; - } - glistcnt--; - if (option_debug) - ast_log(LOG_DEBUG, "Closed filestream G.726-%dk.\n", 40 - s->rate * 8); - ast_mutex_unlock(&g726_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; + return g726_open(s, RATE_16); } +/* + * Rate independent format functions (read, write) + */ + static struct ast_frame *g726_read(struct ast_filestream *s, int *whennext) { int res; + struct g726_desc *fs = (struct g726_desc *)s->private; + /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_G726; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.samples = 8 * FRAME_TIME; - s->fr.datalen = frame_size[s->rate]; s->fr.mallocd = 0; - s->fr.data = s->g726; - if ((res = fread(s->g726, 1, s->fr.datalen, s->f)) != s->fr.datalen) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, frame_size[fs->rate]); + s->fr.samples = 8 * FRAME_TIME; + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; @@ -361,9 +148,11 @@ static struct ast_frame *g726_read(struct ast_filestream *s, int *whennext) return &s->fr; } -static int g726_write(struct ast_filestream *fs, struct ast_frame *f) +static int g726_write(struct ast_filestream *s, struct ast_frame *f) { int res; + struct g726_desc *fs = (struct g726_desc *)s->private; + if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -378,19 +167,14 @@ static int g726_write(struct ast_filestream *fs, struct ast_frame *f) f->datalen, frame_size[fs->rate]); return -1; } - if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { - ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", - res, frame_size[fs->rate], strerror(errno)); + if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", + res, frame_size[fs->rate], strerror(errno)); return -1; } return 0; } -static char *g726_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int g726_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { return -1; @@ -406,107 +190,107 @@ static off_t g726_tell(struct ast_filestream *fs) return -1; } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format f[] = { + { + .name = "g726-40", + .exts = "g726-40", + .format = AST_FORMAT_G726, + .open = g726_40_open, + .rewrite = g726_40_rewrite, + .write = g726_write, + .seek = g726_seek, + .trunc = g726_trunc, + .tell = g726_tell, + .read = g726_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct g726_desc), + .lockp = &me, + }, + { + .name = "g726-32", + .exts = "g726-32", + .format = AST_FORMAT_G726, + .open = g726_32_open, + .rewrite = g726_32_rewrite, + .write = g726_write, + .seek = g726_seek, + .trunc = g726_trunc, + .tell = g726_tell, + .read = g726_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct g726_desc), + .lockp = &me, + }, + { + .name = "g726-24", + .exts = "g726-24", + .format = AST_FORMAT_G726, + .open = g726_24_open, + .rewrite = g726_24_rewrite, + .write = g726_write, + .seek = g726_seek, + .trunc = g726_trunc, + .tell = g726_tell, + .read = g726_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct g726_desc), + .lockp = &me, + }, + { + .name = "g726-16", + .exts = "g726-16", + .format = AST_FORMAT_G726, + .open = g726_16_open, + .rewrite = g726_16_rewrite, + .write = g726_write, + .seek = g726_seek, + .trunc = g726_trunc, + .tell = g726_tell, + .read = g726_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct g726_desc), + .lockp = &me, + }, + { .format = 0 } /* terminator */ +}; + /* * Module interface (load_module, unload_module, usecount, description, key) */ int load_module() { - int res; + int i; - res = ast_format_register(name40, exts40, AST_FORMAT_G726, - g726_40_open, - g726_40_rewrite, - g726_write, - g726_seek, - g726_trunc, - g726_tell, - g726_read, - g726_close, - g726_getcomment); - if (res) { - ast_log(LOG_WARNING, "Failed to register format %s.\n", name40); - return(-1); - } - res = ast_format_register(name32, exts32, AST_FORMAT_G726, - g726_32_open, - g726_32_rewrite, - g726_write, - g726_seek, - g726_trunc, - g726_tell, - g726_read, - g726_close, - g726_getcomment); - if (res) { - ast_log(LOG_WARNING, "Failed to register format %s.\n", name32); - return(-1); - } - res = ast_format_register(name24, exts24, AST_FORMAT_G726, - g726_24_open, - g726_24_rewrite, - g726_write, - g726_seek, - g726_trunc, - g726_tell, - g726_read, - g726_close, - g726_getcomment); - if (res) { - ast_log(LOG_WARNING, "Failed to register format %s.\n", name24); - return(-1); - } - res = ast_format_register(name16, exts16, AST_FORMAT_G726, - g726_16_open, - g726_16_rewrite, - g726_write, - g726_seek, - g726_trunc, - g726_tell, - g726_read, - g726_close, - g726_getcomment); - if (res) { - ast_log(LOG_WARNING, "Failed to register format %s.\n", name16); - return(-1); + for (i = 0; f[i].format ; i++) { + if (ast_format_register(&f[i])) { /* errors are fatal */ + ast_log(LOG_WARNING, "Failed to register format %s.\n", f[i].name); + return -1; + } } - return(0); + return 0; } int unload_module() { - int res; + int i; - res = ast_format_unregister(name16); - if (res) { - ast_log(LOG_WARNING, "Failed to unregister format %s.\n", name16); - return(-1); - } - res = ast_format_unregister(name24); - if (res) { - ast_log(LOG_WARNING, "Failed to unregister format %s.\n", name24); - return(-1); - } - res = ast_format_unregister(name32); - if (res) { - ast_log(LOG_WARNING, "Failed to unregister format %s.\n", name32); - return(-1); - } - res = ast_format_unregister(name40); - if (res) { - ast_log(LOG_WARNING, "Failed to unregister format %s.\n", name40); - return(-1); + for (i = 0; f[i].format ; i++) { + if (ast_format_unregister(f[i].name)) + ast_log(LOG_WARNING, "Failed to unregister format %s.\n", f[i].name); } return(0); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw G.726 (16/24/32/40kbps) data"; } char *key() diff --git a/formats/format_g729.c b/formats/format_g729.c index 5194cea05..fc1e3e334 100644 --- a/formats/format_g729.c +++ b/formats/format_g729.c @@ -51,88 +51,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char g729[20]; /* Two Real G729 Frames */ -}; - - -AST_MUTEX_DEFINE_STATIC(g729_lock); -static int glistcnt = 0; - -static char *name = "g729"; -static char *desc = "Raw G729 data"; -static char *exts = "g729"; - -static struct ast_filestream *g729_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g729_lock)) { - ast_log(LOG_WARNING, "Unable to lock g729 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->g729; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_G729A; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&g729_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *g729_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&g729_lock)) { - ast_log(LOG_WARNING, "Unable to lock g729 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&g729_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void g729_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&g729_lock)) { - ast_log(LOG_WARNING, "Unable to lock g729 list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&g729_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; -} +#define BUF_SIZE 20 /* two G729 frames */ +#define G729A_SAMPLES 160 static struct ast_frame *g729_read(struct ast_filestream *s, int *whennext) { @@ -140,13 +60,11 @@ static struct ast_frame *g729_read(struct ast_filestream *s, int *whennext) /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_G729A; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.samples = 160; - s->fr.datalen = 20; s->fr.mallocd = 0; - s->fr.data = s->g729; - if ((res = fread(s->g729, 1, 20, s->f)) != 20) { - if (res && (res != 10)) + s->fr.samples = G729A_SAMPLES; + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) { + if (res && (res != 10)) /* XXX what for ? */ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } @@ -176,11 +94,6 @@ static int g729_write(struct ast_filestream *fs, struct ast_frame *f) return 0; } -static char *g729_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int g729_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { long bytes; @@ -190,7 +103,7 @@ static int g729_seek(struct ast_filestream *fs, off_t sample_offset, int whence) fseeko(fs->f, 0, SEEK_END); max = ftello(fs->f); - bytes = 20 * (sample_offset / 160); + bytes = BUF_SIZE * (sample_offset / G729A_SAMPLES); if (whence == SEEK_SET) offset = bytes; else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) @@ -217,43 +130,45 @@ static int g729_trunc(struct ast_filestream *fs) static off_t g729_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return (offset/20)*160; + off_t offset = ftello(fs->f); + return (offset/BUF_SIZE)*G729A_SAMPLES; } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format g729_f = { + .name = "g729", + .exts = "g729", + .format = AST_FORMAT_G729A, + .write = g729_write, + .seek = g729_seek, + .trunc = g729_trunc, + .tell = g729_tell, + .read = g729_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_G729A, - g729_open, - g729_rewrite, - g729_write, - g729_seek, - g729_trunc, - g729_tell, - g729_read, - g729_close, - g729_getcomment); - - + return ast_format_register(&g729_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(g729_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw G729 data"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_gsm.c b/formats/format_gsm.c index c6ef31f7e..3a27a11fb 100644 --- a/formats/format_gsm.c +++ b/formats/format_gsm.c @@ -50,6 +50,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ +#define GSM_FRAME_SIZE 33 +#define GSM_SAMPLES 160 + /* silent gsm frame */ /* begin binary data: */ char gsm_silence[] = /* 33 */ @@ -58,111 +61,28 @@ char gsm_silence[] = /* 33 */ ,0x92,0x49,0x24}; /* end binary data. size = 33 bytes */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char gsm[66]; /* Two Real GSM Frames */ -}; - - -AST_MUTEX_DEFINE_STATIC(gsm_lock); -static int glistcnt = 0; - -static char *name = "gsm"; -static char *desc = "Raw GSM data"; -static char *exts = "gsm"; - -static struct ast_filestream *gsm_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&gsm_lock)) { - ast_log(LOG_WARNING, "Unable to lock gsm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->gsm; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_GSM; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&gsm_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *gsm_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&gsm_lock)) { - ast_log(LOG_WARNING, "Unable to lock gsm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&gsm_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void gsm_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&gsm_lock)) { - ast_log(LOG_WARNING, "Unable to lock gsm list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&gsm_lock); - ast_update_use_count(); - fclose(s->f); - free(s); -} - static struct ast_frame *gsm_read(struct ast_filestream *s, int *whennext) { int res; + s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_GSM; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.samples = 160; - s->fr.datalen = 33; + FR_SET_BUF(&(s->fr), s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE) s->fr.mallocd = 0; - s->fr.data = s->gsm; - if ((res = fread(s->gsm, 1, 33, s->f)) != 33) { + if ((res = fread(s->fr.data, 1, GSM_FRAME_SIZE, s->f)) != GSM_FRAME_SIZE) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - *whennext = 160; + *whennext = s->fr.samples = GSM_SAMPLES; return &s->fr; } static int gsm_write(struct ast_filestream *fs, struct ast_frame *f) { int res; - unsigned char gsm[66]; + unsigned char gsm[2*GSM_FRAME_SIZE]; + if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -176,14 +96,14 @@ static int gsm_write(struct ast_filestream *fs, struct ast_frame *f) int len=0; while(len < f->datalen) { conv65(f->data + len, gsm); - if ((res = fwrite(gsm, 1, 66, fs->f)) != 66) { + if ((res = fwrite(gsm, 1, 2*GSM_FRAME_SIZE, fs->f)) != 2*GSM_FRAME_SIZE) { ast_log(LOG_WARNING, "Bad write (%d/66): %s\n", res, strerror(errno)); return -1; } len += 65; } } else { - if (f->datalen % 33) { + if (f->datalen % GSM_FRAME_SIZE) { ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 33\n", f->datalen); return -1; } @@ -204,7 +124,7 @@ static int gsm_seek(struct ast_filestream *fs, off_t sample_offset, int whence) fseeko(fs->f, 0, SEEK_END); max = ftello(fs->f); /* have to fudge to frame here, so not fully to sample */ - distance = (sample_offset/160) * 33; + distance = (sample_offset/GSM_SAMPLES) * GSM_FRAME_SIZE; if(whence == SEEK_SET) offset = distance; else if(whence == SEEK_CUR || whence == SEEK_FORCECUR) @@ -218,8 +138,8 @@ static int gsm_seek(struct ast_filestream *fs, off_t sample_offset, int whence) } else if (offset > max) { int i; fseeko(fs->f, 0, SEEK_END); - for (i=0; i< (offset - max) / 33; i++) { - fwrite(gsm_silence, 1, 33, fs->f); + for (i=0; i< (offset - max) / GSM_FRAME_SIZE; i++) { + fwrite(gsm_silence, 1, GSM_FRAME_SIZE, fs->f); } } return fseeko(fs->f, offset, SEEK_SET); @@ -232,48 +152,45 @@ static int gsm_trunc(struct ast_filestream *fs) static off_t gsm_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return (offset/33)*160; + off_t offset = ftello(fs->f); + return (offset/GSM_FRAME_SIZE)*GSM_SAMPLES; } -static char *gsm_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format gsm_f = { + .name = "gsm", + .exts = "gsm", + .format = AST_FORMAT_GSM, + .write = gsm_write, + .seek = gsm_seek, + .trunc = gsm_trunc, + .tell = gsm_tell, + .read = gsm_read, + .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET, /* 2 gsm frames */ + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_GSM, - gsm_open, - gsm_rewrite, - gsm_write, - gsm_seek, - gsm_trunc, - gsm_tell, - gsm_read, - gsm_close, - gsm_getcomment); - - + return ast_format_register(&gsm_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(gsm_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw GSM data"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_h263.c b/formats/format_h263.c index 45692a354..fa5b5c60b 100644 --- a/formats/format_h263.c +++ b/formats/format_h263.c @@ -48,133 +48,60 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ +#define BUF_SIZE 4096 /* Two Real h263 Frames */ + +struct h263_desc { unsigned int lastts; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char h263[4096]; /* Two Real h263 Frames */ }; -AST_MUTEX_DEFINE_STATIC(h263_lock); -static int glistcnt = 0; - -static char *name = "h263"; -static char *desc = "Raw h263 data"; -static char *exts = "h263"; - -static struct ast_filestream *h263_open(FILE *f) +static int h263_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; unsigned int ts; int res; - if ((res = fread(&ts, 1, sizeof(ts), f)) < sizeof(ts)) { - ast_log(LOG_WARNING, "Empty file!\n"); - return NULL; - } - - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&h263_lock)) { - ast_log(LOG_WARNING, "Unable to lock h263 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->h263; - tmp->fr.frametype = AST_FRAME_VIDEO; - tmp->fr.subclass = AST_FORMAT_H263; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&h263_lock); - ast_update_use_count(); - } - return tmp; -} -static struct ast_filestream *h263_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&h263_lock)) { - ast_log(LOG_WARNING, "Unable to lock h263 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&h263_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void h263_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&h263_lock)) { - ast_log(LOG_WARNING, "Unable to lock h263 list\n"); - return; + if ((res = fread(&ts, 1, sizeof(ts), s->f)) < sizeof(ts)) { + ast_log(LOG_WARNING, "Empty file!\n"); + return -1; } - glistcnt--; - ast_mutex_unlock(&h263_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; + return 0; } static struct ast_frame *h263_read(struct ast_filestream *s, int *whennext) { int res; - int mark=0; + int mark; unsigned short len; unsigned int ts; + struct h263_desc *fs = (struct h263_desc *)s->private; + /* Send a frame from the file to the appropriate channel */ - s->fr.frametype = AST_FRAME_VIDEO; - s->fr.subclass = AST_FORMAT_H263; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.mallocd = 0; - s->fr.data = s->h263; - if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) { + if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) return NULL; - } len = ntohs(len); - if (len & 0x8000) { - mark = 1; - } + mark = (len & 0x8000) ? 1 : 0; len &= 0x7fff; - if (len > sizeof(s->h263)) { + if (len > BUF_SIZE) { ast_log(LOG_WARNING, "Length %d is too long\n", len); + len = BUF_SIZE; /* XXX truncate ? */ } - if ((res = fread(s->h263, 1, len, s->f)) != len) { + s->fr.frametype = AST_FRAME_VIDEO; + s->fr.subclass = AST_FORMAT_H263; + s->fr.mallocd = 0; + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = s->lastts; + s->fr.samples = fs->lastts; /* XXX what ? */ s->fr.datalen = len; s->fr.subclass |= mark; s->fr.delivery.tv_sec = 0; s->fr.delivery.tv_usec = 0; if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) { - s->lastts = ntohl(ts); - *whennext = s->lastts * 4/45; + fs->lastts = ntohl(ts); + *whennext = fs->lastts * 4/45; } else *whennext = 0; return &s->fr; @@ -216,11 +143,6 @@ static int h263_write(struct ast_filestream *fs, struct ast_frame *f) return 0; } -static char *h263_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int h263_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { /* No way Jose */ @@ -237,44 +159,47 @@ static int h263_trunc(struct ast_filestream *fs) static off_t h263_tell(struct ast_filestream *fs) { - /* XXX This is totally bogus XXX */ - off_t offset; - offset = ftello(fs->f); - return (offset/20)*160; + off_t offset = ftello(fs->f); + return offset; /* XXX totally bogus, needs fixing */ } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format h263_f = { + .name = "h263", + .exts = "h264", + .format = AST_FORMAT_H263, + .open = h263_open, + .write = h263_write, + .seek = h263_seek, + .trunc = h263_trunc, + .tell = h263_tell, + .read = h263_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct h263_desc), + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_H263, - h263_open, - h263_rewrite, - h263_write, - h263_seek, - h263_trunc, - h263_tell, - h263_read, - h263_close, - h263_getcomment); - - + return ast_format_register(&h263_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(h263_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw h263 data"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_h264.c b/formats/format_h264.c index 185b2680c..5beb826ec 100644 --- a/formats/format_h264.c +++ b/formats/format_h264.c @@ -47,96 +47,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Some Ideas for this code came from makeh264e.c by Jeffrey Chilton */ /* Portions of the conversion code are by guido@sienanet.it */ - -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ +#define BUF_SIZE 4096 /* Two Real h264 Frames */ +struct h264_desc { unsigned int lastts; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char h264[4096]; /* Two Real h264 Frames */ }; - -AST_MUTEX_DEFINE_STATIC(h264_lock); -static int glistcnt = 0; - -static char *name = "h264"; -static char *desc = "Raw h264 data"; -static char *exts = "h264"; - -static struct ast_filestream *h264_open(FILE *f) +static int h264_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; unsigned int ts; int res; - if ((res = fread(&ts, 1, sizeof(ts), f)) < sizeof(ts)) { + if ((res = fread(&ts, 1, sizeof(ts), s->f)) < sizeof(ts)) { ast_log(LOG_WARNING, "Empty file!\n"); - return NULL; - } - - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&h264_lock)) { - ast_log(LOG_WARNING, "Unable to lock h264 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->h264; - tmp->fr.frametype = AST_FRAME_VIDEO; - tmp->fr.subclass = AST_FORMAT_H264; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&h264_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *h264_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&h264_lock)) { - ast_log(LOG_WARNING, "Unable to lock h264 list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&h264_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void h264_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&h264_lock)) { - ast_log(LOG_WARNING, "Unable to lock h264 list\n"); - return; + return -1; } - glistcnt--; - ast_mutex_unlock(&h264_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; + return 0; } static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext) @@ -145,82 +69,73 @@ static struct ast_frame *h264_read(struct ast_filestream *s, int *whennext) int mark=0; unsigned short len; unsigned int ts; + struct h264_desc *fs = (struct h264_desc *)s->private; + /* Send a frame from the file to the appropriate channel */ - s->fr.frametype = AST_FRAME_VIDEO; - s->fr.subclass = AST_FORMAT_H264; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.mallocd = 0; - s->fr.data = s->h264; - if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) { + if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) return NULL; - } len = ntohs(len); - if (len & 0x8000) { - mark = 1; - } + mark = (len & 0x8000) ? 1 : 0; len &= 0x7fff; - if (len > sizeof(s->h264)) { + if (len > BUF_SIZE) { ast_log(LOG_WARNING, "Length %d is too long\n", len); + len = BUF_SIZE; /* XXX truncate */ } - if ((res = fread(s->h264, 1, len, s->f)) != len) { + s->fr.frametype = AST_FRAME_VIDEO; + s->fr.subclass = AST_FORMAT_H264; + s->fr.mallocd = 0; + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) { if (res) ast_log(LOG_WARNING, "Short read (%d of %d) (%s)!\n", res, len, strerror(errno)); return NULL; } - s->fr.samples = s->lastts; + s->fr.samples = fs->lastts; s->fr.datalen = len; s->fr.subclass |= mark; s->fr.delivery.tv_sec = 0; s->fr.delivery.tv_usec = 0; if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) { - s->lastts = ntohl(ts); - *whennext = s->lastts * 4/45; + fs->lastts = ntohl(ts); + *whennext = fs->lastts * 4/45; } else *whennext = 0; return &s->fr; } -static int h264_write(struct ast_filestream *fs, struct ast_frame *f) +static int h264_write(struct ast_filestream *s, struct ast_frame *f) { int res; unsigned int ts; unsigned short len; - int subclass; - int mark=0; + int mark; + if (f->frametype != AST_FRAME_VIDEO) { ast_log(LOG_WARNING, "Asked to write non-video frame!\n"); return -1; } - subclass = f->subclass; - if (subclass & 0x1) - mark=0x8000; - subclass &= ~0x1; - if (subclass != AST_FORMAT_H264) { + mark = (f->subclass & 0x1) ? 0x8000 : 0; + if ((f->subclass & ~0x1) != AST_FORMAT_H264) { ast_log(LOG_WARNING, "Asked to write non-h264 frame (%d)!\n", f->subclass); return -1; } ts = htonl(f->samples); - if ((res = fwrite(&ts, 1, sizeof(ts), fs->f)) != sizeof(ts)) { - ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno)); - return -1; + if ((res = fwrite(&ts, 1, sizeof(ts), s->f)) != sizeof(ts)) { + ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno)); + return -1; } len = htons(f->datalen | mark); - if ((res = fwrite(&len, 1, sizeof(len), fs->f)) != sizeof(len)) { - ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno)); - return -1; + if ((res = fwrite(&len, 1, sizeof(len), s->f)) != sizeof(len)) { + ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno)); + return -1; } - if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { - ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); - return -1; + if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; } return 0; } -static char *h264_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int h264_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { /* No way Jose */ @@ -237,44 +152,47 @@ static int h264_trunc(struct ast_filestream *fs) static off_t h264_tell(struct ast_filestream *fs) { - /* XXX This is totally bogus XXX */ - off_t offset; - offset = ftell(fs->f); - return (offset/20)*160; + off_t offset = ftell(fs->f); + return offset; /* XXX totally bogus, needs fixing */ } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format h264_f = { + .name = "h264", + .exts = "h264", + .format = AST_FORMAT_H264, + .open = h264_open, + .write = h264_write, + .seek = h264_seek, + .trunc = h264_trunc, + .tell = h264_tell, + .read = h264_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct h264_desc), + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_H264, - h264_open, - h264_rewrite, - h264_write, - h264_seek, - h264_trunc, - h264_tell, - h264_read, - h264_close, - h264_getcomment); - - + return ast_format_register(&h264_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(h264_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw h264 data"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_ilbc.c b/formats/format_ilbc.c index f0bcfbf4e..daaab3937 100644 --- a/formats/format_ilbc.c +++ b/formats/format_ilbc.c @@ -50,88 +50,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char ilbc[50]; /* One Real iLBC Frame */ -}; - - -AST_MUTEX_DEFINE_STATIC(ilbc_lock); -static int glistcnt = 0; - -static char *name = "iLBC"; -static char *desc = "Raw iLBC data"; -static char *exts = "ilbc"; - -static struct ast_filestream *ilbc_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&ilbc_lock)) { - ast_log(LOG_WARNING, "Unable to lock ilbc list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->ilbc; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_ILBC; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&ilbc_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *ilbc_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&ilbc_lock)) { - ast_log(LOG_WARNING, "Unable to lock ilbc list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&ilbc_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void ilbc_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&ilbc_lock)) { - ast_log(LOG_WARNING, "Unable to lock ilbc list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&ilbc_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; -} +#define ILBC_BUF_SIZE 50 /* One Real iLBC Frame */ +#define ILBC_SAMPLES 240 static struct ast_frame *ilbc_read(struct ast_filestream *s, int *whennext) { @@ -139,17 +59,14 @@ static struct ast_frame *ilbc_read(struct ast_filestream *s, int *whennext) /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_ILBC; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.samples = 240; - s->fr.datalen = 50; s->fr.mallocd = 0; - s->fr.data = s->ilbc; - if ((res = fread(s->ilbc, 1, 50, s->f)) != 50) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, ILBC_BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) != s->fr.datalen) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - *whennext = s->fr.samples; + *whennext = s->fr.samples = ILBC_SAMPLES; return &s->fr; } @@ -175,11 +92,6 @@ static int ilbc_write(struct ast_filestream *fs, struct ast_frame *f) return 0; } -static char *ilbc_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int ilbc_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { long bytes; @@ -189,7 +101,7 @@ static int ilbc_seek(struct ast_filestream *fs, off_t sample_offset, int whence) fseeko(fs->f, 0, SEEK_END); max = ftello(fs->f); - bytes = 50 * (sample_offset / 240); + bytes = ILBC_BUF_SIZE * (sample_offset / ILBC_SAMPLES); if (whence == SEEK_SET) offset = bytes; else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) @@ -216,43 +128,46 @@ static int ilbc_trunc(struct ast_filestream *fs) static off_t ilbc_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return (offset/50)*240; + off_t offset = ftello(fs->f); + return (offset/ILBC_BUF_SIZE)*ILBC_SAMPLES; } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format ilbc_f = { + .name = "iLBC", + .exts = "ilbc", + .format = AST_FORMAT_ILBC, + .write = ilbc_write, + .seek = ilbc_seek, + .trunc = ilbc_trunc, + .tell = ilbc_tell, + .read = ilbc_read, + .buf_size = ILBC_BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_ILBC, - ilbc_open, - ilbc_rewrite, - ilbc_write, - ilbc_seek, - ilbc_trunc, - ilbc_tell, - ilbc_read, - ilbc_close, - ilbc_getcomment); - - + return ast_format_register(&ilbc_f); } + int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(ilbc_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw iLBC data"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_ogg_vorbis.c b/formats/format_ogg_vorbis.c index 0437a38ef..061684f74 100644 --- a/formats/format_ogg_vorbis.c +++ b/formats/format_ogg_vorbis.c @@ -51,11 +51,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define SAMPLES_MAX 160 #define BLOCK_SIZE 4096 -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - - FILE *f; - +struct vorbis_desc { /* structures for handling the Ogg container */ ogg_sync_state oy; ogg_stream_state os; @@ -73,14 +69,6 @@ struct ast_filestream { /*! \brief Indicates whether an End of Stream condition has been detected. */ int eos; - - /*! \brief Buffer to hold audio data. */ - short buffer[SAMPLES_MAX]; - - /*! \brief Asterisk frame object. */ - struct ast_frame fr; - char waste[AST_FRIENDLY_OFFSET]; - char empty; }; AST_MUTEX_DEFINE_STATIC(ogg_vorbis_lock); @@ -96,176 +84,123 @@ static char *exts = "ogg"; * \param f File that points to on disk storage of the OGG/Vorbis data. * \return The new filestream. */ -static struct ast_filestream *ogg_vorbis_open(FILE * f) +static int ogg_vorbis_open(struct ast_filestream *s) { int i; int bytes; int result; char **ptr; char *buffer; + struct vorbis_desc *tmp = (struct vorbis_desc *)s->private; - struct ast_filestream *tmp; - - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - - tmp->writing = 0; - tmp->f = f; - - ogg_sync_init(&tmp->oy); + tmp->writing = 0; + tmp->f = f; - buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE); - bytes = fread(buffer, 1, BLOCK_SIZE, f); - ogg_sync_wrote(&tmp->oy, bytes); - - result = ogg_sync_pageout(&tmp->oy, &tmp->og); - if (result != 1) { - if (bytes < BLOCK_SIZE) { - ast_log(LOG_ERROR, "Run out of data...\n"); - } else { - ast_log(LOG_ERROR, - "Input does not appear to be an Ogg bitstream.\n"); - } - fclose(f); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } - - ogg_stream_init(&tmp->os, ogg_page_serialno(&tmp->og)); - vorbis_info_init(&tmp->vi); - vorbis_comment_init(&tmp->vc); - - if (ogg_stream_pagein(&tmp->os, &tmp->og) < 0) { - ast_log(LOG_ERROR, - "Error reading first page of Ogg bitstream data.\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } + ogg_sync_init(&tmp->oy); - if (ogg_stream_packetout(&tmp->os, &tmp->op) != 1) { - ast_log(LOG_ERROR, "Error reading initial header packet.\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } + buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE); + bytes = fread(buffer, 1, BLOCK_SIZE, f); + ogg_sync_wrote(&tmp->oy, bytes); - if (vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op) < 0) { - ast_log(LOG_ERROR, "This Ogg bitstream does not contain Vorbis audio data.\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; + result = ogg_sync_pageout(&tmp->oy, &tmp->og); + if (result != 1) { + if(bytes < BLOCK_SIZE) { + ast_log(LOG_ERROR, "Run out of data...\n"); + } else { + ast_log(LOG_ERROR, "Input does not appear to be an Ogg bitstream.\n"); } - - i = 0; + ogg_sync_clear(&tmp->oy); + return -1; + } + + ogg_stream_init(&tmp->os, ogg_page_serialno(&tmp->og)); + vorbis_info_init(&tmp->vi); + vorbis_comment_init(&tmp->vc); + + if (ogg_stream_pagein(&tmp->os, &tmp->og) < 0) { + ast_log(LOG_ERROR, "Error reading first page of Ogg bitstream data.\n"); +error: + ogg_stream_clear(&tmp->os); + vorbis_comment_clear(&tmp->vc); + vorbis_info_clear(&tmp->vi); + ogg_sync_clear(&tmp->oy); + return -1; + } + + if (ogg_stream_packetout(&tmp->os, &tmp->op) != 1) { + ast_log(LOG_ERROR, "Error reading initial header packet.\n"); + goto error; + } + + if (vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op) < 0) { + ast_log(LOG_ERROR, "This Ogg bitstream does not contain Vorbis audio data.\n"); + goto error; + } + + for (i = 0; i < 2 ; ) { while (i < 2) { - while (i < 2) { - result = ogg_sync_pageout(&tmp->oy, &tmp->og); - if (result == 0) - break; - if (result == 1) { - ogg_stream_pagein(&tmp->os, &tmp->og); - while (i < 2) { - result = ogg_stream_packetout(&tmp->os, &tmp->op); - if (result == 0) - break; - if (result < 0) { - ast_log(LOG_ERROR, "Corrupt secondary header. Exiting.\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } - vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op); - i++; + result = ogg_sync_pageout(&tmp->oy, &tmp->og); + if (result == 0) + break; + if (result == 1) { + ogg_stream_pagein(&tmp->os, &tmp->og); + while(i < 2) { + result = ogg_stream_packetout(&tmp->os,&tmp->op); + if(result == 0) + break; + if(result < 0) { + ast_log(LOG_ERROR, "Corrupt secondary header. Exiting.\n"); + goto error; } + vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op); + i++; } } - - buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE); - bytes = fread(buffer, 1, BLOCK_SIZE, f); - if (bytes == 0 && i < 2) { - ast_log(LOG_ERROR, "End of file before finding all Vorbis headers!\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } - ogg_sync_wrote(&tmp->oy, bytes); - } - - ptr = tmp->vc.user_comments; - while (*ptr) { - ast_log(LOG_DEBUG, "OGG/Vorbis comment: %s\n", *ptr); - ++ptr; } - ast_log(LOG_DEBUG, "OGG/Vorbis bitstream is %d channel, %ldHz\n", - tmp->vi.channels, tmp->vi.rate); - ast_log(LOG_DEBUG, "OGG/Vorbis file encoded by: %s\n", - tmp->vc.vendor); - if (tmp->vi.channels != 1) { - ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n"); - ogg_stream_clear(&tmp->os); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; + buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE); + bytes = fread(buffer, 1, BLOCK_SIZE, f); + if(bytes == 0 && i < 2) { + ast_log(LOG_ERROR, "End of file before finding all Vorbis headers!\n"); + goto error; } + ogg_sync_wrote(&tmp->oy, bytes); + } + + ptr = tmp->vc.user_comments; + while(*ptr){ + ast_log(LOG_DEBUG, "OGG/Vorbis comment: %s\n", *ptr); + ++ptr; + } + ast_log(LOG_DEBUG, "OGG/Vorbis bitstream is %d channel, %ldHz\n", tmp->vi.channels, tmp->vi.rate); + ast_log(LOG_DEBUG, "OGG/Vorbis file encoded by: %s\n", tmp->vc.vendor); - if (tmp->vi.rate != 8000) { - ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_block_clear(&tmp->vb); - vorbis_dsp_clear(&tmp->vd); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } + if(tmp->vi.channels != 1) { + ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n"); + goto error; + } + - vorbis_synthesis_init(&tmp->vd, &tmp->vi); - vorbis_block_init(&tmp->vd, &tmp->vb); + if(tmp->vi.rate != DEFAULT_SAMPLE_RATE) { + ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n"); + vorbis_block_clear(&tmp->vb); + vorbis_dsp_clear(&tmp->vd); + goto error; + } + + vorbis_synthesis_init(&tmp->vd, &tmp->vi); + vorbis_block_init(&tmp->vd, &tmp->vb); - if (ast_mutex_lock(&ogg_vorbis_lock)) { - ast_log(LOG_WARNING, "Unable to lock ogg_vorbis list\n"); - fclose(f); - ogg_stream_clear(&tmp->os); - vorbis_block_clear(&tmp->vb); - vorbis_dsp_clear(&tmp->vd); - vorbis_comment_clear(&tmp->vc); - vorbis_info_clear(&tmp->vi); - ogg_sync_clear(&tmp->oy); - free(tmp); - return NULL; - } - glistcnt++; - ast_mutex_unlock(&ogg_vorbis_lock); - ast_update_use_count(); + if(ast_mutex_lock(&ogg_vorbis_lock)) { + ast_log(LOG_WARNING, "Unable to lock ogg_vorbis list\n"); + vorbis_block_clear(&tmp->vb); + vorbis_dsp_clear(&tmp->vd); + goto error; } - return tmp; + glistcnt++; + ast_mutex_unlock(&ogg_vorbis_lock); + ast_update_use_count(); +return 0; } /*! @@ -291,7 +226,7 @@ static struct ast_filestream *ogg_vorbis_rewrite(FILE * f, vorbis_info_init(&tmp->vi); - if (vorbis_encode_init_vbr(&tmp->vi, 1, 8000, 0.4)) { + if (vorbis_encode_init_vbr(&tmp->vi, 1, DEFAULT_SAMPLE_RATE, 0.4)) { ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n"); free(tmp); return NULL; @@ -440,9 +375,6 @@ static void ogg_vorbis_close(struct ast_filestream *s) if (s->writing) { ogg_sync_clear(&s->oy); } - - fclose(s->f); - free(s); } /*! @@ -643,18 +575,28 @@ static char *ogg_vorbis_getcomment(struct ast_filestream *s) return NULL; } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format vorbis_f = { + .name = + .ext = + .format = AST_FORMAT_SLINEAR, + .open = ogg_vorbis_open, + .rewrite = ogg_vorbis_rewrite, + .write = ogg_vorbis_write, + .seek = ogg_vorbis_seek, + .trunc = ogg_vorbis_trunc, + .tell = ogg_vorbis_tell, + .read = ogg_vorbis_read, + .close = ogg_vorbis_close, + .buf_sie = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct vorbis_desc), + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_SLINEAR, - ogg_vorbis_open, - ogg_vorbis_rewrite, - ogg_vorbis_write, - ogg_vorbis_seek, - ogg_vorbis_trunc, - ogg_vorbis_tell, - ogg_vorbis_read, - ogg_vorbis_close, - ogg_vorbis_getcomment); + return ast_format_register(&vorbis_f); } int unload_module() @@ -664,7 +606,7 @@ int unload_module() int usecount() { - return glistcnt; + return me.usecnt; } char *description() diff --git a/formats/format_pcm.c b/formats/format_pcm.c index d3f73e3b3..4624de518 100644 --- a/formats/format_pcm.c +++ b/formats/format_pcm.c @@ -45,135 +45,67 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/endian.h" #include "asterisk/ulaw.h" +#include "asterisk/alaw.h" -#define BUF_SIZE 160 /* 160 samples */ - -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_channel *owner; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char buf[BUF_SIZE]; /* Output Buffer */ - struct timeval last; -}; - +#define BUF_SIZE 160 /* 160 bytes, and same number of samples */ -AST_MUTEX_DEFINE_STATIC(pcm_lock); -static int glistcnt = 0; +static char ulaw_silence[BUF_SIZE]; +static char alaw_silence[BUF_SIZE]; -static char *name = "pcm"; -static char *desc = "Raw uLaw 8khz Audio support (PCM)"; -static char *exts = "pcm|ulaw|ul|mu"; +/* #define REALTIME_WRITE */ /* XXX does it work at all ? */ -static char ulaw_silence[BUF_SIZE]; +#ifdef REALTIME_WRITE +struct pcm_desc { + unsigned long start_time; +}; -static struct ast_filestream *pcm_open(FILE *f) +/* Returns time in msec since system boot. */ +static unsigned long get_time(void) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_ULAW; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); + struct tms buf; + clock_t cur; + + cur = times( &buf ); + if( cur < 0 ) { + ast_log( LOG_WARNING, "Cannot get current time\n" ); + return 0; } - return tmp; + return cur * 1000 / sysconf( _SC_CLK_TCK ); } -static struct ast_filestream *pcm_rewrite(FILE *f, const char *comment) +static int pcma_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + if (s->fmt->format == AST_FORMAT_ALAW) + pd->starttime = get_time(); + return 0; } -static void pcm_close(struct ast_filestream *s) +static int pcma_rewrite(struct ast_filestream *s, const char *comment) { - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; + return pcma_open(s); } +#endif static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext) { int res; - int delay; + /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; - s->fr.subclass = AST_FORMAT_ULAW; - s->fr.offset = AST_FRIENDLY_OFFSET; + s->fr.subclass = s->fmt->format; s->fr.mallocd = 0; - s->fr.data = s->buf; - if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = res; s->fr.datalen = res; - delay = s->fr.samples; - *whennext = delay; + *whennext = s->fr.samples = res; return &s->fr; } -static int pcm_write(struct ast_filestream *fs, struct ast_frame *f) -{ - int res; - if (f->frametype != AST_FRAME_VOICE) { - ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); - return -1; - } - if (f->subclass != AST_FORMAT_ULAW) { - ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass); - return -1; - } - if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { - ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); - return -1; - } - return 0; -} - static int pcm_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { off_t cur, max, offset = 0; @@ -204,13 +136,13 @@ static int pcm_seek(struct ast_filestream *fs, off_t sample_offset, int whence) } if (whence == SEEK_FORCECUR && offset > max) { /* extend the file */ size_t left = offset - max; + const char *src = (fs->fmt->format == AST_FORMAT_ALAW) ? alaw_silence : ulaw_silence; while (left) { - size_t written = fwrite(ulaw_silence, sizeof(ulaw_silence[0]), - (left > BUF_SIZE) ? BUF_SIZE : left, fs->f); + size_t written = fwrite(src, 1, (left > BUF_SIZE) ? BUF_SIZE : left, fs->f); if (written == -1) break; /* error */ - left -= written * sizeof(ulaw_silence[0]); + left -= written; } ret = 0; /* successful */ } else { @@ -230,51 +162,336 @@ static int pcm_trunc(struct ast_filestream *fs) static off_t pcm_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return offset; + return ftello(fs->f); } -static char *pcm_getcomment(struct ast_filestream *s) +static int pcm_write(struct ast_filestream *fs, struct ast_frame *f) { - return NULL; + int res; + + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); + return -1; + } + if (f->subclass != fs->fmt->format) { + ast_log(LOG_WARNING, "Asked to write incompatible format frame (%d)!\n", f->subclass); + return -1; + } + +#ifdef REALTIME_WRITE + if (s->fmt->format == AST_FORMAT_ALAW) { + struct pcm_desc *pd = (struct pcm_desc *)fs->private; + struct stat stat_buf; + unsigned long cur_time = get_time(); + unsigned long fpos = ( cur_time - pd->start_time ) * 8; /* 8 bytes per msec */ + /* Check if we have written to this position yet. If we have, then increment pos by one frame + * for some degree of protection against receiving packets in the same clock tick. + */ + + fstat(fileno(fs->f), &stat_buf ); + if (stat_buf.st_size > fpos ) + fpos += f->datalen; /* Incrementing with the size of this current frame */ + + if (stat_buf.st_size < fpos) { + /* fill the gap with 0x55 rather than 0. */ + char buf[1024]; + unsigned long cur, to_write; + + cur = stat_buf.st_size; + if (fseek(fs->f, cur, SEEK_SET) < 0) { + ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) ); + return -1; + } + memset(buf, 0x55, 512); + while (cur < fpos) { + to_write = fpos - cur; + if (to_write > sizeof(buf)) + to_write = sizeof(buf); + fwrite(buf, 1, to_write, fs->f); + cur += to_write; + } + } + + if (fseek(s->f, fpos, SEEK_SET) < 0) { + ast_log( LOG_WARNING, "Cannot seek in file: %s\n", strerror(errno) ); + return -1; + } + } +#endif /* REALTIME_WRITE */ + + if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; + } + return 0; } +/* SUN .au support routines */ + +#define AU_HEADER_SIZE 24 +#define AU_HEADER(var) u_int32_t var[6] + +#define AU_HDR_MAGIC_OFF 0 +#define AU_HDR_HDR_SIZE_OFF 1 +#define AU_HDR_DATA_SIZE_OFF 2 +#define AU_HDR_ENCODING_OFF 3 +#define AU_HDR_SAMPLE_RATE_OFF 4 +#define AU_HDR_CHANNELS_OFF 5 + +#define AU_ENC_8BIT_ULAW 1 + +#define AU_MAGIC 0x2e736e64 +#if __BYTE_ORDER == __BIG_ENDIAN +#define htoll(b) (b) +#define htols(b) (b) +#define ltohl(b) (b) +#define ltohs(b) (b) +#else +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htoll(b) \ + (((((b) ) & 0xFF) << 24) | \ + ((((b) >> 8) & 0xFF) << 16) | \ + ((((b) >> 16) & 0xFF) << 8) | \ + ((((b) >> 24) & 0xFF) )) +#define htols(b) \ + (((((b) ) & 0xFF) << 8) | \ + ((((b) >> 8) & 0xFF) )) +#define ltohl(b) htoll(b) +#define ltohs(b) htols(b) +#else +#error "Endianess not defined" +#endif +#endif + +static int check_header(FILE *f) +{ + AU_HEADER(header); + u_int32_t magic; + u_int32_t hdr_size; + u_int32_t data_size; + u_int32_t encoding; + u_int32_t sample_rate; + u_int32_t channels; + + if (fread(header, 1, AU_HEADER_SIZE, f) != AU_HEADER_SIZE) { + ast_log(LOG_WARNING, "Read failed (header)\n"); + return -1; + } + magic = ltohl(header[AU_HDR_MAGIC_OFF]); + if (magic != (u_int32_t) AU_MAGIC) { + ast_log(LOG_WARNING, "Bad magic: 0x%x\n", magic); + } +/* hdr_size = ltohl(header[AU_HDR_HDR_SIZE_OFF]); + if (hdr_size < AU_HEADER_SIZE)*/ + hdr_size = AU_HEADER_SIZE; +/* data_size = ltohl(header[AU_HDR_DATA_SIZE_OFF]); */ + encoding = ltohl(header[AU_HDR_ENCODING_OFF]); + if (encoding != AU_ENC_8BIT_ULAW) { + ast_log(LOG_WARNING, "Unexpected format: %d. Only 8bit ULAW allowed (%d)\n", encoding, AU_ENC_8BIT_ULAW); + return -1; + } + sample_rate = ltohl(header[AU_HDR_SAMPLE_RATE_OFF]); + if (sample_rate != DEFAULT_SAMPLE_RATE) { + ast_log(LOG_WARNING, "Sample rate can only be 8000 not %d\n", sample_rate); + return -1; + } + channels = ltohl(header[AU_HDR_CHANNELS_OFF]); + if (channels != 1) { + ast_log(LOG_WARNING, "Not in mono: channels=%d\n", channels); + return -1; + } + /* Skip to data */ + fseek(f, 0, SEEK_END); + data_size = ftell(f) - hdr_size; + if (fseek(f, hdr_size, SEEK_SET) == -1 ) { + ast_log(LOG_WARNING, "Failed to skip to data: %d\n", hdr_size); + return -1; + } + return data_size; +} + +static int update_header(FILE *f) +{ + off_t cur, end; + u_int32_t datalen; + int bytes; + + cur = ftell(f); + fseek(f, 0, SEEK_END); + end = ftell(f); + /* data starts 24 bytes in */ + bytes = end - AU_HEADER_SIZE; + datalen = htoll(bytes); + + if (cur < 0) { + ast_log(LOG_WARNING, "Unable to find our position\n"); + return -1; + } + if (fseek(f, AU_HDR_DATA_SIZE_OFF * sizeof(u_int32_t), SEEK_SET)) { + ast_log(LOG_WARNING, "Unable to set our position\n"); + return -1; + } + if (fwrite(&datalen, 1, sizeof(datalen), f) != sizeof(datalen)) { + ast_log(LOG_WARNING, "Unable to set write file size\n"); + return -1; + } + if (fseek(f, cur, SEEK_SET)) { + ast_log(LOG_WARNING, "Unable to return to position\n"); + return -1; + } + return 0; +} + +static int write_header(FILE *f) +{ + AU_HEADER(header); + + header[AU_HDR_MAGIC_OFF] = htoll((u_int32_t) AU_MAGIC); + header[AU_HDR_HDR_SIZE_OFF] = htoll(AU_HEADER_SIZE); + header[AU_HDR_DATA_SIZE_OFF] = 0; + header[AU_HDR_ENCODING_OFF] = htoll(AU_ENC_8BIT_ULAW); + header[AU_HDR_SAMPLE_RATE_OFF] = htoll(DEFAULT_SAMPLE_RATE); + header[AU_HDR_CHANNELS_OFF] = htoll(1); + + /* Write an au header, ignoring sizes which will be filled in later */ + fseek(f, 0, SEEK_SET); + if (fwrite(header, 1, AU_HEADER_SIZE, f) != AU_HEADER_SIZE) { + ast_log(LOG_WARNING, "Unable to write header\n"); + return -1; + } + return 0; +} + +static int au_open(struct ast_filestream *s) +{ + if (check_header(s->f) < 0) + return -1; + return 0; +} + +static int au_rewrite(struct ast_filestream *s, const char *comment) +{ + if (write_header(s->f)) + return -1; + return 0; +} + +/* XXX check this, probably incorrect */ +static int au_seek(struct ast_filestream *fs, off_t sample_offset, int whence) +{ + off_t min, max, cur; + long offset = 0, samples; + + samples = sample_offset; + min = AU_HEADER_SIZE; + cur = ftello(fs->f); + fseek(fs->f, 0, SEEK_END); + max = ftello(fs->f); + if (whence == SEEK_SET) + offset = samples + min; + else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) + offset = samples + cur; + else if (whence == SEEK_END) + offset = max - samples; + if (whence != SEEK_FORCECUR) { + offset = (offset > max) ? max : offset; + } + /* always protect the header space. */ + offset = (offset < min) ? min : offset; + return fseeko(fs->f, offset, SEEK_SET); +} + +static int au_trunc(struct ast_filestream *fs) +{ + if (ftruncate(fileno(fs->f), ftell(fs->f))) + return -1; + return update_header(fs->f); +} + +static off_t au_tell(struct ast_filestream *fs) +{ + off_t offset = ftello(fs->f); + return offset - AU_HEADER_SIZE; +} + +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format alaw_f = { + .name = "alaw", + .exts = "alaw|al", + .format = AST_FORMAT_ALAW, + .write = pcm_write, + .seek = pcm_seek, + .trunc = pcm_trunc, + .tell = pcm_tell, + .read = pcm_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +#ifdef REALTIME_WRITE + .open = pcma_open, + .rewrite = pcma_rewrite, + .desc_size = sizeof(struct pcm_desc), +#endif +}; + +static const struct ast_format pcm_f = { + .name = "pcm", + .exts = "pcm|ulaw|ul|mu", + .format = AST_FORMAT_ULAW, + .write = pcm_write, + .seek = pcm_seek, + .trunc = pcm_trunc, + .tell = pcm_tell, + .read = pcm_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; + +static const struct ast_format au_f = { + .name = "au", + .exts = "au", + .format = AST_FORMAT_ULAW, + .open = au_open, + .rewrite = au_rewrite, + .write = pcm_write, + .seek = au_seek, + .trunc = au_trunc, + .tell = au_tell, + .read = pcm_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, /* this many shorts */ + .lockp = &me, +}; + int load_module() { int index; + /* XXX better init ? */ for (index = 0; index < (sizeof(ulaw_silence) / sizeof(ulaw_silence[0])); index++) ulaw_silence[index] = AST_LIN2MU(0); + for (index = 0; index < (sizeof(alaw_silence) / sizeof(alaw_silence[0])); index++) + alaw_silence[index] = AST_LIN2A(0); - return ast_format_register(name, exts, AST_FORMAT_ULAW, - pcm_open, - pcm_rewrite, - pcm_write, - pcm_seek, - pcm_trunc, - pcm_tell, - pcm_read, - pcm_close, - pcm_getcomment); + return ast_format_register(&pcm_f) || ast_format_register(&alaw_f) + || ast_format_register(&au_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(pcm_f.name) || ast_format_unregister(alaw_f.name) + || ast_format_unregister(au_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw/Sun uLaw/ALaw 8khz Audio support (PCM,PCMA,AU)"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_pcm_alaw.c b/formats/format_pcm_alaw.c index af1705f79..dcca10a9d 100644 --- a/formats/format_pcm_alaw.c +++ b/formats/format_pcm_alaw.c @@ -47,42 +47,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/endian.h" #include "asterisk/alaw.h" -#define BUF_SIZE 160 /* 160 samples */ - -/* #define REALTIME_WRITE */ - -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* Believe it or not, we must decode/recode to account for the - weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char buf[BUF_SIZE]; /* Output Buffer */ +#define BUF_SIZE 160 /* 160 bytes, and same number of samples */ + +/* #define REALTIME_WRITE */ /* XXX does it work at all ? */ + +struct pcma_desc { #ifdef REALTIME_WRITE unsigned long start_time; #endif }; - -AST_MUTEX_DEFINE_STATIC(pcm_lock); -static int glistcnt = 0; - -static char *name = "alaw"; -static char *desc = "Raw aLaw 8khz PCM Audio support"; -static char *exts = "alaw|al"; - static char alaw_silence[BUF_SIZE]; - #if 0 /* Returns time in msec since system boot. */ -static unsigned long get_time(void) +static unsigned long get_time(struct ast_filestream *s) { struct tms buf; clock_t cur; + unsigned long *res; cur = times( &buf ); if( cur < 0 ) @@ -90,77 +73,26 @@ static unsigned long get_time(void) ast_log( LOG_WARNING, "Cannot get current time\n" ); return 0; } - return cur * 1000 / sysconf( _SC_CLK_TCK ); -} -#endif - -static struct ast_filestream *pcm_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_ALAW; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; -#ifdef REALTIME_WRITE - tmp->start_time = get_time(); -#endif - glistcnt++; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); + res = cur * 1000 / sysconf( _SC_CLK_TCK ); + if (s) { + struct pcma_desc *d = (struct pcma_filestream *)s->private; + d->start_time = res; } - return tmp; + return res; } +#endif -static struct ast_filestream *pcm_rewrite(FILE *f, const char *comment) +static int pcm_open(struct ast_filestream *s) { - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - free(tmp); - return NULL; - } - tmp->f = f; #ifdef REALTIME_WRITE - tmp->start_time = get_time(); + get_time(s); #endif - glistcnt++; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + return 0; } -static void pcm_close(struct ast_filestream *s) +static int pcm_rewrite(struct ast_filestream *s, const char *comment) { - if (ast_mutex_lock(&pcm_lock)) { - ast_log(LOG_WARNING, "Unable to lock pcm list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&pcm_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; + return pcm_open(s); } static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext) @@ -170,17 +102,15 @@ static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext) s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_ALAW; - s->fr.offset = AST_FRIENDLY_OFFSET; s->fr.mallocd = 0; - s->fr.data = s->buf; - if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = res; s->fr.datalen = res; - *whennext = s->fr.samples; + *whennext = s->fr.samples = res; return &s->fr; } @@ -191,6 +121,7 @@ static int pcm_write(struct ast_filestream *fs, struct ast_frame *f) unsigned long cur_time; unsigned long fpos; struct stat stat_buf; + struct pcma_filestream *s = (struct pcma_filestream *)fs->private; #endif if (f->frametype != AST_FRAME_VOICE) { @@ -204,7 +135,7 @@ static int pcm_write(struct ast_filestream *fs, struct ast_frame *f) #ifdef REALTIME_WRITE cur_time = get_time(); - fpos = ( cur_time - fs->start_time ) * 8; /* 8 bytes per msec */ + fpos = ( cur_time - s->start_time ) * 8; /* 8 bytes per msec */ /* Check if we have written to this position yet. If we have, then increment pos by one frame * for some degree of protection against receiving packets in the same clock tick. */ @@ -306,16 +237,28 @@ static int pcm_trunc(struct ast_filestream *fs) static off_t pcm_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return offset; + return ftello(fs->f); } - -static char *pcm_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format alaw_f = { + .name = "alaw", + .exts = "alaw|al", + .format = AST_FORMAT_ALAW, + .open = pcm_open, + .rewrite = pcm_rewrite, + .write = pcm_write, + .seek = pcm_seek, + .trunc = pcm_trunc, + .tell = pcm_tell, + .read = pcm_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +#ifdef REALTIME_WRITE + .desc_size = sizeof(struct pcma_desc), +#endif +}; int load_module() { @@ -324,34 +267,24 @@ int load_module() for (index = 0; index < (sizeof(alaw_silence) / sizeof(alaw_silence[0])); index++) alaw_silence[index] = AST_LIN2A(0); - return ast_format_register(name, exts, AST_FORMAT_ALAW, - pcm_open, - pcm_rewrite, - pcm_write, - pcm_seek, - pcm_trunc, - pcm_tell, - pcm_read, - pcm_close, - pcm_getcomment); + return ast_format_register(&alaw_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(alaw_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw aLaw 8khz PCM Audio support"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_sln.c b/formats/format_sln.c index 74792c605..d3a759131 100644 --- a/formats/format_sln.c +++ b/formats/format_sln.c @@ -43,111 +43,26 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/endian.h" -#define BUF_SIZE 320 /* 320 samples */ - -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_channel *owner; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char buf[BUF_SIZE]; /* Output Buffer */ - struct timeval last; -}; - - -AST_MUTEX_DEFINE_STATIC(slinear_lock); -static int glistcnt = 0; - -static char *name = "sln"; -static char *desc = "Raw Signed Linear Audio support (SLN)"; -static char *exts = "sln|raw"; - -static struct ast_filestream *slinear_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&slinear_lock)) { - ast_log(LOG_WARNING, "Unable to lock slinear list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_SLINEAR; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - glistcnt++; - ast_mutex_unlock(&slinear_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *slinear_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&slinear_lock)) { - ast_log(LOG_WARNING, "Unable to lock slinear list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&slinear_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void slinear_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&slinear_lock)) { - ast_log(LOG_WARNING, "Unable to lock slinear list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&slinear_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; -} +#define BUF_SIZE 320 /* 320 bytes, 160 samples */ +#define SLIN_SAMPLES 160 static struct ast_frame *slinear_read(struct ast_filestream *s, int *whennext) { int res; - int delay; /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_SLINEAR; s->fr.offset = AST_FRIENDLY_OFFSET; s->fr.mallocd = 0; - s->fr.data = s->buf; - if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = res/2; + *whennext = s->fr.samples = res/2; s->fr.datalen = res; - delay = s->fr.samples; - *whennext = delay; return &s->fr; } @@ -199,48 +114,44 @@ static int slinear_trunc(struct ast_filestream *fs) static off_t slinear_tell(struct ast_filestream *fs) { - off_t offset; - offset = ftello(fs->f); - return offset / 2; + return ftello(fs->f) / 2; } -static char *slinear_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format slin_f = { + .name = "sln", + .exts = "sln|raw", + .format = AST_FORMAT_SLINEAR, + .write = slinear_write, + .seek = slinear_seek, + .trunc = slinear_trunc, + .tell = slinear_tell, + .read = slinear_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_SLINEAR, - slinear_open, - slinear_rewrite, - slinear_write, - slinear_seek, - slinear_trunc, - slinear_tell, - slinear_read, - slinear_close, - slinear_getcomment); - - + return ast_format_register(&slin_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(slin_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Raw Signed Linear Audio support (SLN)"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_vox.c b/formats/format_vox.c index 8cc143178..2bd8b3650 100644 --- a/formats/format_vox.c +++ b/formats/format_vox.c @@ -45,117 +45,29 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/endian.h" -#define BUF_SIZE 80 /* 160 samples */ - -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char buf[BUF_SIZE]; /* Output Buffer */ - int lasttimeout; - struct timeval last; - short signal; /* Signal level (file side) */ - short ssindex; /* Signal ssindex (file side) */ - unsigned char zero_count; /* counter of consecutive zero samples */ - unsigned char next_flag; -}; - - -AST_MUTEX_DEFINE_STATIC(vox_lock); -static int glistcnt = 0; - -static char *name = "vox"; -static char *desc = "Dialogic VOX (ADPCM) File Format"; -static char *exts = "vox"; - -static struct ast_filestream *vox_open(FILE *f) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&vox_lock)) { - ast_log(LOG_WARNING, "Unable to lock vox list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_ADPCM; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - tmp->lasttimeout = -1; - glistcnt++; - ast_mutex_unlock(&vox_lock); - ast_update_use_count(); - } - return tmp; -} - -static struct ast_filestream *vox_rewrite(FILE *f, const char *comment) -{ - /* We don't have any header to read or anything really, but - if we did, it would go here. We also might want to check - and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (ast_mutex_lock(&vox_lock)) { - ast_log(LOG_WARNING, "Unable to lock vox list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&vox_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; -} - -static void vox_close(struct ast_filestream *s) -{ - if (ast_mutex_lock(&vox_lock)) { - ast_log(LOG_WARNING, "Unable to lock vox list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&vox_lock); - ast_update_use_count(); - fclose(s->f); - free(s); - s = NULL; -} +#define BUF_SIZE 80 /* 80 bytes, 160 samples */ +#define VOX_SAMPLES 160 static struct ast_frame *vox_read(struct ast_filestream *s, int *whennext) { int res; + /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_ADPCM; - s->fr.offset = AST_FRIENDLY_OFFSET; s->fr.mallocd = 0; - s->fr.data = s->buf; - if ((res = fread(s->buf, 1, BUF_SIZE, s->f)) < 1) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data, 1, s->fr.datalen, s->f)) < 1) { if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } - s->fr.samples = res * 2; + *whennext = s->fr.samples = res * 2; s->fr.datalen = res; - *whennext = s->fr.samples; return &s->fr; } -static int vox_write(struct ast_filestream *fs, struct ast_frame *f) +static int vox_write(struct ast_filestream *s, struct ast_frame *f) { int res; if (f->frametype != AST_FRAME_VOICE) { @@ -166,18 +78,13 @@ static int vox_write(struct ast_filestream *fs, struct ast_frame *f) ast_log(LOG_WARNING, "Asked to write non-ADPCM frame (%d)!\n", f->subclass); return -1; } - if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) { + if ((res = fwrite(f->data, 1, f->datalen, s->f)) != f->datalen) { ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); return -1; } return 0; } -static char *vox_getcomment(struct ast_filestream *s) -{ - return NULL; -} - static int vox_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { off_t offset=0,min,cur,max,distance; @@ -199,8 +106,7 @@ static int vox_seek(struct ast_filestream *fs, off_t sample_offset, int whence) offset = (offset > max)?max:offset; offset = (offset < min)?min:offset; } - fseeko(fs->f, offset, SEEK_SET); - return ftello(fs->f); + return fseeko(fs->f, offset, SEEK_SET); } static int vox_trunc(struct ast_filestream *fs) @@ -215,38 +121,41 @@ static off_t vox_tell(struct ast_filestream *fs) return offset; } +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format vox_f = { + .name = "vox", + .exts = "vox", + .format = AST_FORMAT_ADPCM, + .write = vox_write, + .seek = vox_seek, + .trunc = vox_trunc, + .tell = vox_tell, + .read = vox_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .lockp = &me, +}; + int load_module() { - return ast_format_register(name, exts, AST_FORMAT_ADPCM, - vox_open, - vox_rewrite, - vox_write, - vox_seek, - vox_trunc, - vox_tell, - vox_read, - vox_close, - vox_getcomment); - - + return ast_format_register(&vox_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(vox_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Dialogic VOX (ADPCM) File Format"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_wav.c b/formats/format_wav.c index 67df4163b..f46d75b1b 100644 --- a/formats/format_wav.c +++ b/formats/format_wav.c @@ -49,30 +49,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ +#define WAV_BUF_SIZE 320 + +struct wav_desc { /* format-specific parameters */ int bytes; int needsgain; - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - short buf[160]; - int foffset; int lasttimeout; int maxlen; struct timeval last; }; - -AST_MUTEX_DEFINE_STATIC(wav_lock); -static int glistcnt = 0; - -static char *name = "wav"; -static char *desc = "Microsoft WAV format (8000hz Signed Linear)"; -static char *exts = "wav"; - #define BLOCKSIZE 160 #define GAIN 2 /* 2^GAIN is the multiple to increase the volume by */ @@ -165,7 +151,7 @@ static int check_header(FILE *f) ast_log(LOG_WARNING, "Read failed (freq)\n"); return -1; } - if (ltohl(freq) != 8000) { + if (ltohl(freq) != DEFAULT_SAMPLE_RATE) { ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq)); return -1; } @@ -233,7 +219,6 @@ static int update_header(FILE *f) off_t cur,end; int datalen,filelen,bytes; - cur = ftello(f); fseek(f, 0, SEEK_END); end = ftello(f); @@ -333,135 +318,90 @@ static int write_header(FILE *f) return 0; } -static struct ast_filestream *wav_open(FILE *f) +static int wav_open(struct ast_filestream *s) { /* We don't have any header to read or anything really, but if we did, it would go here. We also might want to check and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if ((tmp->maxlen = check_header(f)) < 0) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->needsgain = 1; - tmp->fr.data = tmp->buf; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_SLINEAR; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - tmp->bytes = 0; - glistcnt++; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); - } - return tmp; + struct wav_desc *tmp = (struct wav_desc *)s->private; + if ((tmp->maxlen = check_header(s->f)) < 0) + return -1; + return 0; } -static struct ast_filestream *wav_rewrite(FILE *f, const char *comment) +static int wav_rewrite(struct ast_filestream *s, const char *comment) { /* We don't have any header to read or anything really, but if we did, it would go here. We also might want to check and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (write_header(f)) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + + if (write_header(s->f)) + return -1; + return 0; } static void wav_close(struct ast_filestream *s) { char zero = 0; - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); + struct wav_desc *fs = (struct wav_desc *)s->private; /* Pad to even length */ - if (s->bytes & 0x1) + if (fs->bytes & 0x1) fwrite(&zero, 1, 1, s->f); - fclose(s->f); - free(s); - s = NULL; } static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext) { int res; - int delay; + int samples; /* actual samples read */ int x; - short tmp[sizeof(s->buf) / 2]; - int bytes = sizeof(tmp); + short *tmp; + int bytes = WAV_BUF_SIZE; /* in bytes */ off_t here; /* Send a frame from the file to the appropriate channel */ + struct wav_desc *fs = (struct wav_desc *)s->private; + here = ftello(s->f); - if ((s->maxlen - here) < bytes) - bytes = s->maxlen - here; + if (fs->maxlen - here < bytes) /* truncate if necessary */ + bytes = fs->maxlen - here; if (bytes < 0) bytes = 0; /* ast_log(LOG_DEBUG, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */ + s->fr.frametype = AST_FRAME_VOICE; + s->fr.subclass = AST_FORMAT_SLINEAR; + s->fr.mallocd = 0; + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, bytes); - if ( (res = fread(tmp, 1, bytes, s->f)) <= 0 ) { - if (res) { + if ( (res = fread(s->fr.data, 1, s->fr.datalen, s->f)) <= 0 ) { + if (res) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); - } return NULL; } + s->fr.datalen = res; + s->fr.samples = samples = res / 2; + tmp = (short *)(s->fr.data); #if __BYTE_ORDER == __BIG_ENDIAN - for( x = 0; x < sizeof(tmp)/2; x++) tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); + /* file format is little endian so we need to swap */ + for( x = 0; x < samples; x++) + tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); #endif - if (s->needsgain) { - for (x=0;xneedsgain) { + for (x=0; x < samples; x++) { if (tmp[x] & ((1 << GAIN) - 1)) { /* If it has data down low, then it's not something we've artificially increased gain on, so we don't need to gain adjust it */ - s->needsgain = 0; + fs->needsgain = 0; + break; } - } - if (s->needsgain) { - for (x=0;xbuf[x] = tmp[x] >> GAIN; } - } else { - memcpy(s->buf, tmp, sizeof(s->buf)); + if (fs->needsgain) { + for (x=0; x < samples; x++) + tmp[x] = tmp[x] >> GAIN; + } } - delay = res / 2; - s->fr.frametype = AST_FRAME_VOICE; - s->fr.subclass = AST_FORMAT_SLINEAR; - s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.datalen = res; - s->fr.data = s->buf; - s->fr.mallocd = 0; - s->fr.samples = delay; - *whennext = delay; + *whennext = samples; return &s->fr; } @@ -470,6 +410,9 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f) int x; short tmp[8000], *tmpi; float tmpf; + struct wav_desc *s = (struct wav_desc *)fs->private; + int res; + if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -489,33 +432,28 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f) printf("Data Length: %d\n", f->datalen); #endif - if (fs->buf) { - tmpi = f->data; - /* Volume adjust here to accomodate */ - for (x=0;xdatalen/2;x++) { - tmpf = ((float)tmpi[x]) * ((float)(1 << GAIN)); - if (tmpf > 32767.0) - tmpf = 32767.0; - if (tmpf < -32768.0) - tmpf = -32768.0; - tmp[x] = tmpf; - tmp[x] &= ~((1 << GAIN) - 1); + tmpi = f->data; + /* Volume adjust here to accomodate */ + for (x=0;xdatalen/2;x++) { + tmpf = ((float)tmpi[x]) * ((float)(1 << GAIN)); + if (tmpf > 32767.0) + tmpf = 32767.0; + if (tmpf < -32768.0) + tmpf = -32768.0; + tmp[x] = tmpf; + tmp[x] &= ~((1 << GAIN) - 1); #if __BYTE_ORDER == __BIG_ENDIAN - tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); + tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); #endif - } - if ((fwrite(tmp, 1, f->datalen, fs->f) != f->datalen) ) { - ast_log(LOG_WARNING, "Bad write (%d): %s\n", errno, strerror(errno)); - return -1; - } - } else { - ast_log(LOG_WARNING, "Cannot write data to file.\n"); + } + if ((res = fwrite(tmp, 1, f->datalen, fs->f)) != f->datalen ) { + ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno)); return -1; } - - fs->bytes += f->datalen; + + s->bytes += f->datalen; update_header(fs->f); return 0; @@ -560,43 +498,45 @@ static off_t wav_tell(struct ast_filestream *fs) return (offset - 44)/2; } -static char *wav_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format wav_f = { + .name = "wav", + .exts = "wav", + .format = AST_FORMAT_SLINEAR, + .open = wav_open, + .rewrite = wav_rewrite, + .write = wav_write, + .seek = wav_seek, + .trunc = wav_trunc, + .tell = wav_tell, + .read = wav_read, + .close = wav_close, + .buf_size = WAV_BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct wav_desc), + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_SLINEAR, - wav_open, - wav_rewrite, - wav_write, - wav_seek, - wav_trunc, - wav_tell, - wav_read, - wav_close, - wav_getcomment); - - + return ast_format_register(&wav_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(wav_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Microsoft WAV format (8000hz Signed Linear)"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/formats/format_wav_gsm.c b/formats/format_wav_gsm.c index 0875e77fe..888a96020 100644 --- a/formats/format_wav_gsm.c +++ b/formats/format_wav_gsm.c @@ -54,6 +54,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* Portions of the conversion code are by guido@sienanet.it */ +#define GSM_FRAME_SIZE 33 +#define MSGSM_FRAME_SIZE 65 +#define MSGSM_DATA_OFS 60 /* offset of data bytes */ +#define GSM_SAMPLES 160 /* samples in a GSM block */ +#define MSGSM_SAMPLES (2*GSM_SAMPLES) /* samples in an MSGSM block */ + /* begin binary data: */ char msgsm_silence[] = /* 65 */ {0x48,0x17,0xD6,0x84,0x02,0x80,0x24,0x49,0x92,0x24,0x89,0x02,0x80,0x24,0x49 @@ -63,29 +69,12 @@ char msgsm_silence[] = /* 65 */ ,0x92,0x24,0x49,0x92,0x00}; /* end binary data. size = 65 bytes */ -struct ast_filestream { - void *reserved[AST_RESERVED_POINTERS]; +struct wavg_desc { /* Believe it or not, we must decode/recode to account for the weird MS format */ - /* This is what a filestream means to us */ - FILE *f; /* Descriptor */ - struct ast_frame fr; /* Frame information */ - char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ - char empty; /* Empty character */ - unsigned char gsm[66]; /* Two Real GSM Frames */ - int foffset; int secondhalf; /* Are we on the second half */ - struct timeval last; }; - -AST_MUTEX_DEFINE_STATIC(wav_lock); -static int glistcnt = 0; - -static char *name = "wav49"; -static char *desc = "Microsoft WAV format (Proprietary GSM)"; -static char *exts = "WAV|wav49"; - #if __BYTE_ORDER == __LITTLE_ENDIAN #define htoll(b) (b) #define htols(b) (b) @@ -173,7 +162,7 @@ static int check_header(FILE *f) ast_log(LOG_WARNING, "Read failed (freq)\n"); return -1; } - if (ltohl(freq) != 8000) { + if (ltohl(freq) != DEFAULT_SAMPLE_RATE) { ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq)); return -1; } @@ -236,7 +225,7 @@ static int update_header(FILE *f) fseek(f, 0, SEEK_END); end = ftello(f); /* in a gsm WAV, data starts 60 bytes in */ - bytes = end - 60; + bytes = end - MSGSM_DATA_OFS; datalen = htoll((bytes + 1) & ~0x1); filelen = htoll(52 + ((bytes + 1) & ~0x1)); if (cur < 0) { @@ -268,7 +257,7 @@ static int update_header(FILE *f) static int write_header(FILE *f) { - unsigned int hz=htoll(8000); + unsigned int hz=htoll(DEFAULT_SAMPLE_RATE); /* XXX the following are relate to DEFAULT_SAMPLE_RATE ? */ unsigned int bhz = htoll(1625); unsigned int hs = htoll(20); unsigned short fmt = htols(49); @@ -347,119 +336,78 @@ static int write_header(FILE *f) return 0; } -static struct ast_filestream *wav_open(FILE *f) +static int wav_open(struct ast_filestream *s) { /* We don't have any header to read or anything really, but if we did, it would go here. We also might want to check and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (check_header(f)) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - tmp->fr.data = tmp->gsm; - tmp->fr.frametype = AST_FRAME_VOICE; - tmp->fr.subclass = AST_FORMAT_GSM; - /* datalen will vary for each frame */ - tmp->fr.src = name; - tmp->fr.mallocd = 0; - tmp->secondhalf = 0; - glistcnt++; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); - } - return tmp; + struct wavg_desc *fs = (struct wavg_desc *)s->private; + + if (check_header(s->f)) + return -1; + fs->secondhalf = 0; /* not strictly necessary */ + return 0; } -static struct ast_filestream *wav_rewrite(FILE *f, const char *comment) +static int wav_rewrite(struct ast_filestream *s, const char *comment) { /* We don't have any header to read or anything really, but if we did, it would go here. We also might want to check and be sure it's a valid file. */ - struct ast_filestream *tmp; - if ((tmp = malloc(sizeof(struct ast_filestream)))) { - memset(tmp, 0, sizeof(struct ast_filestream)); - if (write_header(f)) { - free(tmp); - return NULL; - } - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - free(tmp); - return NULL; - } - tmp->f = f; - glistcnt++; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); - } else - ast_log(LOG_WARNING, "Out of memory\n"); - return tmp; + + if (write_header(s->f)) + return -1; + return 0; } static void wav_close(struct ast_filestream *s) { char zero = 0; - if (ast_mutex_lock(&wav_lock)) { - ast_log(LOG_WARNING, "Unable to lock wav list\n"); - return; - } - glistcnt--; - ast_mutex_unlock(&wav_lock); - ast_update_use_count(); /* Pad to even length */ fseek(s->f, 0, SEEK_END); if (ftello(s->f) & 0x1) fwrite(&zero, 1, 1, s->f); - fclose(s->f); - free(s); - s = NULL; } static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext) { - int res; - char msdata[66]; /* Send a frame from the file to the appropriate channel */ + struct wavg_desc *fs = (struct wavg_desc *)s->private; s->fr.frametype = AST_FRAME_VOICE; s->fr.subclass = AST_FORMAT_GSM; s->fr.offset = AST_FRIENDLY_OFFSET; - s->fr.samples = 160; - s->fr.datalen = 33; + s->fr.samples = GSM_SAMPLES; s->fr.mallocd = 0; - if (s->secondhalf) { + FR_SET_BUF(&s->fr, s->buf, AST_FRIENDLY_OFFSET, GSM_FRAME_SIZE); + if (fs->secondhalf) { /* Just return a frame based on the second GSM frame */ - s->fr.data = s->gsm + 33; + s->fr.data = (char *)s->fr.data + GSM_FRAME_SIZE; + s->fr.offset += GSM_FRAME_SIZE; } else { - if ((res = fread(msdata, 1, 65, s->f)) != 65) { + /* read and convert */ + char msdata[MSGSM_FRAME_SIZE]; + int res; + + if ((res = fread(msdata, 1, MSGSM_FRAME_SIZE, s->f)) != MSGSM_FRAME_SIZE) { if (res && (res != 1)) ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } /* Convert from MS format to two real GSM frames */ - conv65(msdata, s->gsm); - s->fr.data = s->gsm; + conv65(msdata, s->fr.data); } - s->secondhalf = !s->secondhalf; - *whennext = 160; + fs->secondhalf = !fs->secondhalf; + *whennext = GSM_SAMPLES; return &s->fr; } -static int wav_write(struct ast_filestream *fs, struct ast_frame *f) +static int wav_write(struct ast_filestream *s, struct ast_frame *f) { - int res; - char msdata[66]; - int len =0; - int alreadyms=0; + int len; + int size; + struct wavg_desc *fs = (struct wavg_desc *)s->private; + if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -468,65 +416,70 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f) ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass); return -1; } - if (!(f->datalen % 65)) - alreadyms = 1; - while(len < f->datalen) { - if (alreadyms) { + /* XXX this might fail... if the input is a multiple of MSGSM_FRAME_SIZE + * we assume it is already in the correct format. + */ + if (!(f->datalen % MSGSM_FRAME_SIZE)) { + size = MSGSM_FRAME_SIZE; + fs->secondhalf = 0; + } else { + size = GSM_FRAME_SIZE; + } + for (len = 0; len < f->datalen ; len += size) { + int res; + char *src, msdata[MSGSM_FRAME_SIZE]; + if (fs->secondhalf) { /* second half of raw gsm to be converted */ + memcpy(s->buf + GSM_FRAME_SIZE, f->data + len, GSM_FRAME_SIZE); + conv66(s->buf, msdata); + src = msdata; fs->secondhalf = 0; - if ((res = fwrite(f->data + len, 1, 65, fs->f)) != 65) { - ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno)); - return -1; - } - update_header(fs->f); - len += 65; - } else { - if (fs->secondhalf) { - memcpy(fs->gsm + 33, f->data + len, 33); - conv66(fs->gsm, msdata); - if ((res = fwrite(msdata, 1, 65, fs->f)) != 65) { - ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno)); - return -1; - } - update_header(fs->f); - } else { - /* Copy the data and do nothing */ - memcpy(fs->gsm, f->data + len, 33); - } - fs->secondhalf = !fs->secondhalf; - len += 33; + } else if (size == GSM_FRAME_SIZE) { /* first half of raw gsm */ + memcpy(s->buf, f->data + len, GSM_FRAME_SIZE); + src = NULL; /* nothing to write */ + fs->secondhalf = 1; + } else { /* raw msgsm data */ + src = f->data + len; } + if (src && (res = fwrite(src, 1, size, s->f)) != size) { + ast_log(LOG_WARNING, "Bad write (%d/65): %s\n", res, strerror(errno)); + return -1; + } + update_header(s->f); /* XXX inefficient! */ } return 0; } static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { - off_t offset=0,distance,cur,min,max; - min = 60; - cur = ftello(fs->f); + off_t offset=0, distance, max; + struct wavg_desc *s = (struct wavg_desc *)fs->private; + + off_t min = MSGSM_DATA_OFS; + off_t cur = ftello(fs->f); fseek(fs->f, 0, SEEK_END); - max = ftello(fs->f); - /* I'm getting sloppy here, I'm only going to go to even splits of the 2 - * frames, if you want tighter cuts use format_gsm, format_pcm, or format_wav */ - distance = (sample_offset/320) * 65; - if(whence == SEEK_SET) + max = ftello(fs->f); /* XXX ideally, should round correctly */ + /* Compute the distance in bytes, rounded to the block size */ + distance = (sample_offset/MSGSM_SAMPLES) * MSGSM_FRAME_SIZE; + if (whence == SEEK_SET) offset = distance + min; - else if(whence == SEEK_CUR || whence == SEEK_FORCECUR) + else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) offset = distance + cur; - else if(whence == SEEK_END) + else if (whence == SEEK_END) offset = max - distance; /* always protect against seeking past end of header */ - offset = (offset < min)?min:offset; + if (offset < min) + offset = min; if (whence != SEEK_FORCECUR) { - offset = (offset > max)?max:offset; + if (offset > max) + offset = max; } else if (offset > max) { int i; fseek(fs->f, 0, SEEK_END); - for (i=0; i< (offset - max) / 65; i++) { - fwrite(msgsm_silence, 1, 65, fs->f); + for (i=0; i< (offset - max) / MSGSM_FRAME_SIZE; i++) { + fwrite(msgsm_silence, 1, MSGSM_FRAME_SIZE, fs->f); } } - fs->secondhalf = 0; + s->secondhalf = 0; return fseeko(fs->f, offset, SEEK_SET); } @@ -543,46 +496,49 @@ static off_t wav_tell(struct ast_filestream *fs) offset = ftello(fs->f); /* since this will most likely be used later in play or record, lets stick * to that level of resolution, just even frames boundaries */ - return (offset - 52)/65*320; + /* XXX why 52 ? */ + return (offset - 52)/MSGSM_FRAME_SIZE*MSGSM_SAMPLES; } -static char *wav_getcomment(struct ast_filestream *s) -{ - return NULL; -} +static struct ast_format_lock me = { .usecnt = -1 }; + +static const struct ast_format wav49_f = { + .name = "wav49", + .exts = "WAV|wav49", + .format = AST_FORMAT_GSM, + .open = wav_open, + .rewrite = wav_rewrite, + .write = wav_write, + .seek = wav_seek, + .trunc = wav_trunc, + .tell = wav_tell, + .read = wav_read, + .close = wav_close, + .buf_size = 2*GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct wavg_desc), + .lockp = &me, +}; int load_module() { - return ast_format_register(name, exts, AST_FORMAT_GSM, - wav_open, - wav_rewrite, - wav_write, - wav_seek, - wav_trunc, - wav_tell, - wav_read, - wav_close, - wav_getcomment); - - + return ast_format_register(&wav49_f); } int unload_module() { - return ast_format_unregister(name); + return ast_format_unregister(wav49_f.name); } int usecount() { - return glistcnt; + return me.usecnt; } char *description() { - return desc; + return "Microsoft WAV format (Proprietary GSM)"; } - char *key() { return ASTERISK_GPL_KEY; diff --git a/include/asterisk/file.h b/include/asterisk/file.h index 67150cf95..905ea0a0d 100644 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -40,28 +40,109 @@ extern "C" { #define AST_DIGIT_ANY "0123456789#*ABCD" #define AST_DIGIT_ANYNUM "0123456789" +/*! structure used for lock and refcount of format handlers. + * Should not be here, but this is a temporary workaround + * until we implement a more general mechanism. + * The format handler should include a pointer to + * this structure. + * As a trick, if usecnt is initialized with -1, + * ast_format_register will init the mutex for you. + */ +struct ast_format_lock { + ast_mutex_t lock; + int usecnt; /* number of active clients */ +}; + +/*! + * Each supported file format is described by the following fields. + * Not all are necessary, the support routine implement default + * values for some of them. + * A handler typically fills a structure initializing the desired + * fields, and then calls ast_format_register() with the (readonly) + * structure as an argument. + */ +struct ast_format { + char name[80]; /*! Name of format */ + char exts[80]; /*! Extensions (separated by | if more than one) + this format can read. First is assumed for writing (e.g. .mp3) */ + int format; /*! Format of frames it uses/provides (one only) */ + /*! Prepare an input stream for playback. Return 0 on success, -1 on error. + * The FILE is already open (in s->f) so this function only needs to perform + * any applicable validity checks on the file. If none is required, the + * function can be omitted. + */ + int (*open)(struct ast_filestream *s); + /*! Prepare a stream for output, and comment it appropriately if applicable. + * Return 0 on success, -1 on error. Same as the open, the FILE is already + * open so the function just needs to prepare any header and other fields, + * if any. The function can be omitted if nothing is needed. + */ + int (*rewrite)(struct ast_filestream *s, const char *comment); + /*! Write a frame to a channel */ + int (*write)(struct ast_filestream *, struct ast_frame *); + /*! seek num samples into file, whence - like a normal seek but with offset in samples */ + int (*seek)(struct ast_filestream *, off_t, int); + int (*trunc)(struct ast_filestream *fs); /*! trunc file to current position */ + off_t (*tell)(struct ast_filestream *fs); /*! tell current position */ + /*! Read the next frame from the filestream (if available) and report + * when to get next frame (in samples) + */ + struct ast_frame * (*read)(struct ast_filestream *, int *whennext); + /*! Do any closing actions, if any. The descriptor and structure are closed + * and destroyed by the generic routines, so they must not be done here. */ + void (*close)(struct ast_filestream *); + char * (*getcomment)(struct ast_filestream *); /*! Retrieve file comment */ + + AST_LIST_ENTRY(ast_format) list; /*! Link */ + + /*! + * If the handler needs a buffer (for read, typically) + * and/or a private descriptor, put here the + * required size (in bytes) and the support routine will allocate them + * for you, pointed by s->buf and s->private, respectively. + * When allocating a buffer, remember to leave AST_FRIENDLY_OFFSET + * spare bytes at the bginning. + */ + int buf_size; /*! size of frame buffer, if any, aligned to 8 bytes. */ + int desc_size; /*! size of private descriptor, if any */ + + struct ast_format_lock *lockp; +}; + +/* + * This structure is allocated by file.c in one chunk, + * together with buf_size and desc_size bytes of memory + * to be used for private purposes (e.g. buffers etc.) + */ +struct ast_filestream { + /*! Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */ + struct ast_format *fmt; /* need to write to the lock and usecnt */ + int flags; + mode_t mode; + char *filename; + char *realfilename; + /*! Video file stream */ + struct ast_filestream *vfs; + /*! Transparently translate from another format -- just once */ + struct ast_trans_pvt *trans; + struct ast_tranlator_pvt *tr; + int lastwriteformat; + int lasttimeout; + struct ast_channel *owner; + FILE *f; + struct ast_frame fr; /* frame produced by read, typically */ + char *buf; /* buffer pointed to by ast_frame; */ + void *private; /* pointer to private buffer */ +}; + #define SEEK_FORCECUR 10 -/* Defined by individual formats. First item MUST be a - pointer for use by the stream manager */ -struct ast_filestream; - -/*! Registers a new file format */ /*! Register a new file format capability - * Adds a format to asterisk's format abilities. Fill in the fields, and it will work. For examples, look at some of the various format code. + * Adds a format to asterisk's format abilities. * returns 0 on success, -1 on failure */ -int ast_format_register(const char *name, const char *exts, int format, - struct ast_filestream * (*open)(FILE *f), - struct ast_filestream * (*rewrite)(FILE *f, const char *comment), - int (*write)(struct ast_filestream *, struct ast_frame *), - int (*seek)(struct ast_filestream *, off_t offset, int whence), - int (*trunc)(struct ast_filestream *), - off_t (*tell)(struct ast_filestream *), - struct ast_frame * (*read)(struct ast_filestream *, int *timetonext), - void (*close)(struct ast_filestream *), - char * (*getcomment)(struct ast_filestream *)); - +int ast_format_register(const struct ast_format *f); + /*! Unregisters a file format */ /*! * \param name the name of the format you wish to unregister -- cgit v1.2.3