accept any name starting with X11 for X11 grabbers - this lets
[asterisk/asterisk.git] / channels / vgrabbers.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright 2007, Luigi Rizzo 
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*
18  * Video grabbers used in console_video.
19  *
20  * $Revision$
21  *
22  * Each grabber is implemented through open/read/close calls,
23  * plus an additional move() function used e.g. to change origin
24  * for the X grabber (this may be extended in the future to support
25  * more controls e.g. resolution changes etc.).
26  *
27  * open() should try to open and initialize the grabber, returning NULL on error.
28  * On success it allocates a descriptor for its private data (including
29  * a buffer for the video) and returns a pointer to the descriptor.
30  * read() will return NULL on failure, or a pointer to a buffer with data
31  * on success.
32  * close() should release resources.
33  * move() is optional.
34  * For more details look at the X11 grabber below.
35  *
36  * NOTE: at the moment we expect uncompressed video frames in YUV format,
37  * because this is what current sources supply and also is a convenient
38  * format for display. It is conceivable that one might want to support
39  * an already compressed stream, in which case we should redesign the
40  * pipeline used for the local source, which at the moment is
41  *
42  *                        .->--[loc_dpy]
43  *   [src]-->--[enc_in]--+
44  *                        `->--[enc_out]
45  */
46
47 #include "asterisk.h"
48 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
49 #include <sys/ioctl.h>
50 #include "asterisk/file.h"
51 #include "asterisk/utils.h"     /* ast_calloc */
52
53 #include "console_video.h"
54
55 #if defined(HAVE_VIDEO_CONSOLE)
56
57 #ifdef HAVE_X11
58
59 /* A simple X11 grabber, supporting only truecolor formats */
60
61 #include <X11/Xlib.h>
62
63 /*! \brief internal info used by the X11 grabber */
64 struct grab_x11_desc {
65         Display         *dpy;
66         XImage          *image;
67         int             screen_width;   /* width of X screen */
68         int             screen_height;  /* height of X screen */
69         struct fbuf_t   b;              /* geometry and pointer into the XImage */
70 };
71
72 /*! \brief open the grabber.
73  * We use the special name 'X11' to indicate this grabber.
74  */
75 static void *grab_x11_open(const char *name, struct fbuf_t *geom, int fps)
76 {
77         XImage *im;
78         int screen_num;
79         struct grab_x11_desc *v;
80         struct fbuf_t *b;
81
82         /* all names starting with X11 identify this grabber */
83         if (strncasecmp(name, "X11", 3))
84                 return NULL;    /* not us */
85         v = ast_calloc(1, sizeof(*v));
86         if (v == NULL)
87                 return NULL;    /* no memory */
88
89         /* init the connection with the X server */
90         v->dpy = XOpenDisplay(NULL);
91         if (v->dpy == NULL) {
92                 ast_log(LOG_WARNING, "error opening display\n");
93                 goto error;
94         }
95
96         v->b = *geom;   /* copy geometry */
97         b = &v->b;      /* shorthand */
98         /* find width and height of the screen */
99         screen_num = DefaultScreen(v->dpy);
100         v->screen_width = DisplayWidth(v->dpy, screen_num);
101         v->screen_height = DisplayHeight(v->dpy, screen_num);
102
103         v->image = im = XGetImage(v->dpy,
104                 RootWindow(v->dpy, DefaultScreen(v->dpy)),
105                 b->x, b->y, b->w, b->h, AllPlanes, ZPixmap);
106         if (v->image == NULL) {
107                 ast_log(LOG_WARNING, "error creating Ximage\n");
108                 goto error;
109         }
110         switch (im->bits_per_pixel) {
111         case 32:
112                 b->pix_fmt = PIX_FMT_RGBA32;
113                 break;
114         case 16:
115                 b->pix_fmt = (im->green_mask == 0x7e0) ? PIX_FMT_RGB565 : PIX_FMT_RGB555;
116                 break;
117         }
118
119         ast_log(LOG_NOTICE, "image: data %p %d bpp fmt %d, mask 0x%lx 0x%lx 0x%lx\n",
120                 im->data,
121                 im->bits_per_pixel,
122                 b->pix_fmt,
123                 im->red_mask, im->green_mask, im->blue_mask);
124
125         /* set the pointer but not the size as this is not malloc'ed */
126         b->data = (uint8_t *)im->data;
127         return v;
128
129 error:
130         /* XXX maybe XDestroy (v->image) ? */
131         if (v->dpy)
132                 XCloseDisplay(v->dpy);
133         v->dpy = NULL;
134         ast_free(v);
135         return NULL;
136 }
137
138 static struct fbuf_t *grab_x11_read(void *desc)
139 {
140         /* read frame from X11 */
141         struct grab_x11_desc *v = desc;
142         struct fbuf_t *b = &v->b;
143
144         XGetSubImage(v->dpy,
145                 RootWindow(v->dpy, DefaultScreen(v->dpy)),
146                         b->x, b->y, b->w, b->h, AllPlanes, ZPixmap, v->image, 0, 0);
147
148         b->data = (uint8_t *)v->image->data;
149         return b;
150 }
151
152 static int boundary_checks(int x, int limit)
153 {
154         return (x <= 0) ? 0 : (x > limit ? limit : x);
155 }
156
157 /*! \brief move the origin for the grabbed area, making sure we do not
158  * overflow the screen.
159  */
160 static void grab_x11_move(void *desc, int dx, int dy)
161 {
162         struct grab_x11_desc *v = desc;
163
164         v->b.x = boundary_checks(v->b.x + dx, v->screen_width - v->b.w);
165         v->b.y = boundary_checks(v->b.y + dy, v->screen_height - v->b.h);
166 }
167
168 /*! \brief disconnect from the server and release memory */
169 static void *grab_x11_close(void *desc)
170 {
171         struct grab_x11_desc *v = desc;
172
173         XCloseDisplay(v->dpy);
174         v->dpy = NULL;
175         v->image = NULL;
176         ast_free(v);
177         return NULL;
178 }
179
180 static struct grab_desc grab_x11_desc = {
181         .name = "X11",
182         .open = grab_x11_open,
183         .read = grab_x11_read,
184         .move = grab_x11_move,
185         .close = grab_x11_close,
186 };
187 #endif  /* HAVE_X11 */
188
189 #ifdef HAVE_VIDEODEV_H
190 #include <linux/videodev.h>     /* Video4Linux stuff is only used in grab_v4l1_open() */
191
192 struct grab_v4l1_desc {
193         int fd;                 /* device handle */
194         struct fbuf_t   b;      /* buffer (allocated) with grabbed image */
195 };
196
197 /*! \brief
198  * Open the local video source and allocate a buffer
199  * for storing the image.
200  */
201 static void *grab_v4l1_open(const char *dev, struct fbuf_t *geom, int fps)
202 {
203         struct video_window vw = { 0 }; /* camera attributes */
204         struct video_picture vp;
205         int fd, i;
206         struct grab_v4l1_desc *v;
207         struct fbuf_t *b;
208
209         /* name should be something under /dev/ */
210         if (strncmp(dev, "/dev/", 5)) 
211                 return NULL;
212         fd = open(dev, O_RDONLY | O_NONBLOCK);
213         if (fd < 0) {
214                 ast_log(LOG_WARNING, "error opening camera %s\n", dev);
215                 return NULL;
216         }
217
218         v = ast_calloc(1, sizeof(*v));
219         if (v == NULL) {
220                 ast_log(LOG_WARNING, "no memory for camera %s\n", dev);
221                 close(fd);
222                 return NULL;    /* no memory */
223         }
224         v->fd = fd;
225         v->b = *geom;
226         b = &v->b;      /* shorthand */
227
228         i = fcntl(fd, F_GETFL);
229         if (-1 == fcntl(fd, F_SETFL, i | O_NONBLOCK)) {
230                 /* non fatal, just emit a warning */
231                 ast_log(LOG_WARNING, "error F_SETFL for %s [%s]\n",
232                         dev, strerror(errno));
233         }
234         /* set format for the camera.
235          * In principle we could retry with a different format if the
236          * one we are asking for is not supported.
237          */
238         vw.width = b->w;
239         vw.height = b->h;
240         vw.flags = fps << 16;
241         if (ioctl(fd, VIDIOCSWIN, &vw) == -1) {
242                 ast_log(LOG_WARNING, "error setting format for %s [%s]\n",
243                         dev, strerror(errno));
244                 goto error;
245         }
246         if (ioctl(fd, VIDIOCGPICT, &vp) == -1) {
247                 ast_log(LOG_WARNING, "error reading picture info\n");
248                 goto error;
249         }
250         ast_log(LOG_WARNING,
251                 "contrast %d bright %d colour %d hue %d white %d palette %d\n",
252                 vp.contrast, vp.brightness,
253                 vp.colour, vp.hue,
254                 vp.whiteness, vp.palette);
255         /* set the video format. Here again, we don't necessary have to
256          * fail if the required format is not supported, but try to use
257          * what the camera gives us.
258          */
259         b->pix_fmt = vp.palette;
260         vp.palette = VIDEO_PALETTE_YUV420P;
261         if (ioctl(v->fd, VIDIOCSPICT, &vp) == -1) {
262                 ast_log(LOG_WARNING, "error setting palette, using %d\n",
263                         b->pix_fmt);
264         } else
265                 b->pix_fmt = vp.palette;
266         /* allocate the source buffer.
267          * XXX, the code here only handles yuv411, for other formats
268          * we need to look at pix_fmt and set size accordingly
269          */
270         b->size = (b->w * b->h * 3)/2;  /* yuv411 */
271         ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n",
272                 dev, b->w, b->h, b->size);
273         b->data = ast_calloc(1, b->size);
274         if (!b->data) {
275                 ast_log(LOG_WARNING, "error allocating buffer %d bytes\n",
276                         b->size);
277                 goto error;
278         }
279         ast_log(LOG_WARNING, "success opening camera\n");
280         return v;
281
282 error:
283         close(v->fd);
284         fbuf_free(b);
285         ast_free(v);
286         return NULL;
287 }
288
289 /*! \brief read until error, no data or buffer full.
290  * This might be blocking but no big deal since we are in the
291  * display thread.
292  */
293 static struct fbuf_t *grab_v4l1_read(void *desc)
294 {
295         struct grab_v4l1_desc *v = desc;
296         struct fbuf_t *b = &v->b;
297         for (;;) {
298                 int r, l = b->size - b->used;
299                 r = read(v->fd, b->data + b->used, l);
300                 // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l);
301                 if (r < 0)      /* read error */
302                         break;
303                 if (r == 0)     /* no data */
304                         break;
305                 b->used += r;
306                 if (r == l) {
307                         b->used = 0; /* prepare for next frame */
308                         return b;
309                 }
310         }
311         return NULL;
312 }
313
314 static void *grab_v4l1_close(void *desc)
315 {
316         struct grab_v4l1_desc *v = desc;
317
318         close(v->fd);
319         v->fd = -1;
320         fbuf_free(&v->b);
321         ast_free(v);
322         return NULL;
323 }
324
325 /*! \brief our descriptor. We don't have .move */
326 static struct grab_desc grab_v4l1_desc = {
327         .name = "v4l1",
328         .open = grab_v4l1_open,
329         .read = grab_v4l1_read,
330         .close = grab_v4l1_close,
331 };
332 #endif /* HAVE_VIDEODEV_H */
333
334 /*
335  * Here you can add more grabbers, e.g. V4L2, firewire,
336  * a file, a still image...
337  */
338
339 /*! \brief The list of grabbers supported, with a NULL at the end */
340 struct grab_desc *console_grabbers[] = {
341 #ifdef HAVE_X11
342         &grab_x11_desc,
343 #endif
344 #ifdef HAVE_VIDEODEV_H
345         &grab_v4l1_desc,
346 #endif
347         NULL
348 };
349
350 #endif /* HAVE_VIDEO_CONSOLE */