2 * Asterisk -- A telephony toolkit for Linux.
4 * Flat, binary, ulaw PCM file format.
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/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>
29 #define BUF_SIZE 160 /* 160 samples */
31 struct ast_filestream {
32 void *reserved[AST_RESERVED_POINTERS];
33 /* Believe it or not, we must decode/recode to account for the
35 /* This is what a filestream means to us */
36 int fd; /* Descriptor */
37 struct ast_channel *owner;
38 struct ast_frame fr; /* Frame information */
39 char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
40 char empty; /* Empty character */
41 unsigned char buf[BUF_SIZE]; /* Output Buffer */
45 struct ast_filestream *next;
49 static struct ast_filestream *glist = NULL;
50 static pthread_mutex_t pcm_lock = PTHREAD_MUTEX_INITIALIZER;
51 static int glistcnt = 0;
53 static char *name = "pcm";
54 static char *desc = "Raw uLaw 8khz Audio support (PCM)";
55 static char *exts = "pcm|ulaw|ul|mu";
57 static struct ast_filestream *pcm_open(int fd)
59 /* We don't have any header to read or anything really, but
60 if we did, it would go here. We also might want to check
61 and be sure it's a valid file. */
62 struct ast_filestream *tmp;
63 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
64 memset(tmp, 0, sizeof(struct ast_filestream));
65 if (pthread_mutex_lock(&pcm_lock)) {
66 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
74 tmp->fr.data = tmp->buf;
75 tmp->fr.frametype = AST_FRAME_VOICE;
76 tmp->fr.subclass = AST_FORMAT_ULAW;
77 /* datalen will vary for each frame */
80 tmp->lasttimeout = -1;
82 pthread_mutex_unlock(&pcm_lock);
83 ast_update_use_count();
88 static struct ast_filestream *pcm_rewrite(int fd, char *comment)
90 /* We don't have any header to read or anything really, but
91 if we did, it would go here. We also might want to check
92 and be sure it's a valid file. */
93 struct ast_filestream *tmp;
94 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
95 memset(tmp, 0, sizeof(struct ast_filestream));
96 if (pthread_mutex_lock(&pcm_lock)) {
97 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
105 tmp->lasttimeout = -1;
107 pthread_mutex_unlock(&pcm_lock);
108 ast_update_use_count();
110 ast_log(LOG_WARNING, "Out of memory\n");
114 static struct ast_frame *pcm_read(struct ast_filestream *s)
119 static void pcm_close(struct ast_filestream *s)
121 struct ast_filestream *tmp, *tmpl = NULL;
122 if (pthread_mutex_lock(&pcm_lock)) {
123 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
130 tmpl->next = tmp->next;
140 s->owner->stream = NULL;
141 if (s->owner->streamid > -1)
142 ast_sched_del(s->owner->sched, s->owner->streamid);
143 s->owner->streamid = -1;
145 pthread_mutex_unlock(&pcm_lock);
146 ast_update_use_count();
148 ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
153 static int ast_read_callback(void *data)
158 struct ast_filestream *s = data;
160 /* Send a frame from the file to the appropriate channel */
162 s->fr.frametype = AST_FRAME_VOICE;
163 s->fr.subclass = AST_FORMAT_ULAW;
164 s->fr.offset = AST_FRIENDLY_OFFSET;
167 if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
169 ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
170 s->owner->streamid = -1;
173 s->fr.timelen = res / 8;
175 delay = s->fr.timelen;
176 /* Lastly, process the frame */
177 if (ast_write(s->owner, &s->fr)) {
178 ast_log(LOG_WARNING, "Failed to write frame\n");
179 s->owner->streamid = -1;
182 if (s->last.tv_usec || s->last.tv_usec) {
184 gettimeofday(&tv, NULL);
185 ms = 1000 * (tv.tv_sec - s->last.tv_sec) +
186 (tv.tv_usec - s->last.tv_usec) / 1000;
187 s->last.tv_sec = tv.tv_sec;
188 s->last.tv_usec = tv.tv_usec;
189 if ((ms - delay) * (ms - delay) > 4) {
190 /* Compensate if we're more than 2 ms off */
191 s->adj -= (ms - delay);
194 fprintf(stdout, "Delay is %d, adjustment is %d, last was %d\n", delay, s->adj, ms);
200 gettimeofday(&s->last, NULL);
201 if (s->lasttimeout != delay) {
202 /* We'll install the next timeout now. */
203 s->owner->streamid = ast_sched_add(s->owner->sched,
204 delay, ast_read_callback, s);
205 s->lasttimeout = delay;
207 /* Just come back again at the same time */
213 static int pcm_apply(struct ast_channel *c, struct ast_filestream *s)
215 /* Select our owner for this stream, and get the ball rolling. */
217 ast_read_callback(s);
221 static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
224 if (f->frametype != AST_FRAME_VOICE) {
225 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
228 if (f->subclass != AST_FORMAT_ULAW) {
229 ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass);
232 if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
233 ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
239 static char *pcm_getcomment(struct ast_filestream *s)
246 return ast_format_register(name, exts, AST_FORMAT_ULAW,
260 struct ast_filestream *tmp, *tmpl;
261 if (pthread_mutex_lock(&pcm_lock)) {
262 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
268 ast_softhangup(tmp->owner);
273 pthread_mutex_unlock(&pcm_lock);
274 return ast_format_unregister(name);
280 if (pthread_mutex_lock(&pcm_lock)) {
281 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
285 pthread_mutex_unlock(&pcm_lock);
297 return ASTERISK_GPL_KEY;