(closes issue #13557)
[asterisk/asterisk.git] / channels / vcodecs.c
index 197726e..ecb7a8c 100644 (file)
@@ -1,8 +1,33 @@
 /*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright 2007-2008, Sergio Fadda, Luigi Rizzo
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*
  * Video codecs support for console_video.c
  * $Revision$
  */
 
+#include "asterisk.h"
+#include "console_video.h"
+#include "asterisk/frame.h"
+#include "asterisk/utils.h"    /* ast_calloc() */
+
+struct video_out_desc;
+struct video_dec_desc;
+struct fbuf_t;
+
 /*
  * Each codec is defined by a number of callbacks
  */
@@ -13,7 +38,7 @@ typedef int (*encoder_init_f)(AVCodecContext *v);
 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,
+typedef struct ast_frame *(*encoder_encap_f)(struct fbuf_t *, int mtu,
                struct ast_frame **tail);
 
 /*! \brief inizialize the decoder */
@@ -25,7 +50,7 @@ typedef int (*decoder_init_f)(AVCodecContext *enc_ctx);
 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);
+typedef int (*decoder_decode_f)(struct video_dec_desc *v, struct fbuf_t *b);
 
 struct video_codec_desc {
        const char              *name;          /* format name */
@@ -38,6 +63,35 @@ struct video_codec_desc {
        decoder_decode_f        dec_run;
 };
 
+/*
+ * Descriptor for the incoming stream, with multiple buffers for the bitstream
+ * extracted from the RTP packets, RTP reassembly info, and a frame buffer
+ * for the decoded frame (buf).
+ * The descriptor is allocated as the first frame comes in.
+ *
+ * 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_lock.
+ * When the list is full dec_in_cur = NULL (we cannot store new data),
+ * when the list is empty dec_in_dpy = NULL (we cannot display frames).
+ */
+struct video_dec_desc {
+       struct video_codec_desc *d_callbacks;   /* decoder callbacks */
+       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 */
+       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) */
+};
+
 #ifdef debugging_only
 
 /* some debugging code to check the bitstream:
@@ -150,7 +204,7 @@ void dump_buf(struct fbuf_t *b)
                if ( x == 0) {  /* new line */
                        if (i != 0)
                                ast_log(LOG_WARNING, "%s\n", buf);
-                       bzero(buf, sizeof(buf));
+                       memset(buf, '\0', sizeof(buf));
                        sprintf(buf, "%04x: ", i);
                }
                sprintf(buf + 6 + x*3, "%02x ", b->data[i]);
@@ -161,6 +215,109 @@ void dump_buf(struct fbuf_t *b)
                ast_log(LOG_WARNING, "%s\n", buf);
 }
 #endif /* debugging_only */
+
+/*!
+ * 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.ptr = 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;
+}
+
+
+/*
+ * 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;
+}
+
 /*
  * Here starts the glue code for the various supported video codecs.
  * For each of them, we need to provide routines for initialization,
@@ -196,12 +353,12 @@ static int h263p_enc_init(AVCodecContext *enc_ctx)
  * 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,
+static struct ast_frame *h263p_encap(struct fbuf_t *b, int mtu,
        struct ast_frame **tail)
 {
        struct ast_frame *cur = NULL, *first = NULL;
-       uint8_t *d = out->enc_out.data;
-       int len = out->enc_out.used;
+       uint8_t *d = b->data;
+       int len = b->used;
        int l = len; /* size of the current fragment. If 0, must look for a psc */
 
        for (;len > 0; len -= l, d += l) {
@@ -218,10 +375,10 @@ static struct ast_frame *h263p_encap(struct video_out_desc *out,
                                }
                        }
                }
