summaryrefslogtreecommitdiff
path: root/modules/avcodec/decode.c
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
committerAlfred E. Heggestad <aeh@db.org>2014-02-09 11:50:07 +0100
commit98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch)
treeebc6ec71f44bff8c42e4eefced61948623df02fc /modules/avcodec/decode.c
parente6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff)
baresip 0.4.10
Diffstat (limited to 'modules/avcodec/decode.c')
-rw-r--r--modules/avcodec/decode.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/modules/avcodec/decode.c b/modules/avcodec/decode.c
new file mode 100644
index 0000000..36550a7
--- /dev/null
+++ b/modules/avcodec/decode.c
@@ -0,0 +1,346 @@
+/**
+ * @file avcodec/decode.c Video codecs using FFmpeg libavcodec -- decoder
+ *
+ * Copyright (C) 2010 - 2013 Creytiv.com
+ */
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/mem.h>
+#include "h26x.h"
+#include "avcodec.h"
+
+
+struct viddec_state {
+ AVCodec *codec;
+ AVCodecContext *ctx;
+ AVFrame *pict;
+ struct mbuf *mb;
+ bool got_keyframe;
+};
+
+
+static void destructor(void *arg)
+{
+ struct viddec_state *st = arg;
+
+ mem_deref(st->mb);
+
+ if (st->ctx) {
+ if (st->ctx->codec)
+ avcodec_close(st->ctx);
+ av_free(st->ctx);
+ }
+
+ if (st->pict)
+ av_free(st->pict);
+}
+
+
+static int init_decoder(struct viddec_state *st, const char *name)
+{
+ enum CodecID codec_id;
+
+ codec_id = avcodec_resolve_codecid(name);
+ if (codec_id == CODEC_ID_NONE)
+ return EINVAL;
+
+ st->codec = avcodec_find_decoder(codec_id);
+ if (!st->codec)
+ return ENOENT;
+
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(92<<8)+0)
+ st->ctx = avcodec_alloc_context3(st->codec);
+#else
+ st->ctx = avcodec_alloc_context();
+#endif
+
+#if LIBAVUTIL_VERSION_INT >= ((52<<16)+(20<<8)+100)
+ st->pict = av_frame_alloc();
+#else
+ st->pict = avcodec_alloc_frame();
+#endif
+
+ if (!st->ctx || !st->pict)
+ return ENOMEM;
+
+#if LIBAVCODEC_VERSION_INT >= ((53<<16)+(8<<8)+0)
+ if (avcodec_open2(st->ctx, st->codec, NULL) < 0)
+ return ENOENT;
+#else
+ if (avcodec_open(st->ctx, st->codec) < 0)
+ return ENOENT;
+#endif
+
+ return 0;
+}
+
+
+int decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp)
+{
+ struct viddec_state *st;
+ int err = 0;
+
+ if (!vdsp || !vc)
+ return EINVAL;
+
+ if (*vdsp)
+ return 0;
+
+ (void)fmtp;
+
+ st = mem_zalloc(sizeof(*st), destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->mb = mbuf_alloc(1024);
+ if (!st->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = init_decoder(st, vc->name);
+ if (err) {
+ warning("avcodec: %s: could not init decoder\n", vc->name);
+ goto out;
+ }
+
+ debug("avcodec: video decoder %s (%s)\n", vc->name, fmtp);
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *vdsp = st;
+
+ return err;
+}
+
+
+/*
+ * TODO: check input/output size
+ */
+static int ffdecode(struct viddec_state *st, struct vidframe *frame,
+ bool eof, struct mbuf *src)
+{
+ int i, got_picture, ret, err;
+
+ /* assemble packets in "mbuf" */
+ err = mbuf_write_mem(st->mb, mbuf_buf(src), mbuf_get_left(src));
+ if (err)
+ return err;
+
+ if (!eof)
+ return 0;
+
+ st->mb->pos = 0;
+
+ if (!st->got_keyframe) {
+ err = EPROTO;
+ goto out;
+ }
+
+#if LIBAVCODEC_VERSION_INT <= ((52<<16)+(23<<8)+0)
+ ret = avcodec_decode_video(st->ctx, st->pict, &got_picture,
+ st->mb->buf,
+ (int)mbuf_get_left(st->mb));
+#else
+ do {
+ AVPacket avpkt;
+
+ av_init_packet(&avpkt);
+ avpkt.data = st->mb->buf;
+ avpkt.size = (int)mbuf_get_left(st->mb);
+
+ ret = avcodec_decode_video2(st->ctx, st->pict,
+ &got_picture, &avpkt);
+ } while (0);
+#endif
+
+ if (ret < 0) {
+ err = EBADMSG;
+ goto out;
+ }
+
+ mbuf_skip_to_end(src);
+
+ if (got_picture) {
+ for (i=0; i<4; i++) {
+ frame->data[i] = st->pict->data[i];
+ frame->linesize[i] = st->pict->linesize[i];
+ }
+ frame->size.w = st->ctx->width;
+ frame->size.h = st->ctx->height;
+ frame->fmt = VID_FMT_YUV420P;
+ }
+
+ out:
+ if (eof)
+ mbuf_rewind(st->mb);
+
+ return err;
+}
+
+
+int h264_decode(struct viddec_state *st, struct mbuf *src)
+{
+ struct h264_hdr h264_hdr;
+ const uint8_t nal_seq[3] = {0, 0, 1};
+ int err;
+
+ err = h264_hdr_decode(&h264_hdr, src);
+ if (err)
+ return err;
+
+ if (h264_hdr.f) {
+ info("avcodec: H264 forbidden bit set!\n");
+ return EBADMSG;
+ }
+
+ /* handle NAL types */
+ if (1 <= h264_hdr.type && h264_hdr.type <= 23) {
+
+ if (!st->got_keyframe) {
+ switch (h264_hdr.type) {
+
+ case H264_NAL_PPS:
+ case H264_NAL_SPS:
+ st->got_keyframe = true;
+ break;
+ }
+ }
+
+ /* prepend H.264 NAL start sequence */
+ mbuf_write_mem(st->mb, nal_seq, 3);
+
+ /* encode NAL header back to buffer */
+ err = h264_hdr_encode(&h264_hdr, st->mb);
+ }
+ else if (H264_NAL_FU_A == h264_hdr.type) {
+ struct fu fu;
+
+ err = fu_hdr_decode(&fu, src);
+ if (err)
+ return err;
+ h264_hdr.type = fu.type;
+
+ if (fu.s) {
+ /* prepend H.264 NAL start sequence */
+ mbuf_write_mem(st->mb, nal_seq, 3);
+
+ /* encode NAL header back to buffer */
+ err = h264_hdr_encode(&h264_hdr, st->mb);
+ }
+ }
+ else {
+ warning("avcodec: unknown NAL type %u\n", h264_hdr.type);
+ return EBADMSG;
+ }
+
+ return err;
+}
+
+
+int decode_h264(struct viddec_state *st, struct vidframe *frame,
+ bool eof, uint16_t seq, struct mbuf *src)
+{
+ int err;
+
+ (void)seq;
+
+ if (!src)
+ return 0;
+
+ err = h264_decode(st, src);
+ if (err)
+ return err;
+
+ return ffdecode(st, frame, eof, src);
+}
+
+
+int decode_mpeg4(struct viddec_state *st, struct vidframe *frame,
+ bool eof, uint16_t seq, struct mbuf *src)
+{
+ if (!src)
+ return 0;
+
+ (void)seq;
+
+ /* let the decoder handle this */
+ st->got_keyframe = true;
+
+ return ffdecode(st, frame, eof, src);
+}
+
+
+int decode_h263(struct viddec_state *st, struct vidframe *frame,
+ bool marker, uint16_t seq, struct mbuf *src)
+{
+ struct h263_hdr hdr;
+ int err;
+
+ if (!st || !frame)
+ return EINVAL;
+
+ if (!src)
+ return 0;
+
+ (void)seq;
+
+ err = h263_hdr_decode(&hdr, src);
+ if (err)
+ return err;
+
+#if 0
+ debug(".....[%s seq=%5u ] MODE %s -"
+ " SBIT=%u EBIT=%u I=%s"
+ " (%5u/%5u bytes)\n",
+ marker ? "M" : " ", seq,
+ h263_hdr_mode(&hdr) == H263_MODE_A ? "A" : "B",
+ hdr.sbit, hdr.ebit, hdr.i ? "Inter" : "Intra",
+ mbuf_get_left(src), st->mb->end);
+#endif
+
+ if (!hdr.i)
+ st->got_keyframe = true;
+
+#if 0
+ if (st->mb->pos == 0) {
+ uint8_t *p = mbuf_buf(src);
+
+ if (p[0] != 0x00 || p[1] != 0x00) {
+ warning("invalid PSC detected (%02x %02x)\n",
+ p[0], p[1]);
+ return EPROTO;
+ }
+ }
+#endif
+
+ /*
+ * The H.263 Bit-stream can be fragmented on bit-level,
+ * indicated by SBIT and EBIT. Example:
+ *
+ * 8 bit 2 bit
+ * .--------.--.
+ * Packet 1 | | |
+ * SBIT=0 '--------'--'
+ * EBIT=6
+ * .------.--------.--------.
+ * Packet 2 | | | |
+ * SBIT=2 '------'--------'--------'
+ * EBIT=0 6bit 8bit 8bit
+ *
+ */
+
+ if (hdr.sbit > 0) {
+ const uint8_t mask = (1 << (8 - hdr.sbit)) - 1;
+ const uint8_t sbyte = mbuf_read_u8(src) & mask;
+
+ st->mb->buf[st->mb->end - 1] |= sbyte;
+ }
+
+ return ffdecode(st, frame, marker, src);
+}