Fix offset calculation for signed linear (bug #4433)
[asterisk/asterisk.git] / formats / format_sln.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * RAW SLINEAR Format
5  * Anthony Minessale (anthmct@yahoo.com)
6  * Derived from format_pcm.c in the asterisk distro
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License
10  */
11  
12 #include "asterisk/lock.h"
13 #include "asterisk/channel.h"
14 #include "asterisk/file.h"
15 #include "asterisk/logger.h"
16 #include "asterisk/sched.h"
17 #include "asterisk/module.h"
18 #include "asterisk/endian.h"
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <stdlib.h>
22 #include <sys/time.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #define BUF_SIZE 320            /* 320 samples */
29
30 struct ast_filestream {
31         void *reserved[AST_RESERVED_POINTERS];
32         /* This is what a filestream means to us */
33         int fd; /* Descriptor */
34         struct ast_channel *owner;
35         struct ast_frame fr;                            /* Frame information */
36         char waste[AST_FRIENDLY_OFFSET];        /* Buffer for sending frames, etc */
37         char empty;                                                     /* Empty character */
38         unsigned char buf[BUF_SIZE];                            /* Output Buffer */
39         struct timeval last;
40 };
41
42
43 AST_MUTEX_DEFINE_STATIC(slinear_lock);
44 static int glistcnt = 0;
45
46 static char *name = "sln";
47 static char *desc = "Raw Signed Linear Audio support (SLN)";
48 static char *exts = "sln|raw";
49
50 static struct ast_filestream *slinear_open(int fd)
51 {
52         /* We don't have any header to read or anything really, but
53            if we did, it would go here.  We also might want to check
54            and be sure it's a valid file.  */
55         struct ast_filestream *tmp;
56         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
57                 memset(tmp, 0, sizeof(struct ast_filestream));
58                 if (ast_mutex_lock(&slinear_lock)) {
59                         ast_log(LOG_WARNING, "Unable to lock slinear list\n");
60                         free(tmp);
61                         return NULL;
62                 }
63                 tmp->fd = fd;
64                 tmp->fr.data = tmp->buf;
65                 tmp->fr.frametype = AST_FRAME_VOICE;
66                 tmp->fr.subclass = AST_FORMAT_SLINEAR;
67                 /* datalen will vary for each frame */
68                 tmp->fr.src = name;
69                 tmp->fr.mallocd = 0;
70                 glistcnt++;
71                 ast_mutex_unlock(&slinear_lock);
72                 ast_update_use_count();
73         }
74         return tmp;
75 }
76
77 static struct ast_filestream *slinear_rewrite(int fd, const char *comment)
78 {
79         /* We don't have any header to read or anything really, but
80            if we did, it would go here.  We also might want to check
81            and be sure it's a valid file.  */
82         struct ast_filestream *tmp;
83         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
84                 memset(tmp, 0, sizeof(struct ast_filestream));
85                 if (ast_mutex_lock(&slinear_lock)) {
86                         ast_log(LOG_WARNING, "Unable to lock slinear list\n");
87                         free(tmp);
88                         return NULL;
89                 }
90                 tmp->fd = fd;
91                 glistcnt++;
92                 ast_mutex_unlock(&slinear_lock);
93                 ast_update_use_count();
94         } else
95                 ast_log(LOG_WARNING, "Out of memory\n");
96         return tmp;
97 }
98
99 static void slinear_close(struct ast_filestream *s)
100 {
101         if (ast_mutex_lock(&slinear_lock)) {
102                 ast_log(LOG_WARNING, "Unable to lock slinear list\n");
103                 return;
104         }
105         glistcnt--;
106         ast_mutex_unlock(&slinear_lock);
107         ast_update_use_count();
108         close(s->fd);
109         free(s);
110         s = NULL;
111 }
112
113 static struct ast_frame *slinear_read(struct ast_filestream *s, int *whennext)
114 {
115         int res;
116         int delay;
117         /* Send a frame from the file to the appropriate channel */
118
119         s->fr.frametype = AST_FRAME_VOICE;
120         s->fr.subclass = AST_FORMAT_SLINEAR;
121         s->fr.offset = AST_FRIENDLY_OFFSET;
122         s->fr.mallocd = 0;
123         s->fr.data = s->buf;
124         if ((res = read(s->fd, s->buf, BUF_SIZE)) < 1) {
125                 if (res)
126                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
127                 return NULL;
128         }
129         s->fr.samples = res/2;
130         s->fr.datalen = res;
131         delay = s->fr.samples;
132         *whennext = delay;
133         return &s->fr;
134 }
135
136 static int slinear_write(struct ast_filestream *fs, struct ast_frame *f)
137 {
138         int res;
139         if (f->frametype != AST_FRAME_VOICE) {
140                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
141                 return -1;
142         }
143         if (f->subclass != AST_FORMAT_SLINEAR) {
144                 ast_log(LOG_WARNING, "Asked to write non-slinear frame (%d)!\n", f->subclass);
145                 return -1;
146         }
147         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
148                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
149                         return -1;
150         }
151         return 0;
152 }
153
154 static int slinear_seek(struct ast_filestream *fs, long sample_offset, int whence)
155 {
156         off_t offset=0,min,cur,max;
157
158         min = 0;
159         sample_offset <<= 1;
160         cur = lseek(fs->fd, 0, SEEK_CUR);
161         max = lseek(fs->fd, 0, SEEK_END);
162         if (whence == SEEK_SET)
163                 offset = sample_offset;
164         else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
165                 offset = sample_offset + cur;
166         else if (whence == SEEK_END)
167                 offset = max - sample_offset;
168         if (whence != SEEK_FORCECUR) {
169                 offset = (offset > max)?max:offset;
170         }
171         /* always protect against seeking past begining. */
172         offset = (offset < min)?min:offset;
173         return lseek(fs->fd, offset, SEEK_SET);
174 }
175
176 static int slinear_trunc(struct ast_filestream *fs)
177 {
178         return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
179 }
180
181 static long slinear_tell(struct ast_filestream *fs)
182 {
183         off_t offset;
184         offset = lseek(fs->fd, 0, SEEK_CUR);
185         return offset;
186 }
187
188 static char *slinear_getcomment(struct ast_filestream *s)
189 {
190         return NULL;
191 }
192
193 int load_module()
194 {
195         return ast_format_register(name, exts, AST_FORMAT_SLINEAR,
196                                                                 slinear_open,
197                                                                 slinear_rewrite,
198                                                                 slinear_write,
199                                                                 slinear_seek,
200                                                                 slinear_trunc,
201                                                                 slinear_tell,
202                                                                 slinear_read,
203                                                                 slinear_close,
204                                                                 slinear_getcomment);
205                                                                 
206                                                                 
207 }
208
209 int unload_module()
210 {
211         return ast_format_unregister(name);
212 }       
213
214 int usecount()
215 {
216         int res;
217         if (ast_mutex_lock(&slinear_lock)) {
218                 ast_log(LOG_WARNING, "Unable to lock slinear list\n");
219                 return -1;
220         }
221         res = glistcnt;
222         ast_mutex_unlock(&slinear_lock);
223         return res;
224 }
225
226 char *description()
227 {
228         return desc;
229 }
230
231
232 char *key()
233 {
234         return ASTERISK_GPL_KEY;
235 }