Move grabbers definitions to a separate file, vgrabbers.c, so it is easier
authorLuigi Rizzo <rizzo@icir.org>
Sat, 29 Dec 2007 02:02:03 +0000 (02:02 +0000)
committerLuigi Rizzo <rizzo@icir.org>
Sat, 29 Dec 2007 02:02:03 +0000 (02:02 +0000)
to add more entries. This required moving struct grab_desc to the common
header, and adding an entry in the Makefile.

On passing, cleanup some comments and file headers (some are still missing).

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@95313 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/Makefile
channels/console_video.c
channels/console_video.h
channels/vcodecs.c
channels/vgrabbers.c [new file with mode: 0644]

index 757f209..1afeebf 100644 (file)
@@ -51,7 +51,7 @@ endif
 
 all: _all
 
-chan_oss.so: console_video.o
+chan_oss.so: console_video.o vgrabbers.o
 
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
index fd2f9fe..604a580 100644 (file)
@@ -1,4 +1,20 @@
 /*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007, Marta Carbone, Sergio Fadda, 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.
+ */
+
+/*
  * Experimental support for video sessions. We use SDL for rendering, ffmpeg
  * as the codec library for encoding and decoding, and Video4Linux and X11
  * to generate the local video stream.
@@ -131,7 +147,6 @@ int console_video_formats =
 static void my_scale(struct fbuf_t *in, AVPicture *p_in,
        struct fbuf_t *out, AVPicture *p_out);
 
-struct grab_desc;              /* grabber description */
 struct video_codec_desc;       /* forward declaration */
 /*
  * Descriptor of the local source, made of the following pieces:
@@ -208,7 +223,7 @@ struct video_desc {
 
 static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p);
 
-static void fbuf_free(struct fbuf_t *b)
+void fbuf_free(struct fbuf_t *b)
 {
        struct fbuf_t x = *b;
 
@@ -224,292 +239,14 @@ static void fbuf_free(struct fbuf_t *b)
 #include "vcodecs.c"
 #include "console_gui.c"
 
-/*------ end codec specific code -----*/
-
-/* descriptor for a grabber */
-struct grab_desc {
-       const char *name;
-       void *(*open)(const char *name, struct fbuf_t *geom, int fps);
-       struct fbuf_t *(*read)(void *d);
-       void (*move)(void *d, int dx, int dy);
-       void *(*close)(void *d);
-};
-
-
-#ifdef HAVE_X11
-struct grab_x11_desc {
-       Display         *dpy;                   /* x11 grabber info */
-       XImage          *image;
-       int             screen_width;   /* width of X screen */
-       int             screen_height;  /* height of X screen */
-       struct fbuf_t   b;
-};
-
-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);
-}
-
-/* move the origin for the grabbed area */
-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);
-}
-
-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 */
-
-/* Video4Linux stuff is only used in grabber_open() */
-#ifdef HAVE_VIDEODEV_H
-#include <linux/videodev.h>
-
-struct grab_v4l1_desc {
-       int fd;
-       int fps;
-       struct fbuf_t   b;
-};
-
-/*!
- * Open the local video source and allocate a buffer
- * for storing the image. Return 0 on success, -1 on error
- */
-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;
-}
-
-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;
-}
-
-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 */
-
-static struct grab_desc *my_grabbers[] = {
-       &grab_x11_desc,
-       &grab_v4l1_desc,
-       NULL
-};
-
-/* try to open a video source, return 0 on success, 1 on error
- */
+/*! \brief Try to open a video source, return 0 on success, 1 on error */
 static int grabber_open(struct video_out_desc *v)
 {
        struct grab_desc *g;
        void *g_data;
        int i;
 
-       for (i = 0; (g = my_grabbers[i]); i++) {
+       for (i = 0; (g = console_grabbers[i]); i++) {
                g_data = g->open(v->videodevice, &v->loc_src_geometry, v->fps);
                if (g_data) {
                        v->grabber = g;
@@ -539,22 +276,16 @@ static struct fbuf_t *grabber_read(struct video_out_desc *v)
        return v->grabber->read(v->grabber_data);
 }
 
+/*! \brief handler run when dragging with the left button on
+ * the local source window - the effect is to move the offset
+ * of the captured area.
+ */
 static void grabber_move(struct video_out_desc *v, int dx, int dy)
 {
        if (v->grabber && v->grabber->move)
                 v->grabber->move(v->grabber_data, dx, dy);
 }
 
-/* Helper function to process incoming video.
- * For each incoming video call invoke ffmpeg_init() to intialize
- * the decoding structure then incoming video frames are processed
- * by write_video() which in turn calls pre_process_data(), to extract
- * the bitstream; accumulates data into a buffer within video_desc. When
- * a frame is complete (determined by the marker bit in the RTP header)
- * call decode_video() to decoding and if it successful call show_frame()
- * to display the frame.
- */
-
 /*
  * Map the codec name to the library. If not recognised, use a default.
  * This is useful in the output path where we decide by name, presumably.
@@ -607,8 +338,8 @@ static int video_out_uninit(struct video_desc *env)
 
 /*
  * Initialize the encoder for the local source:
- * - AVCodecContext, AVCodec, AVFrame are used by ffmpeg for encoding;
- * - encbuf is used to store the encoded frame (to be sent)
+ * - enc_ctx, codec, enc_in_frame are used by ffmpeg for encoding;
+ * - enc_out is used to store the encoded frame (to be sent)
  * - mtu is used to determine the max size of video fragment
  * NOTE: we enter here with the video source already open.
  */
@@ -922,7 +653,7 @@ static struct ast_frame *get_video_frames(struct video_desc *env, struct ast_fra
        if (v->enc_out.data == NULL) {
                static volatile int a = 0;
                if (a++ < 2)
-                       ast_log(LOG_WARNING, "fail, no encbuf\n");
+                       ast_log(LOG_WARNING, "fail, no encoder output buffer\n");
                return NULL;
        }
        v->enc->enc_run(v);
index 4a1ef2b..beea23c 100644 (file)
                "console {device}"
 #else
 
-#ifdef HAVE_X11
-#include <X11/Xlib.h>           /* this should be conditional */
-#endif
-        
 #include <ffmpeg/avcodec.h>
 #ifndef OLD_FFMPEG
 #include <ffmpeg/swscale.h>     /* requires a recent ffmpeg */
@@ -66,6 +62,19 @@ struct fbuf_t {              /* frame buffers, dynamically allocated */
        int     pix_fmt;
 };
 
+void fbuf_free(struct fbuf_t *);
+
+/* descriptor for a grabber */
+struct grab_desc {
+       const char *name;
+       void *(*open)(const char *name, struct fbuf_t *geom, int fps);
+       struct fbuf_t *(*read)(void *d);
+       void (*move)(void *d, int dx, int dy);
+       void *(*close)(void *d);
+};
+
+extern struct grab_desc *console_grabbers[];
+
 struct video_desc;             /* opaque type for video support */
 struct video_desc *get_video_desc(struct ast_channel *c);
 
index 8cb1f20..6eccd63 100644 (file)
@@ -1,4 +1,20 @@
 /*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007, Sergio Fadda, 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 codecs support for console_video.c
  * $Revision$
  */
diff --git a/channels/vgrabbers.c b/channels/vgrabbers.c
new file mode 100644 (file)
index 0000000..b589799
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * 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"
+#include <sys/ioctl.h>
+#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 <X11/Xlib.h>
+
+/*! \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 <linux/videodev.h>    /* 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 */