1 /**
2  * @file vp8/decode.c VP8 Decode
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 
7 #include <string.h>
8 #include <re.h>
9 #include <rem.h>
10 #include <baresip.h>
11 #include <vpx/vpx_decoder.h>
12 #include <vpx/vp8dx.h>
13 #include "vp8.h"
14 
15 
16 enum {
17 	DECODE_MAXSZ = 524288,
18 };
19 
20 
21 struct hdr {
22 	unsigned x:1;
23 	unsigned noref:1;
24 	unsigned start:1;
25 	unsigned partid:4;
26 	/* extension fields */
27 	unsigned i:1;
28 	unsigned l:1;
29 	unsigned t:1;
30 	unsigned k:1;
31 	uint16_t picid;
32 	uint8_t tl0picidx;
33 	unsigned tid:2;
34 	unsigned y:1;
35 	unsigned keyidx:5;
36 };
37 
38 struct viddec_state {
39 	vpx_codec_ctx_t ctx;
40 	struct mbuf *mb;
41 	bool ctxup;
42 	bool started;
43 	uint16_t seq;
44 };
45 
46 
destructor(void * arg)47 static void destructor(void *arg)
48 {
49 	struct viddec_state *vds = arg;
50 
51 	if (vds->ctxup)
52 		vpx_codec_destroy(&vds->ctx);
53 
54 	mem_deref(vds->mb);
55 }
56 
57 
vp8_decode_update(struct viddec_state ** vdsp,const struct vidcodec * vc,const char * fmtp)58 int vp8_decode_update(struct viddec_state **vdsp, const struct vidcodec *vc,
59 		       const char *fmtp)
60 {
61 	struct viddec_state *vds;
62 	vpx_codec_err_t res;
63 	int err = 0;
64 	(void)vc;
65 	(void)fmtp;
66 
67 	if (!vdsp)
68 		return EINVAL;
69 
70 	vds = *vdsp;
71 
72 	if (vds)
73 		return 0;
74 
75 	vds = mem_zalloc(sizeof(*vds), destructor);
76 	if (!vds)
77 		return ENOMEM;
78 
79 	vds->mb = mbuf_alloc(1024);
80 	if (!vds->mb) {
81 		err = ENOMEM;
82 		goto out;
83 	}
84 
85 	res = vpx_codec_dec_init(&vds->ctx, &vpx_codec_vp8_dx_algo, NULL, 0);
86 	if (res) {
87 		err = ENOMEM;
88 		goto out;
89 	}
90 
91 	vds->ctxup = true;
92 
93  out:
94 	if (err)
95 		mem_deref(vds);
96 	else
97 		*vdsp = vds;
98 
99 	return err;
100 }
101 
102 
hdr_decode(struct hdr * hdr,struct mbuf * mb)103 static inline int hdr_decode(struct hdr *hdr, struct mbuf *mb)
104 {
105 	uint8_t v;
106 
107 	memset(hdr, 0, sizeof(*hdr));
108 
109 	if (mbuf_get_left(mb) < 1)
110 		return EBADMSG;
111 
112 	v = mbuf_read_u8(mb);
113 
114 	hdr->x      = v>>7 & 0x1;
115 	hdr->noref  = v>>5 & 0x1;
116 	hdr->start  = v>>4 & 0x1;
117 	hdr->partid = v    & 0x07;
118 
119 	if (hdr->x) {
120 
121 		if (mbuf_get_left(mb) < 1)
122 			return EBADMSG;
123 
124 		v = mbuf_read_u8(mb);
125 
126 		hdr->i = v>>7 & 0x1;
127 		hdr->l = v>>6 & 0x1;
128 		hdr->t = v>>5 & 0x1;
129 		hdr->k = v>>4 & 0x1;
130 	}
131 
132 	if (hdr->i) {
133 
134 		if (mbuf_get_left(mb) < 1)
135 			return EBADMSG;
136 
137 		v = mbuf_read_u8(mb);
138 
139 		if (v>>7 & 0x1) {
140 
141 			if (mbuf_get_left(mb) < 1)
142 				return EBADMSG;
143 
144 			hdr->picid  = (v & 0x7f)<<8;
145 			hdr->picid += mbuf_read_u8(mb);
146 		}
147 		else {
148 			hdr->picid = v & 0x7f;
149 		}
150 	}
151 
152 	if (hdr->l) {
153 
154 		if (mbuf_get_left(mb) < 1)
155 			return EBADMSG;
156 
157 		hdr->tl0picidx = mbuf_read_u8(mb);
158 	}
159 
160 	if (hdr->t || hdr->k) {
161 
162 		if (mbuf_get_left(mb) < 1)
163 			return EBADMSG;
164 
165 		v = mbuf_read_u8(mb);
166 
167 		hdr->tid    = v>>6 & 0x3;
168 		hdr->y      = v>>5 & 0x1;
169 		hdr->keyidx = v    & 0x1f;
170 	}
171 
172 	return 0;
173 }
174 
175 
is_keyframe(struct mbuf * mb)176 static inline bool is_keyframe(struct mbuf *mb)
177 {
178 	if (mbuf_get_left(mb) < 1)
179 		return false;
180 
181 	if (mb->buf[mb->pos] & 0x01)
182 		return false;
183 
184 	return true;
185 }
186 
187 
seq_diff(uint16_t x,uint16_t y)188 static inline int16_t seq_diff(uint16_t x, uint16_t y)
189 {
190 	return (int16_t)(y - x);
191 }
192 
193 
vp8_decode(struct viddec_state * vds,struct vidframe * frame,bool * intra,bool marker,uint16_t seq,struct mbuf * mb)194 int vp8_decode(struct viddec_state *vds, struct vidframe *frame,
195 	       bool *intra, bool marker, uint16_t seq, struct mbuf *mb)
196 {
197 	vpx_codec_iter_t iter = NULL;
198 	vpx_codec_err_t res;
199 	vpx_image_t *img;
200 	struct hdr hdr;
201 	int err, i;
202 
203 	if (!vds || !frame || !intra || !mb)
204 		return EINVAL;
205 
206 	*intra = false;
207 
208 	err = hdr_decode(&hdr, mb);
209 	if (err)
210 		return err;
211 
212 #if 0
213 	debug("vp8: header: x=%u noref=%u start=%u partid=%u "
214 	      "i=%u l=%u t=%u k=%u "
215 	      "picid=%u tl0picidx=%u tid=%u y=%u keyidx=%u\n",
216 	      hdr.x, hdr.noref, hdr.start, hdr.partid,
217 	      hdr.i, hdr.l, hdr.t, hdr.k,
218 	      hdr.picid, hdr.tl0picidx, hdr.tid, hdr.y, hdr.keyidx);
219 #endif
220 
221 	if (hdr.start && hdr.partid == 0) {
222 
223 		if (is_keyframe(mb))
224 			*intra = true;
225 
226 		mbuf_rewind(vds->mb);
227 		vds->started = true;
228 	}
229 	else {
230 		if (!vds->started)
231 			return 0;
232 
233 		if (seq_diff(vds->seq, seq) != 1) {
234 			mbuf_rewind(vds->mb);
235 			vds->started = false;
236 			return 0;
237 		}
238 	}
239 
240 	vds->seq = seq;
241 
242 	err = mbuf_write_mem(vds->mb, mbuf_buf(mb), mbuf_get_left(mb));
243 	if (err)
244 		goto out;
245 
246 	if (!marker) {
247 
248 		if (vds->mb->end > DECODE_MAXSZ) {
249 			warning("vp8: decode buffer size exceeded\n");
250 			err = ENOMEM;
251 			goto out;
252 		}
253 
254 		return 0;
255 	}
256 
257 	res = vpx_codec_decode(&vds->ctx, vds->mb->buf,
258 			       (unsigned int)vds->mb->end, NULL, 1);
259 	if (res) {
260 		debug("vp8: decode error: %s\n", vpx_codec_err_to_string(res));
261 		err = EPROTO;
262 		goto out;
263 	}
264 
265 	img = vpx_codec_get_frame(&vds->ctx, &iter);
266 	if (!img) {
267 		debug("vp8: no picture\n");
268 		goto out;
269 	}
270 
271 	if (img->fmt != VPX_IMG_FMT_I420) {
272 		warning("vp8: bad pixel format (%i)\n", img->fmt);
273 		goto out;
274 	}
275 
276 	for (i=0; i<4; i++) {
277 		frame->data[i]     = img->planes[i];
278 		frame->linesize[i] = img->stride[i];
279 	}
280 
281 	frame->size.w = img->d_w;
282 	frame->size.h = img->d_h;
283 	frame->fmt    = VID_FMT_YUV420P;
284 
285  out:
286 	mbuf_rewind(vds->mb);
287 	vds->started = false;
288 
289 	return err;
290 }
291