Add H.263 video support
[asterisk/asterisk.git] / formats / format_h263.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Save to raw, headerless h263 data.
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 <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 #include <pthread.h>
28 #ifdef __linux__
29 #include <endian.h>
30 #else
31 #include <machine/endian.h>
32 #endif
33
34 /* Some Ideas for this code came from makeh263e.c by Jeffery Chilton */
35
36 /* Portions of the conversion code are by guido@sienanet.it */
37
38 struct ast_filestream {
39         void *reserved[AST_RESERVED_POINTERS];
40         /* Believe it or not, we must decode/recode to account for the
41            weird MS format */
42         /* This is what a filestream means to us */
43         int fd; /* Descriptor */
44         unsigned int lastts;
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 h263[4096];                               /* Two Real h263 Frames */
49 };
50
51
52 static pthread_mutex_t h263_lock = AST_MUTEX_INITIALIZER;
53 static int glistcnt = 0;
54
55 static char *name = "h263";
56 static char *desc = "Raw h263 data";
57 static char *exts = "h263";
58
59 static struct ast_filestream *h263_open(int fd)
60 {
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         unsigned int ts;
66         int res;
67         if ((res = read(fd, &ts, sizeof(ts))) < sizeof(ts)) {
68                 ast_log(LOG_WARNING, "Empty file!\n");
69                 return NULL;
70         }
71                 
72         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
73                 memset(tmp, 0, sizeof(struct ast_filestream));
74                 if (ast_pthread_mutex_lock(&h263_lock)) {
75                         ast_log(LOG_WARNING, "Unable to lock h263 list\n");
76                         free(tmp);
77                         return NULL;
78                 }
79                 tmp->fd = fd;
80                 tmp->fr.data = tmp->h263;
81                 tmp->fr.frametype = AST_FRAME_VIDEO;
82                 tmp->fr.subclass = AST_FORMAT_H263;
83                 /* datalen will vary for each frame */
84                 tmp->fr.src = name;
85                 tmp->fr.mallocd = 0;
86                 glistcnt++;
87                 ast_pthread_mutex_unlock(&h263_lock);
88                 ast_update_use_count();
89         }
90         return tmp;
91 }
92
93 static struct ast_filestream *h263_rewrite(int fd, char *comment)
94 {
95         /* We don't have any header to read or anything really, but
96            if we did, it would go here.  We also might want to check
97            and be sure it's a valid file.  */
98         struct ast_filestream *tmp;
99         if ((tmp = malloc(sizeof(struct ast_filestream)))) {
100                 memset(tmp, 0, sizeof(struct ast_filestream));
101                 if (ast_pthread_mutex_lock(&h263_lock)) {
102                         ast_log(LOG_WARNING, "Unable to lock h263 list\n");
103                         free(tmp);
104                         return NULL;
105                 }
106                 tmp->fd = fd;
107                 glistcnt++;
108                 ast_pthread_mutex_unlock(&h263_lock);
109                 ast_update_use_count();
110         } else
111                 ast_log(LOG_WARNING, "Out of memory\n");
112         return tmp;
113 }
114
115 static void h263_close(struct ast_filestream *s)
116 {
117         if (ast_pthread_mutex_lock(&h263_lock)) {
118                 ast_log(LOG_WARNING, "Unable to lock h263 list\n");
119                 return;
120         }
121         glistcnt--;
122         ast_pthread_mutex_unlock(&h263_lock);
123         ast_update_use_count();
124         close(s->fd);
125         free(s);
126         s = NULL;
127 }
128
129 static struct ast_frame *h263_read(struct ast_filestream *s, int *whennext)
130 {
131         int res;
132         int mark=0;
133         unsigned short len;
134         unsigned int ts;
135         /* Send a frame from the file to the appropriate channel */
136         s->fr.frametype = AST_FRAME_VIDEO;
137         s->fr.subclass = AST_FORMAT_H263;
138         s->fr.offset = AST_FRIENDLY_OFFSET;
139         s->fr.mallocd = 0;
140         s->fr.data = s->h263;
141         if ((res = read(s->fd, &len, sizeof(len))) < 1) {
142                 return NULL;
143         }
144         len = ntohs(len);
145         if (len & 0x8000) {
146                 mark = 1;
147         }
148         len &= 0x7fff;
149         if (len > sizeof(s->h263)) {
150                 ast_log(LOG_WARNING, "Length %d is too long\n", len);
151         }
152         if ((res = read(s->fd, s->h263, len)) != len) {
153                 if (res)
154                         ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
155                 return NULL;
156         }
157         s->fr.samples = s->lastts;
158         s->fr.datalen = len;
159         s->fr.subclass |= mark;
160         if ((res = read(s->fd, &ts, sizeof(ts))) == sizeof(ts)) {
161                 s->lastts = *whennext = ntohl(ts) * 4/45;
162         } else
163                 *whennext = 0;
164         return &s->fr;
165 }
166
167 static int h263_write(struct ast_filestream *fs, struct ast_frame *f)
168 {
169         int res;
170         unsigned int ts;
171         unsigned short len;
172         int subclass;
173         int mark=0;
174         if (f->frametype != AST_FRAME_VIDEO) {
175                 ast_log(LOG_WARNING, "Asked to write non-video frame!\n");
176                 return -1;
177         }
178         subclass = f->subclass;
179         if (subclass & 0x1)
180                 mark=0x8000;
181         subclass &= ~0x1;
182         if (subclass != AST_FORMAT_H263) {
183                 ast_log(LOG_WARNING, "Asked to write non-h263 frame (%d)!\n", f->subclass);
184                 return -1;
185         }
186         ts = htonl(f->samples);
187         if ((res = write(fs->fd, &ts, sizeof(ts))) != sizeof(ts)) {
188                         ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno));
189                         return -1;
190         }
191         len = htons(f->datalen | mark);
192         if ((res = write(fs->fd, &len, sizeof(len))) != sizeof(len)) {
193                         ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno));
194                         return -1;
195         }
196         if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
197                         ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno));
198                         return -1;
199         }
200         return 0;
201 }
202
203 static char *h263_getcomment(struct ast_filestream *s)
204 {
205         return NULL;
206 }
207
208 static int h263_seek(struct ast_filestream *fs, long sample_offset, int whence)
209 {
210         /* No way Jose */
211         return -1;
212 }
213
214 static int h263_trunc(struct ast_filestream *fs)
215 {
216         /* Truncate file to current length */
217         if (ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)) < 0)
218                 return -1;
219         return 0;
220 }
221
222 static long h263_tell(struct ast_filestream *fs)
223 {
224         off_t offset;
225         offset = lseek(fs->fd, 0, SEEK_CUR);
226         return (offset/20)*160;
227 }
228
229 int load_module()
230 {
231         return ast_format_register(name, exts, AST_FORMAT_H263,
232                                                                 h263_open,
233                                                                 h263_rewrite,
234                                                                 h263_write,
235                                                                 h263_seek,
236                                                                 h263_trunc,
237                                                                 h263_tell,
238                                                                 h263_read,
239                                                                 h263_close,
240                                                                 h263_getcomment);
241                                                                 
242                                                                 
243 }
244
245 int unload_module()
246 {
247         return ast_format_unregister(name);
248 }       
249
250 int usecount()
251 {
252         int res;
253         if (ast_pthread_mutex_lock(&h263_lock)) {
254                 ast_log(LOG_WARNING, "Unable to lock h263 list\n");
255                 return -1;
256         }
257         res = glistcnt;
258         ast_pthread_mutex_unlock(&h263_lock);
259         return res;
260 }
261
262 char *description()
263 {
264         return desc;
265 }
266
267
268 char *key()
269 {
270         return ASTERISK_GPL_KEY;
271 }