summaryrefslogtreecommitdiff
path: root/modules/avcodec
diff options
context:
space:
mode:
Diffstat (limited to 'modules/avcodec')
-rw-r--r--modules/avcodec/avcodec.c176
-rw-r--r--modules/avcodec/avcodec.h62
-rw-r--r--modules/avcodec/decode.c346
-rw-r--r--modules/avcodec/encode.c646
-rw-r--r--modules/avcodec/h263.c176
-rw-r--r--modules/avcodec/h264.c188
-rw-r--r--modules/avcodec/h26x.h165
-rw-r--r--modules/avcodec/module.mk20
8 files changed, 1779 insertions, 0 deletions
diff --git a/modules/avcodec/avcodec.c b/modules/avcodec/avcodec.c
new file mode 100644
index 0000000..d6ce3de
--- /dev/null
+++ b/modules/avcodec/avcodec.c
@@ -0,0 +1,176 @@
+/**
+ * @file avcodec.c Video codecs using FFmpeg libavcodec
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#ifdef USE_X264
+#include <x264.h>
+#endif
+#include "h26x.h"
+#include "avcodec.h"
+
+
+int avcodec_resolve_codecid(const char *s)
+{
+ if (0 == str_casecmp(s, "H263"))
+ return CODEC_ID_H263;
+ else if (0 == str_casecmp(s, "H264"))
+ return CODEC_ID_H264;
+ else if (0 == str_casecmp(s, "MP4V-ES"))
+ return CODEC_ID_MPEG4;
+ else
+ return CODEC_ID_NONE;
+}
+
+
+static uint32_t packetization_mode(const char *fmtp)
+{
+ struct pl pl, mode;
+
+ if (!fmtp)
+ return 0;
+
+ pl_set_str(&pl, fmtp);
+
+ if (fmt_param_get(&pl, "packetization-mode", &mode))
+ return pl_u32(&mode);
+
+ return 0;
+}
+
+
+static int h264_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt,
+ bool offer, void *arg)
+{
+ struct vidcodec *vc = arg;
+ const uint8_t profile_idc = 0x42; /* baseline profile */
+ const uint8_t profile_iop = 0x80;
+ (void)offer;
+
+ if (!mb || !fmt || !vc)
+ return 0;
+
+ return mbuf_printf(mb, "a=fmtp:%s"
+ " packetization-mode=0"
+ ";profile-level-id=%02x%02x%02x"
+ "\r\n",
+ fmt->id, profile_idc, profile_iop, h264_level_idc);
+}
+
+
+static bool h264_fmtp_cmp(const char *fmtp1, const char *fmtp2, void *data)
+{
+ (void)data;
+
+ return packetization_mode(fmtp1) == packetization_mode(fmtp2);
+}
+
+
+static int h263_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt,
+ bool offer, void *arg)
+{
+ (void)offer;
+ (void)arg;
+
+ if (!mb || !fmt)
+ return 0;
+
+ return mbuf_printf(mb, "a=fmtp:%s CIF=1;CIF4=1\r\n", fmt->id);
+}
+
+
+static int mpg4_fmtp_enc(struct mbuf *mb, const struct sdp_format *fmt,
+ bool offer, void *arg)
+{
+ (void)offer;
+ (void)arg;
+
+ if (!mb || !fmt)
+ return 0;
+
+ return mbuf_printf(mb, "a=fmtp:%s profile-level-id=3\r\n", fmt->id);
+}
+
+
+static struct vidcodec h264 = {
+ .name = "H264",
+ .variant = "packetization-mode=0",
+ .encupdh = encode_update,
+#ifdef USE_X264
+ .ench = encode_x264,
+#else
+ .ench = encode,
+#endif
+ .decupdh = decode_update,
+ .dech = decode_h264,
+ .fmtp_ench = h264_fmtp_enc,
+ .fmtp_cmph = h264_fmtp_cmp,
+};
+
+static struct vidcodec h263 = {
+ .pt = "34",
+ .name = "H263",
+ .encupdh = encode_update,
+ .ench = encode,
+ .decupdh = decode_update,
+ .dech = decode_h263,
+ .fmtp_ench = h263_fmtp_enc,
+};
+
+static struct vidcodec mpg4 = {
+ .name = "MP4V-ES",
+ .encupdh = encode_update,
+ .ench = encode,
+ .decupdh = decode_update,
+ .dech = decode_mpeg4,
+ .fmtp_ench = mpg4_fmtp_enc,
+};
+
+
+static int module_init(void)
+{
+#ifdef USE_X264
+ debug("avcodec: x264 build %d\n", X264_BUILD);
+#else
+ debug("avcodec: using FFmpeg H.264 encoder\n");
+#endif
+
+#if LIBAVCODEC_VERSION_INT < ((53<<16)+(10<<8)+0)
+ avcodec_init();
+#endif
+
+ avcodec_register_all();
+
+ if (avcodec_find_decoder(CODEC_ID_H264))
+ vidcodec_register(&h264);
+
+ if (avcodec_find_decoder(CODEC_ID_H263))
+ vidcodec_register(&h263);
+
+ if (avcodec_find_decoder(CODEC_ID_MPEG4))
+ vidcodec_register(&mpg4);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ vidcodec_unregister(&mpg4);
+ vidcodec_unregister(&h263);
+ vidcodec_unregister(&h264);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(avcodec) = {
+ "avcodec",
+ "codec",
+ module_init,
+ module_close
+};
diff --git a/modules/avcodec/avcodec.h b/modules/avcodec/avcodec.h
new file mode 100644
index 0000000..bbd022a
--- /dev/null
+++ b/modules/avcodec/avcodec.h
@@ -0,0 +1,62 @@
+/**
+ * @file avcodec.h Video codecs using FFmpeg libavcodec -- internal API
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+#if LIBAVCODEC_VERSION_INT >= ((54<<16)+(25<<8)+0)
+#define CodecID AVCodecID
+#endif
+
+
+extern const uint8_t h264_level_idc;
+
+
+/*
+ * Encode
+ */
+
+struct videnc_state;
+
+int encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp);
+int encode(struct videnc_state *st, bool update, const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg);
+#ifdef USE_X264
+int encode_x264(struct videnc_state *st, bool update,
+ const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg);
+#endif
+
+
+/*
+ * Decode
+ */
+
+struct viddec_state;
+
+int decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp);
+int decode_h263(struct viddec_state *st, struct vidframe *frame,
+ bool eof, uint16_t seq, struct mbuf *src);
+int decode_h264(struct viddec_state *st, struct vidframe *frame,
+ bool eof, uint16_t seq, struct mbuf *src);
+int decode_mpeg4(struct viddec_state *st, struct vidframe *frame,
+ bool eof, uint16_t seq, struct mbuf *src);
+int decode_h263_test(struct viddec_state *st, struct vidframe *frame,
+ bool marker, uint16_t seq, struct mbuf *src);
+
+
+int decode_sdpparam_h264(struct videnc_state *st, const struct pl *name,
+ const struct pl *val);
+int h264_packetize(struct mbuf *mb, size_t pktsize,
+ videnc_packet_h *pkth, void *arg);
+int h264_decode(struct viddec_state *st, struct mbuf *src);
+int h264_nal_send(bool first, bool last,
+ bool marker, uint32_t ihdr, const uint8_t *buf,
+ size_t size, size_t maxsz,
+ videnc_packet_h *pkth, void *arg);
+
+
+int avcodec_resolve_codecid(const char *s);
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);
+}
diff --git a/modules/avcodec/encode.c b/modules/avcodec/encode.c
new file mode 100644
index 0000000..559c53e
--- /dev/null
+++ b/modules/avcodec/encode.c
@@ -0,0 +1,646 @@
+/**
+ * @file avcodec/encode.c Video codecs using FFmpeg libavcodec -- encoder
+ *
+ * Copyright (C) 2010 - 2013 Creytiv.com
+ */
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/mem.h>
+#ifdef USE_X264
+#include <x264.h>
+#endif
+#include "h26x.h"
+#include "avcodec.h"
+
+
+enum {
+ DEFAULT_GOP_SIZE = 10,
+};
+
+
+struct picsz {
+ enum h263_fmt fmt; /**< Picture size */
+ uint8_t mpi; /**< Minimum Picture Interval (1-32) */
+};
+
+
+struct videnc_state {
+ AVCodec *codec;
+ AVCodecContext *ctx;
+ AVFrame *pict;
+ struct mbuf *mb;
+ size_t sz_max; /* todo: figure out proper buffer size */
+ int64_t pts;
+ struct mbuf *mb_frag;
+ struct videnc_param encprm;
+ struct vidsz encsize;
+ enum CodecID codec_id;
+
+ union {
+ struct {
+ struct picsz picszv[8];
+ uint32_t picszn;
+ } h263;
+
+ struct {
+ uint32_t packetization_mode;
+ uint32_t profile_idc;
+ uint32_t profile_iop;
+ uint32_t level_idc;
+ uint32_t max_fs;
+ uint32_t max_smbps;
+ } h264;
+ } u;
+
+#ifdef USE_X264
+ x264_t *x264;
+#endif
+};
+
+
+static void destructor(void *arg)
+{
+ struct videnc_state *st = arg;
+
+ mem_deref(st->mb);
+ mem_deref(st->mb_frag);
+
+#ifdef USE_X264
+ if (st->x264)
+ x264_encoder_close(st->x264);
+#endif
+
+ if (st->ctx) {
+ if (st->ctx->codec)
+ avcodec_close(st->ctx);
+ av_free(st->ctx);
+ }
+
+ if (st->pict)
+ av_free(st->pict);
+}
+
+
+static enum h263_fmt h263_fmt(const struct pl *name)
+{
+ if (0 == pl_strcasecmp(name, "sqcif")) return H263_FMT_SQCIF;
+ if (0 == pl_strcasecmp(name, "qcif")) return H263_FMT_QCIF;
+ if (0 == pl_strcasecmp(name, "cif")) return H263_FMT_CIF;
+ if (0 == pl_strcasecmp(name, "cif4")) return H263_FMT_4CIF;
+ if (0 == pl_strcasecmp(name, "cif16")) return H263_FMT_16CIF;
+ return H263_FMT_OTHER;
+}
+
+
+static int decode_sdpparam_h263(struct videnc_state *st, const struct pl *name,
+ const struct pl *val)
+{
+ enum h263_fmt fmt = h263_fmt(name);
+ const int mpi = pl_u32(val);
+
+ if (fmt == H263_FMT_OTHER) {
+ info("h263: unknown param '%r'\n", name);
+ return 0;
+ }
+ if (mpi < 1 || mpi > 32) {
+ info("h263: %r: MPI out of range %d\n", name, mpi);
+ return 0;
+ }
+
+ if (st->u.h263.picszn >= ARRAY_SIZE(st->u.h263.picszv)) {
+ info("h263: picszv overflow: %r\n", name);
+ return 0;
+ }
+
+ st->u.h263.picszv[st->u.h263.picszn].fmt = fmt;
+ st->u.h263.picszv[st->u.h263.picszn].mpi = mpi;
+
+ ++st->u.h263.picszn;
+
+ return 0;
+}
+
+
+static int init_encoder(struct videnc_state *st)
+{
+ st->codec = avcodec_find_encoder(st->codec_id);
+ if (!st->codec)
+ return ENOENT;
+
+ return 0;
+}
+
+
+static int open_encoder(struct videnc_state *st,
+ const struct videnc_param *prm,
+ const struct vidsz *size)
+{
+ int err = 0;
+
+ if (st->ctx) {
+ if (st->ctx->codec)
+ avcodec_close(st->ctx);
+ av_free(st->ctx);
+ }
+
+ if (st->pict)
+ av_free(st->pict);
+
+#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) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ st->ctx->bit_rate = prm->bitrate;
+ st->ctx->width = size->w;
+ st->ctx->height = size->h;
+ st->ctx->gop_size = DEFAULT_GOP_SIZE;
+ st->ctx->pix_fmt = PIX_FMT_YUV420P;
+ st->ctx->time_base.num = 1;
+ st->ctx->time_base.den = prm->fps;
+
+ /* params to avoid ffmpeg/x264 default preset error */
+ if (st->codec_id == CODEC_ID_H264) {
+ st->ctx->me_method = ME_UMH;
+ st->ctx->me_range = 16;
+ st->ctx->qmin = 10;
+ st->ctx->qmax = 51;
+ st->ctx->max_qdiff = 4;
+ }
+
+#if LIBAVCODEC_VERSION_INT >= ((53<<16)+(8<<8)+0)
+ if (avcodec_open2(st->ctx, st->codec, NULL) < 0) {
+ err = ENOENT;
+ goto out;
+ }
+#else
+ if (avcodec_open(st->ctx, st->codec) < 0) {
+ err = ENOENT;
+ goto out;
+ }
+#endif
+
+ out:
+ if (err) {
+ if (st->ctx) {
+ if (st->ctx->codec)
+ avcodec_close(st->ctx);
+ av_free(st->ctx);
+ st->ctx = NULL;
+ }
+
+ if (st->pict) {
+ av_free(st->pict);
+ st->pict = NULL;
+ }
+ }
+ else
+ st->encsize = *size;
+
+ return err;
+}
+
+
+int decode_sdpparam_h264(struct videnc_state *st, const struct pl *name,
+ const struct pl *val)
+{
+ if (0 == pl_strcasecmp(name, "packetization-mode")) {
+ st->u.h264.packetization_mode = pl_u32(val);
+
+ if (st->u.h264.packetization_mode != 0) {
+ warning("avcodec: illegal packetization-mode %u\n",
+ st->u.h264.packetization_mode);
+ return EPROTO;
+ }
+ }
+ else if (0 == pl_strcasecmp(name, "profile-level-id")) {
+ struct pl prof = *val;
+ if (prof.l != 6) {
+ warning("avcodec: invalid profile-level-id (%r)\n",
+ val);
+ return EPROTO;
+ }
+
+ prof.l = 2;
+ st->u.h264.profile_idc = pl_x32(&prof); prof.p += 2;
+ st->u.h264.profile_iop = pl_x32(&prof); prof.p += 2;
+ st->u.h264.level_idc = pl_x32(&prof);
+ }
+ else if (0 == pl_strcasecmp(name, "max-fs")) {
+ st->u.h264.max_fs = pl_u32(val);
+ }
+ else if (0 == pl_strcasecmp(name, "max-smbps")) {
+ st->u.h264.max_smbps = pl_u32(val);
+ }
+
+ return 0;
+}
+
+
+static void param_handler(const struct pl *name, const struct pl *val,
+ void *arg)
+{
+ struct videnc_state *st = arg;
+
+ if (st->codec_id == CODEC_ID_H263)
+ (void)decode_sdpparam_h263(st, name, val);
+ else if (st->codec_id == CODEC_ID_H264)
+ (void)decode_sdpparam_h264(st, name, val);
+}
+
+
+static int general_packetize(struct mbuf *mb, size_t pktsize,
+ videnc_packet_h *pkth, void *arg)
+{
+ int err = 0;
+
+ /* Assemble frame into smaller packets */
+ while (!err) {
+ size_t sz, left = mbuf_get_left(mb);
+ bool last = (left < pktsize);
+ if (!left)
+ break;
+
+ sz = last ? left : pktsize;
+
+ err = pkth(last, NULL, 0, mbuf_buf(mb), sz, arg);
+
+ mbuf_advance(mb, sz);
+ }
+
+ return err;
+}
+
+
+static int h263_packetize(struct videnc_state *st, struct mbuf *mb,
+ videnc_packet_h *pkth, void *arg)
+{
+ struct h263_strm h263_strm;
+ struct h263_hdr h263_hdr;
+ size_t pos;
+ int err;
+
+ /* Decode bit-stream header, used by packetizer */
+ err = h263_strm_decode(&h263_strm, mb);
+ if (err)
+ return err;
+
+ h263_hdr_copy_strm(&h263_hdr, &h263_strm);
+
+ st->mb_frag->pos = st->mb_frag->end = 0;
+ err = h263_hdr_encode(&h263_hdr, st->mb_frag);
+ pos = st->mb_frag->pos;
+
+ /* Assemble frame into smaller packets */
+ while (!err) {
+ size_t sz, left = mbuf_get_left(mb);
+ bool last = (left < st->encprm.pktsize);
+ if (!left)
+ break;
+
+ sz = last ? left : st->encprm.pktsize;
+
+ st->mb_frag->pos = st->mb_frag->end = pos;
+ err = mbuf_write_mem(st->mb_frag, mbuf_buf(mb), sz);
+ if (err)
+ break;
+
+ st->mb_frag->pos = 0;
+
+ err = pkth(last, NULL, 0, mbuf_buf(st->mb_frag),
+ mbuf_get_left(st->mb_frag), arg);
+
+ mbuf_advance(mb, sz);
+ }
+
+ return err;
+}
+
+
+#ifdef USE_X264
+static int open_encoder_x264(struct videnc_state *st, struct videnc_param *prm,
+ const struct vidsz *size)
+{
+ x264_param_t xprm;
+
+ x264_param_default(&xprm);
+
+#if X264_BUILD >= 87
+ x264_param_apply_profile(&xprm, "baseline");
+#endif
+
+ xprm.i_level_idc = h264_level_idc;
+ xprm.i_width = size->w;
+ xprm.i_height = size->h;
+ xprm.i_csp = X264_CSP_I420;
+ xprm.i_fps_num = prm->fps;
+ xprm.i_fps_den = 1;
+ xprm.rc.i_bitrate = prm->bitrate / 1024; /* kbit/s */
+ xprm.rc.i_rc_method = X264_RC_CQP;
+ xprm.i_log_level = X264_LOG_WARNING;
+
+ /* ultrafast preset */
+ xprm.i_frame_reference = 1;
+ xprm.i_scenecut_threshold = 0;
+ xprm.b_deblocking_filter = 0;
+ xprm.b_cabac = 0;
+ xprm.i_bframe = 0;
+ xprm.analyse.intra = 0;
+ xprm.analyse.inter = 0;
+ xprm.analyse.b_transform_8x8 = 0;
+ xprm.analyse.i_me_method = X264_ME_DIA;
+ xprm.analyse.i_subpel_refine = 0;
+#if X264_BUILD >= 59
+ xprm.rc.i_aq_mode = 0;
+#endif
+ xprm.analyse.b_mixed_references = 0;
+ xprm.analyse.i_trellis = 0;
+#if X264_BUILD >= 63
+ xprm.i_bframe_adaptive = X264_B_ADAPT_NONE;
+#endif
+#if X264_BUILD >= 70
+ xprm.rc.b_mb_tree = 0;
+#endif
+
+ /* slice-based threading (--tune=zerolatency) */
+#if X264_BUILD >= 80
+ xprm.rc.i_lookahead = 0;
+ xprm.i_sync_lookahead = 0;
+ xprm.i_bframe = 0;
+#endif
+
+ /* put SPS/PPS before each keyframe */
+ xprm.b_repeat_headers = 1;
+
+#if X264_BUILD >= 82
+ /* needed for x264_encoder_intra_refresh() */
+ xprm.b_intra_refresh = 1;
+#endif
+
+ if (st->x264)
+ x264_encoder_close(st->x264);
+
+ st->x264 = x264_encoder_open(&xprm);
+ if (!st->x264) {
+ warning("avcodec: x264_encoder_open() failed\n");
+ return ENOENT;
+ }
+
+ st->encsize = *size;
+
+ return 0;
+}
+#endif
+
+
+int encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp)
+{
+ struct videnc_state *st;
+ int err = 0;
+
+ if (!vesp || !vc || !prm)
+ return EINVAL;
+
+ if (*vesp)
+ return 0;
+
+ st = mem_zalloc(sizeof(*st), destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->encprm = *prm;
+
+ st->codec_id = avcodec_resolve_codecid(vc->name);
+ if (st->codec_id == CODEC_ID_NONE) {
+ err = EINVAL;
+ goto out;
+ }
+
+ st->mb = mbuf_alloc(FF_MIN_BUFFER_SIZE * 20);
+ st->mb_frag = mbuf_alloc(1024);
+ if (!st->mb || !st->mb_frag) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ st->sz_max = st->mb->size;
+
+ if (st->codec_id == CODEC_ID_H264) {
+#ifndef USE_X264
+ err = init_encoder(st);
+#endif
+ }
+ else
+ err = init_encoder(st);
+ if (err) {
+ warning("avcodec: %s: could not init encoder\n", vc->name);
+ goto out;
+ }
+
+ if (str_isset(fmtp)) {
+ struct pl sdp_fmtp;
+
+ pl_set_str(&sdp_fmtp, fmtp);
+
+ fmt_param_apply(&sdp_fmtp, param_handler, st);
+ }
+
+ debug("avcodec: video encoder %s: %d fps, %d bit/s, pktsize=%u\n",
+ vc->name, prm->fps, prm->bitrate, prm->pktsize);
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *vesp = st;
+
+ return err;
+}
+
+
+#ifdef USE_X264
+int encode_x264(struct videnc_state *st, bool update,
+ const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg)
+{
+ x264_picture_t pic_in, pic_out;
+ x264_nal_t *nal;
+ int i_nal;
+ int i, err, ret;
+
+ if (!st->x264 || !vidsz_cmp(&st->encsize, &frame->size)) {
+
+ err = open_encoder_x264(st, &st->encprm, &frame->size);
+ if (err)
+ return err;
+ }
+
+ if (update) {
+#if X264_BUILD >= 95
+ x264_encoder_intra_refresh(st->x264);
+#endif
+ debug("avcodec: x264 picture update\n");
+ }
+
+ x264_picture_init(&pic_in);
+
+ pic_in.i_type = update ? X264_TYPE_IDR : X264_TYPE_AUTO;
+ pic_in.i_qpplus1 = 0;
+ pic_in.i_pts = ++st->pts;
+
+ pic_in.img.i_csp = X264_CSP_I420;
+ pic_in.img.i_plane = 3;
+ for (i=0; i<3; i++) {
+ pic_in.img.i_stride[i] = frame->linesize[i];
+ pic_in.img.plane[i] = frame->data[i];
+ }
+
+ ret = x264_encoder_encode(st->x264, &nal, &i_nal, &pic_in, &pic_out);
+ if (ret < 0) {
+ fprintf(stderr, "x264 [error]: x264_encoder_encode failed\n");
+ }
+ if (i_nal == 0)
+ return 0;
+
+ err = 0;
+ for (i=0; i<i_nal && !err; i++) {
+ const uint8_t hdr = nal[i].i_ref_idc<<5 | nal[i].i_type<<0;
+ int offset = 0;
+
+#if X264_BUILD >= 76
+ const uint8_t *p = nal[i].p_payload;
+
+ /* Find the NAL Escape code [00 00 01] */
+ if (nal[i].i_payload > 4 && p[0] == 0x00 && p[1] == 0x00) {
+ if (p[2] == 0x00 && p[3] == 0x01)
+ offset = 4 + 1;
+ else if (p[2] == 0x01)
+ offset = 3 + 1;
+ }
+#endif
+
+ /* skip Supplemental Enhancement Information (SEI) */
+ if (nal[i].i_type == H264_NAL_SEI)
+ continue;
+
+ err = h264_nal_send(true, true, (i+1)==i_nal, hdr,
+ nal[i].p_payload + offset,
+ nal[i].i_payload - offset,
+ st->encprm.pktsize, pkth, arg);
+ }
+
+ return err;
+}
+#endif
+
+
+int encode(struct videnc_state *st, bool update, const struct vidframe *frame,
+ videnc_packet_h *pkth, void *arg)
+{
+ int i, err, ret;
+
+ if (!st || !frame || !pkth)
+ return EINVAL;
+
+ if (!st->ctx || !vidsz_cmp(&st->encsize, &frame->size)) {
+
+ err = open_encoder(st, &st->encprm, &frame->size);
+ if (err) {
+ warning("avcodec: open_encoder: %m\n", err);
+ return err;
+ }
+ }
+
+ for (i=0; i<4; i++) {
+ st->pict->data[i] = frame->data[i];
+ st->pict->linesize[i] = frame->linesize[i];
+ }
+ st->pict->pts = st->pts++;
+ if (update) {
+ debug("avcodec: encoder picture update\n");
+ st->pict->key_frame = 1;
+#ifdef FF_I_TYPE
+ st->pict->pict_type = FF_I_TYPE; /* Infra Frame */
+#else
+ st->pict->pict_type = AV_PICTURE_TYPE_I;
+#endif
+ }
+ else {
+ st->pict->key_frame = 0;
+ st->pict->pict_type = 0;
+ }
+
+ mbuf_rewind(st->mb);
+
+#if LIBAVCODEC_VERSION_INT >= ((54<<16)+(1<<8)+0)
+ do {
+ AVPacket avpkt;
+ int got_packet;
+
+ av_init_packet(&avpkt);
+
+ avpkt.data = st->mb->buf;
+ avpkt.size = (int)st->mb->size;
+
+ ret = avcodec_encode_video2(st->ctx, &avpkt,
+ st->pict, &got_packet);
+ if (ret < 0)
+ return EBADMSG;
+ if (!got_packet)
+ return 0;
+
+ mbuf_set_end(st->mb, avpkt.size);
+
+ } while (0);
+#else
+ ret = avcodec_encode_video(st->ctx, st->mb->buf,
+ (int)st->mb->size, st->pict);
+ if (ret < 0 )
+ return EBADMSG;
+
+ /* todo: figure out proper buffer size */
+ if (ret > (int)st->sz_max) {
+ debug("avcodec: grow encode buffer %u --> %d\n",
+ st->sz_max, ret);
+ st->sz_max = ret;
+ }
+
+ mbuf_set_end(st->mb, ret);
+#endif
+
+ switch (st->codec_id) {
+
+ case CODEC_ID_H263:
+ err = h263_packetize(st, st->mb, pkth, arg);
+ break;
+
+ case CODEC_ID_H264:
+ err = h264_packetize(st->mb, st->encprm.pktsize, pkth, arg);
+ break;
+
+ case CODEC_ID_MPEG4:
+ err = general_packetize(st->mb, st->encprm.pktsize, pkth, arg);
+ break;
+
+ default:
+ err = EPROTO;
+ break;
+ }
+
+ return err;
+}
diff --git a/modules/avcodec/h263.c b/modules/avcodec/h263.c
new file mode 100644
index 0000000..7e29ecd
--- /dev/null
+++ b/modules/avcodec/h263.c
@@ -0,0 +1,176 @@
+/**
+ * @file h263.c H.263 video codec (RFC 4629)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#ifdef USE_X264
+#include <x264.h>
+#endif
+#include "h26x.h"
+#include "avcodec.h"
+
+
+int h263_hdr_encode(const struct h263_hdr *hdr, struct mbuf *mb)
+{
+ uint32_t v; /* host byte order */
+
+ v = hdr->f<<31 | hdr->p<<30 | hdr->sbit<<27 | hdr->ebit<<24;
+ v |= hdr->src<<21 | hdr->i<<20 | hdr->u<<19 | hdr->s<<18 | hdr->a<<17;
+ v |= hdr->r<<13 | hdr->dbq<<11 | hdr->trb<<8 | hdr->tr<<0;
+
+ return mbuf_write_u32(mb, htonl(v));
+}
+
+
+enum h263_mode h263_hdr_mode(const struct h263_hdr *hdr)
+{
+ if (!hdr->f) {
+ return H263_MODE_A;
+ }
+ else {
+ if (!hdr->p)
+ return H263_MODE_B;
+ else
+ return H263_MODE_C;
+ }
+}
+
+
+int h263_hdr_decode(struct h263_hdr *hdr, struct mbuf *mb)
+{
+ uint32_t v;
+
+ if (!hdr)
+ return EINVAL;
+ if (mbuf_get_left(mb) < H263_HDR_SIZE_MODEA)
+ return EBADMSG;
+
+ v = ntohl(mbuf_read_u32(mb));
+
+ /* Common */
+ hdr->f = v>>31 & 0x1;
+ hdr->p = v>>30 & 0x1;
+ hdr->sbit = v>>27 & 0x7;
+ hdr->ebit = v>>24 & 0x7;
+ hdr->src = v>>21 & 0x7;
+
+ switch (h263_hdr_mode(hdr)) {
+
+ case H263_MODE_A:
+ hdr->i = v>>20 & 0x1;
+ hdr->u = v>>19 & 0x1;
+ hdr->s = v>>18 & 0x1;
+ hdr->a = v>>17 & 0x1;
+ hdr->r = v>>13 & 0xf;
+ hdr->dbq = v>>11 & 0x3;
+ hdr->trb = v>>8 & 0x7;
+ hdr->tr = v>>0 & 0xff;
+ break;
+
+ case H263_MODE_B:
+ hdr->quant = v>>16 & 0x1f;
+ hdr->gobn = v>>11 & 0x1f;
+ hdr->mba = v>>2 & 0x1ff;
+
+ if (mbuf_get_left(mb) < 4)
+ return EBADMSG;
+
+ v = ntohl(mbuf_read_u32(mb));
+
+ hdr->i = v>>31 & 0x1;
+ hdr->u = v>>30 & 0x1;
+ hdr->s = v>>29 & 0x1;
+ hdr->a = v>>28 & 0x1;
+ hdr->hmv1 = v>>21 & 0x7f;
+ hdr->vmv1 = v>>14 & 0x7f;
+ hdr->hmv2 = v>>7 & 0x7f;
+ hdr->vmv2 = v>>0 & 0x7f;
+ break;
+
+ case H263_MODE_C:
+ /* NOTE: Mode C is optional, only parts decoded */
+ if (mbuf_get_left(mb) < 8)
+ return EBADMSG;
+
+ v = ntohl(mbuf_read_u32(mb));
+ hdr->i = v>>31 & 0x1;
+ hdr->u = v>>30 & 0x1;
+ hdr->s = v>>29 & 0x1;
+ hdr->a = v>>28 & 0x1;
+
+ (void)mbuf_read_u32(mb); /* ignore */
+ break;
+ }
+
+ return 0;
+}
+
+
+/** Find PSC (Picture Start Code) in bit-stream */
+const uint8_t *h263_strm_find_psc(const uint8_t *p, uint32_t size)
+{
+ const uint8_t *end = p + size - 1;
+
+ for (; p < end; p++) {
+ if (p[0] == 0x00 && p[1] == 0x00)
+ return p;
+ }
+
+ return NULL;
+}
+
+
+int h263_strm_decode(struct h263_strm *s, struct mbuf *mb)
+{
+ const uint8_t *p;
+
+ if (mbuf_get_left(mb) < 6)
+ return EINVAL;
+
+ p = mbuf_buf(mb);
+
+ s->psc[0] = p[0];
+ s->psc[1] = p[1];
+
+ s->temp_ref = (p[2]<<6 & 0xc0) | (p[3]>>2 & 0x3f);
+
+ s->split_scr = p[4]>>7 & 0x1;
+ s->doc_camera = p[4]>>6 & 0x1;
+ s->pic_frz_rel = p[4]>>5 & 0x1;
+ s->src_fmt = p[4]>>2 & 0x7;
+ s->pic_type = p[4]>>1 & 0x1;
+ s->umv = p[4]>>0 & 0x1;
+
+ s->sac = p[5]>>7 & 0x1;
+ s->apm = p[5]>>6 & 0x1;
+ s->pb = p[5]>>5 & 0x1;
+ s->pquant = p[5]>>0 & 0x1f;
+
+ s->cpm = p[6]>>7 & 0x1;
+ s->pei = p[6]>>6 & 0x1;
+
+ return 0;
+}
+
+
+/** Copy H.263 bit-stream to H.263 RTP payload header */
+void h263_hdr_copy_strm(struct h263_hdr *hdr, const struct h263_strm *s)
+{
+ hdr->f = 0; /* Mode A */
+ hdr->p = 0;
+ hdr->sbit = 0;
+ hdr->ebit = 0;
+ hdr->src = s->src_fmt;
+ hdr->i = s->pic_type;
+ hdr->u = s->umv;
+ hdr->s = s->sac;
+ hdr->a = s->apm;
+ hdr->r = 0;
+ hdr->dbq = 0; /* No PB-frames */
+ hdr->trb = 0; /* No PB-frames */
+ hdr->tr = s->temp_ref;
+}
diff --git a/modules/avcodec/h264.c b/modules/avcodec/h264.c
new file mode 100644
index 0000000..4c2aa59
--- /dev/null
+++ b/modules/avcodec/h264.c
@@ -0,0 +1,188 @@
+/**
+ * @file avcodec/h264.c H.264 video codec (RFC 3984)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <libavcodec/avcodec.h>
+#ifdef USE_X264
+#include <x264.h>
+#endif
+#include "h26x.h"
+#include "avcodec.h"
+
+
+const uint8_t h264_level_idc = 0x0c;
+
+
+int h264_hdr_encode(const struct h264_hdr *hdr, struct mbuf *mb)
+{
+ uint8_t v;
+
+ v = hdr->f<<7 | hdr->nri<<5 | hdr->type<<0;
+
+ return mbuf_write_u8(mb, v);
+}
+
+
+int h264_hdr_decode(struct h264_hdr *hdr, struct mbuf *mb)
+{
+ uint8_t v;
+
+ if (mbuf_get_left(mb) < 1)
+ return ENOENT;
+
+ v = mbuf_read_u8(mb);
+
+ hdr->f = v>>7 & 0x1;
+ hdr->nri = v>>5 & 0x3;
+ hdr->type = v>>0 & 0x1f;
+
+ return 0;
+}
+
+
+int fu_hdr_encode(const struct fu *fu, struct mbuf *mb)
+{
+ uint8_t v = fu->s<<7 | fu->s<<6 | fu->r<<5 | fu->type;
+ return mbuf_write_u8(mb, v);
+}
+
+
+int fu_hdr_decode(struct fu *fu, struct mbuf *mb)
+{
+ uint8_t v;
+
+ if (mbuf_get_left(mb) < 1)
+ return ENOENT;
+
+ v = mbuf_read_u8(mb);
+
+ fu->s = v>>7 & 0x1;
+ fu->e = v>>6 & 0x1;
+ fu->r = v>>5 & 0x1;
+ fu->type = v>>0 & 0x1f;
+
+ return 0;
+}
+
+
+/*
+ * Find the NAL start sequence in a H.264 byte stream
+ *
+ * @note: copied from ffmpeg source
+ */
+const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end)
+{
+ const uint8_t *a = p + 4 - ((long)p & 3);
+
+ for (end -= 3; p < a && p < end; p++ ) {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ for (end -= 3; p < end; p += 4) {
+ uint32_t x = *(const uint32_t*)(void *)p;
+ if ( (x - 0x01010101) & (~x) & 0x80808080 ) {
+ if (p[1] == 0 ) {
+ if ( p[0] == 0 && p[2] == 1 )
+ return p;
+ if ( p[2] == 0 && p[3] == 1 )
+ return p+1;
+ }
+ if ( p[3] == 0 ) {
+ if ( p[2] == 0 && p[4] == 1 )
+ return p+2;
+ if ( p[4] == 0 && p[5] == 1 )
+ return p+3;
+ }
+ }
+ }
+
+ for (end += 3; p < end; p++) {
+ if (p[0] == 0 && p[1] == 0 && p[2] == 1)
+ return p;
+ }
+
+ return end + 3;
+}
+
+
+static int rtp_send_data(const uint8_t *hdr, size_t hdr_sz,
+ const uint8_t *buf, size_t sz, bool eof,
+ videnc_packet_h *pkth, void *arg)
+{
+ return pkth(eof, hdr, hdr_sz, buf, sz, arg);
+}
+
+
+int h264_nal_send(bool first, bool last,
+ bool marker, uint32_t ihdr, const uint8_t *buf,
+ size_t size, size_t maxsz,
+ videnc_packet_h *pkth, void *arg)
+{
+ uint8_t hdr = (uint8_t)ihdr;
+ int err = 0;
+
+ if (first && last && size <= maxsz) {
+ err = rtp_send_data(&hdr, 1, buf, size, marker,
+ pkth, arg);
+ }
+ else {
+ uint8_t fu_hdr[2];
+ const uint8_t type = hdr & 0x1f;
+ const uint8_t nri = hdr & 0x60;
+ const size_t sz = maxsz - 2;
+
+ fu_hdr[0] = nri | H264_NAL_FU_A;
+ fu_hdr[1] = first ? (1<<7 | type) : type;
+
+ while (size > sz) {
+ err |= rtp_send_data(fu_hdr, 2, buf, sz, false,
+ pkth, arg);
+ buf += sz;
+ size -= sz;
+ fu_hdr[1] &= ~(1 << 7);
+ }
+
+ if (last)
+ fu_hdr[1] |= 1<<6; /* end bit */
+
+ err |= rtp_send_data(fu_hdr, 2, buf, size, marker && last,
+ pkth, arg);
+ }
+
+ return err;
+}
+
+
+int h264_packetize(struct mbuf *mb, size_t pktsize,
+ videnc_packet_h *pkth, void *arg)
+{
+ const uint8_t *start = mb->buf;
+ const uint8_t *end = start + mb->end;
+ const uint8_t *r;
+ int err = 0;
+
+ r = h264_find_startcode(mb->buf, end);
+
+ while (r < end) {
+ const uint8_t *r1;
+
+ /* skip zeros */
+ while (!*(r++))
+ ;
+
+ r1 = h264_find_startcode(r, end);
+
+ err |= h264_nal_send(true, true, (r1 >= end), r[0],
+ r+1, r1-r-1, pktsize,
+ pkth, arg);
+ r = r1;
+ }
+
+ return err;
+}
diff --git a/modules/avcodec/h26x.h b/modules/avcodec/h26x.h
new file mode 100644
index 0000000..7a21696
--- /dev/null
+++ b/modules/avcodec/h26x.h
@@ -0,0 +1,165 @@
+/**
+ * @file h26x.h Interface to H.26x video codecs
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+/*
+ * H.263
+ */
+
+
+enum h263_mode {
+ H263_MODE_A,
+ H263_MODE_B,
+ H263_MODE_C
+};
+
+enum {
+ H263_HDR_SIZE_MODEA = 4,
+ H263_HDR_SIZE_MODEB = 8,
+ H263_HDR_SIZE_MODEC = 12
+};
+
+/** H.263 picture size format */
+enum h263_fmt {
+ H263_FMT_SQCIF = 1, /**< 128 x 96 */
+ H263_FMT_QCIF = 2, /**< 176 x 144 */
+ H263_FMT_CIF = 3, /**< 352 x 288 */
+ H263_FMT_4CIF = 4, /**< 704 x 576 */
+ H263_FMT_16CIF = 5, /**< 1408 x 1152 */
+ H263_FMT_OTHER = 7,
+};
+
+/**
+ * H.263 Header defined in RFC 2190
+ */
+struct h263_hdr {
+
+ /* common */
+ unsigned f:1; /**< 1 bit - Flag; 0=mode A, 1=mode B/C */
+ unsigned p:1; /**< 1 bit - PB-frames, 0=mode B, 1=mode C */
+ unsigned sbit:3; /**< 3 bits - Start Bit Position (SBIT) */
+ unsigned ebit:3; /**< 3 bits - End Bit Position (EBIT) */
+ unsigned src:3; /**< 3 bits - Source format */
+
+ /* mode A */
+ unsigned i:1; /**< 1 bit - 0=intra-coded, 1=inter-coded */
+ unsigned u:1; /**< 1 bit - Unrestricted Motion Vector */
+ unsigned s:1; /**< 1 bit - Syntax-based Arithmetic Coding */
+ unsigned a:1; /**< 1 bit - Advanced Prediction option */
+ unsigned r:4; /**< 4 bits - Reserved (zero) */
+ unsigned dbq:2; /**< 2 bits - DBQUANT */
+ unsigned trb:3; /**< 3 bits - Temporal Reference for B-frame */
+ unsigned tr:8; /**< 8 bits - Temporal Reference for P-frame */
+
+ /* mode B */
+ unsigned quant:5; //=0 for GOB header
+ unsigned gobn:5; // gob number
+ unsigned mba:9; // address
+ unsigned hmv1:7; // horizontal motion vector
+ unsigned vmv1:7; // vertical motion vector
+ unsigned hmv2:7;
+ unsigned vmv2:7;
+
+
+};
+
+enum {I_FRAME=0, P_FRAME=1};
+
+/** H.263 bit-stream header */
+struct h263_strm {
+ uint8_t psc[2]; /**< Picture Start Code (PSC) */
+
+ uint8_t temp_ref; /**< Temporal Reference */
+ unsigned split_scr:1; /**< Split Screen Indicator */
+ unsigned doc_camera:1; /**< Document Camera Indicator */
+ unsigned pic_frz_rel:1; /**< Full Picture Freeze Release */
+ unsigned src_fmt:3; /**< Source Format. 3=CIF */
+ unsigned pic_type:1; /**< Picture Coding Type. 0=I, 1=P */
+ unsigned umv:1; /**< Unrestricted Motion Vector mode */
+ unsigned sac:1; /**< Syntax-based Arithmetic Coding */
+ unsigned apm:1; /**< Advanced Prediction mode */
+ unsigned pb:1; /**< PB-frames mode */
+ unsigned pquant:5; /**< Quantizer Information */
+ unsigned cpm:1; /**< Continuous Presence Multipoint */
+ unsigned pei:1; /**< Extra Insertion Information */
+ /* H.263 bit-stream ... */
+};
+
+int h263_hdr_encode(const struct h263_hdr *hdr, struct mbuf *mb);
+int h263_hdr_decode(struct h263_hdr *hdr, struct mbuf *mb);
+enum h263_mode h263_hdr_mode(const struct h263_hdr *hdr);
+
+const uint8_t *h263_strm_find_psc(const uint8_t *p, uint32_t size);
+int h263_strm_decode(struct h263_strm *s, struct mbuf *mb);
+void h263_hdr_copy_strm(struct h263_hdr *hdr, const struct h263_strm *s);
+
+
+/*
+ * H.264
+ */
+
+
+/** NAL unit types (RFC 3984, Table 1) */
+enum {
+ H264_NAL_UNKNOWN = 0,
+ /* 1-23 NAL unit Single NAL unit packet per H.264 */
+ H264_NAL_SLICE = 1,
+ H264_NAL_DPA = 2,
+ H264_NAL_DPB = 3,
+ H264_NAL_DPC = 4,
+ H264_NAL_IDR_SLICE = 5,
+ H264_NAL_SEI = 6,
+ H264_NAL_SPS = 7,
+ H264_NAL_PPS = 8,
+ H264_NAL_AUD = 9,
+ H264_NAL_END_SEQUENCE = 10,
+ H264_NAL_END_STREAM = 11,
+ H264_NAL_FILLER_DATA = 12,
+ H264_NAL_SPS_EXT = 13,
+ H264_NAL_AUX_SLICE = 19,
+
+ H264_NAL_STAP_A = 24, /**< Single-time aggregation packet */
+ H264_NAL_STAP_B = 25, /**< Single-time aggregation packet */
+ H264_NAL_MTAP16 = 26, /**< Multi-time aggregation packet */
+ H264_NAL_MTAP24 = 27, /**< Multi-time aggregation packet */
+ H264_NAL_FU_A = 28, /**< Fragmentation unit */
+ H264_NAL_FU_B = 29, /**< Fragmentation unit */
+};
+
+/**
+ * H.264 Header defined in RFC 3984
+ *
+ * <pre>
+ +---------------+
+ |0|1|2|3|4|5|6|7|
+ +-+-+-+-+-+-+-+-+
+ |F|NRI| Type |
+ +---------------+
+ * </pre>
+ */
+struct h264_hdr {
+ unsigned f:1; /**< 1 bit - Forbidden zero bit (must be 0) */
+ unsigned nri:2; /**< 2 bits - nal_ref_idc */
+ unsigned type:5; /**< 5 bits - nal_unit_type */
+};
+
+int h264_hdr_encode(const struct h264_hdr *hdr, struct mbuf *mb);
+int h264_hdr_decode(struct h264_hdr *hdr, struct mbuf *mb);
+
+/** Fragmentation Unit header */
+struct fu {
+ unsigned s:1; /**< Start bit */
+ unsigned e:1; /**< End bit */
+ unsigned r:1; /**< The Reserved bit MUST be equal to 0 */
+ unsigned type:5; /**< The NAL unit payload type */
+};
+
+int fu_hdr_encode(const struct fu *fu, struct mbuf *mb);
+int fu_hdr_decode(struct fu *fu, struct mbuf *mb);
+
+const uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end);
+
+int h264_decode_sprop_params(AVCodecContext *codec, struct pl *pl);
diff --git a/modules/avcodec/module.mk b/modules/avcodec/module.mk
new file mode 100644
index 0000000..b209a57
--- /dev/null
+++ b/modules/avcodec/module.mk
@@ -0,0 +1,20 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+USE_X264 := $(shell [ -f $(SYSROOT)/include/x264.h ] || \
+ [ -f $(SYSROOT)/local/include/x264.h ] || \
+ [ -f $(SYSROOT_ALT)/include/x264.h ] && echo "yes")
+
+MOD := avcodec
+$(MOD)_SRCS += avcodec.c h263.c h264.c encode.c decode.c
+$(MOD)_LFLAGS += -lavcodec -lavutil
+CFLAGS += -I/usr/include/ffmpeg
+ifneq ($(USE_X264),)
+CFLAGS += -DUSE_X264
+$(MOD)_LFLAGS += -lx264
+endif
+
+include mk/mod.mk