2 * Asterisk -- A telephony toolkit for Linux.
4 * Save to raw, headerless G729 data.
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/lock.h>
15 #include <asterisk/channel.h>
16 #include <asterisk/file.h>
17 #include <asterisk/logger.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/module.h>
20 #include <arpa/inet.h>
31 #include <machine/endian.h>
34 /* Some Ideas for this code came from makeg729e.c by Jeffery Chilton */
36 /* Portions of the conversion code are by guido@sienanet.it */
38 struct ast_filestream {
39 void *reserved[AST_RESERVED_POINTERS];
40 /* Believe it or not, we must decode/recode to account for the
42 /* This is what a filestream means to us */
43 int fd; /* Descriptor */
44 struct ast_channel *owner;
45 struct ast_frame fr; /* Frame information */
46 char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
47 char empty; /* Empty character */
48 unsigned char g729[20]; /* Two Real G729 Frames */
52 struct ast_filestream *next;
56 static struct ast_filestream *glist = NULL;
57 static pthread_mutex_t g729_lock = AST_MUTEX_INITIALIZER;
58 static int glistcnt = 0;
60 static char *name = "g729";
61 static char *desc = "Raw G729 data";
62 static char *exts = "g729";
64 static struct ast_filestream *g729_open(int fd)
66 /* We don't have any header to read or anything really, but
67 if we did, it would go here. We also might want to check
68 and be sure it's a valid file. */
69 struct ast_filestream *tmp;
70 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
71 memset(tmp, 0, sizeof(struct ast_filestream));
72 if (ast_pthread_mutex_lock(&g729_lock)) {
73 ast_log(LOG_WARNING, "Unable to lock g729 list\n");
81 tmp->fr.data = tmp->g729;
82 tmp->fr.frametype = AST_FRAME_VOICE;
83 tmp->fr.subclass = AST_FORMAT_G729A;
84 /* datalen will vary for each frame */
87 tmp->lasttimeout = -1;
89 ast_pthread_mutex_unlock(&g729_lock);
90 ast_update_use_count();
95 static struct ast_filestream *g729_rewrite(int fd, char *comment)
97 /* We don't have any header to read or anything really, but
98 if we did, it would go here. We also might want to check
99 and be sure it's a valid file. */
100 struct ast_filestream *tmp;
101 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
102 memset(tmp, 0, sizeof(struct ast_filestream));
103 if (ast_pthread_mutex_lock(&g729_lock)) {
104 ast_log(LOG_WARNING, "Unable to lock g729 list\n");
112 tmp->lasttimeout = -1;
114 ast_pthread_mutex_unlock(&g729_lock);
115 ast_update_use_count();
117 ast_log(LOG_WARNING, "Out of memory\n");
121 static struct ast_frame *g729_read(struct ast_filestream *s)
126 static void g729_close(struct ast_filestream *s)
128 struct ast_filestream *tmp, *tmpl = NULL;
129 if (ast_pthread_mutex_lock(&g729_lock)) {
130 ast_log(LOG_WARNING, "Unable to lock g729 list\n");
137 tmpl->next = tmp->next;
147 s->owner->stream = NULL;
148 if (s->owner->streamid > -1)
149 ast_sched_del(s->owner->sched, s->owner->streamid);
150 s->owner->streamid = -1;
152 ast_pthread_mutex_unlock(&g729_lock);
153 ast_update_use_count();
155 ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
161 static int ast_read_callback(void *data)
166 struct ast_filestream *s = data;
168 /* Send a frame from the file to the appropriate channel */
170 s->fr.frametype = AST_FRAME_VOICE;
171 s->fr.subclass = AST_FORMAT_G729A;
172 s->fr.offset = AST_FRIENDLY_OFFSET;
176 s->fr.data = s->g729;
177 if ((res = read(s->fd, s->g729, 20)) != 20) {
179 ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
180 s->owner->streamid = -1;
183 /* Lastly, process the frame */
184 if (ast_write(s->owner, &s->fr)) {
185 ast_log(LOG_WARNING, "Failed to write frame\n");
186 s->owner->streamid = -1;
189 if (s->last.tv_usec || s->last.tv_usec) {
191 gettimeofday(&tv, NULL);
192 ms = 1000 * (tv.tv_sec - s->last.tv_sec) +
193 (tv.tv_usec - s->last.tv_usec) / 1000;
194 s->last.tv_sec = tv.tv_sec;
195 s->last.tv_usec = tv.tv_usec;
196 if ((ms - delay) * (ms - delay) > 4) {
197 /* Compensate if we're more than 2 ms off */
198 s->adj -= (ms - delay);
201 fprintf(stdout, "Delay is %d, adjustment is %d, last was %d\n", delay, s->adj, ms);
207 gettimeofday(&s->last, NULL);
208 if (s->lasttimeout != delay) {
209 /* We'll install the next timeout now. */
210 s->owner->streamid = ast_sched_add(s->owner->sched,
211 delay, ast_read_callback, s);
212 s->lasttimeout = delay;
214 /* Just come back again at the same time */
220 static int g729_apply(struct ast_channel *c, struct ast_filestream *s)
222 /* Select our owner for this stream, and get the ball rolling. */
227 static int g729_play(struct ast_filestream *s)
229 ast_read_callback(s);
233 static int g729_write(struct ast_filestream *fs, struct ast_frame *f)
236 if (f->frametype != AST_FRAME_VOICE) {
237 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
240 if (f->subclass != AST_FORMAT_G729A) {
241 ast_log(LOG_WARNING, "Asked to write non-G729 frame (%d)!\n", f->subclass);
244 if (f->datalen % 20) {
245 ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 20\n", f->datalen);
248 if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
249 ast_log(LOG_WARNING, "Bad write (%d/20): %s\n", res, strerror(errno));
255 static char *g729_getcomment(struct ast_filestream *s)
260 static int g729_seek(struct ast_filestream *fs, long sample_offset, int whence)
263 off_t min,cur,max,offset;
265 cur = lseek(fs->fd, 0, SEEK_CUR);
266 max = lseek(fs->fd, 0, SEEK_END);
268 bytes = 20 * (sample_offset / 160);
269 if(whence == SEEK_SET)
271 if(whence == SEEK_CUR)
272 offset = cur + bytes;
273 if(whence == SEEK_END)
274 offset = max - bytes;
275 offset = (offset > max)?max:offset;
276 offset = (offset < min)?min:offset;
277 if (lseek(fs->fd, offset, SEEK_SET) < 0)
282 static int g729_trunc(struct ast_filestream *fs)
284 /* Truncate file to current length */
285 if (ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)) < 0)
290 static long g729_tell(struct ast_filestream *fs)
293 offset = lseek(fs->fd, 0, SEEK_CUR);
294 return (offset/20)*160;
299 return ast_format_register(name, exts, AST_FORMAT_G729A,
317 struct ast_filestream *tmp, *tmpl;
318 if (ast_pthread_mutex_lock(&g729_lock)) {
319 ast_log(LOG_WARNING, "Unable to lock g729 list\n");
325 ast_softhangup(tmp->owner, AST_SOFTHANGUP_APPUNLOAD);
330 ast_pthread_mutex_unlock(&g729_lock);
331 return ast_format_unregister(name);
337 if (ast_pthread_mutex_lock(&g729_lock)) {
338 ast_log(LOG_WARNING, "Unable to lock g729 list\n");
342 ast_pthread_mutex_unlock(&g729_lock);
354 return ASTERISK_GPL_KEY;