-               if (l > out->mtu || l > len) { /* psc not found, split */
-                       l = MIN(len, out->mtu);
+               if (l > mtu || l > len) { /* psc not found, split */
+                       l = MIN(len, mtu);
                }
-               if (l < 1 || l > out->mtu) {
+               if (l < 1 || l > mtu) {
                        ast_log(LOG_WARNING, "--- frame error l %d\n", l);
                        break;
                }
@@ -236,7 +393,7 @@ static struct ast_frame *h263p_encap(struct video_out_desc *out,
                if (!f)
                        break;
 
-               data = f->data;
+               data = f->data.ptr;
                if (h == 0) {   /* we start with a psc */
                        data[0] |= 0x04;        // set P == 1, and we are done
                } else {        /* no psc, create a header */
@@ -318,7 +475,7 @@ static int ffmpeg_encode(struct video_out_desc *v)
  * 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)
+static int ffmpeg_decode(struct video_dec_desc *v, struct fbuf_t *b)
 {
        uint8_t *src = b->data;
        int srclen = b->used;
@@ -347,7 +504,7 @@ static int ffmpeg_decode(struct video_in_desc *v, struct fbuf_t *b)
                }
        }
        if (srclen != 0)        /* update b with leftover data */
-               bcopy(src, b->data, srclen);
+               memmove(b->data, src, srclen);
        b->used = srclen;
        b->ebit = 0;
        return full_frame;
@@ -411,11 +568,11 @@ static int h263_enc_init(AVCodecContext *enc_ctx)
  * 
  * The assumption below is that we start with a PSC.
  */
-static struct ast_frame *h263_encap(struct video_out_desc *out,
+static struct ast_frame *h263_encap(struct fbuf_t *b, int mtu,
                struct ast_frame **tail)
 {
-       uint8_t *d = out->enc_out.data;
-       int start = 0, i, len = out->enc_out.used;
+       uint8_t *d = b->data;
+       int start = 0, i, len = b->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 */
@@ -425,7 +582,7 @@ static struct ast_frame *h263_encap(struct video_out_desc *out,
        if (len < H263_MIN_LEN) /* unreasonably small */
                return NULL;
 
-       bzero(h263_hdr, sizeof(h263_hdr));
+       memset(h263_hdr, '\0', 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
@@ -490,7 +647,7 @@ static struct ast_frame *h263_encap(struct video_out_desc *out,
 
                if (!f)
                        break;
-               bcopy(h, f->data, 4);   /* copy the h263 header */
+               memmove(f->data.ptr, h, 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
                 */
@@ -565,11 +722,11 @@ static int h261_enc_init(AVCodecContext *enc_ctx)
  * 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,
+static struct ast_frame *h261_encap(struct fbuf_t *b, int mtu,
                struct ast_frame **tail)
 {
-       uint8_t *d = out->enc_out.data;
-       int start = 0, i, len = out->enc_out.used;
+       uint8_t *d = b->data;
+       int start = 0, i, len = b->used;
        struct ast_frame *f, *cur = NULL, *first = NULL;
        const int pheader_len = 4;
        uint8_t h261_hdr[4];
@@ -580,7 +737,7 @@ static struct ast_frame *h261_encap(struct video_out_desc *out,
        if (len < H261_MIN_LEN) /* unreasonably small */
                return NULL;
 
-       bzero(h261_hdr, sizeof(h261_hdr));
+       memset(h261_hdr, '\0', 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
@@ -616,7 +773,7 @@ static struct ast_frame *h261_encap(struct video_out_desc *out,
                        /* 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 */
+                       if (i - start > mtu) /* too large, stop now */
                                break;
                        found_ebit = ebit;
                        found = i;
@@ -626,7 +783,7 @@ static struct ast_frame *h261_encap(struct video_out_desc *out,
                        i = len;
                        ebit = 0;       /* hopefully... should ask the bitstream ? */
                }
-               if (i - start > out->mtu && found) {
+               if (i - start > mtu && found) {
                        /* use the previous GBSC, hope is within the mtu */
                        i = found;
                        ebit = found_ebit;
@@ -644,7 +801,7 @@ static struct ast_frame *h261_encap(struct video_out_desc *out,
                        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 */
+               memmove(f->data.ptr, h, 4);     /* copy the h261 header */
                if (ebit)       /* not aligned, restart from previous byte */
                        i--;
                sbit = (8 - ebit) & 7;
@@ -707,17 +864,17 @@ static int mpeg4_enc_init(AVCodecContext *enc_ctx)
 }
 
 /* simplistic encapsulation - just split frames in mtu-size units */
-static struct ast_frame *mpeg4_encap(struct  video_out_desc *out,
+static struct ast_frame *mpeg4_encap(struct fbuf_t *b, int mtu,
        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;
+       uint8_t *d = b->data;
+       uint8_t *end = d + b->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);
+               len = MIN(mtu, end - d);
+               f = create_video_frame(d, d + len, AST_FORMAT_MP4_VIDEO, 0, cur);
                if (!f)
                        break;
                if (!first)
@@ -734,7 +891,7 @@ 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)
+static int mpeg4_decode(struct video_dec_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,
@@ -745,7 +902,7 @@ static int mpeg4_decode(struct video_in_desc *v, struct fbuf_t *b)
        }
        datalen -= ret;
        if (datalen > 0)        /* update b with leftover bytes */
-               bcopy(b->data + ret, b->data, datalen);
+               memmove(b->data, b->data + ret, datalen);
        b->used = datalen;
        b->ebit = 0;
        return full_frame;
@@ -795,12 +952,12 @@ static int h264_dec_init(AVCodecContext *dec_ctx)
  * 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,
+static struct ast_frame *h264_encap(struct fbuf_t *b, int mtu,
        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;
+       uint8_t *d, *start = b->data;
+       uint8_t *end = start + b->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.
@@ -832,13 +989,13 @@ static struct ast_frame *h264_encap(struct video_out_desc *out,
                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);
+                       ty, d - (uint8_t *)b->data, b->used);
                continue;
        }
 
        size = d - start - 4;   /* don't count the end */
 
-       if (size < out->mtu) {  // test - don't fragment
+       if (size < mtu) {       // test - don't fragment
                // Single NAL Unit
                f = create_video_frame(start, d - 4, AST_FORMAT_H264, 0, cur);
                if (!f)
@@ -856,7 +1013,7 @@ static struct ast_frame *h264_encap(struct video_out_desc *out,
        size--;         /* skip the NAL header */
        while (size) {
                uint8_t *data;
-               int frag_size = MIN(size, out->mtu);
+               int frag_size = MIN(size, mtu);
 
                f = create_video_frame(start, start+frag_size, AST_FORMAT_H264, 2, cur);
                if (!f)
@@ -864,7 +1021,7 @@ static struct ast_frame *h264_encap(struct video_out_desc *out,
                size -= frag_size;      /* skip this data block */
                start += frag_size;
 
-               data = f->data;
+               data = f->data.ptr;
                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 */
@@ -953,7 +1110,7 @@ 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;
+       //struct video_codec_desc *codec_desc;
 };
 
 static struct _cm video_formats[] = {
@@ -1006,4 +1163,91 @@ static struct video_codec_desc *map_video_codec(int fmt)
        return NULL;
 }
 
+/*! \brief uninitialize the descriptor for remote video stream */
+static struct video_dec_desc *dec_uninit(struct video_dec_desc *v)
+{
+       int i;
+
+       if (v == NULL)          /* not initialized yet */
+               return NULL;
+       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->d_callbacks = 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);
+       ast_free(v);
+       return NULL;    /* error, in case someone cares */
+}
+
+/*
+ * initialize ffmpeg resources used for decoding frames from the network.
+ */
+static struct video_dec_desc *dec_init(uint32_t the_ast_format)
+{
+       enum CodecID codec;
+       struct video_dec_desc *v = ast_calloc(1, sizeof(*v));
+       if (v == NULL)
+               return NULL;
+
+       v->discard = 1;
+
+       v->d_callbacks = map_video_codec(the_ast_format);
+       if (v->d_callbacks == NULL) {
+               ast_log(LOG_WARNING, "cannot find video codec, drop input 0x%x\n", the_ast_format);
+               return dec_uninit(v);
+       }
+
+       codec = map_video_format(v->d_callbacks->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 dec_uninit(v);
+       }
+       /*
+        * Initialize the codec context.
+        */
+       v->dec_ctx = avcodec_alloc_context();
+       if (!v->dec_ctx) {
+               ast_log(LOG_WARNING, "Cannot allocate the decoder context\n");
+               return dec_uninit(v);
+       }
+       /* XXX call dec_init() ? */
+       if (avcodec_open(v->dec_ctx, v->codec) < 0) {
+               ast_log(LOG_WARNING, "Cannot open the decoder context\n");
+               av_free(v->dec_ctx);
+               v->dec_ctx = NULL;
+               return dec_uninit(v);
+       }
+
+       v->parser = av_parser_init(codec);
+       if (!v->parser) {
+               ast_log(LOG_WARNING, "Cannot initialize the decoder parser\n");
+               return dec_uninit(v);
+       }
+
+       v->d_frame = avcodec_alloc_frame();
+       if (!v->d_frame) {
+               ast_log(LOG_WARNING, "Cannot allocate decoding video frame\n");
+               return dec_uninit(v);
+       }
+        v->dec_in_cur = &v->dec_in[0]; /* buffer for incoming frames */
+        v->dec_in_dpy = NULL;      /* nothing to display */
+
+       return v;       /* ok */
+}
 /*------ end codec specific code -----*/