~ubuntu-branches/ubuntu/karmic/asterisk/karmic

« back to all changes in this revision

Viewing changes to formats/format_pcm.c

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2002-04-27 21:19:32 UTC
  • Revision ID: james.westby@ubuntu.com-20020427211932-kqaertc4bg7ss5mc
Tags: upstream-0.1.11
ImportĀ upstreamĀ versionĀ 0.1.11

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Asterisk -- A telephony toolkit for Linux.
 
3
 *
 
4
 * Flat, binary, ulaw PCM file format.
 
5
 * 
 
6
 * Copyright (C) 1999, Mark Spencer
 
7
 *
 
8
 * Mark Spencer <markster@linux-support.net>
 
9
 *
 
10
 * This program is free software, distributed under the terms of
 
11
 * the GNU General Public License
 
12
 */
 
13
 
 
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>
 
20
#include <stdlib.h>
 
21
#include <sys/time.h>
 
22
#include <stdio.h>
 
23
#include <unistd.h>
 
24
#include <errno.h>
 
25
#include <string.h>
 
26
#include <pthread.h>
 
27
#include <endian.h>
 
28
 
 
29
#define BUF_SIZE 160            /* 160 samples */
 
30
 
 
31
struct ast_filestream {
 
32
        void *reserved[AST_RESERVED_POINTERS];
 
33
        /* Believe it or not, we must decode/recode to account for the
 
34
           weird MS format */
 
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 */
 
42
        int lasttimeout;
 
43
        struct timeval last;
 
44
        int adj;
 
45
        struct ast_filestream *next;
 
46
};
 
47
 
 
48
 
 
49
static struct ast_filestream *glist = NULL;
 
50
static pthread_mutex_t pcm_lock = PTHREAD_MUTEX_INITIALIZER;
 
51
static int glistcnt = 0;
 
52
 
 
53
static char *name = "pcm";
 
54
static char *desc = "Raw uLaw 8khz Audio support (PCM)";
 
55
static char *exts = "pcm|ulaw|ul|mu";
 
56
 
 
57
static struct ast_filestream *pcm_open(int fd)
 
58
{
 
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");
 
67
                        free(tmp);
 
68
                        return NULL;
 
69
                }
 
70
                tmp->next = glist;
 
71
                glist = tmp;
 
72
                tmp->fd = fd;
 
73
                tmp->owner = NULL;
 
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 */
 
78
                tmp->fr.src = name;
 
79
                tmp->fr.mallocd = 0;
 
80
                tmp->lasttimeout = -1;
 
81
                glistcnt++;
 
82
                pthread_mutex_unlock(&pcm_lock);
 
83
                ast_update_use_count();
 
84
        }
 
85
        return tmp;
 
86
}
 
87
 
 
88
static struct ast_filestream *pcm_rewrite(int fd, char *comment)
 
89
{
 
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");
 
98
                        free(tmp);
 
99
                        return NULL;
 
100
                }
 
101
                tmp->next = glist;
 
102
                glist = tmp;
 
103
                tmp->fd = fd;
 
104
                tmp->owner = NULL;
 
105
                tmp->lasttimeout = -1;
 
106
                glistcnt++;
 
107
                pthread_mutex_unlock(&pcm_lock);
 
108
                ast_update_use_count();
 
109
        } else
 
110
                ast_log(LOG_WARNING, "Out of memory\n");
 
111
        return tmp;
 
112
}
 
113
 
 
114
static struct ast_frame *pcm_read(struct ast_filestream *s)
 
115
{
 
116
        return NULL;
 
117
}
 
118
 
 
119
static void pcm_close(struct ast_filestream *s)
 
120
{
 
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");
 
124
                return;
 
125
        }
 
126
        tmp = glist;
 
127
        while(tmp) {
 
128
                if (tmp == s) {
 
129
                        if (tmpl)
 
130
                                tmpl->next = tmp->next;
 
131
                        else
 
132
                                glist = tmp->next;
 
133
                        break;
 
134
                }
 
135
                tmpl = tmp;
 
136
                tmp = tmp->next;
 
137
        }
 
138
        glistcnt--;
 
139
        if (s->owner) {
 
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;
 
144
        }
 
145
        pthread_mutex_unlock(&pcm_lock);
 
146
        ast_update_use_count();
 
147
        if (!tmp) 
 
148
                ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
 
