From f8247040e6231c4b3b5099ea3a526348b7941566 Mon Sep 17 00:00:00 2001 From: russell Date: Sat, 19 Jan 2008 00:19:29 +0000 Subject: Creating tag for the release of asterisk-1.6.0-beta1 git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.6.0-beta1@99163 f38db490-d61c-443f-a65b-d21fe96a405b --- trunk/channels/vgrabbers.c | 346 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 trunk/channels/vgrabbers.c (limited to 'trunk/channels/vgrabbers.c') diff --git a/trunk/channels/vgrabbers.c b/trunk/channels/vgrabbers.c new file mode 100644 index 000000000..0e4e62f56 --- /dev/null +++ b/trunk/channels/vgrabbers.c @@ -0,0 +1,346 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright 2007, Luigi Rizzo + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * Video grabbers used in console_video. + * + * $Revision$ + * + * Each grabber is implemented through open/read/close calls, + * plus an additional move() function used e.g. to change origin + * for the X grabber (this may be extended in the future to support + * more controls e.g. resolution changes etc.). + * + * open() should try to open and initialize the grabber, returning NULL on error. + * On success it allocates a descriptor for its private data (including + * a buffer for the video) and returns a pointer to the descriptor. + * read() will return NULL on failure, or a pointer to a buffer with data + * on success. + * close() should release resources. + * move() is optional. + * For more details look at the X11 grabber below. + * + * NOTE: at the moment we expect uncompressed video frames in YUV format, + * because this is what current sources supply and also is a convenient + * format for display. It is conceivable that one might want to support + * an already compressed stream, in which case we should redesign the + * pipeline used for the local source, which at the moment is + * + * .->--[loc_dpy] + * [src]-->--[enc_in]--+ + * `->--[enc_out] + */ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include +#include "asterisk/file.h" +#include "asterisk/utils.h" /* ast_calloc */ + +#include "console_video.h" + +#if defined(HAVE_VIDEO_CONSOLE) + +#ifdef HAVE_X11 + +/* A simple X11 grabber, supporting only truecolor formats */ + +#include + +/*! \brief internal info used by the X11 grabber */ +struct grab_x11_desc { + Display *dpy; + XImage *image; + int screen_width; /* width of X screen */ + int screen_height; /* height of X screen */ + struct fbuf_t b; /* geometry and pointer into the XImage */ +}; + +/*! \brief open the grabber. + * We use the special name 'X11' to indicate this grabber. + */ +static void *grab_x11_open(const char *name, struct fbuf_t *geom, int fps) +{ + XImage *im; + int screen_num; + struct grab_x11_desc *v; + struct fbuf_t *b; + + if (strcasecmp(name, "X11")) + return NULL; /* not us */ + v = ast_calloc(1, sizeof(*v)); + if (v == NULL) + return NULL; /* no memory */ + + /* init the connection with the X server */ + v->dpy = XOpenDisplay(NULL); + if (v->dpy == NULL) { + ast_log(LOG_WARNING, "error opening display\n"); + goto error; + } + + v->b = *geom; /* copy geometry */ + b = &v->b; /* shorthand */ + /* find width and height of the screen */ + screen_num = DefaultScreen(v->dpy); + v->screen_width = DisplayWidth(v->dpy, screen_num); + v->screen_height = DisplayHeight(v->dpy, screen_num); + + v->image = im = XGetImage(v->dpy, + RootWindow(v->dpy, DefaultScreen(v->dpy)), + b->x, b->y, b->w, b->h, AllPlanes, ZPixmap); + if (v->image == NULL) { + ast_log(LOG_WARNING, "error creating Ximage\n"); + goto error; + } + switch (im->bits_per_pixel) { + case 32: + b->pix_fmt = PIX_FMT_RGBA32; + break; + case 16: + b->pix_fmt = (im->green_mask == 0x7e0) ? PIX_FMT_RGB565 : PIX_FMT_RGB555; + break; + } + + ast_log(LOG_NOTICE, "image: data %p %d bpp fmt %d, mask 0x%lx 0x%lx 0x%lx\n", + im->data, + im->bits_per_pixel, + b->pix_fmt, + im->red_mask, im->green_mask, im->blue_mask); + + /* set the pointer but not the size as this is not malloc'ed */ + b->data = (uint8_t *)im->data; + return v; + +error: + /* XXX maybe XDestroy (v->image) ? */ + if (v->dpy) + XCloseDisplay(v->dpy); + v->dpy = NULL; + ast_free(v); + return NULL; +} + +static struct fbuf_t *grab_x11_read(void *desc) +{ + /* read frame from X11 */ + struct grab_x11_desc *v = desc; + struct fbuf_t *b = &v->b; + + XGetSubImage(v->dpy, + RootWindow(v->dpy, DefaultScreen(v->dpy)), + b->x, b->y, b->w, b->h, AllPlanes, ZPixmap, v->image, 0, 0); + + b->data = (uint8_t *)v->image->data; + return b; +} + +static int boundary_checks(int x, int limit) +{ + return (x <= 0) ? 0 : (x > limit ? limit : x); +} + +/*! \brief move the origin for the grabbed area, making sure we do not + * overflow the screen. + */ +static void grab_x11_move(void *desc, int dx, int dy) +{ + struct grab_x11_desc *v = desc; + + v->b.x = boundary_checks(v->b.x + dx, v->screen_width - v->b.w); + v->b.y = boundary_checks(v->b.y + dy, v->screen_height - v->b.h); +} + +/*! \brief disconnect from the server and release memory */ +static void *grab_x11_close(void *desc) +{ + struct grab_x11_desc *v = desc; + + XCloseDisplay(v->dpy); + v->dpy = NULL; + v->image = NULL; + ast_free(v); + return NULL; +} + +static struct grab_desc grab_x11_desc = { + .name = "X11", + .open = grab_x11_open, + .read = grab_x11_read, + .move = grab_x11_move, + .close = grab_x11_close, +}; +#endif /* HAVE_X11 */ + +#ifdef HAVE_VIDEODEV_H +#include /* Video4Linux stuff is only used in grab_v4l1_open() */ + +struct grab_v4l1_desc { + int fd; /* device handle */ + struct fbuf_t b; /* buffer (allocated) with grabbed image */ +}; + +/*! \brief + * Open the local video source and allocate a buffer + * for storing the image. + */ +static void *grab_v4l1_open(const char *dev, struct fbuf_t *geom, int fps) +{ + struct video_window vw = { 0 }; /* camera attributes */ + struct video_picture vp; + int fd, i; + struct grab_v4l1_desc *v; + struct fbuf_t *b; + + fd = open(dev, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + ast_log(LOG_WARNING, "error opening camera %s\n", dev); + return NULL; + } + + v = ast_calloc(1, sizeof(*v)); + if (v == NULL) { + ast_log(LOG_WARNING, "no memory for camera %s\n", dev); + close(fd); + return NULL; /* no memory */ + } + v->fd = fd; + v->b = *geom; + b = &v->b; /* shorthand */ + + i = fcntl(fd, F_GETFL); + if (-1 == fcntl(fd, F_SETFL, i | O_NONBLOCK)) { + /* non fatal, just emit a warning */ + ast_log(LOG_WARNING, "error F_SETFL for %s [%s]\n", + dev, strerror(errno)); + } + /* set format for the camera. + * In principle we could retry with a different format if the + * one we are asking for is not supported. + */ + vw.width = b->w; + vw.height = b->h; + vw.flags = fps << 16; + if (ioctl(fd, VIDIOCSWIN, &vw) == -1) { + ast_log(LOG_WARNING, "error setting format for %s [%s]\n", + dev, strerror(errno)); + goto error; + } + if (ioctl(fd, VIDIOCGPICT, &vp) == -1) { + ast_log(LOG_WARNING, "error reading picture info\n"); + goto error; + } + ast_log(LOG_WARNING, + "contrast %d bright %d colour %d hue %d white %d palette %d\n", + vp.contrast, vp.brightness, + vp.colour, vp.hue, + vp.whiteness, vp.palette); + /* set the video format. Here again, we don't necessary have to + * fail if the required format is not supported, but try to use + * what the camera gives us. + */ + b->pix_fmt = vp.palette; + vp.palette = VIDEO_PALETTE_YUV420P; + if (ioctl(v->fd, VIDIOCSPICT, &vp) == -1) { + ast_log(LOG_WARNING, "error setting palette, using %d\n", + b->pix_fmt); + } else + b->pix_fmt = vp.palette; + /* allocate the source buffer. + * XXX, the code here only handles yuv411, for other formats + * we need to look at pix_fmt and set size accordingly + */ + b->size = (b->w * b->h * 3)/2; /* yuv411 */ + ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n", + dev, b->w, b->h, b->size); + b->data = ast_calloc(1, b->size); + if (!b->data) { + ast_log(LOG_WARNING, "error allocating buffer %d bytes\n", + b->size); + goto error; + } + ast_log(LOG_WARNING, "success opening camera\n"); + return v; + +error: + close(v->fd); + fbuf_free(b); + ast_free(v); + return NULL; +} + +/*! \brief read until error, no data or buffer full. + * This might be blocking but no big deal since we are in the + * display thread. + */ +static struct fbuf_t *grab_v4l1_read(void *desc) +{ + struct grab_v4l1_desc *v = desc; + struct fbuf_t *b = &v->b; + for (;;) { + int r, l = b->size - b->used; + r = read(v->fd, b->data + b->used, l); + // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l); + if (r < 0) /* read error */ + break; + if (r == 0) /* no data */ + break; + b->used += r; + if (r == l) { + b->used = 0; /* prepare for next frame */ + return b; + } + } + return NULL; +} + +static void *grab_v4l1_close(void *desc) +{ + struct grab_v4l1_desc *v = desc; + + close(v->fd); + v->fd = -1; + fbuf_free(&v->b); + ast_free(v); + return NULL; +} + +/*! \brief our descriptor. We don't have .move */ +static struct grab_desc grab_v4l1_desc = { + .name = "v4l1", + .open = grab_v4l1_open, + .read = grab_v4l1_read, + .close = grab_v4l1_close, +}; +#endif /* HAVE_VIDEODEV_H */ + +/* + * Here you can add more grabbers, e.g. V4L2, firewire, + * a file, a still image... + */ + +/*! \brief The list of grabbers supported, with a NULL at the end */ +struct grab_desc *console_grabbers[] = { +#ifdef HAVE_X11 + &grab_x11_desc, +#endif +#ifdef HAVE_VIDEODEV_H + &grab_v4l1_desc, +#endif + NULL +}; + +#endif /* HAVE_VIDEO_CONSOLE */ -- cgit v1.2.3