Merge FreeBSD locking fixes (bug #1411)
[asterisk/asterisk.git] / formats / format_pcm.c
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/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 <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <pthread.h>
29 #ifdef __linux__
30 #include <endian.h>
31 #else
32 #include <machine/endian.h>
33 #endif
34
35 #define BUF_SIZE 160            /* 160 samples */
36
37 struct ast_filestream {
38         void *reserved[AST_RESERVED_POINTERS];
39         /* This is what a filestream means to us */
40         int fd; /* Descriptor */
41         struct ast_channel *owner;
42         struct ast_frame fr;                            /* Frame information */
43         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
44         char empty;                                                     /* Empty character */
45         unsigned char buf[BUF_SIZE];                            /* Output Buffer */
46         struct timeval last;
47 };
48
49
50 AST_MUTEX_DEFINE_STATIC(pcm_lock);
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 (ast_mutex_lock(&pcm_lock)) {
66                         ast_log(LOG_WARNING, "Unable to lock pcm list\n");
67                         free(tmp);
68                         return NULL;
69                 }
70                 tmp->fd = fd;
71                 tmp->fr.data = tmp->buf;
72                 tmp->fr.frametype = AST_FRAME_VOICE;
73                 tmp->fr.subclass = AST_FORMAT_ULAW;
74                 /* datalen will vary for each frame */
75                 tmp->fr.src = name;
76                 tmp->fr.mallocd = 0;
77                 glistcnt++;
78                 ast_mutex_unlock(&pcm_lock);
79                 ast_update_use_count();
80         }
81         return tmp;
82 }
83
84 static struct ast_filestream *pcm_rewrite(int fd, char *comment)
85 {
86         /* We don't have any header to read or anything really, but
87            if we did, it would go here.  We also might want to check
88            and be sure it's a valid file.  */
89         struct ast_filestream *tmp;
90         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
91                 memset(tmp, 0, sizeof(struct ast_filestream));
92                 if (ast_mutex_lock(&pcm_lock)) {
93                         ast_log(LOG_WARNING, "Unable to lock pcm list\n");
94                         free(tmp);
95                         return NULL;
96                 }
97                 tmp->fd = fd;
98                 glistcnt++;
99                 ast_mutex_unlock(&pcm_lock);
100                 ast_update_use_count();
101         } else
102                 ast_log(LOG_WARNING, "Out of memory\n");
103         return tmp;
104 }
105
106 static void pcm_close(struct ast_filestream *s)
107 {
108         if (ast_mutex_lock(&pcm_lock)) {
109                 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
110                 return;
111         }
112         ast_mutex_unlock(&pcm_lock);
113         ast_update_use_count();
114         close(s->fd);
115         free(s);
116         s = NULL;
117 }
118
119 static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
120 {
121         int res;
122         int delay;
123         /* Send a frame from the file to the appropriate channel */
124
125         s->fr.frametype = AST_FRAME_VOICE;
126         s->fr.subclass = AST_FORMAT_ULAW;
127         s->fr.offset = AST_FRIENDLY_OFFSET;
128         s->fr.mallocd = 0;
129         s->fr.data = s->buf;
130         if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
131                 if (res)
132                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
133                 return NULL;
134         }
135         s->fr.samples = res;
136         s->fr.datalen = res;
137         delay = s->fr.samples;
138         *whennext = delay;
139         return &s->fr;
140 }
141
142 static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
143 {
144         int res;
145         if (f->frametype != AST_FRAME_VOICE) {
146                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
147                 return -1;
148         }
149         if (f->subclass != AST_FORMAT_ULAW) {
150                 ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass);
151                 return -1;
152         }
153         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
154                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
155                         return -1;
156         }
157         return 0;
158 }
159
160 static int pcm_seek(struct ast_filestream *fs, long sample_offset, int whence)
161 {
162         off_t offset=0,min,cur,max;
163
164         min = 0;
165         cur = lseek(fs->fd, 0, SEEK_CUR);
166         max = lseek(fs->fd, 0, SEEK_END);
167         if (whence == SEEK_SET)
168                 offset = sample_offset;
169         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
170                 offset = sample_offset + cur;
171         else if (whence == SEEK_END)
172                 offset = max - sample_offset;
173         if (whence != SEEK_FORCECUR) {
174                 offset = (offset > max)?max:offset;
175         }
176         // always protect against seeking past begining.
177         offset = (offset < min)?min:offset;
178         return lseek(fs->fd, offset, SEEK_SET);
179 }
180
181 static int pcm_trunc(struct ast_filestream *fs)
182 {
183         return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
184 }
185
186 static long pcm_tell(struct ast_filestream *fs)
187 {
188         off_t offset;
189         offset = lseek(fs->fd, 0, SEEK_CUR);
190         return offset;
191 }
192
193 static char *pcm_getcomment(struct ast_filestream *s)
194 {
195         return NULL;
196 }
197
198 int load_module()
199 {
200         return ast_format_register(name, exts, AST_FORMAT_ULAW,
201                                                                 pcm_open,
202                                                                 pcm_rewrite,
203                                                                 pcm_write,
204                                                                 pcm_seek,
205                                                                 pcm_trunc,
206                                                                 pcm_tell,
207                                                                 pcm_read,
208                                                                 pcm_close,
209                                                                 pcm_getcomment);
210                                                                 
211                                                                 
212 }
213
214 int unload_module()
215 {
216         return ast_format_unregister(name);
217 }       
218
219 int usecount()
220 {
221         int res;
222         if (ast_mutex_lock(&pcm_lock)) {
223                 ast_log(LOG_WARNING, "Unable to lock pcm list\n");
224                 return -1;
225         }
226         res = glistcnt;
227         ast_mutex_unlock(&pcm_lock);
228         return res;
229 }
230
231 char *description()
232 {
233         return desc;
234 }
235
236
237 char *key()
238 {
239         return ASTERISK_GPL_KEY;
240 }