1 |
|
|
/* |
2 |
|
|
* Beam Software VB decoder |
3 |
|
|
* Copyright (c) 2007 Konstantin Shishkov |
4 |
|
|
* |
5 |
|
|
* This file is part of FFmpeg. |
6 |
|
|
* |
7 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
8 |
|
|
* modify it under the terms of the GNU Lesser General Public |
9 |
|
|
* License as published by the Free Software Foundation; either |
10 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
* |
12 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
13 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
* Lesser General Public License for more details. |
16 |
|
|
* |
17 |
|
|
* You should have received a copy of the GNU Lesser General Public |
18 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
19 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* VB Video decoder |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include <stdio.h> |
28 |
|
|
#include <stdlib.h> |
29 |
|
|
|
30 |
|
|
#include "avcodec.h" |
31 |
|
|
#include "bytestream.h" |
32 |
|
|
#include "internal.h" |
33 |
|
|
|
34 |
|
|
enum VBFlags { |
35 |
|
|
VB_HAS_GMC = 0x01, |
36 |
|
|
VB_HAS_AUDIO = 0x04, |
37 |
|
|
VB_HAS_VIDEO = 0x08, |
38 |
|
|
VB_HAS_PALETTE = 0x10, |
39 |
|
|
VB_HAS_LENGTH = 0x20 |
40 |
|
|
}; |
41 |
|
|
|
42 |
|
|
typedef struct VBDecContext { |
43 |
|
|
AVCodecContext *avctx; |
44 |
|
|
|
45 |
|
|
uint8_t *frame, *prev_frame; |
46 |
|
|
uint32_t pal[AVPALETTE_COUNT]; |
47 |
|
|
GetByteContext stream; |
48 |
|
|
} VBDecContext; |
49 |
|
|
|
50 |
|
|
static const uint16_t vb_patterns[64] = { |
51 |
|
|
0x0660, 0xFF00, 0xCCCC, 0xF000, 0x8888, 0x000F, 0x1111, 0xFEC8, |
52 |
|
|
0x8CEF, 0x137F, 0xF731, 0xC800, 0x008C, 0x0013, 0x3100, 0xCC00, |
53 |
|
|
0x00CC, 0x0033, 0x3300, 0x0FF0, 0x6666, 0x00F0, 0x0F00, 0x2222, |
54 |
|
|
0x4444, 0xF600, 0x8CC8, 0x006F, 0x1331, 0x318C, 0xC813, 0x33CC, |
55 |
|
|
0x6600, 0x0CC0, 0x0066, 0x0330, 0xF900, 0xC88C, 0x009F, 0x3113, |
56 |
|
|
0x6000, 0x0880, 0x0006, 0x0110, 0xCC88, 0xFC00, 0x00CF, 0x88CC, |
57 |
|
|
0x003F, 0x1133, 0x3311, 0xF300, 0x6FF6, 0x0603, 0x08C6, 0x8C63, |
58 |
|
|
0xC631, 0x6310, 0xC060, 0x0136, 0x136C, 0x36C8, 0x6C80, 0x324C |
59 |
|
|
}; |
60 |
|
|
|
61 |
|
1 |
static void vb_decode_palette(VBDecContext *c, int data_size) |
62 |
|
|
{ |
63 |
|
|
int start, size, i; |
64 |
|
|
|
65 |
|
1 |
start = bytestream2_get_byte(&c->stream); |
66 |
|
1 |
size = (bytestream2_get_byte(&c->stream) - 1) & 0xFF; |
67 |
✗✓ |
1 |
if (start + size > 255) { |
68 |
|
|
av_log(c->avctx, AV_LOG_ERROR, "Palette change runs beyond entry 256\n"); |
69 |
|
|
return; |
70 |
|
|
} |
71 |
✗✓ |
1 |
if (size*3+2 > data_size) { |
72 |
|
|
av_log(c->avctx, AV_LOG_ERROR, "Palette data runs beyond chunk size\n"); |
73 |
|
|
return; |
74 |
|
|
} |
75 |
✓✓ |
237 |
for (i = start; i <= start + size; i++) |
76 |
|
236 |
c->pal[i] = 0xFFU << 24 | bytestream2_get_be24(&c->stream); |
77 |
|
|
} |
78 |
|
|
|
79 |
|
67819 |
static inline int check_pixel(uint8_t *buf, uint8_t *start, uint8_t *end) |
80 |
|
|
{ |
81 |
✓✗✓✗
|
67819 |
return buf >= start && buf < end; |
82 |
|
|
} |
83 |
|
|
|
84 |
|
369468 |
static inline int check_line(uint8_t *buf, uint8_t *start, uint8_t *end) |
85 |
|
|
{ |
86 |
✓✗✓✗
|
369468 |
return buf >= start && (buf + 4) <= end; |
87 |
|
|
} |
88 |
|
|
|
89 |
|
37 |
static int vb_decode_framedata(VBDecContext *c, int offset) |
90 |
|
|
{ |
91 |
|
|
GetByteContext g; |
92 |
|
|
uint8_t *prev, *cur; |
93 |
|
|
int blk, blocks, t, blk2; |
94 |
|
37 |
int blocktypes = 0; |
95 |
|
|
int x, y, a, b; |
96 |
|
|
int pattype, pattern; |
97 |
|
37 |
const int width = c->avctx->width; |
98 |
|
37 |
uint8_t *pstart = c->prev_frame; |
99 |
|
37 |
uint8_t *pend = c->prev_frame + width*c->avctx->height; |
100 |
|
|
|
101 |
|
37 |
g = c->stream; |
102 |
|
|
|
103 |
|
37 |
prev = c->prev_frame + offset; |
104 |
|
37 |
cur = c->frame; |
105 |
|
|
|
106 |
|
37 |
blocks = (c->avctx->width >> 2) * (c->avctx->height >> 2); |
107 |
|
37 |
blk2 = 0; |
108 |
✓✓ |
177637 |
for (blk = 0; blk < blocks; blk++) { |
109 |
✓✓ |
177600 |
if (!(blk & 3)) { |
110 |
✗✓ |
44400 |
if (bytestream2_get_bytes_left(&g) < 1) { |
111 |
|
|
av_log(c->avctx, AV_LOG_ERROR, "Insufficient data\n"); |
112 |
|
|
return AVERROR_INVALIDDATA; |
113 |
|
|
} |
114 |
|
44400 |
blocktypes = bytestream2_get_byte(&g); |
115 |
|
|
} |
116 |
✓✓✓✓ ✗ |
177600 |
switch (blocktypes & 0xC0) { |
117 |
|
14413 |
case 0x00: //skip |
118 |
✓✓ |
72065 |
for (y = 0; y < 4; y++) |
119 |
✓✗ |
57652 |
if (check_line(prev + y*width, pstart, pend)) |
120 |
|
57652 |
memcpy(cur + y*width, prev + y*width, 4); |
121 |
|
|
else |
122 |
|
|
memset(cur + y*width, 0, 4); |
123 |
|
14413 |
break; |
124 |
|
93230 |
case 0x40: |
125 |
|
93230 |
t = bytestream2_get_byte(&g); |
126 |
✓✓ |
93230 |
if (!t) { //raw block |
127 |
✗✓ |
15276 |
if (bytestream2_get_bytes_left(&g) < 16) { |
128 |
|
|
av_log(c->avctx, AV_LOG_ERROR, "Insufficient data\n"); |
129 |
|
|
return AVERROR_INVALIDDATA; |
130 |
|
|
} |
131 |
✓✓ |
76380 |
for (y = 0; y < 4; y++) |
132 |
|
61104 |
bytestream2_get_buffer(&g, cur + y * width, 4); |
133 |
|
|
} else { // motion compensation |
134 |
|
77954 |
x = ((t & 0xF)^8) - 8; |
135 |
|
77954 |
y = ((t >> 4) ^8) - 8; |
136 |
|
77954 |
t = x + y*width; |
137 |
✓✓ |
389770 |
for (y = 0; y < 4; y++) |
138 |
✓✗ |
311816 |
if (check_line(prev + t + y*width, pstart, pend)) |
139 |
|
311816 |
memcpy(cur + y*width, prev + t + y*width, 4); |
140 |
|
|
else |
141 |
|
|
memset(cur + y*width, 0, 4); |
142 |
|
|
} |
143 |
|
93230 |
break; |
144 |
|
11328 |
case 0x80: // fill |
145 |
|
11328 |
t = bytestream2_get_byte(&g); |
146 |
✓✓ |
56640 |
for (y = 0; y < 4; y++) |
147 |
|
45312 |
memset(cur + y*width, t, 4); |
148 |
|
11328 |
break; |
149 |
|
58629 |
case 0xC0: // pattern fill |
150 |
|
58629 |
t = bytestream2_get_byte(&g); |
151 |
|
58629 |
pattype = t >> 6; |
152 |
✓✓✓✗ ✗ |
58629 |
pattern = vb_patterns[t & 0x3F]; |
153 |
|
|
switch (pattype) { |
154 |
|
51372 |
case 0: |
155 |
|
51372 |
a = bytestream2_get_byte(&g); |
156 |
|
51372 |
b = bytestream2_get_byte(&g); |
157 |
✓✓ |
256860 |
for (y = 0; y < 4; y++) |
158 |
✓✓ |
1027440 |
for (x = 0; x < 4; x++, pattern >>= 1) |
159 |
✓✓ |
821952 |
cur[x + y*width] = (pattern & 1) ? b : a; |
160 |
|
51372 |
break; |
161 |
|
4746 |
case 1: |
162 |
|
4746 |
pattern = ~pattern; |
163 |
|
7257 |
case 2: |
164 |
|
7257 |
a = bytestream2_get_byte(&g); |
165 |
✓✓ |
36285 |
for (y = 0; y < 4; y++) |
166 |
✓✓ |
145140 |
for (x = 0; x < 4; x++, pattern >>= 1) |
167 |
✓✓✓✗
|
116112 |
if (pattern & 1 && check_pixel(prev + x + y*width, pstart, pend)) |
168 |
|
67819 |
cur[x + y*width] = prev[x + y*width]; |
169 |
|
|
else |
170 |
|
48293 |
cur[x + y*width] = a; |
171 |
|
7257 |
break; |
172 |
|
|
case 3: |
173 |
|
|
av_log(c->avctx, AV_LOG_ERROR, "Invalid opcode seen @%d\n", blk); |
174 |
|
|
return AVERROR_INVALIDDATA; |
175 |
|
|
} |
176 |
|
58629 |
break; |
177 |
|
|
} |
178 |
|
177600 |
blocktypes <<= 2; |
179 |
|
177600 |
cur += 4; |
180 |
|
177600 |
prev += 4; |
181 |
|
177600 |
blk2++; |
182 |
✓✓ |
177600 |
if (blk2 == (width >> 2)) { |
183 |
|
2220 |
blk2 = 0; |
184 |
|
2220 |
cur += width * 3; |
185 |
|
2220 |
prev += width * 3; |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
37 |
return 0; |
189 |
|
|
} |
190 |
|
|
|
191 |
|
37 |
static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, |
192 |
|
|
AVPacket *avpkt) |
193 |
|
|
{ |
194 |
|
37 |
VBDecContext * const c = avctx->priv_data; |
195 |
|
37 |
AVFrame *frame = data; |
196 |
|
|
uint8_t *outptr, *srcptr; |
197 |
|
|
int i, j, ret; |
198 |
|
|
int flags; |
199 |
|
|
uint32_t size; |
200 |
|
37 |
int offset = 0; |
201 |
|
|
|
202 |
✗✓ |
37 |
if (avpkt->size < 2) |
203 |
|
|
return AVERROR_INVALIDDATA; |
204 |
|
|
|
205 |
|
37 |
bytestream2_init(&c->stream, avpkt->data, avpkt->size); |
206 |
|
|
|
207 |
✗✓ |
37 |
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
208 |
|
|
return ret; |
209 |
|
|
|
210 |
|
37 |
flags = bytestream2_get_le16(&c->stream); |
211 |
|
|
|
212 |
✓✓ |
37 |
if (flags & VB_HAS_GMC) { |
213 |
|
8 |
i = (int16_t)bytestream2_get_le16(&c->stream); |
214 |
|
8 |
j = (int16_t)bytestream2_get_le16(&c->stream); |
215 |
✗✓ |
8 |
if (FFABS(j) > avctx->height) { |
216 |
|
|
av_log(avctx, AV_LOG_ERROR, "GMV out of range\n"); |
217 |
|
|
return AVERROR_INVALIDDATA; |
218 |
|
|
} |
219 |
|
8 |
offset = i + j * avctx->width; |
220 |
|
|
} |
221 |
✓✗ |
37 |
if (flags & VB_HAS_VIDEO) { |
222 |
|
37 |
size = bytestream2_get_le32(&c->stream); |
223 |
✓✗✗✓
|
37 |
if(size > bytestream2_get_bytes_left(&c->stream)+4 || size<4){ |
224 |
|
|
av_log(avctx, AV_LOG_ERROR, "Frame size invalid\n"); |
225 |
|
|
return -1; |
226 |
|
|
} |
227 |
|
37 |
vb_decode_framedata(c, offset); |
228 |
|
37 |
bytestream2_skip(&c->stream, size - 4); |
229 |
|
|
} |
230 |
✓✓ |
37 |
if (flags & VB_HAS_PALETTE) { |
231 |
|
1 |
size = bytestream2_get_le32(&c->stream); |
232 |
|
1 |
vb_decode_palette(c, size); |
233 |
|
|
} |
234 |
|
|
|
235 |
|
37 |
memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); |
236 |
|
37 |
frame->palette_has_changed = flags & VB_HAS_PALETTE; |
237 |
|
|
|
238 |
|
37 |
outptr = frame->data[0]; |
239 |
|
37 |
srcptr = c->frame; |
240 |
|
|
|
241 |
✓✓ |
8917 |
for (i = 0; i < avctx->height; i++) { |
242 |
|
8880 |
memcpy(outptr, srcptr, avctx->width); |
243 |
|
8880 |
srcptr += avctx->width; |
244 |
|
8880 |
outptr += frame->linesize[0]; |
245 |
|
|
} |
246 |
|
|
|
247 |
|
37 |
FFSWAP(uint8_t*, c->frame, c->prev_frame); |
248 |
|
|
|
249 |
|
37 |
*got_frame = 1; |
250 |
|
|
|
251 |
|
|
/* always report that the buffer was completely consumed */ |
252 |
|
37 |
return avpkt->size; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
3 |
static av_cold int decode_init(AVCodecContext *avctx) |
256 |
|
|
{ |
257 |
|
3 |
VBDecContext * const c = avctx->priv_data; |
258 |
|
|
|
259 |
|
3 |
c->avctx = avctx; |
260 |
|
3 |
avctx->pix_fmt = AV_PIX_FMT_PAL8; |
261 |
|
|
|
262 |
|
3 |
c->frame = av_mallocz(avctx->width * avctx->height); |
263 |
|
3 |
c->prev_frame = av_mallocz(avctx->width * avctx->height); |
264 |
|
|
|
265 |
✓✗✗✓
|
3 |
if (!c->frame || !c->prev_frame) |
266 |
|
|
return AVERROR(ENOMEM); |
267 |
|
|
|
268 |
|
3 |
return 0; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
3 |
static av_cold int decode_end(AVCodecContext *avctx) |
272 |
|
|
{ |
273 |
|
3 |
VBDecContext *c = avctx->priv_data; |
274 |
|
|
|
275 |
|
3 |
av_freep(&c->frame); |
276 |
|
3 |
av_freep(&c->prev_frame); |
277 |
|
|
|
278 |
|
3 |
return 0; |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
AVCodec ff_vb_decoder = { |
282 |
|
|
.name = "vb", |
283 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Beam Software VB"), |
284 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
285 |
|
|
.id = AV_CODEC_ID_VB, |
286 |
|
|
.priv_data_size = sizeof(VBDecContext), |
287 |
|
|
.init = decode_init, |
288 |
|
|
.close = decode_end, |
289 |
|
|
.decode = decode_frame, |
290 |
|
|
.capabilities = AV_CODEC_CAP_DR1, |
291 |
|
|
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP, |
292 |
|
|
}; |