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