diff options
author | Alfred E. Heggestad <aeh@db.org> | 2015-06-28 12:04:30 +0200 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2015-06-28 12:04:30 +0200 |
commit | 502385d05c9a5b962f6636dd19df6683bd29860f (patch) | |
tree | 05949bc573995ffdedbdafefa4f17b2e31885fb1 /modules | |
parent | 8b5ced23d0986ccb510c2c9b1eb59bda2200fe81 (diff) |
daala: new experimental video-codec module
Diffstat (limited to 'modules')
-rw-r--r-- | modules/daala/daala.c | 62 | ||||
-rw-r--r-- | modules/daala/daala.h | 20 | ||||
-rw-r--r-- | modules/daala/decode.c | 177 | ||||
-rw-r--r-- | modules/daala/encode.c | 276 | ||||
-rw-r--r-- | modules/daala/module.mk | 13 |
5 files changed, 548 insertions, 0 deletions
diff --git a/modules/daala/daala.c b/modules/daala/daala.c new file mode 100644 index 0000000..130ca59 --- /dev/null +++ b/modules/daala/daala.c @@ -0,0 +1,62 @@ +/** + * @file daala.c Experimental video-codec using Daala + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +#include <re.h> +#include <baresip.h> +#include <daala/codec.h> +#include "daala.h" + + +/** + * @defgroup daala daala + * + * Very experimental video-codec using Daala + * + * TODO: + * + * - Define and implement fragmentation of large packets + * - Define an IETF RTP Payload type + * + * + * References: + * + * https://wiki.xiph.org/Daala + */ + + +static struct vidcodec daala = { + .name = "daala", + .encupdh = daala_encode_update, + .ench = daala_encode, + .decupdh = daala_decode_update, + .dech = daala_decode, +}; + + +static int module_init(void) +{ + info("daala: using version '%s'\n", daala_version_string()); + + vidcodec_register(&daala); + + return 0; +} + + +static int module_close(void) +{ + vidcodec_unregister(&daala); + + return 0; +} + + +EXPORT_SYM const struct mod_export DECL_EXPORTS(daala) = { + "daala", + "video codec", + module_init, + module_close, +}; diff --git a/modules/daala/daala.h b/modules/daala/daala.h new file mode 100644 index 0000000..3649253 --- /dev/null +++ b/modules/daala/daala.h @@ -0,0 +1,20 @@ +/** + * @file daala.h Experimental video-codec using Daala -- internal api + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + + +/* Encode */ +int daala_encode_update(struct videnc_state **vesp, const struct vidcodec *vc, + struct videnc_param *prm, const char *fmtp, + videnc_packet_h *pkth, void *arg); +int daala_encode(struct videnc_state *ves, bool update, + const struct vidframe *frame); + + +/* Decode */ +int daala_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc, + const char *fmtp); +int daala_decode(struct viddec_state *vds, struct vidframe *frame, + bool marker, uint16_t seq, struct mbuf *mb); diff --git a/modules/daala/decode.c b/modules/daala/decode.c new file mode 100644 index 0000000..646605e --- /dev/null +++ b/modules/daala/decode.c @@ -0,0 +1,177 @@ +/** + * @file daala/decode.c Experimental video-codec using Daala -- decoder + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +#include <string.h> +#include <re.h> +#include <rem.h> +#include <baresip.h> +#include <daala/daaladec.h> +#include "daala.h" + + +struct viddec_state { + daala_dec_ctx *dec; + + bool got_headers; + + daala_info di; + daala_comment dc; + daala_setup_info *ds; + + struct { + bool valid; + size_t n_frame; + size_t n_header; + size_t n_keyframe; + size_t n_packet; + } stats; +}; + + +static void dump_stats(const struct viddec_state *vds) +{ + re_printf("~~~~~ Daala Decoder stats ~~~~~\n"); + re_printf("num frames: %zu\n", vds->stats.n_frame); + re_printf("num headers: %zu\n", vds->stats.n_header); + re_printf("key-frames packets: %zu\n", vds->stats.n_keyframe); + re_printf("total packets: %zu\n", vds->stats.n_packet); +} + + +static void destructor(void *arg) +{ + struct viddec_state *vds = arg; + + if (vds->stats.valid) + dump_stats(vds); + + if (vds->dec) + daala_decode_free(vds->dec); + + if (vds->ds) + daala_setup_free(vds->ds); + daala_comment_clear(&vds->dc); + daala_info_clear(&vds->di); +} + + +int daala_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc, + const char *fmtp) +{ + struct viddec_state *vds; + 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; + + daala_info_init(&vds->di); + daala_comment_init(&vds->dc); + + if (err) + mem_deref(vds); + else + *vdsp = vds; + + return err; +} + + +int daala_decode(struct viddec_state *vds, struct vidframe *frame, + bool marker, uint16_t seq, struct mbuf *mb) +{ + ogg_packet op; + bool ishdr; + int i, r, err = 0; + (void)seq; + + if (!vds || !frame || !mb) + return EINVAL; + + ++vds->stats.n_packet; + ++vds->stats.valid; + + ishdr = daala_packet_isheader(mbuf_buf(mb), mbuf_get_left(mb)); + + if (ishdr) + ++vds->stats.n_header; + else if (daala_packet_iskeyframe(mbuf_buf(mb), mbuf_get_left(mb)) > 0) + ++vds->stats.n_keyframe; + +#if 0 + re_printf("decode: [%s] %zu bytes\n", + ishdr ? "HEADER" : "DATA", + mbuf_get_left(mb)); +#endif + + memset(&op, 0, sizeof(op)); + + op.packet = mbuf_buf(mb); + op.bytes = mbuf_get_left(mb); + op.b_o_s = marker; + + if (daala_packet_isheader(mbuf_buf(mb), mbuf_get_left(mb))) { + + r = daala_decode_header_in(&vds->di, &vds->dc, &vds->ds, + &op); + if (r < 0) { + warning("daala: decoder: decode_header_in failed" + " (ret = %d)\n", + r); + return EPROTO; + } + else if (r == 0) { + vds->got_headers = true; + info("daala: all headers received\n"); + + vds->dec = daala_decode_alloc(&vds->di, vds->ds); + if (!vds->dec) { + warning("daala: decoder: alloc failed\n"); + return ENOMEM; + } + } + else { + /* waiting for more headers */ + } + } + else { + od_img img; + + if (!vds->got_headers) { + warning("daala: decode: still waiting for headers\n"); + return EPROTO; + } + + r = daala_decode_packet_in(vds->dec, &img, &op); + if (r < 0) { + warning("daala: decode: packet_in error (%d)\n", r); + return EPROTO; + } + + for (i=0; i<3; i++) { + frame->data[i] = img.planes[i].data; + frame->linesize[i] = img.planes[i].ystride; + } + + frame->size.w = img.width; + frame->size.h = img.height; + frame->fmt = VID_FMT_YUV420P; + + ++vds->stats.n_frame; + } + + return err; +} diff --git a/modules/daala/encode.c b/modules/daala/encode.c new file mode 100644 index 0000000..338efcf --- /dev/null +++ b/modules/daala/encode.c @@ -0,0 +1,276 @@ +/** + * @file daala/encode.c Experimental video-codec using Daala -- encoder + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ + +#include <string.h> +#include <re.h> +#include <rem.h> +#include <baresip.h> +#include <daala/daalaenc.h> +#include "daala.h" + + +struct videnc_state { + struct vidsz size; + daala_enc_ctx *enc; + int64_t pts; + unsigned fps; + unsigned bitrate; + unsigned pktsize; + videnc_packet_h *pkth; + void *arg; + + struct { + bool valid; + size_t n_frame; + size_t n_header; + size_t n_keyframe; + size_t n_packet; + } stats; +}; + + +static void dump_stats(const struct videnc_state *ves) +{ + re_printf("~~~~~ Daala Encoder stats ~~~~~\n"); + re_printf("num frames: %zu\n", ves->stats.n_frame); + re_printf("num headers: %zu\n", ves->stats.n_header); + re_printf("key-frames packets: %zu\n", ves->stats.n_keyframe); + re_printf("total packets: %zu\n", ves->stats.n_packet); +} + + +static int send_packet(struct videnc_state *ves, bool marker, + const uint8_t *pld, size_t pld_len) +{ + int err; + + err = ves->pkth(marker, NULL, 0, pld, pld_len, ves->arg); + if (err) + return err; + + ++ves->stats.n_packet; + ++ves->stats.valid; + + if (daala_packet_isheader(pld, pld_len)) + ++ves->stats.n_header; + else if (daala_packet_iskeyframe(pld, pld_len) > 0) + ++ves->stats.n_keyframe; + + return 0; +} + + +static void destructor(void *arg) +{ + struct videnc_state *ves = arg; + + if (ves->stats.valid) + dump_stats(ves); + + if (ves->enc) + daala_encode_free(ves->enc); +} + + +int daala_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; + (void)fmtp; + + if (!vesp || !vc || !prm || prm->pktsize < 3 || !pkth) + return EINVAL; + + ves = *vesp; + + if (!ves) { + + ves = mem_zalloc(sizeof(*ves), destructor); + if (!ves) + return ENOMEM; + + *vesp = ves; + } + else { + if (ves->enc && (ves->bitrate != prm->bitrate || + ves->pktsize != prm->pktsize || + ves->fps != prm->fps)) { + + info("daala: encoder: params changed\n"); + + daala_encode_free(ves->enc); + ves->enc = NULL; + } + } + + 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) +{ + daala_info di; + daala_comment dc; + ogg_packet op; + int err = 0; + int complexity = 0; + int video_q = 10; + + info("daala: open encoder (%d x %d)\n", size->w, size->h); + + if (ves->enc) { + debug("daala: re-opening encoder\n"); + daala_encode_free(ves->enc); + } + + daala_info_init(&di); + daala_comment_init(&dc); + + di.pic_width = size->w; + di.pic_height = size->h; + di.timebase_numerator = 1; + di.timebase_denominator = ves->fps; + di.frame_duration = 1; + di.pixel_aspect_numerator = -1; + di.pixel_aspect_denominator = -1; + di.nplanes = 3; + di.plane_info[0].xdec = 0; /* YUV420P */ + di.plane_info[0].ydec = 0; + di.plane_info[1].xdec = 1; + di.plane_info[1].ydec = 1; + di.plane_info[2].xdec = 1; + di.plane_info[2].ydec = 1; + + di.keyframe_rate = 100; + + info("daala: open encoder with bitstream version %u.%u.%u\n", + di.version_major, di.version_minor, di.version_sub); + + ves->enc = daala_encode_create(&di); + if (!ves->enc) { + warning("daala: failed to open DAALA encoder\n"); + return ENOMEM; + } + + daala_encode_ctl(ves->enc, OD_SET_QUANT, + &video_q, sizeof(video_q)); + + daala_encode_ctl(ves->enc, OD_SET_COMPLEXITY, + &complexity, sizeof(complexity)); + + for (;;) { + int r; + + r = daala_encode_flush_header(ves->enc, &dc, &op); + if (r < 0) { + warning("daala: flush_header returned %d\n", r); + break; + } + else if (r == 0) + break; + + debug("daala: header: %lld bytes header=%d key=%d\n", + op.bytes, + daala_packet_isheader(op.packet, op.bytes), + daala_packet_iskeyframe(op.packet, op.bytes)); + +#if 0 + re_printf("bos=%lld, eos=%lld, granule=%lld, packetno=%lld\n", + op.b_o_s, + op.e_o_s, + op.granulepos, + op.packetno); +#endif + + err = send_packet(ves, op.b_o_s, op.packet, op.bytes); + if (err) + break; + } + + daala_info_clear(&di); + daala_comment_clear(&dc); + + return err; +} + + +int daala_encode(struct videnc_state *ves, bool update, + const struct vidframe *frame) +{ + int r, err = 0; + od_img img; + (void)update; /* XXX: how to force a KEY-frame? */ + + if (!ves || !frame || frame->fmt != VID_FMT_YUV420P) + return EINVAL; + + ++ves->stats.n_frame; + + if (!ves->enc || !vidsz_cmp(&ves->size, &frame->size)) { + + err = open_encoder(ves, &frame->size); + if (err) + return err; + + ves->size = frame->size; + } + + img.planes[0].data = frame->data[0]; + img.planes[0].xdec = 0; + img.planes[0].ydec = 0; + img.planes[0].xstride = 1; + img.planes[0].ystride = frame->linesize[0]; + + img.planes[1].data = frame->data[1]; + img.planes[1].xdec = 1; + img.planes[1].ydec = 1; + img.planes[1].xstride = 1; + img.planes[1].ystride = frame->linesize[1]; + + img.planes[2].data = frame->data[2]; + img.planes[2].xdec = 1; + img.planes[2].ydec = 1; + img.planes[2].xstride = 1; + img.planes[2].ystride = frame->linesize[2]; + + img.nplanes = 3; + + img.width = frame->size.w; + img.height = frame->size.h; + + r = daala_encode_img_in(ves->enc, &img, 0); + if (r != 0) { + warning("daala: encoder: encode_img_in failed (ret = %d)\n", + r); + return EPROTO; + } + + for (;;) { + ogg_packet op; + + r = daala_encode_packet_out(ves->enc, 0, &op); + if (r < 0) { + warning("daala: encoder: packet_out ret=%d\n", r); + break; + } + else if (r == 0) { + break; + } + + err = send_packet(ves, op.b_o_s, op.packet, op.bytes); + if (err) + break; + } + + return 0; +} diff --git a/modules/daala/module.mk b/modules/daala/module.mk new file mode 100644 index 0000000..93f88ac --- /dev/null +++ b/modules/daala/module.mk @@ -0,0 +1,13 @@ +# +# module.mk +# +# Copyright (C) 2010 Creytiv.com +# + +MOD := daala +$(MOD)_SRCS += decode.c +$(MOD)_SRCS += encode.c +$(MOD)_SRCS += daala.c +$(MOD)_LFLAGS += -ldaalaenc -ldaaladec -ldaalabase + +include mk/mod.mk |