don't take locks when reading usecounts, they are not necessary (bug #4585)
[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 <unistd.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <stdlib.h>
18 #include <sys/time.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <string.h>
22
23 #include "asterisk.h"
24
25 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
26
27 #include "asterisk/lock.h"
28 #include "asterisk/channel.h"
29 #include "asterisk/file.h"
30 #include "asterisk/logger.h"
31 #include "asterisk/sched.h"
32 #include "asterisk/module.h"
33 #include "asterisk/endian.h"
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, const 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         glistcnt--;
113         ast_mutex_unlock(&pcm_lock);
114         ast_update_use_count();
115         close(s->fd);
116         free(s);
117         s = NULL;
118 }
119
120 static struct ast_frame *pcm_read(struct ast_filestream *s, int *whennext)
121 {
122         int res;
123         int delay;
124         /* Send a frame from the file to the appropriate channel */
125
126         s->fr.frametype = AST_FRAME_VOICE;
127         s->fr.subclass = AST_FORMAT_ULAW;
128         s->fr.offset = AST_FRIENDLY_OFFSET;
129         s->fr.mallocd = 0;
130         s->fr.data = s->buf;
131         if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
132                 if (res)
133                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
134                 return NULL;
135         }
136         s->fr.samples = res;
137         s->fr.datalen = res;
138         delay = s->fr.samples;
139         *whennext = delay;
140         return &s->fr;
141 }
142
143 static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
144 {
145         int res;
146         if (f->frametype != AST_FRAME_VOICE) {
147                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
148                 return -1;
149         }
150         if (f->subclass != AST_FORMAT_ULAW) {
151                 ast_log(LOG_WARNING, "Asked to write non-ulaw frame (%d)!\n", f->subclass);
152                 return -1;
153         }
154         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
155                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
156                         return -1;
157         }
158         return 0;
159 }
160
161 static int pcm_seek(struct ast_filestream *fs, long sample_offset, int whence)
162 {
163         off_t offset=0,min,cur,max;
164
165         min = 0;
166         cur = lseek(fs->fd, 0, SEEK_CUR);
167         max = lseek(fs->fd, 0, SEEK_END);
168         if (whence == SEEK_SET)
169                 offset = sample_offset;
170         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
171                 offset = sample_offset + cur;
172         else if (whence == SEEK_END)
173                 offset = max - sample_offset;
174         if (whence != SEEK_FORCECUR) {
175                 offset = (offset > max)?max:offset;
176         }
177         /* always protect against seeking past begining. */
178         offset = (offset < min)?min:offset;
179         return lseek(fs->fd, offset, SEEK_SET);
180 }
181
182 static int pcm_trunc(struct ast_filestream *fs)
183 {
184         return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
185 }
186
187 static long pcm_tell(struct ast_filestream *fs)
188 {
189         off_t offset;
190         offset = lseek(fs->fd, 0, SEEK_CUR);
191         return offset;
192 }
193
194 static char *pcm_getcomment(struct ast_filestream *s)
195 {
196         return NULL;
197 }
198
199 int load_module()
200 {
201         return ast_format_register(name, exts, AST_FORMAT_ULAW,
202                                                                 pcm_open,
203                                                                 pcm_rewrite,
204                                                                 pcm_write,
205                                                                 pcm_seek,
206                                                                 pcm_trunc,
207                                                                 pcm_tell,
208                                                                 pcm_read,
209                                                                 pcm_close,
210                                                                 pcm_getcomment);
211                                                                 
212                                                                 
213 }
214
215 int unload_module()
216 {
217         return ast_format_unregister(name);
218 }       
219
220 int usecount()
221 {
222         return glistcnt;
223 }
224
225 char *description()
226 {
227         return desc;
228 }
229
230
231 char *key()
232 {
233         return ASTERISK_GPL_KEY;
234 }