[asterisk-commits] rizzo: branch rizzo/video_v2 r84860 - /team/rizzo/video_v2/channels/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Oct 5 15:14:51 CDT 2007
Author: rizzo
Date: Fri Oct 5 15:14:51 2007
New Revision: 84860
URL: http://svn.digium.com/view/asterisk?view=rev&rev=84860
Log:
move h263p-specific code in a single block, and start adding
codec descriptors to make them configurable.
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=84860&r1=84859&r2=84860
==============================================================================
--- team/rizzo/video_v2/channels/console_video.c (original)
+++ team/rizzo/video_v2/channels/console_video.c Fri Oct 5 15:14:51 2007
@@ -230,6 +230,39 @@
};
/*
+ * 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 */
+/* XXX len is redundant */
+typedef struct ast_frame (*encoder_encap_f)(struct video_out_desc *out,
+ int len, 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 */
+typedef int (*decoder_decap_f)(struct video_in_desc *v);
+
+/*! \brief actually call the decoder */
+typedef int (*decoder_decode_f)(struct video_in_desc *v);
+
+struct video_codec_desc {
+ 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;
+};
+
+/*
* The overall descriptor, with room for config info, video source and
* received data descriptors, SDL info, etc.
*/
@@ -240,6 +273,8 @@
int bitrate;
char videodevice[64];
+ int rtp_fmt; /* compressed format, AST_FORMAT_* */
+
pthread_t vthread; /* video thread */
int shutdown; /* set to shutdown vthread */
struct ast_channel *owner; /* owner channel */
@@ -254,30 +289,11 @@
SDL_Rect rect[2]; /* loc. of images */
};
-/*
- * 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. 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[] = {
- { 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 },
- { 0, 0, 0 },
-};
static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p);
@@ -294,805 +310,40 @@
b->pix_fmt = x.pix_fmt;
}
-/* Video4Linux stuff is only used in video_open() */
-#if HAVE_V4L > 0
-#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->device, "X11")) {
- int x_ofs = 0;
- int y_ofs = 0;
- XImage *im;
-
- v->dpy = XOpenDisplay(NULL);
- 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, 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;
- }
-#if HAVE_V4L > 0
- else {
- /* V4L specific */
- struct video_window vw = { 0 }; /* camera attributes */
- struct video_picture vp;
- int i;
-
- v->fd = open(v->device, O_RDONLY | O_NONBLOCK);
- if (v->fd < 0) {
- ast_log(LOG_WARNING, "error opening camera %s\n", v->device);
- 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",
- v->device, 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",
- v->device, 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",
- v->device, 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_V4L */
-
- 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);
- if (v->dpy)
- XCloseDisplay(v->dpy);
- v->dpy = NULL;
- if (v->fd >= 0)
- close(v->fd);
- v->fd = -1;
- free_fbuf(&v->loc_src);
- return -1;
-}
-
-/*! \brief complete a buffer from the local video source.
- * Called by get_video_frames(), in turn called by the video thread.
- */
-static int video_read(struct video_out_desc *v)
-{
- struct timeval now = ast_tvnow();
- struct fbuf_t *b = &v->loc_src;
-
- if (b->data == NULL) /* not initialized */
- return 0;
-
- /* 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 */
-
- if (v->image) {
- /* read frame from X11 */
- AVPicture p;
- XGetSubImage(v->dpy,
- RootWindow(v->dpy, DefaultScreen(v->dpy)),
- 0, 0, 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;
- }
- 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;
- }
- }
-}
-
-/* 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 map an asterisk format into an ffmpeg one */
-static enum CodecID map_video_format(uint32_t ast_format, int rw)
-{
- 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;
-}
-
-/*! \brief uninitialize the descriptor for remote video stream */
-static int video_in_uninit(struct video_in_desc *v)
-{
- if (v->dec_ctx) {
- avcodec_close(v->dec_ctx);
- av_free(v->dec_ctx);
- }
- if (v->frame)
- av_free(v->frame);
- free_fbuf(&v->dec_in);
- free_fbuf(&v->dec_out);
- free_fbuf(&v->rem_dpy);
- bzero(v, sizeof(*v)); /* XXX too much, we are losing config info */
- 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;
-
- v->codec = NULL;
- v->dec_ctx = NULL;
- v->frame = NULL;
- v->parser = NULL;
- v->completed = 0;
- 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.
+/*----- codec specific code --------*/
+
+/*
+ * For each codec, we define the various callback in use
+ */
+
+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->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->frame = avcodec_alloc_frame();
- if (!v->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)
-{
- if (v->enc_ctx) {
- avcodec_close(v->enc_ctx);
- av_free(v->enc_ctx);
- }
-
- if (v->frame)
- av_free(v->frame);
- free_fbuf(&v->loc_src);
- free_fbuf(&v->enc_in);
- free_fbuf(&v->enc_out);
- free_fbuf(&v->loc_dpy);
- if (v->image) { /* X11 grabber */
- XCloseDisplay(v->dpy);
- v->dpy = NULL;
- v->image = NULL;
- }
- if (v->fd >= 0)
- close(v->fd);
- bzero(v, sizeof(*v));
- v->fd = -1;
- 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)
- * - mtu is used to determine the max size of video fragment
- */
-static int video_out_init(struct video_out_desc *v, uint32_t format)
-{
- int codec;
- int size;
- struct fbuf_t *enc_in;
-
- v->enc_ctx = NULL;
- v->codec = NULL;
- v->frame = NULL;
- v->lasttxframe = -1;
- 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(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);
- }
-
- v->mtu = 1400; /* set it early so the encoder can use it */
-
- /* allocate the input buffer for encoding.
- * ffmpeg only supports PIX_FMT_YUV420P for the encoding.
- */
- enc_in = &v->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);
- }
- v->frame = avcodec_alloc_frame();
- if (!v->frame) {
- ast_log(LOG_WARNING, "Unable to allocate the encoding video frame\n");
- return video_out_uninit(v);
- }
-
- /* Here we assume that the encoder has some 411 format */
- 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;
- /* 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_tolerance = v->enc_ctx->bit_rate/2;
-
- /* now set codec-specific parameters, which differ for
- * the various video codecs in use.
- * At the moment we only support h263p, but presumably we
- * need to deal with this in some external function.
- */
- if (0) { /* normal h263 */
- } else {
- /* 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->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->bit_rate = v->bitrate;
v->enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
v->enc_ctx->qmin = 3; /* should be configured */
-
- ast_log(LOG_WARNING, "w: %d h: %d fps: %d\n", v->w, v->h, v->fps);
- v->enc_ctx->time_base = (AVRational){1, v->fps};
- if (avcodec_open(v->enc_ctx, v->codec) < 0) {
- ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n",
- codec);
- av_free(v->enc_ctx);
- v->enc_ctx = NULL;
- return video_out_uninit(v);
- }
-
- /*
- * Allocate enough for the encoded bitstream. As we are compressing,
- * we hope that the output is never larger than the input size.
- */
- v->enc_out.data = ast_calloc(1, enc_in->size);
- v->enc_out.size = enc_in->size;
- v->enc_out.used = 0;
-
-
return 0;
}
-/*! \brief uninitialize the entire environment. */
-static void console_video_uninit(struct video_desc *env)
-{
- env->shutdown = 1;
- /*
- * XXX the locking here must be revised.
- * When asterisk calls shutdown, the channel is locked; however
- * the video thread might also need to acquire the lock on the channel
- * to enqueue the frames. So, we unlock the channel here to give
- * vthread a chance to exit the critical section and terminate.
- */
- ast_channel_unlock(env->owner);
- /* wait for video thread to finish */
- pthread_join(env->vthread, NULL);
- ast_channel_lock(env->owner);
-
- /* uninitialize the local and remote video environments */
- video_in_uninit(&env->in);
- video_out_uninit(&env->out);
-
- /* uninitialize the SDL environment */
- if (env->sdl_ok) {
- if (env->bmp[0])
- SDL_FreeYUVOverlay(env->bmp[0]);
- if (env->bmp[1])
- SDL_FreeYUVOverlay(env->bmp[1]);
- SDL_Quit();
- ast_mutex_destroy(&(env->sdl_lock));
- }
- { /* clear the struct but restore some fields */
- struct video_desc x = *env; /* make a copy */
- bzero(env, sizeof(struct video_desc));
- /* restore fields... */
- bcopy(x.videodevice, env->videodevice, sizeof(env->videodevice));
- env->w = x.w;
- env->h = x.h;
- env->fps = x.fps;
- env->bitrate = x.bitrate;
- }
-}
-
-#define MAKE_MASK(bits) ( (1<<(bits)) -1 )
-
-/*
- * Get the P flag from the H.263+ header from the RTP payload (see RFC 2429).
- */
-static inline unsigned int rfc2429_get_P(const uint8_t *header){
- return (header[0]>>2) & 0x1;
-}
-
-/*
- * Get the PLEN variable from the H.263+ header from the RTP payload (see RFC 2429).
- */
-static inline unsigned int rfc2429_get_PLEN(const uint8_t *header){
- unsigned short *p=(unsigned short*)header;
- return (ntohs(p[0])>>3) & MAKE_MASK(6);
-}
-
-/*! \brief extract the bitstreem from the RTP payload.
- * This is format dependent.
- * For h261, the format is defined in RFC 4587
- * and basically has a fixed 4-byte header as follows:
- * 3 bits SBIT start bit, how many msbits to ignore in first byte
- * 3 bits EBIT end bit, how many lsbits to ignore in last byte
- * 1 bit I 1 if frame only contain INTRA blocks
- * 1 bit V 0 if motion vector not used, 1 if may be used
- * 4 bits GOBN gob number at start of packet (0 for gob header)
- * 5 bits MBAP macroblock address predictor
- * 5 bits QUANT quantizer value
- * 5 bits HMVD horiz motion vector
- * 5 bits VMVD vert. motion vector
- *
- * 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.
- */
-static uint8_t *pre_process_data(uint8_t *data, int *len)
-{
- int PLEN;
- int P;
-
- if (*len < 2) {
- ast_log(LOG_WARNING, "invalid framesize %d\n", *len);
- *len = 0;
- return data;
- }
- PLEN = rfc2429_get_PLEN(data);
- P = rfc2429_get_P(data);
-
- if (PLEN > 0) {
- data += PLEN;
- (*len) -= PLEN;
- }
- if (P)
- data[0] = data[1] = 0;
- else {
- data += 2;
- (*len) -= 2;
- }
-
- return data;
-}
-
-/*
- * Decode a valid H.263 frame.
- * 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 decode_video(struct video_in_desc *v, struct fbuf_t *b)
-{
- uint8_t *src = b->data;
- int srclen = b->used;
-
- if (!srclen)
- return 0;
- while (srclen) {
- uint8_t *data;
- int datalen;
- int ret = av_parser_parse(v->parser, v->dec_ctx, &data, &datalen, src, srclen, 0, 0);
- if (datalen) {
- ret = avcodec_decode_video(v->dec_ctx, v->frame, &(v->completed), data, datalen);
- if (ret < 0) {
- ast_log(LOG_NOTICE, "Error decoding\n");
- return 0;
- }
- src += ret;
- srclen -= ret;
- }
- }
- return 1;
-}
-
-/*! fill an AVPicture from our fbuf info, as it is required by
- * the image conversion routines in ffmpeg.
- * XXX This depends on the format.
- */
-static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
-{
- /* provide defaults for commonly used formats */
- 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));
- switch (b->pix_fmt) {
- case PIX_FMT_RGB555:
- case PIX_FMT_RGB565:
- len *= 2;
- luv = 0;
- break;
- case PIX_FMT_RGBA32:
- len *= 4;
- luv = 0;
- break;
- }
- p->data[0] = b->data;
- p->linesize[0] = len;
- /* these are only valid for component images */
- p->data[1] = luv ? b->data + 4*l4 : b->data+len;
- p->data[2] = luv ? b->data + 5*l4 : b->data+len;
- p->linesize[1] = luv;
- p->linesize[2] = luv;
- return p;
-}
-
-/*! convert/scale between an input and an output format.
- * Old version of ffmpeg only have img_convert, which does not rescale.
- * New versions use sws_scale which does both.
- */
-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;
-
- if (p_in == NULL)
- p_in = fill_pict(in, &my_p_in);
- if (p_out == NULL)
- p_out = fill_pict(out, &my_p_out);
-
-#ifdef OLD_FFMPEG
- /* XXX img_convert is deprecated, and does not do rescaling */
- 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,
- SWS_BICUBIC, NULL, NULL, NULL);
- if (convert_ctx == NULL) {
- ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed");
- return;
- }
- 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);
- 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 received data, 1 for the local video, 2 on init (debug)
- */
-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) { /* 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.frame;
- }
- bmp = env->bmp[out];
- 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. */
- ast_mutex_lock(&env->sdl_lock);
- SDL_DisplayYUVOverlay(bmp, &env->rect[out]);
- ast_mutex_unlock(&env->sdl_lock);
- SDL_UnlockYUVOverlay(bmp);
-}
-
-static struct video_desc *get_video_desc(struct ast_channel *c);
-
-/*
- * This function is called (by asterisk) for each video packet
- * coming from the network (the 'in' path) that needs to be processed.
- * We need to reconstruct the entire video frame before we can decode it.
- * After a video packet is received we have to:
- * - extract the bitstream with pre_process_data()
- * - append the bitstream to a buffer
- * - if the fragment is the last (RTP Marker) we decode it with decode_video()
- * - after the decoding is completed we display the decoded frame with show_frame()
- */
-static int console_write_video(struct ast_channel *chan, struct ast_frame *f)
-{
- uint8_t *data;
- int len, need;
- struct video_desc *env = get_video_desc(chan);
- struct video_in_desc *v = &env->in;
-
- if (v->dec_ctx == NULL) {
- ast_log(LOG_WARNING, "cannot decode, dropping frame\n");
- return 0; /* error */
- }
-
-#if defined(DROP_PACKETS) && DROP_PACKETS > 0
- /*
- * 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;
- }
-#endif
- if (v->discard) {
- ast_log(LOG_WARNING, "discard mode, drop frame %d\n", f->seqno);
- /*
- * In discard mode, drop packets until we find one with
- * the RTP marker set (which is the end of frame).
- * XXX note that the RTP marker flag is sent as the LSB of the
- * subclass. This is slightly annoying as it goes to overwrite
- * the payload type entry.
- */
- if (f->subclass & 0x01) {
- v->dec_in.used = 0;
- v->next_seq = f->seqno + 1; /* wrap at 16 bit */
- v->discard = 0;
- ast_log(LOG_WARNING, "out of discard mode, frame %d\n", f->seqno);
- }
- return 0;
- }
-
- /*
- * Only in-order fragments will be accepted. Remember seqno
- * has 16 bit so there is wraparound. Also, ideally we could
- * accept a bit of reordering, but at the moment wr don't.
- */
- if (v->next_seq != f->seqno) {
- ast_log(LOG_WARNING, "discarding frame out of order, %d %d\n",
- v->next_seq, f->seqno);
- v->discard = 1;
- return 0;
- }
- v->next_seq++;
-
- if (f->data == NULL || f->datalen < 2) {
- ast_log(LOG_WARNING, "empty video frame, discard\n");
- return 0;
- }
- len = f->datalen;
- data = pre_process_data(f->data, &len);
- if (len < 1 || len > 128*1024) {
- ast_log(LOG_WARNING, "--- huge frame %d\n", len);
- v->discard = 1;
- return 0;
- }
-
- /*
- * 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->dec_in.data == NULL) {
- v->dec_in.size = need;
- v->dec_in.used = 0;
- v->dec_in.data = ast_malloc(v->dec_in.size);
- } else if (v->dec_in.used + need > v->dec_in.size) {
- v->dec_in.size = v->dec_in.used + need;
- v->dec_in.data = ast_realloc(v->dec_in.data, v->dec_in.size);
- }
- if (v->dec_in.data == NULL) {
- ast_log(LOG_WARNING, "alloc failure for %d, discard\n",
- v->dec_in.size);
- v->discard = 1;
- return 0;
- }
- memcpy(v->dec_in.data + v->dec_in.used, data, len);
- v->dec_in.used += len;
- v->dec_in.data[v->dec_in.used] = '\0';
- if (f->subclass & 0x01) { // RTP Marker
- if (decode_video(v, &v->dec_in)) {
- show_frame(env, 0);
- v->completed = 0;
- v->dec_in.used = 0;
- }
- }
- return 0;
-}
-
-
-#ifndef MIN
-#define MIN(a, b) (a) < (b) ? (a) : (b)
-#endif
/*
* Create RTP/H.263 fragmets to avoid IP fragmentation
*/
-static struct ast_frame *split_frame(struct video_out_desc *out, int len, struct ast_frame **tail)
+static struct ast_frame *h263p_encap(struct video_out_desc *out,
+ int len, struct ast_frame **tail)
{
struct ast_frame *cur = NULL, *first = NULL;
uint8_t *d = out->enc_out.data;
@@ -1173,6 +424,798 @@
return first;
}
+#define MAKE_MASK(bits) ( (1<<(bits)) -1 )
+
+/*
+ * Get the P flag from the H.263+ header from the RTP payload (see RFC 2429).
+ */
+static inline unsigned int rfc2429_get_P(const uint8_t *header){
+ return (header[0]>>2) & 0x1;
+}
+
+/*
+ * Get the PLEN variable from the H.263+ header from the RTP payload (see RFC 2429).
+ */
+static inline unsigned int rfc2429_get_PLEN(const uint8_t *header){
+ unsigned short *p=(unsigned short*)header;
+ return (ntohs(p[0])>>3) & MAKE_MASK(6);
+}
+
+/*! \brief extract the bitstreem from the RTP payload.
+ * This is format dependent.
+ * For h261, the format is defined in RFC 4587
+ * and basically has a fixed 4-byte header as follows:
+ * 3 bits SBIT start bit, how many msbits to ignore in first byte
+ * 3 bits EBIT end bit, how many lsbits to ignore in last byte
+ * 1 bit I 1 if frame only contain INTRA blocks
+ * 1 bit V 0 if motion vector not used, 1 if may be used
+ * 4 bits GOBN gob number at start of packet (0 for gob header)
+ * 5 bits MBAP macroblock address predictor
+ * 5 bits QUANT quantizer value
+ * 5 bits HMVD horiz motion vector
+ * 5 bits VMVD vert. motion vector
+ *
+ * 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.
+ */
+static uint8_t *h263p_decap(uint8_t *data, int *len)
+{
+ int PLEN;
+ int P;
+
+ if (*len < 2) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", *len);
+ *len = 0;
+ return data;
+ }
+ PLEN = rfc2429_get_PLEN(data);
+ P = rfc2429_get_P(data);
+
+ if (PLEN > 0) {
+ data += PLEN;
+ (*len) -= PLEN;
+ }
+ if (P)
+ data[0] = data[1] = 0;
+ else {
+ data += 2;
+ (*len) -= 2;
+ }
+
+ return data;
+}
+
+/*
+ * Decode a valid H.263 frame.
+ * 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 h263p_decode(struct video_in_desc *v, struct fbuf_t *b)
+{
+ uint8_t *src = b->data;
+ int srclen = b->used;
+
+ if (!srclen)
+ return 0;
+ while (srclen) {
+ uint8_t *data;
+ int datalen;
+ int ret = av_parser_parse(v->parser, v->dec_ctx, &data, &datalen, src, srclen, 0, 0);
+ if (datalen) {
+ ret = avcodec_decode_video(v->dec_ctx, v->frame, &(v->completed), data, datalen);
+ if (ret < 0) {
+ ast_log(LOG_NOTICE, "Error decoding\n");
+ return 0;
+ }
+ src += ret;
+ srclen -= ret;
+ }
+ }
+ return 1;
+}
+
+/*------ end codec specific code -----*/
+
+
+/* Video4Linux stuff is only used in video_open() */
+#if HAVE_V4L > 0
+#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->device, "X11")) {
+ int x_ofs = 0;
+ int y_ofs = 0;
+ XImage *im;
+
+ v->dpy = XOpenDisplay(NULL);
+ 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, 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;
+ }
+#if HAVE_V4L > 0
+ else {
+ /* V4L specific */
+ struct video_window vw = { 0 }; /* camera attributes */
+ struct video_picture vp;
+ int i;
+
+ v->fd = open(v->device, O_RDONLY | O_NONBLOCK);
+ if (v->fd < 0) {
+ ast_log(LOG_WARNING, "error opening camera %s\n", v->device);
+ 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",
+ v->device, 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",
+ v->device, 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",
+ v->device, 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_V4L */
+
+ 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);
+ if (v->dpy)
+ XCloseDisplay(v->dpy);
+ v->dpy = NULL;
+ if (v->fd >= 0)
+ close(v->fd);
+ v->fd = -1;
+ free_fbuf(&v->loc_src);
+ return -1;
+}
+
+/*! \brief complete a buffer from the local video source.
+ * Called by get_video_frames(), in turn called by the video thread.
+ */
+static int video_read(struct video_out_desc *v)
+{
+ struct timeval now = ast_tvnow();
+ struct fbuf_t *b = &v->loc_src;
+
+ if (b->data == NULL) /* not initialized */
+ return 0;
+
+ /* 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 */
+
+ if (v->image) {
+ /* read frame from X11 */
+ AVPicture p;
+ XGetSubImage(v->dpy,
+ RootWindow(v->dpy, DefaultScreen(v->dpy)),
+ 0, 0, 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;
+ }
+ 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;
+ }
+ }
+}
+
+/* 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.
+ */
+
+/*
+ * 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 ? */
+ 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 },
+ { 0, 0, 0 },
+};
+
+
+/*! \brief map an asterisk format into an ffmpeg one */
+static enum CodecID map_video_format(uint32_t ast_format, int rw)
+{
+ 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;
+}
+
+/*! \brief uninitialize the descriptor for remote video stream */
+static int video_in_uninit(struct video_in_desc *v)
+{
+ if (v->dec_ctx) {
+ avcodec_close(v->dec_ctx);
+ av_free(v->dec_ctx);
+ }
+ if (v->frame)
+ av_free(v->frame);
+ free_fbuf(&v->dec_in);
+ free_fbuf(&v->dec_out);
+ free_fbuf(&v->rem_dpy);
+ bzero(v, sizeof(*v)); /* XXX too much, we are losing config info */
+ 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;
+
+ v->codec = NULL;
+ v->dec_ctx = NULL;
+ v->frame = NULL;
+ v->parser = NULL;
+ v->completed = 0;
+ 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->frame = avcodec_alloc_frame();
+ if (!v->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)
+{
+ if (v->enc_ctx) {
+ avcodec_close(v->enc_ctx);
+ av_free(v->enc_ctx);
+ }
+
+ if (v->frame)
+ av_free(v->frame);
+ free_fbuf(&v->loc_src);
+ free_fbuf(&v->enc_in);
+ free_fbuf(&v->enc_out);
+ free_fbuf(&v->loc_dpy);
+ if (v->image) { /* X11 grabber */
+ XCloseDisplay(v->dpy);
+ v->dpy = NULL;
+ v->image = NULL;
+ }
+ if (v->fd >= 0)
+ close(v->fd);
+ bzero(v, sizeof(*v));
+ v->fd = -1;
+ 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)
+ * - mtu is used to determine the max size of video fragment
+ */
+static int video_out_init(struct video_out_desc *v, uint32_t format)
+{
+ int codec;
+ int size;
+ struct fbuf_t *enc_in;
+
+ v->enc_ctx = NULL;
+ v->codec = NULL;
+ v->frame = NULL;
+ v->lasttxframe = -1;
+ 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(format, CM_WR);
[... 364 lines stripped ...]
More information about the asterisk-commits
mailing list