From 3bb22d2bb13a1700617b6c40eac08811a1b5b775 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Sat, 4 Jun 2016 23:22:55 +0200 Subject: vp9: add VP9 video codec --- modules/vp9/decode.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 modules/vp9/decode.c (limited to 'modules/vp9/decode.c') diff --git a/modules/vp9/decode.c b/modules/vp9/decode.c new file mode 100644 index 0000000..38b2bbc --- /dev/null +++ b/modules/vp9/decode.c @@ -0,0 +1,266 @@ +/** + * @file vp9/decode.c VP9 Decode + * + * Copyright (C) 2010 - 2016 Creytiv.com + */ + +#include +#include +#include +#include +#include +#include +#include "vp9.h" + + +enum { + DECODE_MAXSZ = 524288, +}; + + +struct hdr { + /* header: */ + unsigned i:1; /* I: Picture ID (PID) present */ + unsigned p:1; /* P: Inter-picture predicted layer frame */ + unsigned l:1; /* L: Layer indices present */ + unsigned f:1; /* F: Flexible mode */ + unsigned b:1; /* B: Start of a layer frame */ + unsigned e:1; /* E: End of a layer frame */ + unsigned v:1; /* V: Scalability structure (SS) data present */ + + /* extension fields */ + uint16_t picid; +}; + +struct viddec_state { + vpx_codec_ctx_t ctx; + struct mbuf *mb; + bool ctxup; + bool started; + uint16_t seq; + + unsigned n_frames; + size_t n_bytes; +}; + + +static void destructor(void *arg) +{ + struct viddec_state *vds = arg; + + re_printf("vp9: decoder: total frames %u, total bytes %zu\n", + vds->n_frames, vds->n_bytes); + + if (vds->ctxup) + vpx_codec_destroy(&vds->ctx); + + mem_deref(vds->mb); +} + + +int vp9_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc, + const char *fmtp) +{ + struct viddec_state *vds; + vpx_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 = vpx_codec_dec_init(&vds->ctx, &vpx_codec_vp9_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->i = v>>7 & 0x1; + hdr->p = v>>6 & 0x1; + hdr->l = v>>5 & 0x1; + hdr->f = v>>4 & 0x1; + hdr->b = v>>3 & 0x1; + hdr->e = v>>2 & 0x1; + hdr->v = v>>1 & 0x1; + + if (hdr->p) { + warning("vp9: decode: P-bit not supported\n"); + return EPROTO; + } + if (hdr->l) { + warning("vp9: decode: L-bit not supported\n"); + return EPROTO; + } + if (hdr->f) { + warning("vp9: decode: F-bit not supported\n"); + return EPROTO; + } + if (hdr->v) { + warning("vp9: decode: V-bit not supported\n"); + return EPROTO; + } + + 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; + } + } + + return 0; +} + + +static inline int16_t seq_diff(uint16_t x, uint16_t y) +{ + return (int16_t)(y - x); +} + + +int vp9_decode(struct viddec_state *vds, struct vidframe *frame, + bool marker, uint16_t seq, struct mbuf *mb) +{ + vpx_codec_iter_t iter = NULL; + vpx_codec_err_t res; + vpx_image_t *img; + struct hdr hdr; + int err, i; + + if (!vds || !frame || !mb) + return EINVAL; + + vds->n_bytes += mbuf_get_left(mb); + + err = hdr_decode(&hdr, mb); + if (err) + return err; + +#if 1 + debug("vp9: [%c] header: i=%u start=%u end=%u " + "picid=%u \n", + marker ? 'M' : ' ', + hdr.i, hdr.b, hdr.e, + hdr.picid); +#endif + + if (hdr.b) { + + 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("vp9: decode buffer size exceeded\n"); + err = ENOMEM; + goto out; + } + + return 0; + } + + res = vpx_codec_decode(&vds->ctx, vds->mb->buf, + (unsigned int)vds->mb->end, NULL, 1); + if (res) { + debug("vp9: decode error: %s\n", vpx_codec_err_to_string(res)); + err = EPROTO; + goto out; + } + + img = vpx_codec_get_frame(&vds->ctx, &iter); + if (!img) { + debug("vp9: no picture\n"); + goto out; + } + + if (img->fmt != VPX_IMG_FMT_I420) { + warning("vp9: 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; + + ++vds->n_frames; + + out: + mbuf_rewind(vds->mb); + vds->started = false; + + return err; +} -- cgit v1.2.3