(closes issue #13557)
[asterisk/asterisk.git] / channels / console_video.c
index f35813e..88bf807 100644 (file)
@@ -1,4 +1,20 @@
 /*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007-2008, 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.
@@ -21,8 +37,8 @@
 //#define OLD_FFMPEG   1       /* set for old ffmpeg with no swscale */
 
 #include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <sys/ioctl.h>
-#include <math.h>      /* sqrt */
 #include "asterisk/cli.h"
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -71,6 +87,9 @@ codec parameters), as follows:
 
  rem_dpy       the format used to display the remote stream
 
+ src_dpy       is the format used to display the local video source streams
+       The number of these fbuf_t is determined at run time, with dynamic allocation
+
 We store the format info together with the buffer storing the data.
 As a future optimization, a format/buffer may reference another one
 if the formats are equivalent. This will save some unnecessary format
@@ -89,10 +108,10 @@ iax.conf too) the following:
 
 /*
  * Codecs are absolutely necessary or we cannot do anything.
- * In principle SDL is optional too (used for rendering only, but we
- * could still source data withouth it), however at the moment it is required.
+ * SDL is optional (used for rendering only), so that we can still
+ * stream video withouth displaying it.
  */
-#if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG) || !defined(HAVE_SDL)
+#if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
 /* stubs if required pieces are missing */
 int console_write_video(struct ast_channel *chan, struct ast_frame *f)
 {
@@ -111,13 +130,18 @@ int console_video_config(struct video_desc **penv, const char *var, const char *
 
 void console_video_start(struct video_desc *env, struct ast_channel *owner)
 {
-       ast_log(LOG_WARNING, "console video support not present\n");
+       ast_log(LOG_NOTICE, "voice only, console video support not present\n");
 }
 
 void console_video_uninit(struct video_desc *env)
 {
 }
 
+int get_gui_startup(struct video_desc* env)
+{
+       return 0; /* no gui here */
+}
+
 int console_video_formats = 0;
 
 #else /* defined(HAVE_FFMPEG) && defined(HAVE_SDL) */
@@ -127,40 +151,27 @@ int console_video_formats =
        AST_FORMAT_H263_PLUS | AST_FORMAT_H263 |
        AST_FORMAT_MP4_VIDEO | AST_FORMAT_H264 | AST_FORMAT_H261 ;
 
-#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 */
-#endif
 
-#include <SDL/SDL.h>
-#ifdef HAVE_SDL_IMAGE
-#include <SDL/SDL_image.h>     /* for loading images */
-#endif
-#ifdef HAVE_SDL_TTF
-#include <SDL/SDL_ttf.h>       /* render text on sdl surfaces */
-#endif
+/* function to scale and encode buffers */
+static void my_scale(struct fbuf_t *in, AVPicture *p_in,
+       struct fbuf_t *out, AVPicture *p_out);
 
 /*
- * In many places we use buffers to store the raw frames (but not only),
- * so here is a structure to keep all the info. data = NULL means the
- * structure is not initialized, so the other fields are invalid.
- * size = 0 means the buffer is not malloc'ed so we don't have to free it.
+ * this structure will be an entry in the table containing
+ * every device specified in the file oss.conf, it contains various infomation
+ * about the device
  */
-struct fbuf_t {                /* frame buffers, dynamically allocated */
-       uint8_t *data;  /* memory, malloced if size > 0, just reference
-                        * otherwise */
-       int     size;   /* total size in bytes */
-       int     used;   /* space used so far */
-       int     ebit;   /* bits to ignore at the end */
-       int     x;      /* origin, if necessary */
-       int     y;
-       int     w;      /* size */ 
-       int     h;
-       int     pix_fmt;
+struct video_device {
+       char                    *name;          /* name of the device                   */
+       /* allocated dynamically (see fill_table function) */
+       struct grab_desc        *grabber;       /* the grabber for the device type      */
+       void                    *grabber_data;  /* device's private data structure      */
+       struct fbuf_t           *dev_buf;       /* buffer for incoming data             */
+       struct timeval          last_frame;     /* when we read the last frame ?        */
+       int                     status_index;   /* what is the status of the device (source) */
+       /* status index is set using the IS_ON, IS_PRIMARY and IS_SECONDARY costants */
+       /* status_index is the index of the status message in the src_msgs array in console_gui.c */
 };
 
 struct video_codec_desc;       /* forward declaration */
@@ -172,7 +183,8 @@ struct video_codec_desc;    /* forward declaration */
  *  + the encoding and RTP info, including timestamps to generate
  *    frames at the correct rate;
  *  + source-specific info, i.e. fd for /dev/video, dpy-image for x11, etc,
- *    filled in by video_open
+ *    filled in by grabber_open, part of source_specific information are in 
+ *    the device table (devices member), others are shared;
  * NOTE: loc_src.data == NULL means the rest of the struct is invalid, and
  *     the video source is not available.
  */
@@ -183,1468 +195,202 @@ struct video_out_desc {
         * If we are successful, webcam_bufsize > 0 and we can read.
         */
        /* all the following is config file info copied from the parent */
-       char            videodevice[64];
        int             fps;
        int             bitrate;
        int             qmin;
 
        int sendvideo;
 
-       struct fbuf_t   loc_src;        /* local source buffer, allocated in video_open() */
-       struct fbuf_t   enc_in;         /* encoder input buffer, allocated in video_out_init() */
+       struct fbuf_t   loc_src_geometry;       /* local source geometry only (from config file) */
        struct fbuf_t   enc_out;        /* encoder output buffer, allocated in video_out_init() */
-       struct fbuf_t   loc_dpy;        /* display source buffer, no buffer (managed by SDL in bmp[1]) */
-       struct fbuf_t   keypad_dpy;     /* keypad source buffer, XXX */
 
        struct video_codec_desc *enc;   /* encoder */
-       AVCodecContext  *enc_ctx;       /* encoding context */
+       void            *enc_ctx;       /* encoding context */
        AVCodec         *codec;
-       AVFrame         *frame; /* The initial part is an AVPicture */
+       AVFrame         *enc_in_frame;  /* enc_in mapped into avcodec format. */
+                                       /* The initial part of AVFrame is an AVPicture */
        int             mtu;
-       struct timeval  last_frame;     /* when we read the last frame ? */
-
-       /* device specific info */
-       int             fd;             /* file descriptor, for webcam */
-#ifdef HAVE_X11
-       Display         *dpy;                   /* x11 grabber info */
-       XImage          *image;
-       int             screen_width;   /* width of X screen */
-       int             screen_height;  /* height of X screen */
-#endif
-};
-
-/*
- * Descriptor for the incoming stream, with a buffer for the bitstream
- * extracted by the RTP packets, RTP reassembly info, and a frame buffer
- * for the decoded frame (buf).
- * and store the result in a suitable frame buffer for later display.
- * NOTE: dec_ctx == NULL means the rest is invalid (e.g. because no
- *     codec, no memory, etc.) and we must drop all incoming frames.
- *
- * Incoming payload is stored in one of the dec_in[] buffers, which are
- * emptied by the video thread. These buffers are organized in a circular
- * queue, with dec_in_cur being the buffer in use by the incoming stream,
- * and dec_in_dpy is the one being displayed. When the pointers need to
- * be changed, we synchronize the access to them with dec_in_lock.
- * When the list is full dec_in_cur = NULL (we cannot store new data),
- * when the list is empty dec_in_dpy is NULL (we cannot display frames).
- */
-struct video_in_desc {
-       struct video_codec_desc *dec;   /* decoder */
-       AVCodecContext          *dec_ctx;       /* information about the codec in the stream */
-       AVCodec                 *codec;         /* reference to the codec */
-       AVFrame                 *d_frame;       /* place to store the decoded frame */
-       AVCodecParserContext    *parser;
-       uint16_t                next_seq;       /* must be 16 bit */
-       int                     discard;        /* flag for discard status */
-#define N_DEC_IN       3       /* number of incoming buffers */
-       struct fbuf_t           *dec_in_cur;    /* buffer being filled in */
-       struct fbuf_t           *dec_in_dpy;    /* buffer to display */
-       ast_mutex_t             dec_in_lock;
-       struct fbuf_t dec_in[N_DEC_IN]; /* incoming bitstream, allocated/extended in fbuf_append() */
-       struct fbuf_t dec_out;  /* decoded frame, no buffer (data is in AVFrame) */
-       struct fbuf_t rem_dpy;  /* display remote image, no buffer (it is in win[WIN_REMOTE].bmp) */
-};
-
-/*
- * Each codec is defined by a number of callbacks
- */
-/*! \brief initialize the encoder */
-typedef int (*encoder_init_f)(struct video_out_desc *v);
-
-/*! \brief actually call the encoder */
-typedef int (*encoder_encode_f)(struct video_out_desc *v);
-
-/*! \brief encapsulate the bistream in RTP frames */
-typedef struct ast_frame *(*encoder_encap_f)(struct video_out_desc *out,
-               struct ast_frame **tail);
-
-/*! \brief inizialize the decoder */
-typedef int (*decoder_init_f)(struct video_in_desc *v);
-
-/*! \brief extract the bitstream from RTP frames and store in the fbuf.
- * return 0 if ok, 1 on error
- */
-typedef int (*decoder_decap_f)(struct fbuf_t *b, uint8_t *data, int len);
-
-/*! \brief actually call the decoder */
-typedef int (*decoder_decode_f)(struct video_in_desc *v, struct fbuf_t *b);
-
-struct video_codec_desc {
-       const char              *name;          /* format name */
-       int                     format;         /* AST_FORMAT_* */
-       encoder_init_f          enc_init;
-       encoder_encap_f         enc_encap;
-       encoder_encode_f        enc_run;
-       decoder_init_f          dec_init;
-       decoder_decap_f         dec_decap;
-       decoder_decode_f        dec_run;
-};
-
-/* our representation of a displayed window. SDL can only do one main
- * window so we map everything within that one
- */
-enum { WIN_LOCAL, WIN_REMOTE, WIN_KEYPAD, WIN_MAX };
-
-struct display_window  {
-       SDL_Overlay             *bmp;
-       SDL_Rect                rect;   /* loc. of images */
-};
+       
+       /* Table of devices specified with "videodevice=" in oss.conf.
+        * Static size as we have a limited number of entries.
+        */
+       struct video_device     devices[MAX_VIDEO_SOURCES]; 
+       int                     device_num; /*number of devices in table*/
+       int                     device_primary; /*index of the actual primary device in the table*/
+       int                     device_secondary; /*index of the actual secondary device in the table*/
 
-#define GUI_BUFFER_LEN 256                     /* buffer lenght used for input buffers */
+       int                     picture_in_picture; /*Is the PiP mode activated? 0 = NO | 1 = YES*/
 
-enum kp_type { KP_NONE, KP_RECT, KP_CIRCLE };
-struct keypad_entry {
-       int c;  /* corresponding character */
-       int x0, y0, x1, y1, h;  /* arguments */
-       enum kp_type type;
-};
-
-/*! \brief info related to the gui: button status, mouse coords, etc. */
-struct gui_info {
-       char                    inbuf[GUI_BUFFER_LEN];  /* buffer for to-dial buffer */
-       int                     inbuf_pos;      /* next free position in inbuf */
-       char                    msgbuf[GUI_BUFFER_LEN]; /* buffer for text-message buffer */
-       int                     msgbuf_pos;     /* next free position in msgbuf */
-       int                     text_mode;      /* switch to-dial and text-message mode */
-       int                     drag_mode;      /* switch phone and drag-source mode */
-       int                     x_drag;         /* x coordinate where the drag starts */
-       int                     y_drag;         /* y coordinate where the drag starts */
-#ifdef HAVE_SDL_TTF
-       TTF_Font                *font;          /* font to be used */ 
-#endif
-       int                     outfd;          /* fd for output */
-       SDL_Surface             *keypad;        /* the pixmap for the keypad */
-       int kp_size, kp_used;
-       struct keypad_entry *kp;
+       /* these are the coordinates of the picture inside the picture (visible if PiP mode is active) 
+       these coordinates are valid considering the containing buffer with cif geometry*/
+       int                     pip_x;
+       int                     pip_y;
 };
 
 /*
  * The overall descriptor, with room for config info, video source and
  * received data descriptors, SDL info, etc.
+ * This should be globally visible to all modules (grabber, vcodecs, gui)
+ * and contain all configurtion info.
  */
 struct video_desc {
        char                    codec_name[64]; /* the codec we use */
 
+       int                     stayopen;       /* set if gui starts manually */
        pthread_t               vthread;        /* video thread */
+       ast_mutex_t             dec_lock;       /* sync decoder and video thread */
        int                     shutdown;       /* set to shutdown vthread */
        struct ast_channel      *owner;         /* owner channel */
 
-       struct video_in_desc    in;             /* remote video descriptor */
-       struct video_out_desc   out;            /* local video descriptor */
 
-       struct gui_info         gui;
+       struct fbuf_t   enc_in;         /* encoder input buffer, allocated in video_out_init() */
 
-       /* support for display. */
-       int                     sdl_ok;
-       int                     gui_ok;
-       SDL_Surface             *screen;        /* the main window */
        char                    keypad_file[256];       /* image for the keypad */
-       char                    keypad_mask[256];       /* background for the keypad */
        char                    keypad_font[256];       /* font for the keypad */
-       struct display_window   win[WIN_MAX];
+
+       char                    sdl_videodriver[256];
+
+       struct fbuf_t           rem_dpy;        /* display remote video, no buffer (it is in win[WIN_REMOTE].bmp) */
+       struct fbuf_t           loc_dpy;        /* display local source, no buffer (managed by SDL in bmp[1]) */
+
+       /* geometry of the thumbnails for all video sources. */
+       struct fbuf_t           src_dpy[MAX_VIDEO_SOURCES]; /* no buffer allocated here */
+
+       int frame_freeze;       /* flag to freeze the incoming frame */
+
+       /* local information for grabbers, codecs, gui */
+       struct gui_info         *gui;
+       struct video_dec_desc   *in;            /* remote video descriptor */
+       struct video_out_desc   out;            /* local video descriptor */
 };
 
 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;
 
        if (b->data && b->size)
                ast_free(b->data);
-       bzero(b, sizeof(*b));
+       memset(b, '\0', sizeof(*b));
        /* restore some fields */
        b->w = x.w;
        b->h = x.h;
        b->pix_fmt = x.pix_fmt;
 }
 
-/*
- * Append a chunk of data to a buffer taking care of bit alignment
- * Return 0 on success, != 0 on failure
- */
-static int fbuf_append(struct fbuf_t *b, uint8_t *src, int len,
-       int sbit, int ebit)
-{
-       /*
-        * Allocate buffer. ffmpeg wants an extra FF_INPUT_BUFFER_PADDING_SIZE,
-        * and also wants 0 as a buffer terminator to prevent trouble.
-        */
-       int need = len + FF_INPUT_BUFFER_PADDING_SIZE;
-       int i;
-       uint8_t *dst, mask;
-
-       if (b->data == NULL) {
-               b->size = need;
-               b->used = 0;
-               b->ebit = 0;
-               b->data = ast_calloc(1, b->size);
-       } else if (b->used + need > b->size) {
-               b->size = b->used + need;
-               b->data = ast_realloc(b->data, b->size);
-       }
-       if (b->data == NULL) {
-               ast_log(LOG_WARNING, "alloc failure for %d, discard\n",
-                       b->size);
-               return 1;
-       }
-       if (b->used == 0 && b->ebit != 0) {
-               ast_log(LOG_WARNING, "ebit not reset at start\n");
-               b->ebit = 0;
-       }
-       dst = b->data + b->used;
-       i = b->ebit + sbit;     /* bits to ignore around */
-       if (i == 0) {   /* easy case, just append */
-               /* do everything in the common block */
-       } else if (i == 8) { /* easy too, just handle the overlap byte */
-               mask = (1 << b->ebit) - 1;
-               /* update the last byte in the buffer */
-               dst[-1] &= ~mask;       /* clear bits to ignore */
-               dst[-1] |= (*src & mask);       /* append new bits */
-               src += 1;       /* skip and prepare for common block */
-               len --;
-       } else {        /* must shift the new block, not done yet */
-               ast_log(LOG_WARNING, "must handle shift %d %d at %d\n",
-                       b->ebit, sbit, b->used);
-               return 1;
-       }
-       memcpy(dst, src, len);
-       b->used += len;
-       b->ebit = ebit;
-       b->data[b->used] = 0;   /* padding */
-       return 0;
-}
-
-/*!
- * Build an ast_frame for a given chunk of data, and link it into
- * the queue, with possibly 'head' bytes at the beginning to
- * fill in some fields later.
- */
-static struct ast_frame *create_video_frame(uint8_t *start, uint8_t *end,
-                      int format, int head, struct ast_frame *prev)
-{
-       int len = end-start;
-       uint8_t *data;
-       struct ast_frame *f;
-
-       data = ast_calloc(1, len+head);
-       f = ast_calloc(1, sizeof(*f));
-       if (f == NULL || data == NULL) {
-               ast_log(LOG_WARNING, "--- frame error f %p data %p len %d format %d\n",
-                               f, data, len, format);
-               if (f)
-                       ast_free(f);
-               if (data)
-                       ast_free(data);
-               return NULL;
-       }
-       memcpy(data+head, start, len);
-       f->data = data;
-       f->mallocd = AST_MALLOCD_DATA | AST_MALLOCD_HDR;
-       //f->has_timing_info = 1;
-       //f->ts = ast_tvdiff_ms(ast_tvnow(), out->ts);
-       f->datalen = len+head;
-       f->frametype = AST_FRAME_VIDEO;
-       f->subclass = format;
-       f->samples = 0;
-       f->offset = 0;
-       f->src = "Console";
-       f->delivery.tv_sec = 0;
-       f->delivery.tv_usec = 0;
-       f->seqno = 0;
-       AST_LIST_NEXT(f, frame_list) = NULL;
-
-       if (prev)
-               AST_LIST_NEXT(prev, frame_list) = f;
-
-       return f;
-}
-
-/* some debugging code to check the bitstream:
- * declare a bit buffer, initialize it, and fetch data from it.
- */
-struct bitbuf {
-       const uint8_t *base;
-       int     bitsize;        /* total size in bits */
-       int     ofs;    /* next bit to read */
-};
-
-static struct bitbuf bitbuf_init(const uint8_t *base, int bitsize, int start_ofs)
-{
-       struct bitbuf a;
-       a.base = base;
-       a.bitsize = bitsize;
-       a.ofs = start_ofs;
-       return a;
-}
-
-static int bitbuf_left(struct bitbuf *b)
-{
-       return b->bitsize - b->ofs;
-}
-
-static uint32_t getbits(struct bitbuf *b, int n)
-{
-       int i, ofs;
-       const uint8_t *d;
-       uint8_t mask;
-       uint32_t retval = 0;
-       if (n> 31) {
-               ast_log(LOG_WARNING, "too many bits %d, max 32\n", n);
-               return 0;
-       }
-       if (n + b->ofs > b->bitsize) {
-               ast_log(LOG_WARNING, "bitbuf overflow %d of %d\n", n + b->ofs, b->bitsize);
-               n = b->bitsize - b->ofs;
-       }
-       ofs = 7 - b->ofs % 8;   /* start from msb */
-       mask = 1 << ofs;
-       d = b->base + b->ofs / 8;       /* current byte */
-       for (i=0 ; i < n; i++) {
-               retval += retval + (*d & mask ? 1 : 0); /* shift in new byte */
-               b->ofs++;
-               mask >>= 1;
-               if (mask == 0) {
-                       d++;
-                       mask = 0x80;
-               }
-       }
-       return retval;
-}
-
-static void check_h261(struct fbuf_t *b)
-{
-       struct bitbuf a = bitbuf_init(b->data, b->used * 8, 0);
-       uint32_t x, y;
-       
-       x = getbits(&a, 20);    /* PSC, 0000 0000 0000 0001 0000 */
-       if (x != 0x10) {
-               ast_log(LOG_WARNING, "bad PSC 0x%x\n", x);
-               return;
-       }
-       x = getbits(&a, 5);     /* temporal reference */
-       y = getbits(&a, 6);     /* ptype */
-       if (0)
-       ast_log(LOG_WARNING, "size %d TR %d PTY spl %d doc %d freeze %d %sCIF hi %d\n",
-               b->used,
-               x,
-               (y & 0x20) ? 1 : 0,
-               (y & 0x10) ? 1 : 0,
-               (y & 0x8) ? 1 : 0,
-               (y & 0x4) ? "" : "Q",
-               (y & 0x2) ? 1:0);
-       while ( (x = getbits(&a, 1)) == 1)
-               ast_log(LOG_WARNING, "PSPARE 0x%x\n", getbits(&a, 8));
-       // ast_log(LOG_WARNING, "PSPARE 0 - start GOB LAYER\n");
-       while ( (x = bitbuf_left(&a)) > 0) {
-               // ast_log(LOG_WARNING, "GBSC %d bits left\n", x);
-               x = getbits(&a, 16); /* GBSC 0000 0000 0000 0001 */
-               if (x != 0x1) {
-                       ast_log(LOG_WARNING, "bad GBSC 0x%x\n", x);
-                       break;
-               }
-               x = getbits(&a, 4);     /* group number */
-               y = getbits(&a, 5);     /* gquant */
-               if (x == 0) {
-                       ast_log(LOG_WARNING, "  bad GN %d\n", x);
-                       break;
-               }
-               while ( (x = getbits(&a, 1)) == 1)
-                       ast_log(LOG_WARNING, "GSPARE 0x%x\n", getbits(&a, 8));
-               while ( (x = bitbuf_left(&a)) > 0) { /* MB layer */
-                       break;
-               }
-       }
-}
-
-void dump_buf(struct fbuf_t *b);
-void dump_buf(struct fbuf_t *b)
-{
-       int i, x, last2lines;
-       char buf[80];
-
-       last2lines = (b->used - 16) & ~0xf;
-       ast_log(LOG_WARNING, "buf size %d of %d\n", b->used, b->size);
-       for (i = 0; i < b->used; i++) {
-               x = i & 0xf;
-               if ( x == 0) {  /* new line */
-                       if (i != 0)
-                               ast_log(LOG_WARNING, "%s\n", buf);
-                       bzero(buf, sizeof(buf));
-                       sprintf(buf, "%04x: ", i);
-               }
-               sprintf(buf + 6 + x*3, "%02x ", b->data[i]);
-               if (i > 31 && i < last2lines)
-                       i = last2lines - 1;
-       }
-       if (buf[0])
-               ast_log(LOG_WARNING, "%s\n", buf);
-}
-/*
- * Here starts the glue code for the various supported video codecs.
- * For each of them, we need to provide routines for initialization,
- * calling the encoder, encapsulating the bitstream in ast_frames,
- * extracting payload from ast_frames, and calling the decoder.
- */
-
-/*--- h263+ support --- */
-
-/*! \brief initialization of h263p */
-static int h263p_enc_init(struct video_out_desc *v)
-{
-       /* modes supported are
-       - Unrestricted Motion Vector (annex D)
-       - Advanced Prediction (annex F)
-       - Advanced Intra Coding (annex I)
-       - Deblocking Filter (annex J)
-       - Slice Structure (annex K)
-       - Alternative Inter VLC (annex S)
-       - Modified Quantization (annex T)
-       */
-       v->enc_ctx->flags |=CODEC_FLAG_H263P_UMV; /* annex D */
-       v->enc_ctx->flags |=CODEC_FLAG_AC_PRED; /* annex f ? */
-       v->enc_ctx->flags |=CODEC_FLAG_H263P_SLICE_STRUCT; /* annex k */
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_AIC; /* annex I */
-
-       v->enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
-       return 0;
-}
-
-
-/*
- * Create RTP/H.263 fragments to avoid IP fragmentation. We fragment on a
- * PSC or a GBSC, but if we don't find a suitable place just break somewhere.
- * Everything is byte-aligned.
- */
-static struct ast_frame *h263p_encap(struct video_out_desc *out,
-       struct ast_frame **tail)
-{
-       struct ast_frame *cur = NULL, *first = NULL;
-       uint8_t *d = out->enc_out.data;
-       int len = out->enc_out.used;
-       int l = len; /* size of the current fragment. If 0, must look for a psc */
-
-       for (;len > 0; len -= l, d += l) {
-               uint8_t *data;
-               struct ast_frame *f;
-               int i, h;
-
-               if (len >= 3 && d[0] == 0 && d[1] == 0 && d[2] >= 0x80) {
-                       /* we are starting a new block, so look for a PSC. */
-                       for (i = 3; i < len - 3; i++) {
-                               if (d[i] == 0 && d[i+1] == 0 && d[i+2] >= 0x80) {
-                                       l = i;
-                                       break;
-                               }
-                       }
-               }
-               if (l > out->mtu || l > len) { /* psc not found, split */
-                       l = MIN(len, out->mtu);
-               }
-               if (l < 1 || l > out->mtu) {
-                       ast_log(LOG_WARNING, "--- frame error l %d\n", l);
-                       break;
-               }
-               
-               if (d[0] == 0 && d[1] == 0) { /* we start with a psc */
-                       h = 0;
-               } else { /* no psc, create a header */
-                       h = 2;
-               }
-
-               f = create_video_frame(d, d+l, AST_FORMAT_H263_PLUS, h, cur);
-               if (!f)
-                       break;
-
-               data = f->data;
-               if (h == 0) {   /* we start with a psc */
-                       data[0] |= 0x04;        // set P == 1, and we are done
-               } else {        /* no psc, create a header */
-                       data[0] = data[1] = 0;  // P == 0
-               }
-
-               if (!cur)
-                       first = f;
-               cur = f;
-       }
-
-       if (cur)
-               cur->subclass |= 1; // RTP Marker
-
-       *tail = cur;    /* end of the list */
-       return first;
-}
-
-/*! \brief extract the bitstreem from the RTP payload.
- * This is format dependent.
- * For h263+, the format is defined in RFC 2429
- * and basically has a fixed 2-byte header as follows:
- * 5 bits      RR      reserved, shall be 0
- * 1 bit       P       indicate a start/end condition,
- *                     in which case the payload should be prepended
- *                     by two zero-valued bytes.
- * 1 bit       V       there is an additional VRC header after this header
- * 6 bits      PLEN    length in bytes of extra picture header
- * 3 bits      PEBIT   how many bits to be ignored in the last byte
- *
- * XXX the code below is not complete.
+/* return the status of env->stayopen to chan_oss, as the latter
+ * does not have access to fields of struct video_desc
  */
-static int h263p_decap(struct fbuf_t *b, uint8_t *data, int len)
+int get_gui_startup(struct video_desc* env)
 {
-       int PLEN;
-
-       if (len < 2) {
-               ast_log(LOG_WARNING, "invalid framesize %d\n", len);
-               return 1;
-       }
-       PLEN = ( (data[0] & 1) << 5 ) | ( (data[1] & 0xf8) >> 3);
-
-       if (PLEN > 0) {
-               data += PLEN;
-               len -= PLEN;
-       }
-       if (data[0] & 4)        /* bit P */
-               data[0] = data[1] = 0;
-       else {
-               data += 2;
-               len -= 2;
-       }
-       return fbuf_append(b, data, len, 0, 0); /* ignore trail bits */
+       return env ? env->stayopen : 0;
 }
 
-
-/*
- * generic encoder, used by the various protocols supported here.
- * We assume that the buffer is empty at the beginning.
+#if 0
+/* helper function to print the amount of memory used by the process.
+ * Useful to track memory leaks, unfortunately this code is OS-specific
+ * so we keep it commented out.
  */
-static int ffmpeg_encode(struct video_out_desc *v)
+static int
+used_mem(const char *msg)
 {
-       struct fbuf_t *b = &v->enc_out;
-       int i;
+       char in[128];
 
-       b->used = avcodec_encode_video(v->enc_ctx, b->data, b->size, v->frame);
-       i = avcodec_encode_video(v->enc_ctx, b->data + b->used, b->size - b->used, NULL); /* delayed frames ? */
-       if (i > 0) {
-               ast_log(LOG_WARNING, "have %d more bytes\n", i);
-               b->used += i;
-       }
+       pid_t pid = getpid();
+       sprintf(in, "ps -o vsz= -o rss= %d", pid);
+       ast_log(LOG_WARNING, "used mem (vsize, rss) %s ", msg);
+       system(in);
        return 0;
 }
-
-/*
- * Generic decoder, which is used by h263p, h263 and h261 as it simply
- * invokes ffmpeg's decoder.
- * av_parser_parse should merge a randomly chopped up stream into
- * proper frames. After that, if we have a valid frame, we decode it
- * until the entire frame is processed.
- */
-static int ffmpeg_decode(struct video_in_desc *v, struct fbuf_t *b)
-{
-       uint8_t *src = b->data;
-       int srclen = b->used;
-       int full_frame = 0;
-
-       if (srclen == 0)        /* no data */
-               return 0;
-       if (0)
-               check_h261(b);
-       // ast_log(LOG_WARNING, "rx size %d\n", srclen);
-       while (srclen) {
-               uint8_t *data;
-               int datalen, ret;
-               int len = av_parser_parse(v->parser, v->dec_ctx, &data, &datalen, src, srclen, 0, 0);
-
-               src += len;
-               srclen -= len;
-               /* The parser might return something it cannot decode, so it skips
-                * the block returning no data
-                */
-               if (data == NULL || datalen == 0)
-                       continue;
-               ret = avcodec_decode_video(v->dec_ctx, v->d_frame, &full_frame, data, datalen);
-               if (full_frame == 1)    /* full frame */
-                       break;
-               if (ret < 0) {
-                       ast_log(LOG_NOTICE, "Error decoding\n");
-                       break;
-               }
-       }
-       if (srclen != 0)        /* update b with leftover data */
-               bcopy(src, b->data, srclen);
-       b->used = srclen;
-       b->ebit = 0;
-       return full_frame;
-}
-
-static struct video_codec_desc h263p_codec = {
-       .name = "h263p",
-       .format = AST_FORMAT_H263_PLUS,
-       .enc_init = h263p_enc_init,
-       .enc_encap = h263p_encap,
-       .enc_run = ffmpeg_encode,
-       .dec_init = NULL,
-       .dec_decap = h263p_decap,
-       .dec_run = ffmpeg_decode
-};
-
-/*--- Plain h263 support --------*/
-
-static int h263_enc_init(struct video_out_desc *v)
-{
-       /* XXX check whether these are supported */
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_AIC;
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
-       v->enc_ctx->flags |= CODEC_FLAG_AC_PRED;
-
-       v->enc_ctx->gop_size = v->fps*5;
-
-       return 0;
-}
-
-/*
- * h263 encapsulation is specified in RFC2190. There are three modes
- * defined (A, B, C), with 4, 8 and 12 bytes of header, respectively.
- * The header is made as follows
- *     0.....................|.......................|.............|....31
- *     F:1 P:1 SBIT:3 EBIT:3 SRC:3 I:1 U:1 S:1 A:1 R:4 DBQ:2 TRB:3 TR:8
- * FP = 0- mode A, (only one word of header)
- * FP = 10 mode B, and also means this is an I or P frame
- * FP = 11 mode C, and also means this is a PB frame.
- * SBIT, EBIT nuber of bits to ignore at beginning (msbits) and end (lsbits)
- * SRC  bits 6,7,8 from the h263 PTYPE field
- * I = 0 intra-coded, 1 = inter-coded (bit 9 from PTYPE)
- * U = 1 for Unrestricted Motion Vector (bit 10 from PTYPE)
- * S = 1 for Syntax Based Arith coding (bit 11 from PTYPE)
- * A = 1 for Advanced Prediction (bit 12 from PTYPE)
- * R = reserved, must be 0
- * DBQ = differential quantization, DBQUANT from h263, 0 unless we are using
- *     PB frames
- * TRB = temporal reference for bframes, also 0 unless this is a PB frame
- * TR = temporal reference for P frames, also 0 unless PB frame.
+#endif
+       
+#include "vcodecs.c"
+#include "console_gui.c"
+
+/*! \brief Try to open video sources, return 0 on success, 1 on error
+ * opens all video sources found in the oss.conf configuration files.
+ * Saves the grabber and the datas in the device table (in the devices field
+ * of the descriptor referenced by v).
+ * Initializes the device_primary and device_secondary
+ * fields of v with the first devices that was
+ * successfully opened.
  *
- * Mode B and mode C description omitted.
+ * \param v = video out environment descriptor
  *
- * An RTP frame can start with a PSC 0000 0000 0000 0000 1000 0
- * or with a GBSC, which also has the first 17 bits as a PSC.
- * Note - PSC are byte-aligned, GOB not necessarily. PSC start with
- *     PSC:22 0000 0000 0000 0000 1000 00      picture start code
- *     TR:8   .... ....                        temporal reference
- *      PTYPE:13 or more                       ptype...
- * If we don't fragment a GOB SBIT and EBIT = 0.
- * reference, 8 bit) 
- * 
- * The assumption below is that we start with a PSC.
- */
-static struct ast_frame *h263_encap(struct video_out_desc *out,
-               struct ast_frame **tail)
-{
-       uint8_t *d = out->enc_out.data;
-       int start = 0, i, len = out->enc_out.used;
-       struct ast_frame *f, *cur = NULL, *first = NULL;
-       const int pheader_len = 4;      /* Use RFC-2190 Mode A */
-       uint8_t h263_hdr[12];   /* worst case, room for a type c header */
-       uint8_t *h = h263_hdr;  /* shorthand */
-
-#define H263_MIN_LEN   6
-       if (len < H263_MIN_LEN) /* unreasonably small */
-               return NULL;
-
-       bzero(h263_hdr, sizeof(h263_hdr));
-       /* Now set the header bytes. Only type A by now,
-        * and h[0] = h[2] = h[3] = 0 by default.
-        * PTYPE starts 30 bits in the picture, so the first useful
-        * bit for us is bit 36 i.e. within d[4] (0 is the msbit).
-        * SRC = d[4] & 0x1c goes into data[1] & 0xe0
-        * I   = d[4] & 0x02 goes into data[1] & 0x10
-        * U   = d[4] & 0x01 goes into data[1] & 0x08
-        * S   = d[5] & 0x80 goes into data[1] & 0x04
-        * A   = d[5] & 0x40 goes into data[1] & 0x02
-        * R   = 0           goes into data[1] & 0x01
-        * Optimizing it, we have
-        */
-       h[1] = ( (d[4] & 0x1f) << 3 ) | /* SRC, I, U */
-               ( (d[5] & 0xc0) >> 5 );         /* S, A, R */
-
-       /* now look for the next PSC or GOB header. First try to hit
-        * a '0' byte then look around for the 0000 0000 0000 0000 1 pattern
-        * which is both in the PSC and the GBSC.
-        */
-       for (i = H263_MIN_LEN, start = 0; start < len; start = i, i += 3) {
-               //ast_log(LOG_WARNING, "search at %d of %d/%d\n", i, start, len);
-               for (; i < len ; i++) {
-                       uint8_t x, rpos, lpos;
-                       int rpos_i;     /* index corresponding to rpos */
-                       if (d[i] != 0)          /* cannot be in a GBSC */
-                               continue;
-                       if (i > len - 1)
-                               break;
-                       x = d[i+1];
-                       if (x == 0)     /* next is equally good */
-                               continue;
-                       /* see if around us we can make 16 '0' bits for the GBSC.
-                        * Look for the first bit set on the right, and then
-                        * see if we have enough 0 on the left.
-                        * We are guaranteed to end before rpos == 0
-                        */
-                       for (rpos = 0x80, rpos_i = 8; rpos; rpos >>= 1, rpos_i--)
-                               if (x & rpos)   /* found the '1' bit in GBSC */
-                                       break;
-                       x = d[i-1];             /* now look behind */
-                       for (lpos = rpos; lpos ; lpos >>= 1)
-                               if (x & lpos)   /* too early, not a GBSC */
-                                       break;
-                       if (lpos)               /* as i said... */
+ * returns 0 on success, 1 on error 
+*/
+static int grabber_open(struct video_out_desc *v)
+{
+       struct grab_desc *g;
+       void *g_data;
+       int i, j;
+
+       /* for each device in the device table... */
+       for (i = 0; i < v->device_num; i++) {
+               /* device already open */
+               if (v->devices[i].grabber)
+                       continue;
+               /* for each type of grabber supported... */
+               for (j = 0; (g = console_grabbers[j]); j++) {
+                       /* the grabber is opened and the informations saved in the device table */
+                       g_data = g->open(v->devices[i].name, &v->loc_src_geometry, v->fps);
+                       if (!g_data)
                                continue;
-                       /* now we have a GBSC starting somewhere in d[i-1],
-                        * but it might be not byte-aligned
-                        */
-                       if (rpos == 0x80) {     /* lucky case */
-                               i = i - 1;
-                       } else {        /* XXX to be completed */
-                               ast_log(LOG_WARNING, "unaligned GBSC 0x%x %d\n",
-                                       rpos, rpos_i);
-                       }
-                       break;
+                       v->devices[i].grabber = g;
+                       v->devices[i].grabber_data = g_data;
+                       v->devices[i].status_index |= IS_ON;
                }
-               /* This frame is up to offset i (not inclusive).
-                * We do not split it yet even if larger than MTU.
-                */
-               f = create_video_frame(d + start, d+i, AST_FORMAT_H263,
-                               pheader_len, cur);
-
-               if (!f)
-                       break;
-               bcopy(h, f->data, 4);   /* copy the h263 header */
-               /* XXX to do: if not aligned, fix sbit and ebit,
-                * then move i back by 1 for the next frame
-                */
-               if (!cur)
-                       first = f;
-               cur = f;
        }
-
-       if (cur)
-               cur->subclass |= 1;     // RTP Marker
-
-       *tail = cur;
-       return first;
-}
-
-/* XXX We only drop the header here, but maybe we need more. */
-static int h263_decap(struct fbuf_t *b, uint8_t *data, int len)
-{
-       if (len < 4) {
-               ast_log(LOG_WARNING, "invalid framesize %d\n", len);
-               return 1;       /* error */
-       }
-
-       if ( (data[0] & 0x80) == 0) {
-               len -= 4;
-               data += 4;
-       } else {
-               ast_log(LOG_WARNING, "unsupported mode 0x%x\n",
-                       data[0]);
-               return 1;
-       }
-       return fbuf_append(b, data, len, 0, 0); /* XXX no bit alignment support yet */
-}
-
-static struct video_codec_desc h263_codec = {
-       .name = "h263",
-       .format = AST_FORMAT_H263,
-       .enc_init = h263_enc_init,
-       .enc_encap = h263_encap,
-       .enc_run = ffmpeg_encode,
-       .dec_init = NULL,
-       .dec_decap = h263_decap,
-       .dec_run = ffmpeg_decode
-                                               
-};
-
-/*---- h261 support -----*/
-static int h261_enc_init(struct video_out_desc *v)
-{
-       /* It is important to set rtp_payload_size = 0, otherwise
-        * ffmpeg in h261 mode will produce output that it cannot parse.
-        * Also try to send I frames more frequently than with other codecs.
-        */
-       v->enc_ctx->rtp_payload_size = 0; /* important - ffmpeg fails otherwise */
-       v->enc_ctx->gop_size = v->fps*2;        /* be more responsive */
-
-       return 0;
-}
-
-/*
- * The encapsulation of H261 is defined in RFC4587 which obsoletes RFC2032
- * The bitstream is preceded by a 32-bit header word:
- *  SBIT:3 EBIT:3 I:1 V:1 GOBN:4 MBAP:5 QUANT:5 HMVD:5 VMVD:5
- * SBIT and EBIT are the bits to be ignored at beginning and end,
- * I=1 if the stream has only INTRA frames - cannot change during the stream.
- * V=0 if motion vector is not used. Cannot change.
- * GOBN is the GOB number in effect at the start of packet, 0 if we
- *     start with a GOB header
- * QUANT is the quantizer in effect, 0 if we start with GOB header
- * HMVD  reference horizontal motion vector. 10000 is forbidden
- * VMVD  reference vertical motion vector, as above.
- * Packetization should occur at GOB boundaries, and if not possible
- * with MacroBlock fragmentation. However it is likely that blocks
- * are not bit-aligned so we must take care of this.
- */
-static struct ast_frame *h261_encap(struct video_out_desc *out,
-               struct ast_frame **tail)
-{
-       uint8_t *d = out->enc_out.data;
-       int start = 0, i, len = out->enc_out.used;
-       struct ast_frame *f, *cur = NULL, *first = NULL;
-       const int pheader_len = 4;
-       uint8_t h261_hdr[4];
-       uint8_t *h = h261_hdr;  /* shorthand */
-       int sbit = 0, ebit = 0;
-
-#define H261_MIN_LEN 10
-       if (len < H261_MIN_LEN) /* unreasonably small */
-               return NULL;
-
-       bzero(h261_hdr, sizeof(h261_hdr));
-
-       /* Similar to the code in h263_encap, but the marker there is longer.
-        * Start a few bytes within the bitstream to avoid hitting the marker
-        * twice. Note we might access the buffer at len, but this is ok because
-        * the caller has it oversized.
-        */
-       for (i = H261_MIN_LEN, start = 0; start < len - 1; start = i, i += 4) {
-#if 0  /* test - disable packetization */
-               i = len;        /* wrong... */
-#else
-               int found = 0, found_ebit = 0;  /* last GBSC position found */
-               for (; i < len ; i++) {
-                       uint8_t x, rpos, lpos;
-                       if (d[i] != 0)          /* cannot be in a GBSC */
-                               continue;
-                       x = d[i+1];
-                       if (x == 0)     /* next is equally good */
-                               continue;
-                       /* See if around us we find 15 '0' bits for the GBSC.
-                        * Look for the first bit set on the right, and then
-                        * see if we have enough 0 on the left.
-                        * We are guaranteed to end before rpos == 0
-                        */
-                       for (rpos = 0x80, ebit = 7; rpos; ebit--, rpos >>= 1)
-                               if (x & rpos)   /* found the '1' bit in GBSC */
-                                       break;
-                       x = d[i-1];             /* now look behind */
-                       for (lpos = (rpos >> 1); lpos ; lpos >>= 1)
-                               if (x & lpos)   /* too early, not a GBSC */
-                                       break;
-                       if (lpos)               /* as i said... */
-                               continue;
-                       /* now we have a GBSC starting somewhere in d[i-1],
-                        * but it might be not byte-aligned. Just remember it.
-                        */
-                       if (i - start > out->mtu) /* too large, stop now */
-                               break;
-                       found_ebit = ebit;
-                       found = i;
-                       i += 4; /* continue forward */
-               }
-               if (i >= len) { /* trim if we went too forward */
-                       i = len;
-                       ebit = 0;       /* hopefully... should ask the bitstream ? */
-               }
-               if (i - start > out->mtu && found) {
-                       /* use the previous GBSC, hope is within the mtu */
-                       i = found;
-                       ebit = found_ebit;
-               }
-#endif /* test */
-               if (i - start < 4)      /* XXX too short ? */
+       /* the first working device is selected as the primary one and the secondary one */
+       for (i = 0; i < v->device_num; i++) {
+               if (!v->devices[i].grabber) 
                        continue;
-               /* This frame is up to offset i (not inclusive).
-                * We do not split it yet even if larger than MTU.
-                */
-               f = create_video_frame(d + start, d+i, AST_FORMAT_H261,
-                               pheader_len, cur);
-
-               if (!f)
-                       break;
-               /* recompute header with I=0, V=1 */
-               h[0] = ( (sbit & 7) << 5 ) | ( (ebit & 7) << 2 ) | 1;
-               bcopy(h, f->data, 4);   /* copy the h261 header */
-               if (ebit)       /* not aligned, restart from previous byte */
-                       i--;
-               sbit = (8 - ebit) & 7;
-               ebit = 0;
-               if (!cur)
-                       first = f;
-               cur = f;
-       }
-       if (cur)
-               cur->subclass |= 1;     // RTP Marker
-
-       *tail = cur;
-       return first;
-}
-
-/*
- * Pieces might be unaligned so we really need to put them together.
- */
-static int h261_decap(struct fbuf_t *b, uint8_t *data, int len)
-{
-       int ebit, sbit;
-
-       if (len < 8) {
-               ast_log(LOG_WARNING, "invalid framesize %d\n", len);
-               return 1;
-       }
-       sbit = (data[0] >> 5) & 7;
-       ebit = (data[0] >> 2) & 7;
-       len -= 4;
-       data += 4;
-       return fbuf_append(b, data, len, sbit, ebit);
-}
-
-static struct video_codec_desc h261_codec = {
-       .name = "h261",
-       .format = AST_FORMAT_H261,
-       .enc_init = h261_enc_init,
-       .enc_encap = h261_encap,
-       .enc_run = ffmpeg_encode,
-       .dec_init = NULL,
-       .dec_decap = h261_decap,
-       .dec_run = ffmpeg_decode
-};
-
-/* mpeg4 support */
-static int mpeg4_enc_init(struct video_out_desc *v)
-{
-#if 0
-       //v->enc_ctx->flags |= CODEC_FLAG_LOW_DELAY; /*don't use b frames ?*/
-       v->enc_ctx->flags |= CODEC_FLAG_AC_PRED;
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
-       v->enc_ctx->flags |= CODEC_FLAG_QPEL;
-       v->enc_ctx->flags |= CODEC_FLAG_4MV;
-       v->enc_ctx->flags |= CODEC_FLAG_GMC;
-       v->enc_ctx->flags |= CODEC_FLAG_LOOP_FILTER;
-       v->enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
-#endif
-       v->enc_ctx->gop_size = v->fps*5;
-       v->enc_ctx->rtp_payload_size = 0; /* important - ffmpeg fails otherwise */
-       return 0;
-}
-
-/* simplistic encapsulation - just split frames in mtu-size units */
-static struct ast_frame *mpeg4_encap(struct  video_out_desc *out,
-       struct ast_frame **tail)
-{
-       struct ast_frame *f, *cur = NULL, *first = NULL;
-       uint8_t *d = out->enc_out.data;
-       uint8_t *end = d+out->enc_out.used;
-       int len;
-
-       for (;d < end; d += len, cur = f) {
-               len = MIN(out->mtu, end-d);
-               f = create_video_frame(d, d+len, AST_FORMAT_MP4_VIDEO, 0, cur);
-               if (!f)
-                       break;
-               if (!first)
-                       first = f;
-       }
-       if (cur)
-               cur->subclass |= 1;
-       *tail = cur;
-       return first;
-}
-
-static int mpeg4_decap(struct fbuf_t *b, uint8_t *data, int len)
-{
-       return fbuf_append(b, data, len, 0, 0);
-}
-
-static int mpeg4_decode(struct video_in_desc *v, struct fbuf_t *b)
-{
-       int full_frame = 0, datalen = b->used;
-       int ret = avcodec_decode_video(v->dec_ctx, v->d_frame, &full_frame,
-               b->data, datalen);
-       if (ret < 0) {
-               ast_log(LOG_NOTICE, "Error decoding\n");
-               ret = datalen; /* assume we used everything. */
+               v->device_primary = i;
+               v->device_secondary = i;
+               return 0; /* source found */
        }
-       datalen -= ret;
-       if (datalen > 0)        /* update b with leftover bytes */
-               bcopy(b->data + ret, b->data, datalen);
-       b->used = datalen;
-       b->ebit = 0;
-       return full_frame;
-}
-
-static struct video_codec_desc mpeg4_codec = {
-       .name = "mpeg4",
-       .format = AST_FORMAT_MP4_VIDEO,
-       .enc_init = mpeg4_enc_init,
-       .enc_encap = mpeg4_encap,
-       .enc_run = ffmpeg_encode,
-       .dec_init = NULL,
-       .dec_decap = mpeg4_decap,
-       .dec_run = mpeg4_decode
-};
-
-static int h264_enc_init(struct video_out_desc *v)
-{
-       v->enc_ctx->flags |= CODEC_FLAG_TRUNCATED;
-       //v->enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
-       //v->enc_ctx->flags2 |= CODEC_FLAG2_FASTPSKIP;
-       /* TODO: Maybe we need to add some other flags */
-       v->enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
-       v->enc_ctx->rtp_mode = 0;
-       v->enc_ctx->rtp_payload_size = 0;
-       v->enc_ctx->bit_rate_tolerance = v->enc_ctx->bit_rate;
-       return 0;
-}
-
-static int h264_dec_init(struct video_in_desc *v)
-{
-       v->dec_ctx->flags |= CODEC_FLAG_TRUNCATED;
-
-       return 0;
+       return 1; /* no source found */
 }
 
-/*
- * The structure of a generic H.264 stream is:
- * - 0..n 0-byte(s), unused, optional. one zero-byte is always present
- *   in the first NAL before the start code prefix.
- * - start code prefix (3 bytes): 0x000001
- *   (the first bytestream has a 
- *   like these 0x00000001!)
- * - NAL header byte ( F[1] | NRI[2] | Type[5] ) where type != 0
- * - byte-stream
- * - 0..n 0-byte(s) (padding, unused).
- * Segmentation in RTP only needs to be done on start code prefixes.
- * If fragments are too long... we don't support it yet.
- * - encapsulate (or fragment) the byte-stream (with NAL header included)
- */
-static struct ast_frame *h264_encap(struct video_out_desc *out,
-       struct ast_frame **tail)
-{
-       struct ast_frame *f = NULL, *cur = NULL, *first = NULL;
-       uint8_t *d, *start = out->enc_out.data;
-       uint8_t *end = start + out->enc_out.used;
-
-       /* Search the first start code prefix - ITU-T H.264 sec. B.2,
-        * and move start right after that, on the NAL header byte.
-        */
-#define HAVE_NAL(x) (x[-4] == 0 && x[-3] == 0 && x[-2] == 0 && x[-1] == 1)
-       for (start += 4; start < end; start++) {
-               int ty = start[0] & 0x1f;
-               if (HAVE_NAL(start) && ty != 0 && ty != 31)
-                       break;
-       }
-       /* if not found, or too short, we just skip the next loop and are done. */
-
-       /* Here follows the main loop to create frames. Search subsequent start
-        * codes, and then possibly fragment the unit into smaller fragments.
-        */
-   for (;start < end - 4; start = d) {
-       int size;               /* size of current block */
-       uint8_t hdr[2];         /* add-on header when fragmenting */
-       int ty = 0;
-
-       /* now search next nal */
-       for (d = start + 4; d < end; d++) {
-               ty = d[0] & 0x1f;
-               if (HAVE_NAL(d))
-                       break;  /* found NAL */
-       }
-       /* have a block to send. d past the start code unless we overflow */
-       if (d >= end) { /* NAL not found */
-               d = end + 4;
-       } else if (ty == 0 || ty == 31) { /* found but invalid type, skip */
-               ast_log(LOG_WARNING, "skip invalid nal type %d at %d of %d\n",
-                       ty, d - out->enc_out.data, out->enc_out.used);
-               continue;
-       }
-
-       size = d - start - 4;   /* don't count the end */
-
-       if (size < out->mtu) {  // test - don't fragment
-               // Single NAL Unit
-               f = create_video_frame(start, d - 4, AST_FORMAT_H264, 0, cur);
-               if (!f)
-                       break;
-               if (!first)
-                       first = f;
-
-               cur = f;
-               continue;
-       }
-
-       // Fragmented Unit (Mode A: no DON, very weak)
-       hdr[0] = (*start & 0xe0) | 28;  /* mark as a fragmentation unit */
-       hdr[1] = (*start++ & 0x1f) | 0x80 ;     /* keep type and set START bit */
-       size--;         /* skip the NAL header */
-       while (size) {
-               uint8_t *data;
-               int frag_size = MIN(size, out->mtu);
-
-               f = create_video_frame(start, start+frag_size, AST_FORMAT_H264, 2, cur);
-               if (!f)
-                       break;
-               size -= frag_size;      /* skip this data block */
-               start += frag_size;
-
-               data = f->data;
-               data[0] = hdr[0];
-               data[1] = hdr[1] | (size == 0 ? 0x40 : 0);      /* end bit if we are done */
-               hdr[1] &= ~0x80;        /* clear start bit for subsequent frames */
-               if (!first)
-                       first = f;
-               cur = f;
-       }
-    }
-
-       if (cur)
-               cur->subclass |= 1;     // RTP Marker
-
-       *tail = cur;
-
-       return first;
-}
-
-static int h264_decap(struct fbuf_t *b, uint8_t *data, int len)
-{
-       /* Start Code Prefix (Annex B in specification) */
-       uint8_t scp[] = { 0x00, 0x00, 0x00, 0x01 };
-       int retval = 0;
-       int type, ofs = 0;
-
-       if (len < 2) {
-               ast_log(LOG_WARNING, "--- invalid len %d\n", len);
-               return 1;
-       }
-       /* first of all, check if the packet has F == 0 */
-       if (data[0] & 0x80) {
-               ast_log(LOG_WARNING, "--- forbidden packet; nal: %02x\n",
-                       data[0]);
-               return 1;
-       }
-
-       type = data[0] & 0x1f;
-       switch (type) {
-       case 0:
-       case 31:
-               ast_log(LOG_WARNING, "--- invalid type: %d\n", type);
-               return 1;
-       case 24:
-       case 25:
-       case 26:
-       case 27:
-       case 29:
-               ast_log(LOG_WARNING, "--- encapsulation not supported : %d\n", type);
-               return 1;
-       case 28:        /* FU-A Unit */
-               if (data[1] & 0x80) { // S == 1, import F and NRI from next
-                       data[1] &= 0x1f;        /* preserve type */
-                       data[1] |= (data[0] & 0xe0);    /* import F & NRI */
-                       retval = fbuf_append(b, scp, sizeof(scp), 0, 0);
-                       ofs = 1;
-               } else {
-                       ofs = 2;
-               }
-               break;
-       default:        /* From 1 to 23 (Single NAL Unit) */
-               retval = fbuf_append(b, scp, sizeof(scp), 0, 0);
-       }
-       if (!retval)
-               retval = fbuf_append(b, data + ofs, len - ofs, 0, 0);
-       if (retval)
-               ast_log(LOG_WARNING, "result %d\n", retval);
-       return retval;
-}
-
-static struct video_codec_desc h264_codec = {
-       .name = "h264",
-       .format = AST_FORMAT_H264,
-       .enc_init = h264_enc_init,
-       .enc_encap = h264_encap,
-       .enc_run = ffmpeg_encode,
-       .dec_init = h264_dec_init,
-       .dec_decap = h264_decap,
-       .dec_run = ffmpeg_decode
-};
-
-/*------ end codec specific code -----*/
-
-
-/* Video4Linux stuff is only used in video_open() */
-#ifdef HAVE_VIDEODEV_H
-#include <linux/videodev.h>
-#endif
-
-/*!
- * Open the local video source and allocate a buffer
- * for storing the image. Return 0 on success, -1 on error
- */
-static int video_open(struct video_out_desc *v)
-{
-       struct fbuf_t *b = &v->loc_src;
-       if (b->data)    /* buffer allocated means device already open */
-               return v->fd;
-       v->fd = -1;
-       /*
-        * if the device is "X11", then open the x11 grabber
-        */
-    if (!strcasecmp(v->videodevice, "X11")) {
-       XImage *im;
-       int screen_num;
-
-       /* 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;
-       }
-
-       /* 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;
-       v->fd = -2;
-    }
-#ifdef HAVE_VIDEODEV_H
-    else {
-       /* V4L specific */
-       struct video_window vw = { 0 }; /* camera attributes */
-       struct video_picture vp;
-       int i;
-       const char *dev = v->videodevice;
-
-       v->fd = open(dev, O_RDONLY | O_NONBLOCK);
-       if (v->fd < 0) {
-               ast_log(LOG_WARNING, "error opening camera %s\n", v->videodevice);
-               return v->fd;
-       }
-
-       i = fcntl(v->fd, F_GETFL);
-       if (-1 == fcntl(v->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 = v->loc_src.w;
-       vw.height = v->loc_src.h;
-       vw.flags = v->fps << 16;
-       if (ioctl(v->fd, VIDIOCSWIN, &vw) == -1) {
-               ast_log(LOG_WARNING, "error setting format for %s [%s]\n",
-                       dev, strerror(errno));
-               goto error;
-       }
-       if (ioctl(v->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);
-       v->loc_src.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");
-    }
-#endif /* HAVE_VIDEODEV_H */
-
-       if (v->image == NULL && v->fd < 0)
-               goto error;
-       b->used = 0;
-       return 0;
-
-error:
-       ast_log(LOG_WARNING, "fd %d dpy %p img %p data %p\n",
-               v->fd, v->dpy, v->image, v->loc_src.data);
-       /* XXX maybe XDestroy (v->image) ? */
-       if (v->dpy)
-               XCloseDisplay(v->dpy);
-       v->dpy = NULL;
-       if (v->fd >= 0)
-               close(v->fd);
-       v->fd = -1;
-       fbuf_free(&v->loc_src);
-       return -1;
-}
 
-/*! \brief complete a buffer from the local video source.
+/*! \brief complete a buffer from the specified local video source.
  * Called by get_video_frames(), in turn called by the video thread.
+ *
+ * \param dev = video environment descriptor
+ * \param fps = frame per seconds, for every device
+ *
+ * returns:
+ * - NULL on falure
+ * - reference to the device buffer on success
  */
-static int video_read(struct video_out_desc *v)
+static struct fbuf_t *grabber_read(struct video_device *dev, int fps)
 {
        struct timeval now = ast_tvnow();
-       struct fbuf_t *b = &v->loc_src;
-
-       if (b->data == NULL)    /* not initialized */
-               return 0;
 
+       if (dev->grabber == NULL) /* not initialized */
+               return NULL;
+       
+       /* the last_frame field in this row of the device table (dev)
+       is always initialized, it is set during the parsing of the config
+       file, and never unset, function fill_device_table(). */
        /* check if it is time to read */
-       if (ast_tvzero(v->last_frame))
-               v->last_frame = now;
-       if (ast_tvdiff_ms(now, v->last_frame) < 1000/v->fps)
-               return 0;       /* too early */
-       v->last_frame = now; /* XXX actually, should correct for drift */
-
-#ifdef HAVE_X11
-       if (v->image) {
-               /* read frame from X11 */
-               AVPicture p;
-               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;
-               fill_pict(b, &p);
-               return p.linesize[0] * b->h;
-       }
-#endif
-       if (v->fd < 0)                  /* no other source */
-               return 0;
-       for (;;) {
-               int r, l = v->loc_src.size - v->loc_src.used;
-               r = read(v->fd, v->loc_src.data + v->loc_src.used, l);
-               // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l);
-               if (r < 0)      /* read error */
-                       return 0;
-               if (r == 0)     /* no data */
-                       return 0;
-               v->loc_src.used += r;
-               if (r == l) {
-                       v->loc_src.used = 0; /* prepare for next frame */
-                       return v->loc_src.size;
-               }
-       }
+       if (ast_tvdiff_ms(now, dev->last_frame) < 1000/fps)
+               return NULL; /* too early */
+       dev->last_frame = now; /* XXX actually, should correct for drift */
+       return dev->grabber->read(dev->grabber_data);
 }
 
-/* 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.
+/*! \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.
  */
-
-/*
- * Table of translation between asterisk and ffmpeg formats.
- * We need also a field for read and write (encoding and decoding), because
- * e.g. H263+ uses different codec IDs in ffmpeg when encoding or decoding.
- */
-struct _cm {   /* map ffmpeg codec types to asterisk formats */
-       uint32_t        ast_format;     /* 0 is a terminator */
-       enum CodecID    codec;
-       enum { CM_RD = 1, CM_WR = 2, CM_RDWR = 3 } rw;  /* read or write or both ? */
-       struct video_codec_desc *codec_desc;
-};
-
-static struct _cm video_formats[] = {
-       { AST_FORMAT_H263_PLUS, CODEC_ID_H263,  CM_RD }, /* incoming H263P ? */
-       { AST_FORMAT_H263_PLUS, CODEC_ID_H263P, CM_WR },
-       { AST_FORMAT_H263,      CODEC_ID_H263,  CM_RD },
-       { AST_FORMAT_H263,      CODEC_ID_H263,  CM_WR },
-       { AST_FORMAT_H261,      CODEC_ID_H261,  CM_RDWR },
-       { AST_FORMAT_H264,      CODEC_ID_H264,  CM_RDWR },
-       { AST_FORMAT_MP4_VIDEO, CODEC_ID_MPEG4, CM_RDWR },
-       { 0,                    0, 0 },
-};
-
-
-/*! \brief map an asterisk format into an ffmpeg one */
-static enum CodecID map_video_format(uint32_t ast_format, int rw)
+static void grabber_move(struct video_device *dev, int dx, int dy)
 {
-       struct _cm *i;
-
-       for (i = video_formats; i->ast_format != 0; i++)
-               if (ast_format & i->ast_format && rw & i->rw && rw & i->rw)
-                       return i->codec;
-       return CODEC_ID_NONE;
+       if (dev->grabber && dev->grabber->move)
+                dev->grabber->move(dev->grabber_data, dx, dy);
 }
 
-/* pointers to supported codecs. We assume the first one to be non null. */
-static struct video_codec_desc *supported_codecs[] = {
-       &h263p_codec,
-       &h264_codec,
-       &h263_codec,
-       &h261_codec,
-       &mpeg4_codec,
-       NULL
-};
-
-/*
- * Map the AST_FORMAT to the library. If not recognised, fail.
- * This is useful in the input path where we get frames.
- */
-static struct video_codec_desc *map_video_codec(int fmt)
-{
-       int i;
-
-       for (i = 0; supported_codecs[i]; i++)
-               if (fmt == supported_codecs[i]->format) {
-                       ast_log(LOG_WARNING, "using %s for format 0x%x\n",
-                               supported_codecs[i]->name, fmt);
-                       return supported_codecs[i];
-               }
-       return NULL;
-}
-;
 /*
  * 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.
@@ -1665,114 +411,48 @@ static struct video_codec_desc *map_config_video_format(char *name)
        return supported_codecs[i];
 }
 
-/*! \brief uninitialize the descriptor for remote video stream */
-static int video_in_uninit(struct video_in_desc *v)
-{
-       int i;
-
-       if (v->parser) {
-               av_parser_close(v->parser);
-               v->parser = NULL;
-       }
-       if (v->dec_ctx) {
-               avcodec_close(v->dec_ctx);
-               av_free(v->dec_ctx);
-               v->dec_ctx = NULL;
-       }
-       if (v->d_frame) {
-               av_free(v->d_frame);
-               v->d_frame = NULL;
-       }
-       v->codec = NULL;        /* only a reference */
-       v->dec = NULL;          /* forget the decoder */
-       v->discard = 1;         /* start in discard mode */
-       for (i = 0; i < N_DEC_IN; i++)
-               fbuf_free(&v->dec_in[i]);
-       fbuf_free(&v->dec_out);
-       fbuf_free(&v->rem_dpy);
-       return -1;      /* error, in case someone cares */
-}
-
-/*
- * initialize ffmpeg resources used for decoding frames from the network.
- */
-static int video_in_init(struct video_in_desc *v, uint32_t format)
-{
-       enum CodecID codec;
-
-       /* XXX should check that these are already set */
-       v->codec = NULL;
-       v->dec_ctx = NULL;
-       v->d_frame = NULL;
-       v->parser = NULL;
-       v->discard = 1;
-
-       codec = map_video_format(format, CM_RD);
-
-       v->codec = avcodec_find_decoder(codec);
-       if (!v->codec) {
-               ast_log(LOG_WARNING, "Unable to find the decoder for format %d\n", codec);
-               return video_in_uninit(v);
-       }
-       /*
-       * Initialize the codec context.
-       */
-       v->dec_ctx = avcodec_alloc_context();
-       if (avcodec_open(v->dec_ctx, v->codec) < 0) {
-               ast_log(LOG_WARNING, "Cannot open the codec context\n");
-               av_free(v->dec_ctx);
-               v->dec_ctx = NULL;
-               return video_in_uninit(v);
-       }
-
-       v->parser = av_parser_init(codec);
-       if (!v->parser) {
-               ast_log(LOG_WARNING, "Cannot initialize the decoder parser\n");
-               return video_in_uninit(v);
-       }
-
-       v->d_frame = avcodec_alloc_frame();
-       if (!v->d_frame) {
-               ast_log(LOG_WARNING, "Cannot allocate decoding video frame\n");
-               return video_in_uninit(v);
-       }
-       return 0;       /* ok */
-}
 
 /*! \brief uninitialize the descriptor for local video stream */
-static int video_out_uninit(struct video_out_desc *v)
+static int video_out_uninit(struct video_desc *env)
 {
+       struct video_out_desc *v = &env->out;
+       int i; /* integer variable used as iterator */
+       
+       /* XXX this should be a codec callback */
        if (v->enc_ctx) {
-               avcodec_close(v->enc_ctx);
-               av_free(v->enc_ctx);
+               AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
+               avcodec_close(enc_ctx);
+               av_free(enc_ctx);
                v->enc_ctx = NULL;
        }
-       if (v->frame) {
-               av_free(v->frame);
-               v->frame = NULL;
+       if (v->enc_in_frame) {
+               av_free(v->enc_in_frame);
+               v->enc_in_frame = NULL;
        }
-       v->codec = NULL;        /* only a reference */
-       
-       fbuf_free(&v->loc_src);
-       fbuf_free(&v->enc_in);
+       v->codec = NULL;        /* nothing to free, this is only a reference */
+       /* release the buffers */
+       fbuf_free(&env->enc_in);
        fbuf_free(&v->enc_out);
-       fbuf_free(&v->loc_dpy);
-       if (v->image) { /* X11 grabber */
-               XCloseDisplay(v->dpy);
-               v->dpy = NULL;
-               v->image = NULL;
-       }
-       if (v->fd >= 0) {
-               close(v->fd);
-               v->fd = -1;
+       /* close the grabbers */
+       for (i = 0; i < v->device_num; i++) {
+               if (v->devices[i].grabber){
+                       v->devices[i].grabber_data =
+                               v->devices[i].grabber->close(v->devices[i].grabber_data);
+                       v->devices[i].grabber = NULL;
+                       /* dev_buf is already freed by grabber->close() */
+                       v->devices[i].dev_buf = NULL;
+               }
+               v->devices[i].status_index = 0;
        }
+       v->picture_in_picture = 0;
+       env->frame_freeze = 0;
        return -1;
 }
 
 /*
  * 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.
  */
@@ -1785,19 +465,15 @@ static int video_out_init(struct video_desc *env)
 
        v->enc_ctx              = NULL;
        v->codec                = NULL;
-       v->frame                = NULL;
+       v->enc_in_frame         = NULL;
        v->enc_out.data         = NULL;
 
-       if (v->loc_src.data == NULL) {
-               ast_log(LOG_WARNING, "No local source active\n");
-               return video_out_uninit(v);
-       }
        codec = map_video_format(v->enc->format, CM_WR);
        v->codec = avcodec_find_encoder(codec);
        if (!v->codec) {
                ast_log(LOG_WARNING, "Cannot find the encoder for format %d\n",
                        codec);
-               return video_out_uninit(v);
+               return -1;      /* error, but nothing to undo yet */
        }
 
        v->mtu = 1400;  /* set it early so the encoder can use it */
@@ -1805,54 +481,60 @@ static int video_out_init(struct video_desc *env)
        /* allocate the input buffer for encoding.
         * ffmpeg only supports PIX_FMT_YUV420P for the encoding.
         */
-       enc_in = &v->enc_in;
+       enc_in = &env->enc_in;
        enc_in->pix_fmt = PIX_FMT_YUV420P;
        enc_in->size = (enc_in->w * enc_in->h * 3)/2;
        enc_in->data = ast_calloc(1, enc_in->size);
        if (!enc_in->data) {
                ast_log(LOG_WARNING, "Cannot allocate encoder input buffer\n");
-               return video_out_uninit(v);
+               return video_out_uninit(env);
        }
-       v->frame = avcodec_alloc_frame();
-       if (!v->frame) {
+       /* construct an AVFrame that points into buf_in */
+       v->enc_in_frame = avcodec_alloc_frame();
+       if (!v->enc_in_frame) {
                ast_log(LOG_WARNING, "Unable to allocate the encoding video frame\n");
-               return video_out_uninit(v);
+               return video_out_uninit(env);
        }
 
        /* parameters for PIX_FMT_YUV420P */
        size = enc_in->w * enc_in->h;
-       v->frame->data[0] = enc_in->data;
-       v->frame->data[1] = v->frame->data[0] + size;
-       v->frame->data[2] = v->frame->data[1] + size/4;
-       v->frame->linesize[0] = enc_in->w;
-       v->frame->linesize[1] = enc_in->w/2;
-       v->frame->linesize[2] = enc_in->w/2;
-
-       /* now setup the parameters for the encoder */
-       v->enc_ctx = avcodec_alloc_context();
-       v->enc_ctx->pix_fmt = enc_in->pix_fmt;
-       v->enc_ctx->width = enc_in->w;
-       v->enc_ctx->height = enc_in->h;
+       v->enc_in_frame->data[0] = enc_in->data;
+       v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
+       v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
+       v->enc_in_frame->linesize[0] = enc_in->w;
+       v->enc_in_frame->linesize[1] = enc_in->w/2;
+       v->enc_in_frame->linesize[2] = enc_in->w/2;
+
+       /* now setup the parameters for the encoder.
+        * XXX should be codec-specific
+        */
+    {
+       AVCodecContext *enc_ctx = avcodec_alloc_context();
+       v->enc_ctx = enc_ctx;
+       enc_ctx->pix_fmt = enc_in->pix_fmt;
+       enc_ctx->width = enc_in->w;
+       enc_ctx->height = enc_in->h;
        /* XXX rtp_callback ?
         * rtp_mode so ffmpeg inserts as many start codes as possible.
         */
-       v->enc_ctx->rtp_mode = 1;
-       v->enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2
-       v->enc_ctx->bit_rate = v->bitrate;
-       v->enc_ctx->bit_rate_tolerance = v->enc_ctx->bit_rate/2;
-       v->enc_ctx->qmin = v->qmin;     /* should be configured */
-       v->enc_ctx->time_base = (AVRational){1, v->fps};
-
-       v->enc->enc_init(v);
+       enc_ctx->rtp_mode = 1;
+       enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2
+       enc_ctx->bit_rate = v->bitrate;
+       enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
+       enc_ctx->qmin = v->qmin;        /* should be configured */
+       enc_ctx->time_base = (AVRational){1, v->fps};
+       enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
+
+       v->enc->enc_init(v->enc_ctx);
  
-       if (avcodec_open(v->enc_ctx, v->codec) < 0) {
+       if (avcodec_open(enc_ctx, v->codec) < 0) {
                ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n",
                        codec);
-               av_free(v->enc_ctx);
+               av_free(enc_ctx);
                v->enc_ctx = NULL;
-               return video_out_uninit(v);
+               return video_out_uninit(env);
        }
-
+    }
        /*
         * Allocate enough for the encoded bitstream. As we are compressing,
         * we hope that the output is never larger than the input size.
@@ -1864,38 +546,10 @@ static int video_out_init(struct video_desc *env)
        return 0;
 }
 
-static void cleanup_sdl(struct video_desc *env)  
-{
-       int i;
-
-#ifdef HAVE_SDL_TTF
-       /* unload font file */ 
-       if (env->gui.font) {
-               TTF_CloseFont(env->gui.font);
-               env->gui.font = NULL; 
-       }
-
-       /* uninitialize SDL_ttf library */
-       if ( TTF_WasInit() )
-               TTF_Quit();
-#endif
-
-       /* uninitialize the SDL environment */
-       for (i = 0; i < WIN_MAX; i++) {
-               if (env->win[i].bmp)
-                       SDL_FreeYUVOverlay(env->win[i].bmp);
-       }
-       if (env->gui.keypad)
-               SDL_FreeSurface(env->gui.keypad);
-       env->gui.keypad = NULL;
-       SDL_Quit();
-       env->screen = NULL; /* XXX check reference */
-       bzero(env->win, sizeof(env->win));
-       if (env->sdl_ok)
-               ast_mutex_destroy(&(env->in.dec_in_lock));
-}
-
-/*! \brief uninitialize the entire environment.
+/*! \brief possibly uninitialize the video console.
+ * Called at the end of a call, should reset the 'owner' field,
+ * then possibly terminate the video thread if the gui has
+ * not been started manually.
  * In practice, signal the thread and give it a bit of time to
  * complete, giving up if it gets stuck. Because uninit
  * is called from hangup with the channel locked, and the thread
@@ -1905,18 +559,24 @@ static void cleanup_sdl(struct video_desc *env)
 void console_video_uninit(struct video_desc *env)
 {
        int i, t = 100; /* initial wait is shorter, than make it longer */
-       env->shutdown = 1;
-       for (i=0; env->shutdown && i < 10; i++) {
-                ast_channel_unlock(env->owner);
-                usleep(t);
-               t = 1000000;
-                ast_channel_lock(env->owner);
-        }
-       env->owner = NULL;
+       if (env->stayopen == 0) { /* gui opened by a call, do the shutdown */
+               env->shutdown = 1;
+               for (i=0; env->shutdown && i < 10; i++) {
+                       if (env->owner)
+                               ast_channel_unlock(env->owner);
+                       usleep(t);
+                       t = 1000000;
+                       if (env->owner)
+                               ast_channel_lock(env->owner);
+               }
+               env->vthread = NULL;
+       }
+       env->owner = NULL;      /* this is unconditional */
 }
 
 /*! fill an AVPicture from our fbuf info, as it is required by
- * the image conversion routines in ffmpeg.
+ * the image conversion routines in ffmpeg. Note that the pointers
+ * are recalculated if the fbuf has an offset (and so represents a picture in picture)
  * XXX This depends on the format.
  */
 static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
@@ -1925,23 +585,26 @@ static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
        int l4 = b->w * b->h/4; /* size of U or V frame */
        int len = b->w;         /* Y linesize, bytes */
        int luv = b->w/2;       /* U/V linesize, bytes */
-
-       bzero(p, sizeof(*p));
+       int sample_size = 1;
+       
+       memset(p, '\0', sizeof(*p));
        switch (b->pix_fmt) {
        case PIX_FMT_RGB555:
        case PIX_FMT_RGB565:
-               len *= 2;
+               sample_size = 2;
                luv = 0;
                break;
        case PIX_FMT_RGBA32:
-               len *= 4;
+               sample_size = 4;
                luv = 0;
                break;
        case PIX_FMT_YUYV422:   /* Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr */
-               len *= 2;       /* all data in first plane, probably */
+               sample_size = 2;        /* all data in first plane, probably */
                luv = 0;
                break;
        }
+       len *= sample_size;
+       
        p->data[0] = b->data;
        p->linesize[0] = len;
        /* these are only valid for component images */
@@ -1949,6 +612,14 @@ static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
        p->data[2] = luv ? b->data + 5*l4 : b->data+len;
        p->linesize[1] = luv;
        p->linesize[2] = luv;
+       
+       /* add the offsets to the pointers previously calculated, 
+       it is necessary for the picture in picture mode */
+       p->data[0] += len*b->win_y + b->win_x*sample_size;
+       if (luv) { 
+               p->data[1] += luv*(b->win_y/2) + (b->win_x/2) * sample_size;
+               p->data[2] += luv*(b->win_y/2) + (b->win_x/2) * sample_size;
+       }
        return p;
 }
 
@@ -1960,22 +631,30 @@ static void my_scale(struct fbuf_t *in, AVPicture *p_in,
        struct fbuf_t *out, AVPicture *p_out)
 {
        AVPicture my_p_in, my_p_out;
+       int eff_w=out->w, eff_h=out->h;
 
        if (p_in == NULL)
                p_in = fill_pict(in, &my_p_in);
        if (p_out == NULL)
                p_out = fill_pict(out, &my_p_out);
-
+       
+       /*if win_w is different from zero then we must change 
+       the size of the scaled buffer (the position is already 
+       encoded into the out parameter)*/
+       if (out->win_w) { /* picture in picture enabled */
+               eff_w=out->win_w;
+               eff_h=out->win_h;
+       }
 #ifdef OLD_FFMPEG
-       /* XXX img_convert is deprecated, and does not do rescaling */
+       /* XXX img_convert is deprecated, and does not do rescaling, PiP not supported */
        img_convert(p_out, out->pix_fmt,
                p_in, in->pix_fmt, in->w, in->h);
 #else /* XXX replacement */
     {
        struct SwsContext *convert_ctx;
-
+       
        convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt,
-               out->w, out->h, out->pix_fmt,
+               eff_w, eff_h, out->pix_fmt,
                SWS_BICUBIC, NULL, NULL, NULL);
        if (convert_ctx == NULL) {
                ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed");
@@ -1983,67 +662,15 @@ static void my_scale(struct fbuf_t *in, AVPicture *p_in,
        }
        if (0)
                ast_log(LOG_WARNING, "in %d %dx%d out %d %dx%d\n",
-                       in->pix_fmt, in->w, in->h, out->pix_fmt, out->w, out->h);
+                       in->pix_fmt, in->w, in->h, out->pix_fmt, eff_w, eff_h);
        sws_scale(convert_ctx,
                p_in->data, p_in->linesize,
                in->w, in->h, /* src slice */
                p_out->data, p_out->linesize);
 
        sws_freeContext(convert_ctx);
-    }
-#endif /* XXX replacement */
-}
-
-/*
- * Display video frames (from local or remote stream) using the SDL library.
- * - Set the video mode to use the resolution specified by the codec context
- * - Create a YUV Overlay to copy the frame into it;
- * - After the frame is copied into the overlay, display it
- *
- * The size is taken from the configuration.
- *
- * 'out' is 0 for remote video, 1 for the local video
- */
-static void show_frame(struct video_desc *env, int out)
-{
-       AVPicture *p_in, p_out;
-       struct fbuf_t *b_in, *b_out;
-       SDL_Overlay *bmp;
-
-       if (!env->sdl_ok)
-               return;
-
-       if (out == WIN_LOCAL) { /* webcam/x11 to sdl */
-               b_in = &env->out.enc_in;
-               b_out = &env->out.loc_dpy;
-               p_in = NULL;
-       } else {
-               /* copy input format from the decoding context */
-               AVCodecContext *c = env->in.dec_ctx;
-               b_in = &env->in.dec_out;
-                b_in->pix_fmt = c->pix_fmt;
-                b_in->w = c->width;
-                b_in->h = c->height;
-
-               b_out = &env->in.rem_dpy;
-               p_in = (AVPicture *)env->in.d_frame;
-       }
-       bmp = env->win[out].bmp;
-       SDL_LockYUVOverlay(bmp);
-       /* output picture info - this is sdl, YUV420P */
-       bzero(&p_out, sizeof(p_out));
-       p_out.data[0] = bmp->pixels[0];
-       p_out.data[1] = bmp->pixels[1];
-       p_out.data[2] = bmp->pixels[2];
-       p_out.linesize[0] = bmp->pitches[0];
-       p_out.linesize[1] = bmp->pitches[1];
-       p_out.linesize[2] = bmp->pitches[2];
-
-       my_scale(b_in, p_in, b_out, &p_out);
-
-       /* lock to protect access to Xlib by different threads. */
-       SDL_DisplayYUVOverlay(bmp, &env->win[out].rect);
-       SDL_UnlockYUVOverlay(bmp);
+    }
+#endif /* XXX replacement */
 }
 
 struct video_desc *get_video_desc(struct ast_channel *c);
@@ -2062,24 +689,16 @@ int console_write_video(struct ast_channel *chan, struct ast_frame *f);
 int console_write_video(struct ast_channel *chan, struct ast_frame *f)
 {
        struct video_desc *env = get_video_desc(chan);
-       struct video_in_desc *v = &env->in;
+       struct video_dec_desc *v = env->in;
 
-       if (v->dec == NULL) {   /* try to get the codec */
-               v->dec = map_video_codec(f->subclass & ~1);
-               if (v->dec == NULL) {
-                       ast_log(LOG_WARNING, "cannot find video codec, drop input 0x%x\n", f->subclass);
-                       return 0;
-               }
-               if (video_in_init(v, v->dec->format)) {
-                       /* This is not fatal, but we won't have incoming video */
-                       ast_log(LOG_WARNING, "Cannot initialize input decoder\n");
-                       v->dec = NULL;
-                       return 0;
-               }
-       }
-       if (v->dec_ctx == NULL) {
-               ast_log(LOG_WARNING, "cannot decode, dropping frame\n");
-               return 0;       /* error */
+       if (!env->gui)  /* no gui, no rendering */
+               return 0;
+       if (v == NULL)
+               env->in = v = dec_init(f->subclass & ~1);
+       if (v == NULL) {
+               /* This is not fatal, but we won't have incoming video */
+               ast_log(LOG_WARNING, "Cannot initialize input decoder\n");
+               return 0;
        }
 
        if (v->dec_in_cur == NULL)      /* no buffer for incoming frames, drop */
@@ -2122,18 +741,18 @@ int console_write_video(struct ast_channel *chan, struct ast_frame *f)
        }
        v->next_seq++;
 
-       if (f->data == NULL || f->datalen < 2) {
+       if (f->data.ptr == NULL || f->datalen < 2) {
                ast_log(LOG_WARNING, "empty video frame, discard\n");
                return 0;
        }
-       if (v->dec->dec_decap(v->dec_in_cur, f->data, f->datalen)) {
+       if (v->d_callbacks->dec_decap(v->dec_in_cur, f->data.ptr, f->datalen)) {
                ast_log(LOG_WARNING, "error in dec_decap, enter discard\n");
                v->discard = 1;
        }
        if (f->subclass & 0x01) {       // RTP Marker
                /* prepare to decode: advance the buffer so the video thread knows. */
                struct fbuf_t *tmp = v->dec_in_cur;     /* store current pointer */
-               ast_mutex_lock(&v->dec_in_lock);
+               ast_mutex_lock(&env->dec_lock);
                if (++v->dec_in_cur == &v->dec_in[N_DEC_IN])    /* advance to next, circular */
                        v->dec_in_cur = &v->dec_in[0];
                if (v->dec_in_dpy == NULL) {    /* were not displaying anything, so set it */
@@ -2141,539 +760,96 @@ int console_write_video(struct ast_channel *chan, struct ast_frame *f)
                } else if (v->dec_in_dpy == v->dec_in_cur) { /* current slot is busy */
                        v->dec_in_cur = NULL;
                }
-               ast_mutex_unlock(&v->dec_in_lock);
+               ast_mutex_unlock(&env->dec_lock);
        }
        return 0;
 }
 
 
-/*! \brief read a frame from webcam or X11 through video_read(),
- * display it,  then encode and split it.
+/*! \brief refreshes the buffers of all the device by calling the
+ * grabber_read on each device in the device table.
+ * it encodes the primary source buffer, if the picture in picture mode is
+ * enabled it encodes (in the buffer to split) the secondary source buffer too.
+ * The encoded buffer is splitted to build the local and the remote view.
  * Return a list of ast_frame representing the video fragments.
  * The head pointer is returned by the function, the tail pointer
  * is returned as an argument.
+ *
+ * \param env = video environment descriptor
+ * \param tail = tail ponter (pratically a return value)
  */
 static struct ast_frame *get_video_frames(struct video_desc *env, struct ast_frame **tail)
 {
        struct video_out_desc *v = &env->out;
        struct ast_frame *dummy;
-
-       if (!v->loc_src.data) {
-               static volatile int a = 0;
-               if (a++ < 2)
-                       ast_log(LOG_WARNING, "fail, no loc_src buffer\n");
+       struct fbuf_t *loc_src_primary = NULL, *p_read;
+       int i;
+       /* if no device was found in the config file */
+       if (!env->out.device_num)
                return NULL;
+       /* every time this function is called we refresh the buffers of every device,
+       updating the private device buffer in the device table */
+       for (i = 0; i < env->out.device_num; i++) {
+               p_read = grabber_read(&env->out.devices[i], env->out.fps);
+               /* it is used only if different from NULL, we mantain last good buffer otherwise */
+               if (p_read)
+                       env->out.devices[i].dev_buf = p_read;
+       }
+       /* select the primary device buffer as the one to encode */
+       loc_src_primary = env->out.devices[env->out.device_primary].dev_buf;
+       /* loc_src_primary can be NULL if the device has been turned off during
+       execution of it is read too early */
+       if (loc_src_primary) {
+               /* Scale the video for the encoder, then use it for local rendering
+               so we will see the same as the remote party */
+               my_scale(loc_src_primary, NULL, &env->enc_in, NULL);
+       }
+       if (env->out.picture_in_picture) { /* the picture in picture mode is enabled */
+               struct fbuf_t *loc_src_secondary;
+               /* reads from the secondary source */
+               loc_src_secondary = env->out.devices[env->out.device_secondary].dev_buf;
+               if (loc_src_secondary) {
+                       env->enc_in.win_x = env->out.pip_x;
+                       env->enc_in.win_y = env->out.pip_y;
+                       env->enc_in.win_w = env->enc_in.w/3;
+                       env->enc_in.win_h = env->enc_in.h/3;
+                       /* scales to the correct geometry and inserts in
+                       the enc_in buffer the picture in picture */
+                       my_scale(loc_src_secondary, NULL, &env->enc_in, NULL);
+                       /* returns to normal parameters (not picture in picture) */
+                       env->enc_in.win_x = 0;
+                       env->enc_in.win_y = 0;
+                       env->enc_in.win_w = 0;
+                       env->enc_in.win_h = 0;
+               }
+               else {
+                       /* loc_src_secondary can be NULL if the device has been turned off during
+                       execution of it is read too early */
+                       env->out.picture_in_picture = 0; /* disable picture in picture */
+               }
        }
-       if (!video_read(v))
-               return NULL;    /* can happen, e.g. we are reading too early */
-
+       show_frame(env, WIN_LOCAL); /* local rendering */
+       for (i = 0; i < env->out.device_num; i++) 
+               show_frame(env, i+WIN_SRC1); /* rendering of every source device in thumbnails */
        if (tail == NULL)
                tail = &dummy;
        *tail = NULL;
-       /* Scale the video for the encoder, then use it for local rendering
-        * so we will see the same as the remote party.
-        */
-       my_scale(&v->loc_src, NULL, &v->enc_in, NULL);
-       show_frame(env, WIN_LOCAL);
-       if (!v->sendvideo)
+       /* if no reason for encoding, do not encode */
+       if (!env->owner || !loc_src_primary || !v->sendvideo)
                return NULL;
        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);
-       return v->enc->enc_encap(v, tail);
-}
-
-/*
- * GUI layout, structure and management
- *
-
-For the GUI we use SDL to create a large surface (env->screen)
-containing tree sections: remote video on the left, local video
-on the right, and the keypad with all controls and text windows
-in the center.
-The central section is built using two images: one is the skin,
-the other one is a mask where the sensitive areas of the skin
-are colored in different grayscale levels according to their
-functions. The mapping between colors and function is defined
-in the 'enum pixel_value' below.
-
-Mouse and keyboard events are detected on the whole surface, and
-handled differently according to their location, as follows:
-
-- drag on the local video window are used to move the captured
-  area (in the case of X11 grabber) or the picture-in-picture
-  location (in case of camera included on the X11 grab).
-- click on the keypad are mapped to the corresponding key;
-- drag on some keypad areas (sliders etc.) are mapped to the
-  corresponding functions;
-- keystrokes are used as keypad functions, or as text input
-  if we are in text-input mode.
-
-To manage these behavior we use two status variables,
-that defines if keyboard events should be redirect to dialing functions
-or to write message functions, and if mouse events should be used
-to implement keypad functionalities or to drag the capture device.
-
-Configuration options control the appeareance of the gui:
-
-    keypad = /tmp/phone.jpg            ; the keypad on the screen
-    keypad_mask = /tmp/phone.png       ; the grayscale mask
-    keypad_font = /tmp/font.ttf                ; the font to use for output
-
- *
- */
-
-/* enumerate for the pixel value. 0..127 correspond to ascii chars */
-enum pixel_value {
-       /* answer/close functions */
-       KEY_PICK_UP = 128,
-       KEY_HANG_UP = 129,
-
-       /* other functions */
-       KEY_MUTE = 130,
-       KEY_AUTOANSWER = 131,
-       KEY_SENDVIDEO = 132,
-       KEY_LOCALVIDEO = 133,
-       KEY_REMOTEVIDEO = 134,
-       KEY_WRITEMESSAGE = 135,
-       KEY_GUI_CLOSE = 136,            /* close gui */
-
-       /* other areas within the keypad */
-       KEY_DIGIT_BACKGROUND = 255,
-
-       /* areas outside the keypad - simulated */
-       KEY_OUT_OF_KEYPAD = 251,
-       KEY_REM_DPY = 252,
-       KEY_LOC_DPY = 253,
-};
-
-/*
- * Handlers for the various keypad functions
- */
-
-/*! \brief append a character, or reset if '\0' */
-static void append_char(char *str, int *str_pos, const char c)
-{
-       int i = *str_pos;
-       if (c == '\0')
-               i = 0;
-       else if (i < GUI_BUFFER_LEN - 1)
-               str[i++] = c;
-       else
-               i = GUI_BUFFER_LEN - 1; /* unnecessary, i think */
-       str = '\0';
-       *str_pos = i;
-}
-
-/* accumulate digits, possibly call dial if in connected mode */
-static void keypad_digit(struct video_desc *env, int digit)
-{      
-       if (env->owner) {               /* we have a call, send the digit */
-               struct ast_frame f = { AST_FRAME_DTMF, 0 };
-
-               f.subclass = digit;
-               ast_queue_frame(env->owner, &f);
-       } else {                /* no call, accumulate digits */
-               append_char(env->gui.inbuf, &env->gui.inbuf_pos, digit);
-       }
-}
-
-/* this is a wrapper for actions that are available through the cli */
-/* TODO append arg to command and send the resulting string as cli command */
-static void keypad_send_command(struct video_desc *env, char *command)
-{      
-       ast_log(LOG_WARNING, "keypad_send_command(%s) called\n", command);
-       ast_cli_command(env->gui.outfd, command);
-       return;
-}
-
-/* function used to toggle on/off the status of some variables */
-static char *keypad_toggle(struct video_desc *env, int index)
-{
-       ast_log(LOG_WARNING, "keypad_toggle(%i) called\n", index);
-
-       switch (index) {
-       case KEY_SENDVIDEO:
-               env->out.sendvideo = !env->out.sendvideo;
-               break;
-#ifdef notyet
-       case KEY_MUTE: {
-               struct chan_oss_pvt *o = find_desc(oss_active);
-               o->mute = !o->mute;
-               }
-               break;
-       case KEY_AUTOANSWER: {
-               struct chan_oss_pvt *o = find_desc(oss_active);
-               o->autoanswer = !o->autoanswer;
-               }
-               break;
-#endif
-       }
-       return NULL;
-}
-
-char *console_do_answer(int fd);
-/*
- * Function called when the pick up button is pressed
- * perform actions according the channel status:
- *
- *  - if no one is calling us and no digits was pressed,
- *    the operation have no effects,
- *  - if someone is calling us we answer to the call.
- *  - if we have no call in progress and we pressed some
- *    digit, send the digit to the console.
- */
-static void keypad_pick_up(struct video_desc *env)
-{
-       ast_log(LOG_WARNING, "keypad_pick_up called\n");
-
-       if (env->owner) { /* someone is calling us, just answer */
-               console_do_answer(-1);
-       } else if (env->gui.inbuf_pos) { /* we have someone to call */
-               ast_cli_command(env->gui.outfd, env->gui.inbuf);
-       }
-
-       append_char(env->gui.inbuf, &env->gui.inbuf_pos, '\0'); /* clear buffer */
-}
-
-#if 0 /* still unused */
-/*
- * As an alternative to SDL_TTF, we can simply load the font from
- * an image and blit characters on the background of the GUI.
- *
- * To generate a font we can use the 'fly' command with the
- * following script (3 lines with 32 chars each)
-size 320,64
-name font.png
-transparent 0,0,0
-string 255,255,255,  0, 0,giant, !"#$%&'()*+,-./0123456789:;<=>?
-string 255,255,255,  0,20,giant,@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
-string 255,255,255,  0,40,giant,`abcdefghijklmnopqrstuvwxyz{|}~
-end
-
- */
-
-/* Print given text on the gui */
-static int gui_output(struct video_desc *env, const char *text)
-{
-#ifndef HAVE_SDL_TTF
-       return 1;       /* error, not supported */
-#else
-       int x = 30, y = 20;     /* XXX change */
-       SDL_Surface *output = NULL;
-       SDL_Color color = {0, 0, 0};    /* text color */
-       SDL_Rect dest = {env->win[WIN_KEYPAD].rect.x + x, y};
-
-       /* clean surface each rewrite */
-       SDL_BlitSurface(env->gui.keypad, NULL, env->screen, &env->win[WIN_KEYPAD].rect);
-
-       output = TTF_RenderText_Solid(env->gui.font, text, color);
-       if (output == NULL) {
-               ast_log(LOG_WARNING, "Cannot render text on gui - %s\n", TTF_GetError());
-               return 1;
-       }
-
-       SDL_BlitSurface(output, NULL, env->screen, &dest);
-       
-       SDL_UpdateRects(env->gui.keypad, 1, &env->win[WIN_KEYPAD].rect);
-       SDL_FreeSurface(output);
-       return 0;       /* success */
-#endif
-}
-#endif 
-
-static int video_geom(struct fbuf_t *b, const char *s);
-static void sdl_setup(struct video_desc *env);
-static int kp_match_area(const struct keypad_entry *e, int x, int y);
-
-/*
- * Handle SDL_MOUSEBUTTONDOWN type, finding the palette
- * index value and calling the right callback.
- *
- * x, y are referred to the upper left corner of the main SDL window.
- */
-static void handle_button_event(struct video_desc *env, SDL_MouseButtonEvent button)
-{
-       uint8_t index = KEY_OUT_OF_KEYPAD;      /* the key or region of the display we clicked on */
-
-       /* for each click we come back in normal mode */
-       env->gui.text_mode = 0;
-
-       /* define keypad boundary */
-       if (button.x < env->in.rem_dpy.w)
-               index = KEY_REM_DPY; /* click on remote video */
-       else if (button.x > env->in.rem_dpy.w + env->out.keypad_dpy.w)
-               index = KEY_LOC_DPY; /* click on local video */
-       else if (button.y > env->out.keypad_dpy.h)
-               index = KEY_OUT_OF_KEYPAD; /* click outside the keypad */
-       else if (env->gui.kp) {
-               int i;
-               for (i = 0; i < env->gui.kp_used; i++) {
-                       if (kp_match_area(&env->gui.kp[i], button.x - env->in.rem_dpy.w, button.y)) {
-                               index = env->gui.kp[i].c;
-                               break;
-                       }
-               }
-       }
-
-       /* exec the function */
-       if (index < 128) {      /* surely clicked on the keypad, don't care which key */
-               keypad_digit(env, index);
-               return;
-       }
-       switch (index) {
-       /* answer/close function */
-       case KEY_PICK_UP:
-               keypad_pick_up(env);
-               break;
-       case KEY_HANG_UP:
-               keypad_send_command(env, "console hangup");
-               break;
-
-       /* other functions */
-       case KEY_MUTE:
-       case KEY_AUTOANSWER:
-       case KEY_SENDVIDEO:
-               keypad_toggle(env, index);
-               break;
-
-       case KEY_LOCALVIDEO:
-               break;
-       case KEY_REMOTEVIDEO:
-               break;
-       case KEY_WRITEMESSAGE:
-               /* goes in text-mode */
-               env->gui.text_mode = 1;
-               break;
-
-
-       /* press outside the keypad. right increases size, center decreases, left drags */
-       case KEY_LOC_DPY:
-       case KEY_REM_DPY:
-               if (button.button == SDL_BUTTON_LEFT) {
-                       if (index == KEY_LOC_DPY) {
-                               /* store points where the drag start
-                               * and switch in drag mode */
-                               env->gui.x_drag = button.x;
-                               env->gui.y_drag = button.y;
-                               env->gui.drag_mode = 1;
-                       }
-                       break;
-               } else {
-                       char buf[128];
-                       struct fbuf_t *fb = index == KEY_LOC_DPY ? &env->out.loc_dpy : &env->in.rem_dpy;
-                       sprintf(buf, "%c%dx%d", button.button == SDL_BUTTON_RIGHT ? '>' : '<',
-                               fb->w, fb->h);
-                       video_geom(fb, buf);
-                       sdl_setup(env);
-               }
-               break;
-       case KEY_OUT_OF_KEYPAD:
-               break;
-
-       case KEY_GUI_CLOSE:
-               cleanup_sdl(env);
-               break;
-       case KEY_DIGIT_BACKGROUND:
-               break;
-       default:
-               ast_log(LOG_WARNING, "function not yet defined %i\n", index);
-       }
-}
-
-/*
- * Handle SDL_KEYDOWN type event, put the key pressed
- * in the dial buffer or in the text-message buffer,
- * depending on the text_mode variable value.
- *
- * key is the SDLKey structure corresponding to the key pressed.
- */
-static void handle_keyboard_input(struct video_desc *env, SDLKey key)
-{
-       if (env->gui.text_mode) {
-               /* append in the text-message buffer */
-               if (key == SDLK_RETURN) {
-                       /* send the text message and return in normal mode */
-                       env->gui.text_mode = 0;
-                       keypad_send_command(env, "send text");
-               } else {
-                       /* accumulate the key in the message buffer */
-                       append_char(env->gui.msgbuf, &env->gui.msgbuf_pos, key);
-               }
-       }
-       else {
-               /* append in the dial buffer */
-               append_char(env->gui.inbuf, &env->gui.inbuf_pos, key);
-       }
-
-       return;
-}
-
-/*
- * Check if the grab point is inside the X screen.
- *
- * x represent the new grab value
- * limit represent the upper value to use
- */
-static int boundary_checks(int x, int limit)
-{
-       return (x <= 0) ? 0 : (x > limit ? limit : x);
-}
-
-/* implement superlinear acceleration on the movement */
-static int move_accel(int delta)
-{
-       int d1 = delta*delta / 100;
-       return (delta > 0) ? delta + d1 : delta - d1;
+       return v->enc->enc_encap(&v->enc_out, v->mtu, tail);
 }
 
 /*
- * Move the source of the captured video.
- *
- * x_final_drag and y_final_drag are the coordinates where the drag ends,
- * start coordinares are in the gui_info structure.
- */
-static void move_capture_source(struct video_desc *env, int x_final_drag, int y_final_drag)
-{
-       int new_x, new_y;               /* new coordinates for grabbing local video */
-       int x = env->out.loc_src.x;     /* old value */
-       int y = env->out.loc_src.y;     /* old value */
-
-       /* move the origin */
-#define POLARITY -1            /* +1 or -1 depending on the desired direction */
-       new_x = x + POLARITY*move_accel(x_final_drag - env->gui.x_drag) * 3;
-       new_y = y + POLARITY*move_accel(y_final_drag - env->gui.y_drag) * 3;
-#undef POLARITY
-       env->gui.x_drag = x_final_drag; /* update origin */
-       env->gui.y_drag = y_final_drag;
-
-       /* check boundary and let the source to grab from the new points */
-       env->out.loc_src.x = boundary_checks(new_x, env->out.screen_width - env->out.loc_src.w);
-       env->out.loc_src.y = boundary_checks(new_y, env->out.screen_height - env->out.loc_src.h);
-       return;
-}
-
-/*
- * I am seeing some kind of deadlock or stall around
- * SDL_PumpEvents() while moving the window on a remote X server
- * (both xfree-4.4.0 and xorg 7.2)
- * and windowmaker. It is unclear what causes it.
- */
-
-/* grab a bunch of events */
-static void eventhandler(struct video_desc *env)
-{
-#define N_EVENTS       32
-       int i, n;
-       SDL_Event ev[N_EVENTS];
-
-#define MY_EV (SDL_MOUSEBUTTONDOWN|SDL_KEYDOWN)
-       while ( (n = SDL_PeepEvents(ev, N_EVENTS, SDL_GETEVENT, SDL_ALLEVENTS)) > 0) {
-               for (i = 0; i < n; i++) {
-#if 0
-                       ast_log(LOG_WARNING, "------ event %d at %d %d\n",
-                               ev[i].type,  ev[i].button.x,  ev[i].button.y);
-#endif
-                       switch (ev[i].type) {
-                       case SDL_KEYDOWN:
-                               handle_keyboard_input(env, ev[i].key.keysym.sym);
-                               break;
-                       case SDL_MOUSEMOTION:
-                               if (env->gui.drag_mode != 0)
-                                       move_capture_source(env, ev[i].motion.x, ev[i].motion.y);
-                               break;
-                       case SDL_MOUSEBUTTONDOWN:
-                               handle_button_event(env, ev[i].button);
-                               break;
-                       case SDL_MOUSEBUTTONUP:
-                               if (env->gui.drag_mode != 0) {
-                                       move_capture_source(env, ev[i].button.x, ev[i].button.y);
-                                       env->gui.drag_mode = 0;
-                               }
-                               break;
-                       }
-
-               }
-       }
-       if (1) {
-               struct timeval b, a = ast_tvnow();
-               int i;
-               //SDL_Lock_EventThread();
-               SDL_PumpEvents();
-               b = ast_tvnow();
-               i = ast_tvdiff_ms(b, a);
-               if (i > 3)
-                       fprintf(stderr, "-------- SDL_PumpEvents took %dms\n", i);
-               //SDL_Unlock_EventThread();
-       }
-}
-
-static SDL_Surface *get_keypad(const char *file)
-{
-       SDL_Surface *temp;
-#ifdef HAVE_SDL_IMAGE
-       temp = IMG_Load(file);
-#else
-       temp = SDL_LoadBMP(file);
-#endif
-       if (temp == NULL)
-               fprintf(stderr, "Unable to load image %s: %s\n",
-                       file, SDL_GetError());
-       return temp;
-}
-
-/* TODO: consistency checks, check for bpp, widht and height */
-/* Init the mask image used to grab the action. */
-static int gui_init(struct video_desc *env)
-{
-       /* initialize keypad status */
-       env->gui.text_mode = 0;
-       env->gui.drag_mode = 0;
-
-       /* initialize grab coordinates */
-       env->out.loc_src.x = 0;
-       env->out.loc_src.y = 0;
-
-       /* initialize keyboard buffer */
-       append_char(env->gui.inbuf, &env->gui.inbuf_pos, '\0');
-       append_char(env->gui.msgbuf, &env->gui.msgbuf_pos, '\0');
-
-#ifdef HAVE_SDL_TTF
-       /* Initialize SDL_ttf library and load font */
-       if (TTF_Init() == -1) {
-               ast_log(LOG_WARNING, "Unable to init SDL_ttf, no output available\n");
-               return -1;
-       }
-
-#define GUI_FONTSIZE 28
-       env->gui.font = TTF_OpenFont( env->keypad_font, GUI_FONTSIZE);
-       if (!env->gui.font) {
-               ast_log(LOG_WARNING, "Unable to load font %s, no output available\n", env->keypad_font);
-               return -1;
-       }
-       ast_log(LOG_WARNING, "Loaded font %s\n", env->keypad_font);
-#endif
-
-       env->gui.outfd = open ("/dev/null", O_WRONLY);  /* discard output, temporary */
-       if ( env->gui.outfd < 0 ) {
-               ast_log(LOG_WARNING, "Unable output fd\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-static void sdl_setup(struct video_desc *env);
-
-/*
- * Helper thread to periodically poll the video source and enqueue the
- * generated frames to the channel's queue.
+ * Helper thread to periodically poll the video sources and enqueue the
+ * generated frames directed to the remote party to the channel's queue.
  * Using a separate thread also helps because the encoding can be
  * computationally expensive so we don't want to starve the main thread.
  */
@@ -2681,65 +857,79 @@ static void *video_thread(void *arg)
 {
        struct video_desc *env = arg;
        int count = 0;
+       char save_display[128] = "";
+       int i; /* integer variable used as iterator */
 
-       env->screen = NULL;
-       bzero(env->win, sizeof(env->win));
-
-       if (SDL_Init(SDL_INIT_VIDEO)) {
-               ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
-                       SDL_GetError());
-               /* again not fatal, just we won't display anything */
-       } else {
-               sdl_setup(env);
-               if (env->sdl_ok)
-                       ast_mutex_init(&env->in.dec_in_lock);
-               /* TODO, segfault if not X display present */
-               env->gui_ok = !gui_init(env);
-               if (!env->gui_ok)
-                       ast_log(LOG_WARNING, "cannot init console gui\n");
+       /* if sdl_videodriver is set, override the environment. Also,
+        * if it contains 'console' override DISPLAY around the call to SDL_Init
+        * so we use the console as opposed to the x11 version of aalib
+        */
+       if (!ast_strlen_zero(env->sdl_videodriver)) { /* override */
+               const char *s = getenv("DISPLAY");
+               setenv("SDL_VIDEODRIVER", env->sdl_videodriver, 1);
+               if (s && !strcasecmp(env->sdl_videodriver, "aalib-console")) {
+                       ast_copy_string(save_display, s, sizeof(save_display));
+                       unsetenv("DISPLAY");
+               }
        }
-       if (video_open(&env->out)) {
+       sdl_setup(env);
+       if (!ast_strlen_zero(save_display))
+               setenv("DISPLAY", save_display, 1);
+
+       ast_mutex_init(&env->dec_lock); /* used to sync decoder and renderer */
+
+       if (grabber_open(&env->out)) {
                ast_log(LOG_WARNING, "cannot open local video source\n");
-       } else {
-               /* try to register the fd. Unfortunately, if the webcam
-                * driver does not support select/poll we are out of luck.
-                */
-               if (env->out.fd >= 0)
-                       ast_channel_set_fd(env->owner, 1, env->out.fd);
-               video_out_init(env);
+       } 
+
+       if (env->out.device_num)
+               env->out.devices[env->out.device_primary].status_index |= IS_PRIMARY | IS_SECONDARY;
+       
+       /* even if no device is connected, we must call video_out_init,
+        * as some of the data structures it initializes are
+        * used in get_video_frames()
+        */
+       video_out_init(env);
+
+       /* Writes intial status of the sources. */
+       if (env->gui) {
+           for (i = 0; i < env->out.device_num; i++) {
+               print_message(env->gui->thumb_bd_array[i].board,
+                src_msgs[env->out.devices[i].status_index]);
+           }
        }
 
        for (;;) {
-               /* XXX 20 times/sec */
-               struct timeval t = { 0, 50000 };
+               struct timeval t = { 0, 50000 };        /* XXX 20 times/sec */
                struct ast_frame *p, *f;
-               struct video_in_desc *v = &env->in;
-               struct ast_channel *chan = env->owner;
-               int fd = chan->alertpipe[1];
+               struct ast_channel *chan;
+               int fd;
+               char *caption = NULL, buf[160];
 
                /* determine if video format changed */
                if (count++ % 10 == 0) {
-                       char buf[160];
-                       if (env->out.sendvideo)
+                       if (env->out.sendvideo && env->out.devices)
                            sprintf(buf, "%s %s %dx%d @@ %dfps %dkbps",
-                               env->out.videodevice, env->codec_name,
-                               env->out.enc_in.w, env->out.enc_in.h,
+                               env->out.devices[env->out.device_primary].name, env->codec_name,
+                               env->enc_in.w, env->enc_in.h,
                                env->out.fps, env->out.bitrate/1000);
                        else
                            sprintf(buf, "hold");
-                       SDL_WM_SetCaption(buf, NULL);
+                       caption = buf;
                }
 
                /* manage keypad events */
                /* XXX here we should always check for events,
                * otherwise the drag will not work */ 
-               if (env->gui_ok)
-                       eventhandler(env);
+               if (env->gui)
+                       eventhandler(env, caption);
  
                /* sleep for a while */
                ast_select(0, NULL, NULL, NULL, &t);
 
-               SDL_UpdateRects(env->screen, 1, &env->win[WIN_KEYPAD].rect);// XXX inefficient
+           if (env->in) {
+               struct video_dec_desc *v = env->in;
+               
                /*
                 * While there is something to display, call the decoder and free
                 * the buffer, possibly enabling the receiver to store new data.
@@ -2747,11 +937,12 @@ static void *video_thread(void *arg)
                while (v->dec_in_dpy) {
                        struct fbuf_t *tmp = v->dec_in_dpy;     /* store current pointer */
 
-                       if (v->dec->dec_run(v, tmp))
+                       /* decode the frame, but show it only if not frozen */
+                       if (v->d_callbacks->dec_run(v, tmp) && !env->frame_freeze)
                                show_frame(env, WIN_REMOTE);
                        tmp->used = 0;  /* mark buffer as free */
                        tmp->ebit = 0;
-                       ast_mutex_lock(&v->dec_in_lock);
+                       ast_mutex_lock(&env->dec_lock);
                        if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN])    /* advance to next, circular */
                                v->dec_in_dpy = &v->dec_in[0];
 
@@ -2759,16 +950,26 @@ static void *video_thread(void *arg)
                                v->dec_in_cur = tmp;    /* using the slot just freed */
                        else if (v->dec_in_dpy == v->dec_in_cur) /* this was the last slot */
                                v->dec_in_dpy = NULL;   /* nothing more to display */
-                       ast_mutex_unlock(&v->dec_in_lock);
+                       ast_mutex_unlock(&env->dec_lock);
                }
+           }
 
-
+               if (env->shutdown)
+                       break;
                f = get_video_frames(env, &p);  /* read and display */
                if (!f)
                        continue;
-               if (env->shutdown)
-                       break;
                chan = env->owner;
+               if (chan == NULL) {
+                       /* drop the chain of frames, nobody uses them */
+                       while (f) {
+                               struct ast_frame *g = AST_LIST_NEXT(f, frame_list);
+                               ast_frfree(f);
+                               f = g;
+                       }
+                       continue;
+               }
+               fd = chan->alertpipe[1];
                ast_channel_lock(chan);
 
                /* AST_LIST_INSERT_TAIL is only good for one frame, cannot use here */
@@ -2794,12 +995,12 @@ static void *video_thread(void *arg)
        }
        /* thread terminating, here could call the uninit */
        /* uninitialize the local and remote video environments */
-       video_in_uninit(&env->in);
-       video_out_uninit(&env->out);
-
-       if (env->sdl_ok)
-               cleanup_sdl(env);
+       env->in = dec_uninit(env->in);
+       video_out_uninit(env);
 
+       if (env->gui)
+               env->gui = cleanup_sdl(env->gui, env->out.device_num);
+       ast_mutex_destroy(&env->dec_lock);
        env->shutdown = 0;
        return NULL;
 }
@@ -2818,10 +1019,11 @@ static void copy_geometry(struct fbuf_t *src, struct fbuf_t *dst)
  */
 static void init_env(struct video_desc *env)
 {
-       struct fbuf_t *c = &(env->out.loc_src);         /* local source */
-       struct fbuf_t *ei = &(env->out.enc_in);         /* encoder input */
-       struct fbuf_t *ld = &(env->out.loc_dpy);        /* local display */
-       struct fbuf_t *rd = &(env->in.rem_dpy);         /* remote display */
+       struct fbuf_t *c = &(env->out.loc_src_geometry);                /* local source */
+       struct fbuf_t *ei = &(env->enc_in);             /* encoder input */
+       struct fbuf_t *ld = &(env->loc_dpy);    /* local display */
+       struct fbuf_t *rd = &(env->rem_dpy);            /* remote display */
+       int i; /* integer working as iterator */
 
        c->pix_fmt = PIX_FMT_YUV420P;   /* default - camera format */
        ei->pix_fmt = PIX_FMT_YUV420P;  /* encoder input */
@@ -2834,20 +1036,18 @@ static void init_env(struct video_desc *env)
        copy_geometry(ei, c);   /* camera inherits from encoder input */
        copy_geometry(ei, rd);  /* remote display inherits from encoder input */
        copy_geometry(rd, ld);  /* local display inherits from remote display */
-}
 
-/* setup an sdl overlay and associated info, return 0 on success, != 0 on error */
-static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
-       int w, int h, int x, int y)
-{
-       win->bmp = SDL_CreateYUVOverlay(w, h, fmt, screen);
-       if (win->bmp == NULL)
-               return -1;      /* error */
-       win->rect.x = x;
-       win->rect.y = y;
-       win->rect.w = w;
-       win->rect.h = h;
-       return 0;
+       /* fix the size of buffers for small windows */
+       for (i = 0; i < env->out.device_num; i++) {
+               env->src_dpy[i].pix_fmt = PIX_FMT_YUV420P;
+               env->src_dpy[i].w = SRC_WIN_W;
+               env->src_dpy[i].h = SRC_WIN_H;
+       }
+       /* now we set the default coordinates for the picture in picture
+       frames inside the env_in buffers, those can be changed by dragging the
+       picture in picture with left click */
+       env->out.pip_x = ei->w - ei->w/3;
+       env->out.pip_y = ei->h - ei->h/3;
 }
 
 /*!
@@ -2859,16 +1059,17 @@ static int set_win(SDL_Surface *screen, struct display_window *win, int fmt,
  */
 void console_video_start(struct video_desc *env, struct ast_channel *owner)
 {
+       ast_log(LOG_WARNING, "env %p chan %p\n", env, owner);
        if (env == NULL)        /* video not initialized */
                return;
-       if (owner == NULL)      /* nothing to do if we don't have a channel */
-               return;
-       env->owner = owner;
+       env->owner = owner;     /* work even if no owner is specified */
+       if (env->vthread)
+               return;         /* already initialized, nothing to do */
        init_env(env);
        env->out.enc = map_config_video_format(env->codec_name);
 
        ast_log(LOG_WARNING, "start video out %s %dx%d\n",
-               env->codec_name, env->out.enc_in.w,  env->out.enc_in.h);
+               env->codec_name, env->enc_in.w,  env->enc_in.h);
        /*
         * Register all codecs supported by the ffmpeg library.
         * We only need to do it once, but probably doesn't
@@ -2886,139 +1087,9 @@ void console_video_start(struct video_desc *env, struct ast_channel *owner)
                env->out.bitrate = 65000;
                ast_log(LOG_WARNING, "bitrate unset, forcing to %d\n", env->out.bitrate);
        }
-
-       ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
-}
-
-static int keypad_cfg_read(struct gui_info *gui, const char *val);
-/* [re]set the main sdl window, useful in case of resize */
-static void sdl_setup(struct video_desc *env)
-{
-       int dpy_fmt = SDL_IYUV_OVERLAY; /* YV12 causes flicker in SDL */
-       int maxw, maxh;
-
-       /*
-        * initialize the SDL environment. We have one large window
-        * with local and remote video, and a keypad.
-        * At the moment we arrange them statically, as follows:
-        * - on the left, the remote video;
-        * - on the center, the keypad
-        * - on the right, the local video
-        */
-
-       /* Fetch the keypad now, we need it to know its size */
-       if (!env->gui.keypad)
-               env->gui.keypad = get_keypad(env->keypad_file);
-       if (env->gui.keypad) {
-               int fd = -1;
-               void *p = NULL;
-               off_t l = 0;
-
-               env->out.keypad_dpy.w = env->gui.keypad->w;
-               env->out.keypad_dpy.h = env->gui.keypad->h;
-               /*
-                * If the keypad image has a comment field, try to read
-                * the button location from there. The block must be
-                *      keypad_entry = token shape x0 y0 x1 y1 h
-                *      ...
-                * (basically, lines have the same format as config file entries.
-                * same as the keypad_entry.
-                * You can add it to a jpeg file using wrjpgcom
-                */
-               do { /* only once, in fact */
-                       const unsigned char *s, *e;
-
-                       fd = open(env->keypad_file, O_RDONLY);
-                       if (fd < 0) {
-                               ast_log(LOG_WARNING, "fail to open %s\n", env->keypad_file);
-                               break;
-                       }
-                       l = lseek(fd, 0, SEEK_END);
-                       if (l <= 0) {
-                               ast_log(LOG_WARNING, "fail to lseek %s\n", env->keypad_file);
-                               break;
-                       }
-                       p = mmap(NULL, l, PROT_READ, 0, fd, 0);
-                       if (p == NULL) {
-                               ast_log(LOG_WARNING, "fail to mmap %s size %ld\n", env->keypad_file, (long)l);
-                               break;
-                       }
-                       e = (const unsigned char *)p + l;
-                       for (s = p; s < e - 20 ; s++) {
-                               if (!memcmp(s, "keypad_entry", 12)) { /* keyword found */
-                                       ast_log(LOG_WARNING, "found entry\n");
-                                       break;
-                               }
-                       }
-                       for ( ;s < e - 20; s++) {
-                               char buf[256];
-                               const unsigned char *s1;
-                               if (index(" \t\r\n", *s))       /* ignore blanks */
-                                       continue;
-                               if (*s > 127)   /* likely end of comment */
-                                       break;
-                               if (memcmp(s, "keypad_entry", 12)) /* keyword not found */
-                                       break;
-                               s += 12;
-                               l = MIN(sizeof(buf), e - s);
-                               ast_copy_string(buf, s, l);
-                               s1 = ast_skip_blanks(buf);      /* between token and '=' */
-                               if (*s1++ != '=')       /* missing separator */
-                                       break;
-                               if (*s1 == '>') /* skip => */
-                                       s1++;
-                               keypad_cfg_read(&env->gui, ast_skip_blanks(s1));
-                               /* now wait for a newline */
-                               s1 = s;
-                               while (s1 < e - 20 && !index("\r\n", *s1) && *s1 < 128)
-                                       s1++;
-                               s = s1;
-                       }
-               } while (0);
-               if (p)
-                       munmap(p, l);
-               if (fd >= 0)
-                       close(fd);
-       }
-#define BORDER 5       /* border around our windows */
-       maxw = env->in.rem_dpy.w + env->out.loc_dpy.w + env->out.keypad_dpy.w;
-       maxh = MAX( MAX(env->in.rem_dpy.h, env->out.loc_dpy.h), env->out.keypad_dpy.h);
-       maxw += 4 * BORDER;
-       maxh += 2 * BORDER;
-       env->screen = SDL_SetVideoMode(maxw, maxh, 0, 0);
-       if (!env->screen) {
-               ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
-               goto no_sdl;
-       }
-
-       SDL_WM_SetCaption("Asterisk console Video Output", NULL);
-       if (set_win(env->screen, &env->win[WIN_REMOTE], dpy_fmt,
-                       env->in.rem_dpy.w, env->in.rem_dpy.h, BORDER, BORDER))
-               goto no_sdl;
-       if (set_win(env->screen, &env->win[WIN_LOCAL], dpy_fmt,
-                       env->out.loc_dpy.w, env->out.loc_dpy.h,
-                       3*BORDER+env->in.rem_dpy.w + env->out.keypad_dpy.w, BORDER))
-               goto no_sdl;
-
-       /* display the skin, but do not free it as we need it later to
-        * restore text areas and maybe sliders too.
-        */
-       if (env->gui.keypad) {
-               struct SDL_Rect *dest = &env->win[WIN_KEYPAD].rect;
-               dest->x = 2*BORDER + env->in.rem_dpy.w;
-               dest->y = BORDER;
-               dest->w = env->gui.keypad->w;
-               dest->h = env->gui.keypad->h;
-               SDL_BlitSurface(env->gui.keypad, NULL, env->screen, dest);
-               SDL_UpdateRects(env->screen, 1, dest);
-       }
-       env->in.dec_in_cur = &env->in.dec_in[0];
-       env->in.dec_in_dpy = NULL;      /* nothing to display */
-       env->sdl_ok = 1;
-
-no_sdl:
-       if (env->sdl_ok == 0)   /* free resources in case of errors */
-               cleanup_sdl(env);
+       /* create the thread as detached so memory is freed on termination */
+       ast_pthread_create_detached_background(&env->vthread,
+               NULL, video_thread, env);
 }
 
 /*
@@ -3034,6 +1105,9 @@ static int video_geom(struct fbuf_t *b, const char *s)
        static struct {
                const char *s; int w; int h;
        } *fp, formats[] = {
+               {"16cif",       1408, 1152 },
+               {"xga",         1024, 768 },
+               {"4cif",        704, 576 },
                {"vga",         640, 480 },
                {"cif",         352, 288 },
                {"qvga",        320, 240 },
@@ -3070,155 +1144,48 @@ static int video_geom(struct fbuf_t *b, const char *s)
        return 0;
 }
 
-/*
- * Functions to determine if a point is within a region. Return 1 if success.
- * First rotate the point, with
- *     x' =  (x - x0) * cos A + (y - y0) * sin A
- *     y' = -(x - x0) * sin A + (y - y0) * cos A
- * where cos A = (x1-x0)/l, sin A = (y1 - y0)/l, and
- *     l = sqrt( (x1-x0)^2 + (y1-y0)^2
- * Then determine inclusion by simple comparisons i.e.:
- *     rectangle: x >= 0 && x < l && y >= 0 && y < h
- *     ellipse: (x-xc)^2/l^2 + (y-yc)^2/h2 < 1
- */
-static int kp_match_area(const struct keypad_entry *e, int x, int y)
-{
-       double xp, dx = (e->x1 - e->x0);
-       double yp, dy = (e->y1 - e->y0);
-       double l = sqrt(dx*dx + dy*dy);
-       int ret = 0;
-
-       if (l > 1) { /* large enough */
-               xp = ((x - e->x0)*dx + (y - e->y0)*dy)/l;
-               yp = (-(x - e->x0)*dy + (y - e->y0)*dx)/l;
-               if (e->type == KP_RECT) {
-                       ret = (xp >= 0 && xp < l && yp >=0 && yp < l);
-               } else if (e->type == KP_CIRCLE) {
-                       dx = xp*xp/(l*l) + yp*yp/(e->h*e->h);
-                       ret = (dx < 1);
-               }
-       }
-#if 0
-       ast_log(LOG_WARNING, "result %d [%d] for match %d,%d in type %d p0 %d,%d p1 %d,%d h %d\n",
-               ret, e->c, x, y, e->type, e->x0, e->y0, e->x1, e->y1, e->h);
-#endif
-       return ret;
-}
 
-/*
- * read a keypad entry line in the format
- *     reset
- *     token circle xc yc diameter
- *     token circle xc yc x1 y1 h      # ellipse, main diameter and height
- *     token rect x0 y0 x1 y1 h        # rectangle with main side and eight
- * token is the token to be returned, either a character or a symbol
- * as KEY_* above
+/*! \brief add an entry to the video_device table,
+ * ignoring duplicate names.
+ * The table is a static array of 9 elements.
+ * The last_frame field of each entry of the table is initialized to
+ * the current time (we need a value inside this field, on stop of the
+ * GUI the last_frame value is not changed, to avoid checking if it is 0 we
+ * set the initial value on current time) XXX
+ *
+ * PARAMETERS:
+ * \param devices_p = pointer to the table of devices
+ * \param device_num_p = pointer to the number of devices
+ * \param s = name of the new device to insert
+ *
+ * returns 0 on success, 1 on error
  */
-struct _s_k { const char *s; int k; };
-static struct _s_k gui_key_map[] = {
-       {"PICK_UP",     KEY_PICK_UP },
-       {"PICKUP",      KEY_PICK_UP },
-        {"HANG_UP",    KEY_HANG_UP },
-        {"HANGUP",     KEY_HANG_UP },
-        {"MUTE",       KEY_MUTE },
-        {"AUTOANSWER", KEY_AUTOANSWER },
-        {"SENDVIDEO",  KEY_SENDVIDEO },
-        {"LOCALVIDEO", KEY_LOCALVIDEO },
-        {"REMOTEVIDEO",        KEY_REMOTEVIDEO },
-        {"WRITEMESSAGE", KEY_WRITEMESSAGE },
-        {"GUI_CLOSE",  KEY_GUI_CLOSE },
-        {NULL, 0 } };
-
-static int keypad_cfg_read(struct gui_info *gui, const char *val)
+static int device_table_fill(struct video_device *devices, int *device_num_p, const char *s)
 {
-       struct keypad_entry e;
-       char s1[16], s2[16];
-       int i, ret = 0;
-
-       bzero(&e, sizeof(e));
-       i = sscanf(val, "%14s %14s %d %d %d %d %d",
-                s1, s2, &e.x0, &e.y0, &e.x1, &e.y1, &e.h);
-
-       switch (i) {
-       default:
-               break;
-       case 1: /* only "reset" is allowed */
-               if (strcasecmp(s1, "reset"))    /* invalid */
-                       break;
-               if (gui->kp) {
-                       gui->kp_used = 0;
-               }
-               ret = 1;
-               break;
-       case 5: /* token circle xc yc diameter */
-               if (strcasecmp(s2, "circle"))   /* invalid */
-                       break;
-               e.h = e.x1;
-               e.y1 = e.y0;    /* map radius in x1 y1 */
-               e.x1 = e.x0 + e.h;      /* map radius in x1 y1 */
-               e.x0 = e.x0 - e.h;      /* map radius in x1 y1 */
-               /* fallthrough */
-
-       case 7: /* token circle|rect x0 y0 x1 y1 h */
-               if (e.x1 < e.x0 || e.h <= 0) {
-                       ast_log(LOG_WARNING, "error in coordinates\n");
-                       e.type = 0;
-                       break;
-               }
-               if (!strcasecmp(s2, "circle")) {
-                       /* for a circle we specify the diameter but store center and radii */
-                       e.type = KP_CIRCLE;
-                       e.x0 = (e.x1 + e.x0) / 2;
-                       e.y0 = (e.y1 + e.y0) / 2;
-                       e.h = e.h / 2;
-               } else if (!strcasecmp(s2, "rect")) {
-                       e.type = KP_RECT;
-               } else
-                       break;
-               ret = 1;
-       }
-       // ast_log(LOG_WARNING, "reading [%s] returns %d %d\n", val, i, ret);
-       if (ret == 0)
-               return 0;
-       /* map the string into token to be returned */
-       i = atoi(s1);
-       if (i > 0 || s1[1] == '\0')     /* numbers or single characters */
-               e.c = (i > 9) ? i : s1[0];
-       else {
-               struct _s_k *p;
-               for (p = gui_key_map; p->s; p++) {
-                       if (!strcasecmp(p->s, s1)) {
-                               e.c = p->k;
-                               break;
-                       }
-               }
-       }
-       if (e.c == 0) {
-               ast_log(LOG_WARNING, "missing token\n");
-               return 0;
-       }
-       if (gui->kp_size == 0) {
-               gui->kp = ast_calloc(10, sizeof(e));
-               if (gui->kp == NULL) {
-                       ast_log(LOG_WARNING, "cannot allocate kp");
-                       return 0;
-               }
-               gui->kp_size = 10;
-       }
-       if (gui->kp_size == gui->kp_used) { /* must allocate */
-               struct keypad_entry *a = ast_realloc(gui->kp, sizeof(e)*(gui->kp_size+10));
-               if (a == NULL) {
-                       ast_log(LOG_WARNING, "cannot reallocate kp");
+       int i;
+       struct video_device *p;
+
+       /* with the current implementation, we support a maximum of 9 devices.*/
+       if (*device_num_p >= 9)
+               return 0; /* more devices will be ignored */
+       /* ignore duplicate names */
+       for (i = 0; i < *device_num_p; i++) {
+               if (!strcmp(devices[i].name, s))
                        return 0;
-               }
-               gui->kp = a;
-               gui->kp_size += 10;
        }
-       if (gui->kp_size == gui->kp_used)
-               return 0;
-       ast_log(LOG_WARNING, "allocated entry %d\n", gui->kp_used);
-       gui->kp[gui->kp_used++] = e;
-       return 1;
+       /* inserts the new video device */
+       p = &devices[*device_num_p];
+       /* XXX the string is allocated but NEVER deallocated,
+       the good time to do that is when the module is unloaded, now we skip the problem */
+       p->name = ast_strdup(s);                /* copy the name */
+       /* other fields initially NULL */
+       p->grabber = NULL;
+       p->grabber_data = NULL;
+       p->dev_buf = NULL;
+       p->last_frame = ast_tvnow();
+       p->status_index = 0;
+       (*device_num_p)++;                      /* one device added */
+       return 0;
 }
 
 /* extend ast_cli with video commands. Called by console_video_config */
@@ -3228,24 +1195,38 @@ int console_video_cli(struct video_desc *env, const char *var, int fd)
                return 1;       /* unrecognised */
 
         if (!strcasecmp(var, "videodevice")) {
-               ast_cli(fd, "videodevice is [%s]\n", env->out.videodevice);
+               ast_cli(fd, "videodevice is [%s]\n", env->out.devices[env->out.device_primary].name);
         } else if (!strcasecmp(var, "videocodec")) {
                ast_cli(fd, "videocodec is [%s]\n", env->codec_name);
         } else if (!strcasecmp(var, "sendvideo")) {
                ast_cli(fd, "sendvideo is [%s]\n", env->out.sendvideo ? "on" : "off");
         } else if (!strcasecmp(var, "video_size")) {
+               int in_w = 0, in_h = 0;
+               if (env->in) {
+                       in_w = env->in->dec_out.w;
+                       in_h = env->in->dec_out.h;
+               }
                ast_cli(fd, "sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
-                       env->out.enc_in.w, env->out.enc_in.h,
-                       env->out.loc_src.w, env->out.loc_src.h,
-                       env->out.loc_dpy.w, env->out.loc_src.h,
-                       env->in.rem_dpy.w, env->in.rem_dpy.h,
-                       env->in.dec_out.w, env->in.dec_out.h);
+                       env->enc_in.w, env->enc_in.h,
+                       env->out.loc_src_geometry.w, env->out.loc_src_geometry.h,
+                       env->loc_dpy.w, env->loc_dpy.h,
+                       env->rem_dpy.w, env->rem_dpy.h,
+                       in_w, in_h);
         } else if (!strcasecmp(var, "bitrate")) {
                ast_cli(fd, "bitrate is [%d]\n", env->out.bitrate);
         } else if (!strcasecmp(var, "qmin")) {
                ast_cli(fd, "qmin is [%d]\n", env->out.qmin);
         } else if (!strcasecmp(var, "fps")) {
                ast_cli(fd, "fps is [%d]\n", env->out.fps);
+        } else if (!strcasecmp(var, "startgui")) {
+               env->stayopen = 1;
+               console_video_start(env, NULL);
+        } else if (!strcasecmp(var, "stopgui") && env->stayopen != 0) {
+               env->stayopen = 0;
+               if (env->gui && env->owner)
+                       ast_cli_command(-1, "console hangup");
+               else /* not in a call */
+                       console_video_uninit(env);
         } else {
                return 1;       /* unrecognised */
        }
@@ -3271,27 +1252,30 @@ int console_video_config(struct video_desc **penv,
                        return 1;       /* error */
                
                }
-               /* set default values */
-               ast_copy_string(env->out.videodevice, "X11", sizeof(env->out.videodevice));
+               /* set default values - 0's are already there */
+               env->out.device_primary = 0;
+               env->out.device_secondary = 0;
                env->out.fps = 5;
                env->out.bitrate = 65000;
                env->out.sendvideo = 1;
                env->out.qmin = 3;
+               env->out.device_num = 0;
        }
        CV_START(var, val);
-        CV_STR("videodevice", env->out.videodevice);
-        CV_BOOL("sendvideo", env->out.sendvideo);
-        CV_F("video_size", video_geom(&env->out.enc_in, val));
-        CV_F("camera_size", video_geom(&env->out.loc_src, val));
-        CV_F("local_size", video_geom(&env->out.loc_dpy, val));
-        CV_F("remote_size", video_geom(&env->in.rem_dpy, val));
-        CV_STR("keypad", env->keypad_file);
-        CV_F("keypad_entry", keypad_cfg_read(&env->gui, val));
-        CV_STR("keypad_mask", env->keypad_mask);
+       CV_F("videodevice", device_table_fill(env->out.devices, &env->out.device_num, val));
+       CV_BOOL("sendvideo", env->out.sendvideo);
+       CV_F("video_size", video_geom(&env->enc_in, val));
+       CV_F("camera_size", video_geom(&env->out.loc_src_geometry, val));
+       CV_F("local_size", video_geom(&env->loc_dpy, val));
+       CV_F("remote_size", video_geom(&env->rem_dpy, val));
+       CV_STR("keypad", env->keypad_file);
+       CV_F("region", keypad_cfg_read(env->gui, val));
+       CV_UINT("startgui", env->stayopen);     /* enable gui at startup */
        CV_STR("keypad_font", env->keypad_font);
-        CV_UINT("fps", env->out.fps);
-        CV_UINT("bitrate", env->out.bitrate);
-        CV_UINT("qmin", env->out.qmin);
+       CV_STR("sdl_videodriver", env->sdl_videodriver);
+       CV_UINT("fps", env->out.fps);
+       CV_UINT("bitrate", env->out.bitrate);
+       CV_UINT("qmin", env->out.qmin);
        CV_STR("videocodec", env->codec_name);
        return 1;       /* nothing found */