summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorAlfred E. Heggestad <alfred.heggestad@gmail.com>2016-11-27 09:34:33 +0100
committerAlfred E. Heggestad <alfred.heggestad@gmail.com>2016-11-27 09:34:33 +0100
commit4d842f54af430bf5b162708fb51fb9fee67b45c9 (patch)
tree870f1dbf6437576b0d91ac72cd673f8cc1927f7b /modules
parentd907f999d3923b734abfc45b0c1941f2a9dc4e39 (diff)
added experimental AV1 video codec
Diffstat (limited to 'modules')
-rw-r--r--modules/av1/av1.c51
-rw-r--r--modules/av1/av1.h20
-rw-r--r--modules/av1/decode.c293
-rw-r--r--modules/av1/encode.c255
-rw-r--r--modules/av1/module.mk13
-rw-r--r--modules/av1/sdp.c0
6 files changed, 632 insertions, 0 deletions
diff --git a/modules/av1/av1.c b/modules/av1/av1.c
new file mode 100644
index 0000000..2c89ffb
--- /dev/null
+++ b/modules/av1/av1.c
@@ -0,0 +1,51 @@
+/**
+ * @file av1.c AV1 Video Codec
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "av1.h"
+
+
+/**
+ * @defgroup av1 av1
+ *
+ * The AV1 video codec (Experimental)
+ *
+ * Reference: http://aomedia.org/
+ */
+
+
+static struct vidcodec av1 = {
+ .name = "AV1",
+ .encupdh = av1_encode_update,
+ .ench = av1_encode,
+ .decupdh = av1_decode_update,
+ .dech = av1_decode,
+};
+
+
+static int module_init(void)
+{
+ vidcodec_register(&av1);
+
+ return 0;
+}
+
+
+static int module_close(void)
+{
+ vidcodec_unregister(&av1);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(av1) = {
+ "av1",
+ "codec",
+ module_init,
+ module_close
+};
diff --git a/modules/av1/av1.h b/modules/av1/av1.h
new file mode 100644
index 0000000..7d5a32d
--- /dev/null
+++ b/modules/av1/av1.h
@@ -0,0 +1,20 @@
+/**
+ * @file av1.h Private AV1 Interface
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+
+/* Encode */
+int av1_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp,
+ videnc_packet_h *pkth, void *arg);
+int av1_encode(struct videnc_state *ves, bool update,
+ const struct vidframe *frame);
+
+
+/* Decode */
+int av1_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp);
+int av1_decode(struct viddec_state *vds, struct vidframe *frame,
+ bool *intra, bool marker, uint16_t seq, struct mbuf *mb);
diff --git a/modules/av1/decode.c b/modules/av1/decode.c
new file mode 100644
index 0000000..21ccc75
--- /dev/null
+++ b/modules/av1/decode.c
@@ -0,0 +1,293 @@
+/**
+ * @file av1/decode.c AV1 Decode
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+#include <string.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <aom/aom.h>
+#include <aom/aom_decoder.h>
+#include <aom/aomdx.h>
+#include "av1.h"
+
+
+enum {
+ DECODE_MAXSZ = 524288,
+};
+
+
+/* XXX: re-using VP9 header format for now.. */
+struct hdr {
+ unsigned x:1;
+ unsigned noref:1;
+ unsigned start:1;
+ unsigned partid:4;
+ /* extension fields */
+ unsigned i:1;
+ unsigned l:1;
+ unsigned t:1;
+ unsigned k:1;
+ uint16_t picid;
+ uint8_t tl0picidx;
+ unsigned tid:2;
+ unsigned y:1;
+ unsigned keyidx:5;
+};
+
+struct viddec_state {
+ aom_codec_ctx_t ctx;
+ struct mbuf *mb;
+ bool ctxup;
+ bool started;
+ uint16_t seq;
+};
+
+
+static void destructor(void *arg)
+{
+ struct viddec_state *vds = arg;
+
+ if (vds->ctxup)
+ aom_codec_destroy(&vds->ctx);
+
+ mem_deref(vds->mb);
+}
+
+
+int av1_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
+ const char *fmtp)
+{
+ struct viddec_state *vds;
+ aom_codec_err_t res;
+ int err = 0;
+ (void)vc;
+ (void)fmtp;
+
+ if (!vdsp)
+ return EINVAL;
+
+ vds = *vdsp;
+
+ if (vds)
+ return 0;
+
+ vds = mem_zalloc(sizeof(*vds), destructor);
+ if (!vds)
+ return ENOMEM;
+
+ vds->mb = mbuf_alloc(1024);
+ if (!vds->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ res = aom_codec_dec_init(&vds->ctx, &aom_codec_av1_dx_algo, NULL, 0);
+ if (res) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ vds->ctxup = true;
+
+ out:
+ if (err)
+ mem_deref(vds);
+ else
+ *vdsp = vds;
+
+ return err;
+}
+
+
+static inline int hdr_decode(struct hdr *hdr, struct mbuf *mb)
+{
+ uint8_t v;
+
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ v = mbuf_read_u8(mb);
+
+ hdr->x = v>>7 & 0x1;
+ hdr->noref = v>>5 & 0x1;
+ hdr->start = v>>4 & 0x1;
+ hdr->partid = v & 0x07;
+
+ if (hdr->x) {
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ v = mbuf_read_u8(mb);
+
+ hdr->i = v>>7 & 0x1;
+ hdr->l = v>>6 & 0x1;
+ hdr->t = v>>5 & 0x1;
+ hdr->k = v>>4 & 0x1;
+ }
+
+ if (hdr->i) {
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ v = mbuf_read_u8(mb);
+
+ if (v>>7 & 0x1) {
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ hdr->picid = (v & 0x7f)<<8;
+ hdr->picid += mbuf_read_u8(mb);
+ }
+ else {
+ hdr->picid = v & 0x7f;
+ }
+ }
+
+ if (hdr->l) {
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ hdr->tl0picidx = mbuf_read_u8(mb);
+ }
+
+ if (hdr->t || hdr->k) {
+
+ if (mbuf_get_left(mb) < 1)
+ return EBADMSG;
+
+ v = mbuf_read_u8(mb);
+
+ hdr->tid = v>>6 & 0x3;
+ hdr->y = v>>5 & 0x1;
+ hdr->keyidx = v & 0x1f;
+ }
+
+ return 0;
+}
+
+
+/* XXX: check keyframe flag */
+static inline bool is_keyframe(struct mbuf *mb)
+{
+ if (mbuf_get_left(mb) < 1)
+ return false;
+
+ if (mb->buf[mb->pos] & 0x01)
+ return false;
+
+ return true;
+}
+
+
+static inline int16_t seq_diff(uint16_t x, uint16_t y)
+{
+ return (int16_t)(y - x);
+}
+
+
+int av1_decode(struct viddec_state *vds, struct vidframe *frame,
+ bool *intra, bool marker, uint16_t seq, struct mbuf *mb)
+{
+ aom_codec_iter_t iter = NULL;
+ aom_codec_err_t res;
+ aom_image_t *img;
+ struct hdr hdr;
+ int err, i;
+
+ if (!vds || !frame || !intra || !mb)
+ return EINVAL;
+
+ *intra = false;
+
+ err = hdr_decode(&hdr, mb);
+ if (err)
+ return err;
+
+#if 1
+ debug("av1: header: x=%u noref=%u start=%u partid=%u "
+ "i=%u l=%u t=%u k=%u "
+ "picid=%u tl0picidx=%u tid=%u y=%u keyidx=%u\n",
+ hdr.x, hdr.noref, hdr.start, hdr.partid,
+ hdr.i, hdr.l, hdr.t, hdr.k,
+ hdr.picid, hdr.tl0picidx, hdr.tid, hdr.y, hdr.keyidx);
+#endif
+
+ if (hdr.start && hdr.partid == 0) {
+
+ if (is_keyframe(mb))
+ *intra = true;
+
+ mbuf_rewind(vds->mb);
+ vds->started = true;
+ }
+ else {
+ if (!vds->started)
+ return 0;
+
+ if (seq_diff(vds->seq, seq) != 1) {
+ mbuf_rewind(vds->mb);
+ vds->started = false;
+ return 0;
+ }
+ }
+
+ vds->seq = seq;
+
+ err = mbuf_write_mem(vds->mb, mbuf_buf(mb), mbuf_get_left(mb));
+ if (err)
+ goto out;
+
+ if (!marker) {
+
+ if (vds->mb->end > DECODE_MAXSZ) {
+ warning("av1: decode buffer size exceeded\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ return 0;
+ }
+
+ res = aom_codec_decode(&vds->ctx, vds->mb->buf,
+ (unsigned int)vds->mb->end, NULL, 1);
+ if (res) {
+ debug("av1: decode error: %s\n", aom_codec_err_to_string(res));
+ err = EPROTO;
+ goto out;
+ }
+
+ img = aom_codec_get_frame(&vds->ctx, &iter);
+ if (!img) {
+ debug("av1: no picture\n");
+ goto out;
+ }
+
+ if (img->fmt != AOM_IMG_FMT_I420) {
+ warning("av1: bad pixel format (%i)\n", img->fmt);
+ goto out;
+ }
+
+ for (i=0; i<4; i++) {
+ frame->data[i] = img->planes[i];
+ frame->linesize[i] = img->stride[i];
+ }
+
+ frame->size.w = img->d_w;
+ frame->size.h = img->d_h;
+ frame->fmt = VID_FMT_YUV420P;
+
+ out:
+ mbuf_rewind(vds->mb);
+ vds->started = false;
+
+ return err;
+}
diff --git a/modules/av1/encode.c b/modules/av1/encode.c
new file mode 100644
index 0000000..6402851
--- /dev/null
+++ b/modules/av1/encode.c
@@ -0,0 +1,255 @@
+/**
+ * @file av1/encode.c AV1 Encode
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+#include <string.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <aom/aom.h>
+#include <aom/aom_encoder.h>
+#include <aom/aomcx.h>
+#include "av1.h"
+
+
+enum {
+ HDR_SIZE = 4,
+};
+
+
+struct videnc_state {
+ aom_codec_ctx_t ctx;
+ struct vidsz size;
+ aom_codec_pts_t pts;
+ unsigned fps;
+ unsigned bitrate;
+ unsigned pktsize;
+ bool ctxup;
+ uint16_t picid;
+ videnc_packet_h *pkth;
+ void *arg;
+};
+
+
+static void destructor(void *arg)
+{
+ struct videnc_state *ves = arg;
+
+ if (ves->ctxup)
+ aom_codec_destroy(&ves->ctx);
+}
+
+
+int av1_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
+ struct videnc_param *prm, const char *fmtp,
+ videnc_packet_h *pkth, void *arg)
+{
+ struct videnc_state *ves;
+
+ if (!vesp || !vc || !prm || prm->pktsize < (HDR_SIZE + 1))
+ return EINVAL;
+
+ ves = *vesp;
+
+ if (!ves) {
+
+ ves = mem_zalloc(sizeof(*ves), destructor);
+ if (!ves)
+ return ENOMEM;
+
+ ves->picid = rand_u16();
+
+ *vesp = ves;
+ }
+ else {
+ if (ves->ctxup && (ves->bitrate != prm->bitrate ||
+ ves->fps != prm->fps)) {
+
+ aom_codec_destroy(&ves->ctx);
+ ves->ctxup = false;
+ }
+ }
+
+ ves->bitrate = prm->bitrate;
+ ves->pktsize = prm->pktsize;
+ ves->fps = prm->fps;
+ ves->pkth = pkth;
+ ves->arg = arg;
+
+ return 0;
+}
+
+
+static int open_encoder(struct videnc_state *ves, const struct vidsz *size)
+{
+ aom_codec_enc_cfg_t cfg;
+ aom_codec_err_t res;
+
+ res = aom_codec_enc_config_default(&aom_codec_av1_cx_algo, &cfg, 0);
+ if (res)
+ return EPROTO;
+
+ cfg.g_w = size->w;
+ cfg.g_h = size->h;
+ cfg.g_timebase.num = 1;
+ cfg.g_timebase.den = ves->fps;
+ cfg.g_error_resilient = AOM_ERROR_RESILIENT_DEFAULT;
+ cfg.g_pass = AOM_RC_ONE_PASS;
+ cfg.g_lag_in_frames = 0;
+ cfg.rc_end_usage = AOM_VBR;
+ cfg.rc_target_bitrate = ves->bitrate;
+ cfg.kf_mode = AOM_KF_AUTO;
+
+ if (ves->ctxup) {
+ debug("av1: re-opening encoder\n");
+ aom_codec_destroy(&ves->ctx);
+ ves->ctxup = false;
+ }
+
+ res = aom_codec_enc_init(&ves->ctx, &aom_codec_av1_cx_algo, &cfg,
+ 0);
+ if (res) {
+ warning("av1: enc init: %s\n", aom_codec_err_to_string(res));
+ return EPROTO;
+ }
+
+ ves->ctxup = true;
+
+ res = aom_codec_control(&ves->ctx, AOME_SET_CPUUSED, 8);
+ if (res) {
+ warning("av1: codec ctrl C: %s\n",
+ aom_codec_err_to_string(res));
+ }
+
+ return 0;
+}
+
+
+static inline void hdr_encode(uint8_t hdr[HDR_SIZE], bool noref, bool start,
+ uint8_t partid, uint16_t picid)
+{
+ hdr[0] = 1<<7 | noref<<5 | start<<4 | (partid & 0x7);
+ hdr[1] = 1<<7;
+ hdr[2] = 1<<7 | (picid>>8 & 0x7f);
+ hdr[3] = picid & 0xff;
+}
+
+
+static inline int packetize(bool marker, const uint8_t *buf, size_t len,
+ size_t maxlen, bool noref, uint8_t partid,
+ uint16_t picid, videnc_packet_h *pkth, void *arg)
+{
+ uint8_t hdr[HDR_SIZE];
+ bool start = true;
+ int err = 0;
+
+ maxlen -= sizeof(hdr);
+
+ while (len > maxlen) {
+
+ hdr_encode(hdr, noref, start, partid, picid);
+
+ err |= pkth(false, hdr, sizeof(hdr), buf, maxlen, arg);
+
+ buf += maxlen;
+ len -= maxlen;
+ start = false;
+ }
+
+ hdr_encode(hdr, noref, start, partid, picid);
+
+ err |= pkth(marker, hdr, sizeof(hdr), buf, len, arg);
+
+ return err;
+}
+
+
+int av1_encode(struct videnc_state *ves, bool update,
+ const struct vidframe *frame)
+{
+ aom_enc_frame_flags_t flags = 0;
+ aom_codec_iter_t iter = NULL;
+ aom_codec_err_t res;
+ aom_image_t *img;
+ aom_img_fmt_t img_fmt;
+ int err = 0, i;
+
+ if (!ves || !frame || frame->fmt != VID_FMT_YUV420P)
+ return EINVAL;
+
+ if (!ves->ctxup || !vidsz_cmp(&ves->size, &frame->size)) {
+
+ err = open_encoder(ves, &frame->size);
+ if (err)
+ return err;
+
+ ves->size = frame->size;
+ }
+
+ if (update) {
+ /* debug("av1: picture update\n"); */
+ flags |= AOM_EFLAG_FORCE_KF;
+ }
+
+ img_fmt = AOM_IMG_FMT_I420;
+
+ img = aom_img_wrap(NULL, img_fmt, frame->size.w, frame->size.h,
+ 16, NULL);
+ if (!img) {
+ warning("vp9: encoder: could not allocate image\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ for (i=0; i<4; i++) {
+ img->stride[i] = frame->linesize[i];
+ img->planes[i] = frame->data[i];
+ }
+
+ res = aom_codec_encode(&ves->ctx, img, ves->pts++, 1,
+ flags, AOM_DL_REALTIME);
+ if (res) {
+ warning("av1: enc error: %s\n", aom_codec_err_to_string(res));
+ return ENOMEM;
+ }
+
+ ++ves->picid;
+
+ for (;;) {
+ bool keyframe = false, marker = true;
+ const aom_codec_cx_pkt_t *pkt;
+ uint8_t partid = 0;
+
+ pkt = aom_codec_get_cx_data(&ves->ctx, &iter);
+ if (!pkt)
+ break;
+
+ if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
+ continue;
+
+ if (pkt->data.frame.flags & AOM_FRAME_IS_KEY)
+ keyframe = true;
+
+ if (pkt->data.frame.flags & AOM_FRAME_IS_FRAGMENT)
+ marker = false;
+
+ if (pkt->data.frame.partition_id >= 0)
+ partid = pkt->data.frame.partition_id;
+
+ err = packetize(marker,
+ pkt->data.frame.buf,
+ pkt->data.frame.sz,
+ ves->pktsize, !keyframe, partid, ves->picid,
+ ves->pkth, ves->arg);
+ if (err)
+ return err;
+ }
+
+ out:
+ if (img)
+ aom_img_free(img);
+
+ return err;
+}
diff --git a/modules/av1/module.mk b/modules/av1/module.mk
new file mode 100644
index 0000000..146583f
--- /dev/null
+++ b/modules/av1/module.mk
@@ -0,0 +1,13 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 - 2016 Creytiv.com
+#
+
+MOD := av1
+$(MOD)_SRCS += av1.c
+$(MOD)_SRCS += decode.c
+$(MOD)_SRCS += encode.c
+$(MOD)_LFLAGS += -laom
+
+include mk/mod.mk
diff --git a/modules/av1/sdp.c b/modules/av1/sdp.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/modules/av1/sdp.c