Wed Jan 8 2020 09:49:51

Asterisk developer's documentation


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