diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/av1/av1.c | 51 | ||||
-rw-r--r-- | modules/av1/av1.h | 20 | ||||
-rw-r--r-- | modules/av1/decode.c | 293 | ||||
-rw-r--r-- | modules/av1/encode.c | 255 | ||||
-rw-r--r-- | modules/av1/module.mk | 13 | ||||
-rw-r--r-- | modules/av1/sdp.c | 0 |
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 |