149
        close(s->fd);
 
150
        free(s);
 
151
}
 
152
 
 
153
static int ast_read_callback(void *data)
 
154
{
 
155
        int retval = 0;
 
156
        int res;
 
157
        int delay;
 
158
        struct ast_filestream *s = data;
 
159
        struct timeval tv;
 
160
        /* Send a frame from the file to the appropriate channel */
 
161
 
 
162
        s->fr.frametype = AST_FRAME_VOICE;
 
163
        s->fr.subclass = AST_FORMAT_ULAW;
 
164
        s->fr.offset = AST_FRIENDLY_OFFSET;
 
165
        s->fr.mallocd = 0;
 
166
        s->fr.data = s->buf;
 
167
        if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
 
168
                if (res)
 
169
                        ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
 
170
                s->owner->streamid = -1;
 
171
                return 0;
 
172
        }
 
173
        s->fr.timelen = res / 8;
 
174
        s->fr.datalen = res;
 
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;
 
180
                return 0;
 
181
        }
 
182
        if (s->last.tv_usec || s->last.tv_usec) {
 
183
                int ms;
 
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);
 
192
                }
 
193
#if 0
 
194
                fprintf(stdout, "Delay is %d, adjustment is %d, last was %d\n", delay, s->adj, ms);
 
195
#endif
 
196
                delay += s->adj;
 
197
                if (delay < 1)
 
198
                        delay = 1;
 
199
        } else
 
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;
 
206
        } else {
 
207
                /* Just come back again at the same time */
 
208
                retval = -1;
 
209
        }
 
210
        return retval;
 
211
}
 
212
 
 
213
static int pcm_apply(struct ast_channel *c, struct ast_filestream *s)
 
214
{
 
215
        /* Select our owner for this stream, and get the ball rolling. */
 
216
        s->owner = c;
 
217
        ast_read_callback(s);
 
218
        return 0;
 
219
}
 
220
 
 
221
static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
 
222
{
 
223
        int res;
 
224
        if (f->frametype != AST_FRAME_VOICE) {
 
225
                ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
 
226
                return -1;
 
227
        }
 
228
        if (f->subclass != AST_FORMAT_ULAW) {
 
229
                ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass);
 
230
                return -1;
 
231
        }
 
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));
 
234
                        return -1;
 
235
        }
 
236
        return 0;
 
237
}
 
238
 
 
239
static char *pcm_getcomment(struct ast_filestream *s)
 
240
{
 
241
        return NULL;
 
242
}
 
243
 
 
244
int load_module()
 
245
{
 
246
        return ast_format_register(name, exts, AST_FORMAT_ULAW,
 
247
                                                                pcm_open,
 
248
                                                                pcm_rewrite,
 
249
                                                                pcm_apply,
 
250
                                                                pcm_write,
 
251
                                                                pcm_read,
 
252
                                                                pcm_close,
 
253
                                                                pcm_getcomment);
 
254
                                                                
 
255
                                                                
 
256
}
 
257
 
 
258
int unload_module()
 
259
{
 
260
        struct ast_filestream *tmp, *tmpl;
 
261
        if (pthread_mutex_lock(&pcm_lock)) {
 
262
                ast_log(LOG_WARNING, "Unable to lock pcm list\n");
 
263
                return -1;
 
264
        }
 
265
        tmp = glist;
 
266
        while(tmp) {
 
267
                if (tmp->owner)
 
268
                        ast_softhangup(tmp->owner);
 
269
                tmpl = tmp;
 
270
                tmp = tmp->next;
 
271
                free(tmpl);
 
272
        }
 
273
        pthread_mutex_unlock(&pcm_lock);
 
274
        return ast_format_unregister(name);
 
275
}       
 
276
 
 
277
int usecount()
 
278
{
 
279
        int res;
 
280
        if (pthread_mutex_lock(&pcm_lock)) {
 
281
                ast_log(LOG_WARNING, "Unable to lock pcm list\n");
 
282
                return -1;
 
283
        }
 
284
        res = glistcnt;
 
285
        pthread_mutex_unlock(&pcm_lock);
 
286
        return res;
 
287
}
 
288
 
 
289
char *description()
 
290
{
 
291
        return desc;
 
292
}
 
293
 
 
294
 
 
295
char *key()
 
296
{
 
297
        return ASTERISK_GPL_KEY;
 
298
}