2 * Asterisk -- A telephony toolkit for Linux.
4 * Generic File Format Support.
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/frame.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/options.h>
20 #include <asterisk/translate.h>
35 /* Extensions (separated by | if more than one)
36 this format can read. First is assumed for writing (e.g. .mp3) */
38 /* Format of frames it uses/provides (one only) */
40 /* Open an input stream, and start playback */
41 struct ast_filestream * (*open)(int fd);
42 /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
43 struct ast_filestream * (*rewrite)(int fd, char *comment);
44 /* Apply a reading filestream to a channel */
45 int (*apply)(struct ast_channel *, struct ast_filestream *);
46 /* Write a frame to a channel */
47 int (*write)(struct ast_filestream *, struct ast_frame *);
48 /* Read the next frame from the filestream (if available) */
49 struct ast_frame * (*read)(struct ast_filestream *);
50 /* Close file, and destroy filestream structure */
51 void (*close)(struct ast_filestream *);
52 /* Retrieve file comment */
53 char * (*getcomment)(struct ast_filestream *);
55 struct ast_format *next;
58 struct ast_filestream {
59 /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
60 struct ast_format *fmt;
61 /* Transparently translate from another format -- just once */
62 struct ast_trans_pvt *trans;
63 struct ast_tranlator_pvt *tr;
66 static pthread_mutex_t formatlock = PTHREAD_MUTEX_INITIALIZER;
68 static struct ast_format *formats = NULL;
70 int ast_format_register(char *name, char *exts, int format,
71 struct ast_filestream * (*open)(int fd),
72 struct ast_filestream * (*rewrite)(int fd, char *comment),
73 int (*apply)(struct ast_channel *, struct ast_filestream *),
74 int (*write)(struct ast_filestream *, struct ast_frame *),
75 struct ast_frame * (*read)(struct ast_filestream *),
76 void (*close)(struct ast_filestream *),
77 char * (*getcomment)(struct ast_filestream *))
79 struct ast_format *tmp;
80 if (pthread_mutex_lock(&formatlock)) {
81 ast_log(LOG_WARNING, "Unable to lock format list\n");
86 if (!strcasecmp(name, tmp->name)) {
87 pthread_mutex_unlock(&formatlock);
88 ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
93 tmp = malloc(sizeof(struct ast_format));
95 ast_log(LOG_WARNING, "Out of memory\n");
96 pthread_mutex_unlock(&formatlock);
99 strncpy(tmp->name, name, sizeof(tmp->name));
100 strncpy(tmp->exts, exts, sizeof(tmp->exts));
102 tmp->rewrite = rewrite;
107 tmp->format = format;
108 tmp->getcomment = getcomment;
111 pthread_mutex_unlock(&formatlock);
112 if (option_verbose > 1)
113 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
117 int ast_format_unregister(char *name)
119 struct ast_format *tmp, *tmpl = NULL;
120 if (pthread_mutex_lock(&formatlock)) {
121 ast_log(LOG_WARNING, "Unable to lock format list\n");
126 if (!strcasecmp(name, tmp->name)) {
128 tmpl->next = tmp->next;
132 pthread_mutex_unlock(&formatlock);
133 if (option_verbose > 1)
134 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
139 ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
143 int ast_stopstream(struct ast_channel *tmp)
145 /* Stop a running stream if there is one */
148 tmp->stream->fmt->close(tmp->stream);
149 if (ast_set_write_format(tmp, tmp->oldwriteformat))
150 ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
154 int ast_closestream(struct ast_filestream *f)
156 /* Stop a running stream if there is one */
161 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
163 struct ast_frame *trf;
165 if (f->frametype != AST_FRAME_VOICE) {
166 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
169 if ((fs->fmt->format & f->subclass) == f->subclass) {
170 res = fs->fmt->write(fs, f);
172 ast_log(LOG_WARNING, "Natural write failed\n");
174 ast_log(LOG_WARNING, "Huh??\n");
177 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
178 the one we've setup a translator for, we do the "wrong thing" XXX */
180 fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
182 ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
185 /* Get the translated frame but don't consume the original in case they're using it on another stream */
186 trf = ast_translate(fs->trans, f, 0);
187 res = fs->fmt->write(fs, trf);
189 ast_log(LOG_WARNING, "Translated frame write failed\n");
195 static char *build_filename(char *filename, char *ext)
198 fn = malloc(strlen(AST_SOUNDS) + strlen(filename) + strlen(ext) + 10);
200 if (filename[0] == '/')
201 sprintf(fn, "%s.%s", filename, ext);
203 sprintf(fn, "%s/%s.%s", AST_SOUNDS, filename, ext);
209 #define ACTION_EXISTS 1
210 #define ACTION_DELETE 2
211 #define ACTION_RENAME 3
212 #define ACTION_OPEN 4
214 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
217 struct ast_format *f;
218 struct ast_filestream *s;
220 char *ext=NULL, *exts, *fn, *nfn;
221 struct ast_channel *chan = (struct ast_channel *)filename2;
223 /* Start with negative response */
224 if (action == ACTION_EXISTS)
228 if (action == ACTION_OPEN)
230 /* Check for a specific format */
231 if (pthread_mutex_lock(&formatlock)) {
232 ast_log(LOG_WARNING, "Unable to lock format list\n");
233 if (action == ACTION_EXISTS)
240 if (!fmt || !strcasecmp(f->name, fmt)) {
241 exts = strdup(f->exts);
242 /* Try each kind of extension */
243 ext = strtok(exts, "|");
245 fn = build_filename(filename, ext);
256 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
259 nfn = build_filename(filename2, ext);
261 res = rename(fn, nfn);
263 ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
266 ast_log(LOG_WARNING, "Out of memory\n");
269 if ((ret < 0) && ((chan->writeformat & f->format))) {
270 ret = open(fn, O_RDONLY);
277 if (f->apply(chan, s)) {
280 ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", chan->name);
286 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
289 ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
293 ast_log(LOG_WARNING, "Unknown helper %d\n", action);
295 /* Conveniently this logic is the same for all */
301 ext = strtok(NULL, "|");
307 pthread_mutex_unlock(&formatlock);
308 if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
309 res = ret ? ret : -1;
313 int ast_fileexists(char *filename, char *fmt, char *preflang)
316 char lang2[MAX_LANGUAGE];
318 if (preflang && strlen(preflang)) {
319 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
320 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
322 strncpy(lang2, preflang, sizeof(lang2));
324 if (strcmp(lang2, preflang)) {
325 snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
326 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
331 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
336 int ast_filedelete(char *filename, char *fmt)
338 return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
341 int ast_filerename(char *filename, char *filename2, char *fmt)
343 return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
346 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
348 /* This is a fairly complex routine. Essentially we should do
351 1) Find which file handlers produce our type of format.
352 2) Look for a filename which it can handle.
353 3) If we find one, then great.
354 4) If not, see what files are there
355 5) See what we can actually support
356 6) Choose the one with the least costly translator path and
363 char lang2[MAX_LANGUAGE];
365 ast_stopstream(chan);
366 if (preflang && strlen(preflang)) {
367 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
368 fmts = ast_fileexists(filename2, NULL, NULL);
370 strncpy(lang2, preflang, sizeof(lang2));
371 snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
372 fmts = ast_fileexists(filename2, NULL, NULL);
376 strncpy(filename2, filename, sizeof(filename2));
377 fmts = ast_fileexists(filename2, NULL, NULL);
380 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
383 chan->oldwriteformat = chan->writeformat;
384 /* Set the channel to a format we can work with */
385 res = ast_set_write_format(chan, fmts);
387 fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
390 if (option_verbose > 2)
391 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename2);
395 ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
400 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
403 struct ast_format *f;
404 struct ast_filestream *fs=NULL;
407 if (pthread_mutex_lock(&formatlock)) {
408 ast_log(LOG_WARNING, "Unable to lock format list\n");
413 if (!strcasecmp(f->name, type)) {
414 /* XXX Implement check XXX */
415 ext = strdup(f->exts);
416 ext = strtok(ext, "|");
417 fn = build_filename(filename, ext);
418 fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
421 if ((fs = f->rewrite(fd, comment))) {
425 ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
429 } else if (errno != EEXIST)
430 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
437 pthread_mutex_unlock(&formatlock);
439 ast_log(LOG_WARNING, "No such format '%s'\n", type);
443 char ast_waitstream(struct ast_channel *c, char *breakon)
446 struct ast_frame *fr;
448 res = ast_sched_wait(c->sched);
450 ast_closestream(c->stream);
453 res = ast_waitfor(c, res);
455 ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
462 ast_log(LOG_DEBUG, "Got hung up\n");
467 switch(fr->frametype) {
471 if (strchr(breakon, res))
474 case AST_FRAME_CONTROL:
475 switch(fr->subclass) {
476 case AST_CONTROL_HANGUP:
480 ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
487 ast_sched_runq(c->sched);