2 * Asterisk -- A telephony toolkit for Linux.
4 * Everybody's favorite format: MP3 Files! Yay!
6 * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
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/channel.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/sched.h>
18 #include <asterisk/module.h>
19 #include <arpa/inet.h>
27 #include "../channels/adtranvofr.h"
30 #define MP3_MAX_SIZE 1400
32 struct ast_filestream {
33 /* First entry MUST be reserved for the channel type */
34 void *reserved[AST_RESERVED_POINTERS];
35 /* This is what a filestream means to us */
36 int fd; /* Descriptor */
37 struct ast_channel *owner;
38 struct ast_filestream *next;
39 struct ast_frame *fr; /* Frame representation of buf */
40 char buf[sizeof(struct ast_frame) + MP3_MAX_SIZE + AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
45 static struct ast_filestream *glist = NULL;
46 static pthread_mutex_t mp3_lock = PTHREAD_MUTEX_INITIALIZER;
47 static int glistcnt = 0;
49 static char *name = "mp3";
50 static char *desc = "MPEG-2 Layer 3 File Format Support";
51 static char *exts = "mp3|mpeg3";
54 #define MP3_FRAMELEN 417
56 #define MP3_FRAMELEN 400
58 #define MP3_OUTPUTLEN 2304 /* Bytes */
59 #define MP3_TIMELEN ((MP3_OUTPUTLEN * 1000 / 16000) )
61 static struct ast_filestream *mp3_open(int fd)
63 /* We don't have any header to read or anything really, but
64 if we did, it would go here. We also might want to check
65 and be sure it's a valid file. */
66 struct ast_filestream *tmp;
67 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
68 if (pthread_mutex_lock(&mp3_lock)) {
69 ast_log(LOG_WARNING, "Unable to lock mp3 list\n");
77 tmp->fr = (struct ast_frame *)tmp->buf;
78 tmp->fr->data = tmp->buf + sizeof(struct ast_frame);
79 tmp->fr->frametype = AST_FRAME_VOICE;
80 tmp->fr->subclass = AST_FORMAT_MP3;
81 /* datalen will vary for each frame */
85 pthread_mutex_unlock(&mp3_lock);
86 ast_update_use_count();
91 static struct ast_filestream *mp3_rewrite(int fd, char *comment)
93 /* We don't have any header to read or anything really, but
94 if we did, it would go here. We also might want to check
95 and be sure it's a valid file. */
96 struct ast_filestream *tmp;
97 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
98 if (pthread_mutex_lock(&mp3_lock)) {
99 ast_log(LOG_WARNING, "Unable to lock mp3 list\n");
109 pthread_mutex_unlock(&mp3_lock);
110 ast_update_use_count();
112 ast_log(LOG_WARNING, "Out of memory\n");
116 static struct ast_frame *mp3_read(struct ast_filestream *s)
121 static void mp3_close(struct ast_filestream *s)
123 struct ast_filestream *tmp, *tmpl = NULL;
124 if (pthread_mutex_lock(&mp3_lock)) {
125 ast_log(LOG_WARNING, "Unable to lock mp3 list\n");
132 tmpl->next = tmp->next;
142 s->owner->stream = NULL;
143 if (s->owner->streamid > -1)
144 ast_sched_del(s->owner->sched, s->owner->streamid);
145 s->owner->streamid = -1;
147 pthread_mutex_unlock(&mp3_lock);
148 ast_update_use_count();
150 ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
155 static int ast_read_callback(void *data)
157 /* XXX Don't assume frames are this size XXX */
158 u_int16_t size=MP3_FRAMELEN;
159 u_int32_t delay = -1;
161 struct ast_filestream *s = data;
162 /* Send a frame from the file to the appropriate channel */
163 /* Read the data into the buffer */
164 s->fr->offset = AST_FRIENDLY_OFFSET;
165 s->fr->datalen = size;
166 s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
167 if ((res = read(s->fd, s->fr->data , size)) != size) {
168 ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno));
169 s->owner->streamid = -1;
173 s->fr->timelen = delay;
174 /* Lastly, process the frame */
175 if (ast_write(s->owner, s->fr)) {
176 ast_log(LOG_WARNING, "Failed to write frame\n");
177 s->owner->streamid = -1;
183 static int mp3_apply(struct ast_channel *c, struct ast_filestream *s)
185 /* Select our owner for this stream, and get the ball rolling. */
187 s->owner->streamid = ast_sched_add(s->owner->sched, MP3_TIMELEN, ast_read_callback, s);
188 ast_read_callback(s);
192 static int mp3_write(struct ast_filestream *fs, struct ast_frame *f)
196 ast_log(LOG_WARNING, "Asked to write on a read stream??\n");
199 if (f->frametype != AST_FRAME_VOICE) {
200 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
203 if (f->subclass != AST_FORMAT_MP3) {
204 ast_log(LOG_WARNING, "Asked to write non-mp3 frame!\n");
207 if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
208 ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno));
214 char *mp3_getcomment(struct ast_filestream *s)
221 return ast_format_register(name, exts, AST_FORMAT_MP3,
235 struct ast_filestream *tmp, *tmpl;
236 if (pthread_mutex_lock(&mp3_lock)) {
237 ast_log(LOG_WARNING, "Unable to lock mp3 list\n");
243 ast_softhangup(tmp->owner);
248 pthread_mutex_unlock(&mp3_lock);
249 return ast_format_unregister(name);
255 if (pthread_mutex_lock(&mp3_lock)) {
256 ast_log(LOG_WARNING, "Unable to lock mp3 list\n");
260 pthread_mutex_unlock(&mp3_lock);