Asterisk - The Open Source Telephony Project  21.4.1
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 /*** MODULEINFO
48  <support_level>extended</support_level>
49  ***/
50 
51 #include "asterisk.h"
52 #include <sys/ioctl.h>
53 #include "asterisk/file.h"
54 #include "asterisk/utils.h" /* ast_calloc */
55 
56 #include "console_video.h"
57 
58 #if defined(HAVE_VIDEO_CONSOLE)
59 
60 #ifdef HAVE_X11
61 
62 /* A simple X11 grabber, supporting only truecolor formats */
63 
64 #include <X11/Xlib.h>
65 
66 /*! \brief internal info used by the X11 grabber */
67 struct grab_x11_desc {
68  Display *dpy;
69  XImage *image;
70  int screen_width; /* width of X screen */
71  int screen_height; /* height of X screen */
72  struct fbuf_t b; /* geometry and pointer into the XImage */
73 };
74 
75 static void *grab_x11_close(void *desc); /* forward declaration */
76 
77 /*! \brief open the grabber.
78  * We use the special name 'X11' to indicate this grabber.
79  */
80 static void *grab_x11_open(const char *name, struct fbuf_t *geom, int fps)
81 {
82  XImage *im;
83  int screen_num;
84  struct grab_x11_desc *v;
85  struct fbuf_t *b;
86 
87  /* all names starting with X11 identify this grabber */
88  if (strncasecmp(name, "X11", 3))
89  return NULL; /* not us */
90  v = ast_calloc(1, sizeof(*v));
91  if (v == NULL)
92  return NULL; /* no memory */
93 
94  /* init the connection with the X server */
95  v->dpy = XOpenDisplay(NULL);
96  if (v->dpy == NULL) {
97  ast_log(LOG_WARNING, "error opening display\n");
98  goto error;
99  }
100 
101  v->b = *geom; /* copy geometry */
102  b = &v->b; /* shorthand */
103  /* find width and height of the screen */
104  screen_num = DefaultScreen(v->dpy);
105  v->screen_width = DisplayWidth(v->dpy, screen_num);
106  v->screen_height = DisplayHeight(v->dpy, screen_num);
107 
108  v->image = im = XGetImage(v->dpy,
109  RootWindow(v->dpy, DefaultScreen(v->dpy)),
110  b->x, b->y, b->w, b->h, AllPlanes, ZPixmap);
111  if (v->image == NULL) {
112  ast_log(LOG_WARNING, "error creating Ximage\n");
113  goto error;
114  }
115  switch (im->bits_per_pixel) {
116  case 32:
117  b->pix_fmt = PIX_FMT_RGBA32;
118  break;
119  case 16:
120  b->pix_fmt = (im->green_mask == 0x7e0) ? PIX_FMT_RGB565 : PIX_FMT_RGB555;
121  break;
122  }
123 
124  ast_log(LOG_NOTICE, "image: data %p %d bpp fmt %d, mask 0x%lx 0x%lx 0x%lx\n",
125  im->data,
126  im->bits_per_pixel,
127  b->pix_fmt,
128  im->red_mask, im->green_mask, im->blue_mask);
129 
130  /* set the pointer but not the size as this is not malloc'ed */
131  b->data = (uint8_t *)im->data;
132  return v;
133 
134 error:
135  return grab_x11_close(v);
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  if (v->dpy)
174  XCloseDisplay(v->dpy);
175  v->dpy = NULL;
176  v->image = NULL;
177  ast_free(v);
178  return NULL;
179 }
180 
181 static struct grab_desc grab_x11_desc = {
182  .name = "X11",
183  .open = grab_x11_open,
184  .read = grab_x11_read,
185  .move = grab_x11_move,
186  .close = grab_x11_close,
187 };
188 #endif /* HAVE_X11 */
189 
190 #ifdef HAVE_VIDEODEV_H
191 #include <linux/videodev.h> /* Video4Linux stuff is only used in grab_v4l1_open() */
192 
193 struct grab_v4l1_desc {
194  int fd; /* device handle */
195  struct fbuf_t b; /* buffer (allocated) with grabbed image */
196 };
197 
198 /*! \brief
199  * Open the local video source and allocate a buffer
200  * for storing the image.
201  */
202 static void *grab_v4l1_open(const char *dev, struct fbuf_t *geom, int fps)
203 {
204  struct video_window vw = { 0 }; /* camera attributes */
205  struct video_picture vp;
206  int fd, i;
207  struct grab_v4l1_desc *v;
208  struct fbuf_t *b;
209 
210  /* name should be something under /dev/ */
211  if (strncmp(dev, "/dev/", 5))
212  return NULL;
213  fd = open(dev, O_RDONLY | O_NONBLOCK);
214  if (fd < 0) {
215  ast_log(LOG_WARNING, "error opening camera %s\n", dev);
216  return NULL;
217  }
218 
219  v = ast_calloc(1, sizeof(*v));
220  if (v == NULL) {
221  ast_log(LOG_WARNING, "no memory for camera %s\n", dev);
222  close(fd);
223  return NULL; /* no memory */
224  }
225  v->fd = fd;
226  v->b = *geom;
227  b = &v->b; /* shorthand */
228 
229  ast_fd_set_flags(fd, O_NONBLOCK);
230 
231  /* set format for the camera.
232  * In principle we could retry with a different format if the
233  * one we are asking for is not supported.
234  */
235  vw.width = b->w;
236  vw.height = b->h;
237  vw.flags = fps << 16;
238  if (ioctl(fd, VIDIOCSWIN, &vw) == -1) {
239  ast_log(LOG_WARNING, "error setting format for %s [%s]\n",
240  dev, strerror(errno));
241  goto error;
242  }
243  if (ioctl(fd, VIDIOCGPICT, &vp) == -1) {
244  ast_log(LOG_WARNING, "error reading picture info\n");
245  goto error;
246  }
247  ast_log(LOG_WARNING,
248  "contrast %d bright %d colour %d hue %d white %d palette %d\n",
249  vp.contrast, vp.brightness,
250  vp.colour, vp.hue,
251  vp.whiteness, vp.palette);
252  /* set the video format. Here again, we don't necessary have to
253  * fail if the required format is not supported, but try to use
254  * what the camera gives us.
255  */
256  b->pix_fmt = vp.palette;
257  vp.palette = VIDEO_PALETTE_YUV420P;
258  if (ioctl(v->fd, VIDIOCSPICT, &vp) == -1) {
259  ast_log(LOG_WARNING, "error setting palette, using %d\n",
260  b->pix_fmt);
261  } else
262  b->pix_fmt = vp.palette;
263  /* allocate the source buffer.
264  * XXX, the code here only handles yuv411, for other formats
265  * we need to look at pix_fmt and set size accordingly
266  */
267  b->size = (b->w * b->h * 3)/2; /* yuv411 */
268  ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n",
269  dev, b->w, b->h, b->size);
270  b->data = ast_calloc(1, b->size);
271  if (!b->data) {
272  ast_log(LOG_WARNING, "error allocating buffer %d bytes\n",
273  b->size);
274  goto error;
275  }
276  ast_log(LOG_WARNING, "success opening camera\n");
277  return v;
278 
279 error:
280  close(v->fd);
281  fbuf_free(b);
282  ast_free(v);
283  return NULL;
284 }
285 
286 /*! \brief read until error, no data or buffer full.
287  * This might be blocking but no big deal since we are in the
288  * display thread.
289  */
290 static struct fbuf_t *grab_v4l1_read(void *desc)
291 {
292  struct grab_v4l1_desc *v = desc;
293  struct fbuf_t *b = &v->b;
294  for (;;) {
295  int r, l = b->size - b->used;
296  r = read(v->fd, b->data + b->used, l);
297  // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l);
298  if (r < 0) /* read error */
299  break;
300  if (r == 0) /* no data */
301  break;
302  b->used += r;
303  if (r == l) {
304  b->used = 0; /* prepare for next frame */
305  return b;
306  }
307  }
308  return NULL;
309 }
310 
311 static void *grab_v4l1_close(void *desc)
312 {
313  struct grab_v4l1_desc *v = desc;
314 
315  close(v->fd);
316  v->fd = -1;
317  fbuf_free(&v->b);
318  ast_free(v);
319  return NULL;
320 }
321 
322 /*! \brief our descriptor. We don't have .move */
323 static struct grab_desc grab_v4l1_desc = {
324  .name = "v4l1",
325  .open = grab_v4l1_open,
326  .read = grab_v4l1_read,
327  .close = grab_v4l1_close,
328 };
329 #endif /* HAVE_VIDEODEV_H */
330 
331 /*
332  * Here you can add more grabbers, e.g. V4L2, firewire,
333  * a file, a still image...
334  */
335 
336 /*! \brief The list of grabbers supported, with a NULL at the end */
337 struct grab_desc *console_grabbers[] = {
338 #ifdef HAVE_X11
339  &grab_x11_desc,
340 #endif
341 #ifdef HAVE_VIDEODEV_H
342  &grab_v4l1_desc,
343 #endif
344  NULL
345 };
346 
347 #endif /* HAVE_VIDEO_CONSOLE */
Asterisk main include file. File version handling, generic pbx functions.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
Utility functions.
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1039
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202