[svn-commits] rizzo: branch rizzo/video_v2 r82753 - /team/rizzo/video_v2/channels/
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Tue Sep 18 10:45:22 CDT 2007
Author: rizzo
Date: Tue Sep 18 10:45:22 2007
New Revision: 82753
URL: http://svn.digium.com/view/asterisk?view=rev&rev=82753
Log:
lots of cleanup of this module, including the ability
to run in "headless" configuration i.e. without X or SDL.
Modified:
team/rizzo/video_v2/channels/console_video.c
Modified: team/rizzo/video_v2/channels/console_video.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/video_v2/channels/console_video.c?view=diff&rev=82753&r1=82752&r2=82753
==============================================================================
--- team/rizzo/video_v2/channels/console_video.c (original)
+++ team/rizzo/video_v2/channels/console_video.c Tue Sep 18 10:45:22 2007
@@ -1,24 +1,33 @@
/* header for console video */
/*
- * experimental support for video sessions. We use:
- * - SDL for rendering both incoming and outgoing data,
- * - ffmpeg for encoding and decoding;
- * - video4linux for accessing the source.
- */
+ * 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.
+ *
+ * If one of these pieces is not available, either at compile time or at
+ * runtime, we do our best to run without it. Of course, no codec library
+ * means we can only deal with raw data, no SDL means we cannot do rendering,
+ * no V4L or X11 means we cannot generate data (but eventually we could
+ * stream from a file!).
+ *
+ * We need a recent (2007.07.12 or newer) version of ffmpeg to avoid warnings.
+ * Older versions might give 'deprecated' messages during compilation,
+ * thus not compiling in AST_DEVMODE, or don't have swscale, in which case
+ * you can try to compile #defining OLD_FFMPEG here.
+ */
+
+
//#define DROP_PACKETS 5 // if set, simulate this percentage of lost video packets
#define HAVE_V4L 1
#define HAVE_SDL 1
#define HAVE_FFMPEG 1
-//#define OLD_FFMPEG 1 /* set if no swscale */
-
-/*
- * You need a recent (2007.07.12 or so) version of ffmpeg to avoid warning.
- * Older versions might give 'deprecated' messages during compilation,
- * thus not compiling in AST_DEVMODE, or don't have swscale, in which case
- * you can try to compile #defining OLD_FFMPEG here.
- */
+//#define OLD_FFMPEG 1 /* set for old ffmpeg with no swscale */
+
#if !defined(HAVE_FFMPEG) || HAVE_FFMPEG == 0 || !defined(HAVE_SDL) || HAVE_SDL == 0
-/* stubs for everything */
+/*
+ * Neither SDL nor codec means we cannot do video, so we only give stubs to
+ * keep the compiler happy (basically, a placeholder for config file info).
+ */
struct video_desc { /* empty */
int w;
int h;
@@ -28,22 +37,22 @@
};
#define CONSOLE_FORMAT_VIDEO 0
#define console_write_video NULL
-static void console_video_start(struct video_desc *env,
- struct ast_channel *owner)
-{
-}
-
-static void console_video_uninit(struct video_desc *env)
-{
-}
-
-static struct ast_frame *get_video_frames(struct video_desc *env)
+static void console_video_start(struct video_desc *e, struct ast_channel *c)
+{
+}
+
+static void console_video_uninit(struct video_desc *e)
+{
+}
+
+static struct ast_frame *get_video_frames(struct video_desc *e)
{
return NULL;
}
-#else /* the body of the file */
-/*
+#else
+/*
+ * The real thing, with support for SDL and codecs (and possibly V4l/X11)
* In order to decode video you need the following patch to the
* main Makefile:
@@ -66,33 +75,39 @@
videosupport=yes
*/
-/*
- * Examples for video encoding are at http://www.irisa.fr/texmex/people/dufouil/ffmpegdoxy/apiexample_8c-source.html
- */
-
-#include <X11/Xlib.h>
+
+#include <X11/Xlib.h> /* this should be conditional */
#include <ffmpeg/avcodec.h>
#ifndef OLD_FFMPEG
#include <ffmpeg/swscale.h> /* requires a recent ffmpeg */
#endif
+
#include <SDL/SDL.h>
-/* Structures for ffmpeg processing */
-/*
- * Information for decoding incoming video stream.
- * We need:
- * - one descriptor per output stream (camera or file, plus encoding context)
- * - one descriptor per incoming stream (network data, plus decoding context)
- * - one descriptor per window (SDL).
- */
-
+/*
+ * 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.
+ */
struct fbuf_t { /* frame buffers, dynamically allocated */
- uint8_t *data;
- int size;
- int used;
+ uint8_t *data; /* malloc-ed memory */
+ int size; /* total size in bytes */
+ int used; /* space used so far */
};
+/*
+ * Descriptor of the local source, made of the following pieces:
+ * + configuration info (geometry, device name, fps...). These are read
+ * from the config file and copied here before calling video_out_init();
+ * + the frame buffer (buf) and source pixel format, allocated at init time;
+ * + 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
+ * NOTE: buf.data == NULL means the rest of the struct is invalid, and
+ * the video source is not available.
+ */
struct video_out_desc {
/* video device support.
* videodevice and geometry are read from the config file.
@@ -106,65 +121,85 @@
int fps;
int bitrate;
- /* this is generated here */
- int fd; /* file descriptor */
+ struct fbuf_t buf; /* frame buffer */
int pix_fmt; /* source pixel format */
- struct fbuf_t buf;
AVCodecContext *context; /* encoding context */
AVCodec *codec;
AVFrame *frame;
int lasttxframe; /* XXX - useless: RTP overwrite seqno */
struct fbuf_t encbuf; /* encoding buffer */
int mtu;
-
- struct timeval last_frame;
- Display *dpy;
+ struct timeval last_frame; /* when we read the last frame ? */
+
+ /* device specific info */
+ int fd; /* file descriptor, for webcam */
+ Display *dpy; /* x11 grabber info */
XImage *image;
};
+/*
+ * 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: context == NULL means the rest is invalid (e.g. because no
+ * codec, no memory, etc.) and we must drop all incoming frames.
+ */
struct video_in_desc {
AVCodecContext *context; /* information about the codec in the stream */
AVCodec *codec; /* reference to the codec */
AVFrame *frame; /* place to store the frame */
AVCodecParserContext *parser;
int completed; /* probably unnecessary */
- struct fbuf_t buf;
uint16_t next_seq; /* must be 16 bit */
int discard;
struct timeval ts;
int received;
+ struct fbuf_t buf; /* decoded frame */
};
+/*
+ * The overall descriptor, with room for config info, video source and
+ * received data descriptors, SDL info, etc.
+ */
struct video_desc {
- ast_mutex_t lock;
-
- pthread_t vthread;
- int shutdown; /* set to ask the thread to shutdown */
- struct ast_channel *owner; /* owner channel */
-
int w;
int h;
int fps;
int bitrate;
char videodevice[64];
+ ast_mutex_t lock;
+
+ pthread_t vthread;
+ int shutdown; /* set to ask the thread to shutdown */
+ struct ast_channel *owner; /* owner channel */
+
struct video_in_desc in;
struct video_out_desc out;
SDL_Surface *screen;
- int initialized;
+ int sdl_ok;
SDL_Overlay *bmp[2];
};
+/*
+ * 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_RDWD = 3 } rw; /* read or write or both ? */
};
-/*! the list of video formats we support */
+/*!
+ * The list of video formats we support. You can OR the formats, and
+ * provide corresponding ffmpeg mappings in the table below.
+ */
#define CONSOLE_FORMAT_VIDEO AST_FORMAT_H263_PLUS
static struct _cm video_formats[] = {
@@ -173,11 +208,13 @@
{ 0, 0, 0 },
};
+/* Video4Linux stuff is only used in video_open() */
#if defined(HAVE_V4L) && HAVE_V4L > 0
#include <linux/videodev.h>
#endif
-/* open the local video source and allocate a buffer
+/*!
+ * 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)
@@ -186,6 +223,7 @@
if (v->buf.data) /* buffer allocated means device already open */
return v->fd;
+ v->fd = -1;
/*
* if the device is "X11", then open the x11 grabber
*/
@@ -195,7 +233,10 @@
XImage *im;
v->dpy = XOpenDisplay(NULL);
- ast_log(LOG_WARNING, "XOpenDisplay %pd\n", v->dpy);
+ if (v->dpy == NULL) {
+ ast_log(LOG_WARNING, "error opening display\n");
+ goto error;
+ }
v->image = im = XGetImage(v->dpy,
RootWindow(v->dpy, DefaultScreen(v->dpy)),
x_ofs, y_ofs, v->w, v->h, AllPlanes, ZPixmap);
@@ -210,7 +251,7 @@
v->pix_fmt = PIX_FMT_RGB565;
v->fd = -2;
}
-#if defined(HAVE_V4L) && HAVE_V4L != 0
+#if defined(HAVE_V4L) && HAVE_V4L > 0
else {
struct video_window vw = { 0 }; /* camera attributes */
struct video_picture vp;
@@ -248,6 +289,8 @@
}
}
#endif /* HAVE_V4L */
+ if (v->image == NULL && v->fd < 0)
+ goto error;
v->buf.size = (v->w * v->h * 3)/2; /* yuv411 */
ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n", v->device, v->w, v->h, v->buf.size);
v->buf.data = ast_calloc(1, v->buf.size);
@@ -257,9 +300,11 @@
}
v->buf.used = 0;
- return (v->image || v->fd >= 0) ? 0 : -1 ;
+ return 0;
error:
+ ast_log(LOG_WARNING, "fd %d dpy %p img %p data %p\n",
+ v->fd, v->dpy, v->image, v->buf.data);
if (v->dpy)
XCloseDisplay(v->dpy);
v->dpy = NULL;
@@ -274,13 +319,12 @@
}
/*! \brief complete a buffer from the local video source.
- * called by get_video_frame(), in turn called by asterisk
+ * called by get_video_frames(), in turn called by asterisk
* as part of the read callback
*/
static int video_read(struct video_out_desc *v)
{
struct timeval now = ast_tvnow();
- // fprintf(stderr, "video_read %p buf %p image %p\n", v, v->buf.data, v->image);
if (v->buf.data == NULL) /* not initialized */
return 0;
@@ -347,7 +391,6 @@
* to display the frame.
*/
-static struct video_desc *get_video_desc(struct ast_channel *c);
/*! \brief map an asterisk format into an ffmpeg one */
static enum CodecID map_video_format(uint32_t ast_format, int rw)
@@ -391,7 +434,6 @@
v->ts = ast_tvnow();
codec = map_video_format(format, CM_RD);
- ast_log(LOG_WARNING, "init for format 0x%x gives %d\n", format, codec);
v->codec = avcodec_find_decoder(codec);
if (!v->codec) {
@@ -433,23 +475,19 @@
if (v->frame)
av_free(v->frame);
-
if (v->buf.data)
free(v->buf.data);
-
if (v->image) { /* X11 grabber */
XCloseDisplay(v->dpy);
v->dpy = NULL;
v->image = NULL;
}
- if(v->fd >= 0)
+ if (v->fd >= 0)
close(v->fd);
-
- if(v->encbuf.data)
+ if (v->encbuf.data)
free(v->encbuf.data);
-
+ bzero(v, sizeof(*v));
v->fd = -1;
-
return -1;
}
@@ -499,7 +537,7 @@
v->context->bit_rate = v->bitrate;
v->context->gop_size = (int) v->fps*5; // emit I frame every 5 seconds
- fprintf(stderr, "w: %d...h: %d...fps: %d...", v->w, v->h, v->fps);
+ ast_log(LOG_WARNING, "w: %d h: %d fps: %d\n", v->w, v->h, v->fps);
v->context->time_base = (AVRational){1, v->fps};
if (avcodec_open(v->context, v->codec) < 0) {
ast_log(LOG_WARNING, "Unable to initialize the encoder parser\n");
@@ -547,18 +585,13 @@
ast_log(LOG_WARNING, "thread terminated\n");
video_in_uninit(&env->in);
video_out_uninit(&env->out);
- if (env->bmp[0])
+ if (env->sdl_ok) {
+ if (env->bmp[0])
SDL_FreeYUVOverlay(env->bmp[0]);
- if (env->bmp[1])
+ if (env->bmp[1])
SDL_FreeYUVOverlay(env->bmp[1]);
- SDL_Quit();
-
- /*if (env->out.buf.data) {
- free(env->out.buf.data);
- env->out.buf.data = NULL;
- if (env->out.fd >= 0)
- close(env->out.fd);
- }*/
+ SDL_Quit();
+ }
{ /* clear the struct but restore some fields */
struct video_desc x = *env; /* make a copy */
bzero(env, sizeof(struct video_desc));
@@ -695,11 +728,7 @@
char *src = NULL; /* pixel input */
int pix_fmt;
-
- if (out == 2)
- ast_log(LOG_WARNING, "out %d initialized %d\n", out, env->initialized);
-
- if (!env->initialized)
+ if (!env->sdl_ok)
return;
if (out) { /* webcam/x11 to sdl */
@@ -715,13 +744,7 @@
in_w = c->width;
in_h = c->height;
}
-#if 0
- fprintf(stderr, "thd %p env->out %dx%d in.ctx %dx%d default %dx%d\n",
- pthread_self(),
- env->out.w, env->out.h, c->width, c->height, env->w, env->h);
-#endif
-
- bmp = env->bmp[out ? 1 : 0];
+ bmp = env->bmp[out];
SDL_LockYUVOverlay(bmp);
pict.data[0] = bmp->pixels[0];
pict.data[1] = bmp->pixels[1];
@@ -741,7 +764,7 @@
}
} else { /* decode */
#ifdef OLD_FFMPEG /* XXX img_convert is deprecated */
- /* env->initialized guarantees that in.frame exists */
+ /* env->sdl_ok guarantees that in.frame exists */
img_convert(&pict, PIX_FMT_YUV420P, pict_in, pix_fmt, in_w, in_h);
#else /* XXX replacement */
struct SwsContext *convert_ctx;
@@ -767,14 +790,12 @@
rect.y = 0;
rect.w = w;
rect.h = h;
- if (out > 1)
- ast_log(LOG_WARNING, "about to display\n");
SDL_DisplayYUVOverlay(bmp, &rect);
- if (out > 1)
- ast_log(LOG_WARNING, "SDL_DisplayYUVOverlay done\n");
SDL_UnlockYUVOverlay(bmp);
}
+
+static struct video_desc *get_video_desc(struct ast_channel *c);
/*
* This function is called (by asterisk) for each video packet
@@ -789,20 +810,20 @@
static int console_write_video(struct ast_channel *chan, struct ast_frame *f)
{
uint8_t *data;
- int len;
+ int len, need;
struct video_desc *env = get_video_desc(chan);
struct video_in_desc *v = &env->in;
- if(!env->initialized) {
- ast_log(LOG_WARNING, "here env should be already initialized\n");
- return -1; /* error */
+ if (v->context == NULL) {
+ ast_log(LOG_WARNING, "cannot decode, dropping frame\n");
+ return 0; /* error */
}
v->received++;
#if defined(DROP_PACKETS) && DROP_PACKETS > 0
/*
- * Fragment of code to simulate lost/delayed packets
- */
+ * Fragment of code to simulate lost/delayed packets
+ */
if((random() % 10000) <= 100*DROP_PACKETS) {
ast_log(LOG_NOTICE, "Packet lost [%d]\n", f->seqno);
return 0;
@@ -852,15 +873,16 @@
return 0;
}
- /* allocate buffer as we see fit. ffmpeg wants an extra FF_INPUT_BUFFER_PADDING_SIZE
- * and a 0 as a buffer terminator to prevent trouble.
+ /* Allocate buffer. ffmpeg wants an extra FF_INPUT_BUFFER_PADDING_SIZE,
+ * and also wants 0 as a buffer terminator to prevent trouble.
*/
+ need = len + FF_INPUT_BUFFER_PADDING_SIZE;
if (v->buf.data == NULL) {
- v->buf.size = len + FF_INPUT_BUFFER_PADDING_SIZE;
+ v->buf.size = need;
v->buf.used = 0;
- v->buf.data = ast_malloc(len);
- } else if (v->buf.used + len + FF_INPUT_BUFFER_PADDING_SIZE> v->buf.size) {
- v->buf.size = v->buf.used + len + FF_INPUT_BUFFER_PADDING_SIZE;
+ v->buf.data = ast_malloc(v->buf.size);
+ } else if (v->buf.used + need > v->buf.size) {
+ v->buf.size = v->buf.used + need;
v->buf.data = ast_realloc(v->buf.data, v->buf.size);
}
if (v->buf.data == NULL) {
@@ -872,7 +894,6 @@
memcpy(v->buf.data + v->buf.used, data, len);
v->buf.used += len;
v->buf.data[v->buf.used] = '\0';
- v->buf.data[v->buf.used+1] = '\0'; /* just in case... */
if (f->subclass & 0x01) { // RTP Marker
if (decode_video(v)) {
show_frame(env, 0);
@@ -978,14 +999,20 @@
struct video_out_desc *v = &env->out;
struct fbuf_t *b = &v->encbuf;
+ if (!v->buf.data) {
+ ast_log(LOG_WARNING, "fail, no buffer\n");
+ return NULL;
+ }
+
if (!video_read(v))
- return NULL;
- // fprintf(stderr, "video read\n");
+ return NULL; /* can happen, e.g. we are reading too early */
/* get frame and put them in the queue */
show_frame(env, 1);
- if (b->data == NULL)
+ if (b->data == NULL) {
+ ast_log(LOG_WARNING, "fail, no encbuf\n");
return NULL;
+ }
buflen = avcodec_encode_video(v->context, b->data, b->size, v->frame);
return split_frame(v, buflen);
}
@@ -1005,10 +1032,9 @@
struct ast_frame *f;
if (env->shutdown) {
- fprintf(stderr, "video_thread shutting down%d\n", i);
+ ast_log(LOG_WARNING, "video_thread shutting down%d\n", i);
break;
}
- // fprintf(stderr, "video_thread %d owner %p\n", i, env->owner);
ast_select(0, NULL, NULL, NULL, &t);
if (0) {
f = get_video_frames(env);
@@ -1020,11 +1046,11 @@
}
/*!
- * The first call to the video code, called by the *_new()
- * call (in turn, called by .answer or .dial) when the channel
- * is created. Here we do the following:
- * - make sure that avcodec is properly initialized;
- * - try to open the local video source
+ * The first call to the video code, called by oss_new() or similar.
+ * Here we initialize the various components we use, namely SDL for display,
+ * ffmpeg for encoding/decoding, and a local video source.
+ * We do our best to progress even if some of the components are not
+ * available.
*/
static void console_video_start(struct video_desc *env,
struct ast_channel *owner)
@@ -1033,6 +1059,10 @@
if (owner == NULL) /* nothing to do if we don't have a channel */
return;
+ env->owner = owner;
+ bzero(&env->in, sizeof(env->in));
+ bzero(&env->out, sizeof(env->out));
+
/*
* Register all codecs supported by the ffmpeg library.
* Doing it once is enough.
@@ -1042,7 +1072,9 @@
avcodec_init();
avcodec_register_all();
if (video_in_init(&env->in, CONSOLE_FORMAT_VIDEO)) {
- fprintf(stderr, "Could not initialize input decoder - %s\n", SDL_GetError());
+ /* This is not fatal, but we won't have incoming video */
+ ast_log(LOG_WARNING, "Cannot initialize input decoder - %s\n",
+ SDL_GetError());
}
/*
@@ -1054,39 +1086,40 @@
env->bmp[0] = env->bmp[1] = NULL;
if (SDL_Init(SDL_INIT_VIDEO)) {
- fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
- return;
+ ast_log(LOG_WARNING, "Could not initialize SDL - %s\n",
+ SDL_GetError());
+ /* again not fatal, just we won't display anything */
+ goto no_sdl;
}
env->screen = SDL_SetVideoMode(2 * env->w, env->h, 0, 0);
if (!env->screen) {
ast_log(LOG_ERROR, "SDL: could not set video mode - exiting\n");
- return;
+ goto no_sdl;
}
SDL_WM_SetCaption("Asterisk console Video Output", NULL);
-
env->bmp[0] = SDL_CreateYUVOverlay(env->w, env->h, fmt, env->screen);
env->bmp[1] = SDL_CreateYUVOverlay(env->w, env->h, fmt, env->screen);
-
- /* create windows ? */
- env->owner = owner;
- env->initialized = 1;
-
+ if (env->bmp[0] && env->bmp[1])
+ env->sdl_ok = 1;
+ /* otherwise should release the screen */
+
+no_sdl:
/*
* now handle the local video source
*/
- ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
- /* copy config from parent */
+ ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
+ /* copy video source config from parent */
env->out.device = env->videodevice;
env->out.w = env->w;
env->out.h = env->h;
if (env->fps == 0) {
- ast_log(LOG_WARNING, "fps unset, forcing to 5\n");
- env->fps = 5;
+ env->fps = 15;
+ ast_log(LOG_WARNING, "fps unset, forcing to %d\n", env->fps);
}
env->out.fps = env->fps;
if (env->bitrate == 0) {
- ast_log(LOG_WARNING, "bitrate unset, forcing to 65000\n");
env->bitrate = 65000;
+ ast_log(LOG_WARNING, "bitrate unset, forcing to %d\n", env->bitrate);
}
env->out.bitrate = env->bitrate;
More information about the svn-commits
mailing list