2
* Asterisk -- A telephony toolkit for Linux.
4
* Save to raw, headerless GSM 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/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
/* Some Ideas for this code came from makegsme.c by Jeffery Chilton */
31
/* Portions of the conversion code are by guido@sienanet.it */
33
struct ast_filestream {
34
void *reserved[AST_RESERVED_POINTERS];
35
/* Believe it or not, we must decode/recode to account for the
37
/* This is what a filestream means to us */
38
int fd; /* Descriptor */
39
struct ast_channel *owner;
40
struct ast_frame fr; /* Frame information */
41
char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
42
char empty; /* Empty character */
43
unsigned char gsm[33]; /* Two Real GSM Frames */
47
struct ast_filestream *next;
51
static struct ast_filestream *glist = NULL;
52
static pthread_mutex_t gsm_lock = PTHREAD_MUTEX_INITIALIZER;
53
static int glistcnt = 0;
55
static char *name = "gsm";
56
static char *desc = "Raw GSM data";
57
static char *exts = "gsm";
59
static struct ast_filestream *gsm_open(int fd)
61
/* We don't have any header to read or anything really, but
62
if we did, it would go here. We also might want to check
63
and be sure it's a valid file. */
64
struct ast_filestream *tmp;
65
if ((tmp = malloc(sizeof(struct ast_filestream)))) {
66
memset(tmp, 0, sizeof(struct ast_filestream));
67
if (ast_pthread_mutex_lock(&gsm_lock)) {
68
ast_log(LOG_WARNING, "Unable to lock gsm list\n");
76
tmp->fr.data = tmp->gsm;
77
tmp->fr.frametype = AST_FRAME_VOICE;
78
tmp->fr.subclass = AST_FORMAT_GSM;
79
/* datalen will vary for each frame */
82
tmp->lasttimeout = -1;
84
ast_pthread_mutex_unlock(&gsm_lock);
85
ast_update_use_count();
90
static struct ast_filestream *gsm_rewrite(int fd, char *comment)
92
/* We don't have any header to read or anything really, but
93
if we did, it would go here. We also might want to check
94
and be sure it's a valid file. */
95
struct ast_filestream *tmp;
96
if ((tmp = malloc(sizeof(struct ast_filestream)))) {
97
memset(tmp, 0, sizeof(struct ast_filestream));
98
if (ast_pthread_mutex_lock(&gsm_lock)) {
99
ast_log(LOG_WARNING, "Unable to lock gsm list\n");
107
tmp->lasttimeout = -1;
109
ast_pthread_mutex_unlock(&gsm_lock);
110
ast_update_use_count();
112
ast_log(LOG_WARNING, "Out of memory\n");
116
static struct ast_frame *gsm_read(struct ast_filestream *s)
121
static void gsm_close(struct ast_filestream *s)
123
struct ast_filestream *tmp, *tmpl = NULL;
124
if (ast_pthread_mutex_lock(&gsm_lock)) {
125
ast_log(LOG_WARNING, "Unable to lock gsm 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
ast_pthread_mutex_unlock(&gsm_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)
160
struct ast_filestream *s = data;
162
/* Send a frame from the file to the appropriate channel */
164
s->fr.frametype = AST_FRAME_VOICE;
165
s->fr.subclass = AST_FORMAT_GSM;
166
s->fr.offset = AST_FRIENDLY_OFFSET;
171
if ((res = read(s->fd, s->gsm, 33)) != 33) {
173
ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
174
s->owner->streamid = -1;
177
/* Lastly, process the frame */
178
if (ast_write(s->owner, &s->fr)) {
179
ast_log(LOG_WARNING, "Failed to write frame\n");
180
s->owner->streamid = -1;
183
if (s->last.tv_usec || s->last.tv_usec) {
185
gettimeofday(&tv, NULL);
186
ms = 1000 * (tv.tv_sec - s->last.tv_sec) +
187
(tv.tv_usec - s->last.tv_usec) / 1000;
188
s->last.tv_sec = tv.tv_sec;
189
s->last.tv_usec = tv.tv_usec;
190
if ((ms - delay) * (ms - delay) > 4) {
191
/* Compensate if we're more than 2 ms off */
192
s->adj -= (ms - delay);
195
fprintf(stdout, "Delay is %d, adjustment is %d, last was %d\n", delay, s->adj, ms);
201
gettimeofday(&s->last, NULL);
202
if (s->lasttimeout != delay) {
203
/* We'll install the next timeout now. */
204
s->owner->streamid = ast_sched_add(s->owner->sched,
205
delay, ast_read_callback, s);
206
s->lasttimeout = delay;
208
/* Just come back again at the same time */
214
static int gsm_apply(struct ast_channel *c, struct ast_filestream *s)
216
/* Select our owner for this stream, and get the ball rolling. */
218
ast_read_callback(s);
222
static int gsm_write(struct ast_filestream *fs, struct ast_frame *f)
225
if (f->frametype != AST_FRAME_VOICE) {
226
ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
229
if (f->subclass != AST_FORMAT_GSM) {
230
ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
233
if (f->datalen % 33) {
234
ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 33\n", f->datalen);
237
if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
238
ast_log(LOG_WARNING, "Bad write (%d/33): %s\n", res, strerror(errno));
244
static char *gsm_getcomment(struct ast_filestream *s)
251
return ast_format_register(name, exts, AST_FORMAT_GSM,
265
struct ast_filestream *tmp, *tmpl;
266
if (ast_pthread_mutex_lock(&gsm_lock)) {
267
ast_log(LOG_WARNING, "Unable to lock gsm list\n");
273
ast_softhangup(tmp->owner);
278
ast_pthread_mutex_unlock(&gsm_lock);
279
return ast_format_unregister(name);
285
if (ast_pthread_mutex_lock(&gsm_lock)) {
286
ast_log(LOG_WARNING, "Unable to lock gsm list\n");
290
ast_pthread_mutex_unlock(&gsm_lock);
302
return ASTERISK_GPL_KEY;