1 |
|
|
/* |
2 |
|
|
* Microsoft Video-1 Decoder |
3 |
|
|
* Copyright (C) 2003 The FFmpeg project |
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 |
|
|
* Microsoft Video-1 Decoder by Mike Melanson (melanson@pcisys.net) |
25 |
|
|
* For more information about the MS Video-1 format, visit: |
26 |
|
|
* http://www.pcisys.net/~melanson/codecs/ |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <stdio.h> |
30 |
|
|
#include <stdlib.h> |
31 |
|
|
#include <string.h> |
32 |
|
|
|
33 |
|
|
#include "libavutil/internal.h" |
34 |
|
|
#include "libavutil/intreadwrite.h" |
35 |
|
|
#include "avcodec.h" |
36 |
|
|
#include "internal.h" |
37 |
|
|
|
38 |
|
|
#define PALETTE_COUNT 256 |
39 |
|
|
#define CHECK_STREAM_PTR(n) \ |
40 |
|
|
if ((stream_ptr + n) > s->size ) { \ |
41 |
|
|
av_log(s->avctx, AV_LOG_ERROR, " MS Video-1 warning: stream_ptr out of bounds (%d >= %d)\n", \ |
42 |
|
|
stream_ptr + n, s->size); \ |
43 |
|
|
return; \ |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
typedef struct Msvideo1Context { |
47 |
|
|
|
48 |
|
|
AVCodecContext *avctx; |
49 |
|
|
AVFrame *frame; |
50 |
|
|
|
51 |
|
|
const unsigned char *buf; |
52 |
|
|
int size; |
53 |
|
|
|
54 |
|
|
int mode_8bit; /* if it's not 8-bit, it's 16-bit */ |
55 |
|
|
|
56 |
|
|
uint32_t pal[256]; |
57 |
|
|
} Msvideo1Context; |
58 |
|
|
|
59 |
|
13 |
static av_cold int msvideo1_decode_init(AVCodecContext *avctx) |
60 |
|
|
{ |
61 |
|
13 |
Msvideo1Context *s = avctx->priv_data; |
62 |
|
|
|
63 |
|
13 |
s->avctx = avctx; |
64 |
|
|
|
65 |
✓✗✗✓
|
13 |
if (avctx->width < 4 || avctx->height < 4) |
66 |
|
|
return AVERROR_INVALIDDATA; |
67 |
|
|
|
68 |
|
|
/* figure out the colorspace based on the presence of a palette */ |
69 |
✓✓ |
13 |
if (s->avctx->bits_per_coded_sample == 8) { |
70 |
|
5 |
s->mode_8bit = 1; |
71 |
|
5 |
avctx->pix_fmt = AV_PIX_FMT_PAL8; |
72 |
✓✗ |
5 |
if (avctx->extradata_size >= AVPALETTE_SIZE) |
73 |
|
5 |
memcpy(s->pal, avctx->extradata, AVPALETTE_SIZE); |
74 |
|
|
} else { |
75 |
|
8 |
s->mode_8bit = 0; |
76 |
|
8 |
avctx->pix_fmt = AV_PIX_FMT_RGB555; |
77 |
|
|
} |
78 |
|
|
|
79 |
|
13 |
s->frame = av_frame_alloc(); |
80 |
✗✓ |
13 |
if (!s->frame) |
81 |
|
|
return AVERROR(ENOMEM); |
82 |
|
|
|
83 |
|
13 |
return 0; |
84 |
|
|
} |
85 |
|
|
|
86 |
|
147 |
static void msvideo1_decode_8bit(Msvideo1Context *s) |
87 |
|
|
{ |
88 |
|
|
int block_ptr, pixel_ptr; |
89 |
|
|
int total_blocks; |
90 |
|
|
int pixel_x, pixel_y; /* pixel width and height iterators */ |
91 |
|
|
int block_x, block_y; /* block width and height iterators */ |
92 |
|
|
int blocks_wide, blocks_high; /* width and height in 4x4 blocks */ |
93 |
|
|
int block_inc; |
94 |
|
|
int row_dec; |
95 |
|
|
|
96 |
|
|
/* decoding parameters */ |
97 |
|
|
int stream_ptr; |
98 |
|
|
unsigned char byte_a, byte_b; |
99 |
|
|
unsigned short flags; |
100 |
|
|
int skip_blocks; |
101 |
|
|
unsigned char colors[8]; |
102 |
|
147 |
unsigned char *pixels = s->frame->data[0]; |
103 |
|
147 |
int stride = s->frame->linesize[0]; |
104 |
|
|
|
105 |
|
147 |
stream_ptr = 0; |
106 |
|
147 |
skip_blocks = 0; |
107 |
|
147 |
blocks_wide = s->avctx->width / 4; |
108 |
|
147 |
blocks_high = s->avctx->height / 4; |
109 |
|
147 |
total_blocks = blocks_wide * blocks_high; |
110 |
|
147 |
block_inc = 4; |
111 |
|
147 |
row_dec = stride + 4; |
112 |
|
|
|
113 |
✓✓ |
3977 |
for (block_y = blocks_high; block_y > 0; block_y--) { |
114 |
|
3830 |
block_ptr = ((block_y * 4) - 1) * stride; |
115 |
✓✓ |
186030 |
for (block_x = blocks_wide; block_x > 0; block_x--) { |
116 |
|
|
/* check if this block should be skipped */ |
117 |
✓✓ |
182200 |
if (skip_blocks) { |
118 |
|
94392 |
block_ptr += block_inc; |
119 |
|
94392 |
skip_blocks--; |
120 |
|
94392 |
total_blocks--; |
121 |
|
94392 |
continue; |
122 |
|
|
} |
123 |
|
|
|
124 |
|
87808 |
pixel_ptr = block_ptr; |
125 |
|
|
|
126 |
|
|
/* get the next two bytes in the encoded data stream */ |
127 |
✗✓ |
87808 |
CHECK_STREAM_PTR(2); |
128 |
|
87808 |
byte_a = s->buf[stream_ptr++]; |
129 |
|
87808 |
byte_b = s->buf[stream_ptr++]; |
130 |
|
|
|
131 |
|
|
/* check if the decode is finished */ |
132 |
✓✓✗✓ ✗✗ |
87808 |
if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0)) |
133 |
|
|
return; |
134 |
✓✓ |
87808 |
else if ((byte_b & 0xFC) == 0x84) { |
135 |
|
|
/* skip code, but don't count the current block */ |
136 |
|
8662 |
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1; |
137 |
✓✓ |
79146 |
} else if (byte_b < 0x80) { |
138 |
|
|
/* 2-color encoding */ |
139 |
|
11480 |
flags = (byte_b << 8) | byte_a; |
140 |
|
|
|
141 |
✗✓ |
11480 |
CHECK_STREAM_PTR(2); |
142 |
|
11480 |
colors[0] = s->buf[stream_ptr++]; |
143 |
|
11480 |
colors[1] = s->buf[stream_ptr++]; |
144 |
|
|
|
145 |
✓✓ |
57400 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
146 |
✓✓ |
229600 |
for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) |
147 |
|
183680 |
pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1]; |
148 |
|
45920 |
pixel_ptr -= row_dec; |
149 |
|
|
} |
150 |
✓✓ |
67666 |
} else if (byte_b >= 0x90) { |
151 |
|
|
/* 8-color encoding */ |
152 |
|
56844 |
flags = (byte_b << 8) | byte_a; |
153 |
|
|
|
154 |
✗✓ |
56844 |
CHECK_STREAM_PTR(8); |
155 |
|
56844 |
memcpy(colors, &s->buf[stream_ptr], 8); |
156 |
|
56844 |
stream_ptr += 8; |
157 |
|
|
|
158 |
✓✓ |
284220 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
159 |
✓✓ |
1136880 |
for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) |
160 |
|
909504 |
pixels[pixel_ptr++] = |
161 |
|
909504 |
colors[((pixel_y & 0x2) << 1) + |
162 |
|
909504 |
(pixel_x & 0x2) + ((flags & 0x1) ^ 1)]; |
163 |
|
227376 |
pixel_ptr -= row_dec; |
164 |
|
|
} |
165 |
|
|
} else { |
166 |
|
|
/* 1-color encoding */ |
167 |
|
10822 |
colors[0] = byte_a; |
168 |
|
|
|
169 |
✓✓ |
54110 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
170 |
✓✓ |
216440 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) |
171 |
|
173152 |
pixels[pixel_ptr++] = colors[0]; |
172 |
|
43288 |
pixel_ptr -= row_dec; |
173 |
|
|
} |
174 |
|
|
} |
175 |
|
|
|
176 |
|
87808 |
block_ptr += block_inc; |
177 |
|
87808 |
total_blocks--; |
178 |
|
|
} |
179 |
|
|
} |
180 |
|
|
|
181 |
|
|
/* make the palette available on the way out */ |
182 |
✓✗ |
147 |
if (s->avctx->pix_fmt == AV_PIX_FMT_PAL8) |
183 |
|
147 |
memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
180 |
static void msvideo1_decode_16bit(Msvideo1Context *s) |
187 |
|
|
{ |
188 |
|
|
int block_ptr, pixel_ptr; |
189 |
|
|
int total_blocks; |
190 |
|
|
int pixel_x, pixel_y; /* pixel width and height iterators */ |
191 |
|
|
int block_x, block_y; /* block width and height iterators */ |
192 |
|
|
int blocks_wide, blocks_high; /* width and height in 4x4 blocks */ |
193 |
|
|
int block_inc; |
194 |
|
|
int row_dec; |
195 |
|
|
|
196 |
|
|
/* decoding parameters */ |
197 |
|
|
int stream_ptr; |
198 |
|
|
unsigned char byte_a, byte_b; |
199 |
|
|
unsigned short flags; |
200 |
|
|
int skip_blocks; |
201 |
|
|
unsigned short colors[8]; |
202 |
|
180 |
unsigned short *pixels = (unsigned short *)s->frame->data[0]; |
203 |
|
180 |
int stride = s->frame->linesize[0] / 2; |
204 |
|
|
|
205 |
|
180 |
stream_ptr = 0; |
206 |
|
180 |
skip_blocks = 0; |
207 |
|
180 |
blocks_wide = s->avctx->width / 4; |
208 |
|
180 |
blocks_high = s->avctx->height / 4; |
209 |
|
180 |
total_blocks = blocks_wide * blocks_high; |
210 |
|
180 |
block_inc = 4; |
211 |
|
180 |
row_dec = stride + 4; |
212 |
|
|
|
213 |
✓✓ |
12090 |
for (block_y = blocks_high; block_y > 0; block_y--) { |
214 |
|
11910 |
block_ptr = ((block_y * 4) - 1) * stride; |
215 |
✓✓ |
1003380 |
for (block_x = blocks_wide; block_x > 0; block_x--) { |
216 |
|
|
/* check if this block should be skipped */ |
217 |
✓✓ |
991470 |
if (skip_blocks) { |
218 |
|
281140 |
block_ptr += block_inc; |
219 |
|
281140 |
skip_blocks--; |
220 |
|
281140 |
total_blocks--; |
221 |
|
281140 |
continue; |
222 |
|
|
} |
223 |
|
|
|
224 |
|
710330 |
pixel_ptr = block_ptr; |
225 |
|
|
|
226 |
|
|
/* get the next two bytes in the encoded data stream */ |
227 |
✗✓ |
710330 |
CHECK_STREAM_PTR(2); |
228 |
|
710330 |
byte_a = s->buf[stream_ptr++]; |
229 |
|
710330 |
byte_b = s->buf[stream_ptr++]; |
230 |
|
|
|
231 |
|
|
/* check if the decode is finished */ |
232 |
✓✓✗✓ ✗✗ |
710330 |
if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0)) { |
233 |
|
|
return; |
234 |
✓✓ |
710330 |
} else if ((byte_b & 0xFC) == 0x84) { |
235 |
|
|
/* skip code, but don't count the current block */ |
236 |
|
108271 |
skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1; |
237 |
✓✓ |
602059 |
} else if (byte_b < 0x80) { |
238 |
|
|
/* 2- or 8-color encoding modes */ |
239 |
|
280291 |
flags = (byte_b << 8) | byte_a; |
240 |
|
|
|
241 |
✗✓ |
280291 |
CHECK_STREAM_PTR(4); |
242 |
|
280291 |
colors[0] = AV_RL16(&s->buf[stream_ptr]); |
243 |
|
280291 |
stream_ptr += 2; |
244 |
|
280291 |
colors[1] = AV_RL16(&s->buf[stream_ptr]); |
245 |
|
280291 |
stream_ptr += 2; |
246 |
|
|
|
247 |
✓✓ |
280291 |
if (colors[0] & 0x8000) { |
248 |
|
|
/* 8-color encoding */ |
249 |
✗✓ |
145448 |
CHECK_STREAM_PTR(12); |
250 |
|
145448 |
colors[2] = AV_RL16(&s->buf[stream_ptr]); |
251 |
|
145448 |
stream_ptr += 2; |
252 |
|
145448 |
colors[3] = AV_RL16(&s->buf[stream_ptr]); |
253 |
|
145448 |
stream_ptr += 2; |
254 |
|
145448 |
colors[4] = AV_RL16(&s->buf[stream_ptr]); |
255 |
|
145448 |
stream_ptr += 2; |
256 |
|
145448 |
colors[5] = AV_RL16(&s->buf[stream_ptr]); |
257 |
|
145448 |
stream_ptr += 2; |
258 |
|
145448 |
colors[6] = AV_RL16(&s->buf[stream_ptr]); |
259 |
|
145448 |
stream_ptr += 2; |
260 |
|
145448 |
colors[7] = AV_RL16(&s->buf[stream_ptr]); |
261 |
|
145448 |
stream_ptr += 2; |
262 |
|
|
|
263 |
✓✓ |
727240 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
264 |
✓✓ |
2908960 |
for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) |
265 |
|
2327168 |
pixels[pixel_ptr++] = |
266 |
|
2327168 |
colors[((pixel_y & 0x2) << 1) + |
267 |
|
2327168 |
(pixel_x & 0x2) + ((flags & 0x1) ^ 1)]; |
268 |
|
581792 |
pixel_ptr -= row_dec; |
269 |
|
|
} |
270 |
|
|
} else { |
271 |
|
|
/* 2-color encoding */ |
272 |
✓✓ |
674215 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
273 |
✓✓ |
2696860 |
for (pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) |
274 |
|
2157488 |
pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1]; |
275 |
|
539372 |
pixel_ptr -= row_dec; |
276 |
|
|
} |
277 |
|
|
} |
278 |
|
|
} else { |
279 |
|
|
/* otherwise, it's a 1-color block */ |
280 |
|
321768 |
colors[0] = (byte_b << 8) | byte_a; |
281 |
|
|
|
282 |
✓✓ |
1608840 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
283 |
✓✓ |
6435360 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) |
284 |
|
5148288 |
pixels[pixel_ptr++] = colors[0]; |
285 |
|
1287072 |
pixel_ptr -= row_dec; |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
|
289 |
|
710330 |
block_ptr += block_inc; |
290 |
|
710330 |
total_blocks--; |
291 |
|
|
} |
292 |
|
|
} |
293 |
|
|
} |
294 |
|
|
|
295 |
|
327 |
static int msvideo1_decode_frame(AVCodecContext *avctx, |
296 |
|
|
void *data, int *got_frame, |
297 |
|
|
AVPacket *avpkt) |
298 |
|
|
{ |
299 |
|
327 |
const uint8_t *buf = avpkt->data; |
300 |
|
327 |
int buf_size = avpkt->size; |
301 |
|
327 |
Msvideo1Context *s = avctx->priv_data; |
302 |
|
|
int ret; |
303 |
|
|
|
304 |
|
327 |
s->buf = buf; |
305 |
|
327 |
s->size = buf_size; |
306 |
|
|
|
307 |
|
|
// Discard frame if its smaller than the minimum frame size |
308 |
✗✓ |
327 |
if (buf_size < (avctx->width/4) * (avctx->height/4) / 512) { |
309 |
|
|
av_log(avctx, AV_LOG_ERROR, "Packet is too small\n"); |
310 |
|
|
return AVERROR_INVALIDDATA; |
311 |
|
|
} |
312 |
|
|
|
313 |
✗✓ |
327 |
if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) |
314 |
|
|
return ret; |
315 |
|
|
|
316 |
✓✓ |
327 |
if (s->mode_8bit) { |
317 |
|
|
int size; |
318 |
|
147 |
const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &size); |
319 |
|
|
|
320 |
✓✓✓✗
|
147 |
if (pal && size == AVPALETTE_SIZE) { |
321 |
|
4 |
memcpy(s->pal, pal, AVPALETTE_SIZE); |
322 |
|
4 |
s->frame->palette_has_changed = 1; |
323 |
✗✓ |
143 |
} else if (pal) { |
324 |
|
|
av_log(avctx, AV_LOG_ERROR, "Palette size %d is wrong\n", size); |
325 |
|
|
} |
326 |
|
|
} |
327 |
|
|
|
328 |
✓✓ |
327 |
if (s->mode_8bit) |
329 |
|
147 |
msvideo1_decode_8bit(s); |
330 |
|
|
else |
331 |
|
180 |
msvideo1_decode_16bit(s); |
332 |
|
|
|
333 |
✗✓ |
327 |
if ((ret = av_frame_ref(data, s->frame)) < 0) |
334 |
|
|
return ret; |
335 |
|
|
|
336 |
|
327 |
*got_frame = 1; |
337 |
|
|
|
338 |
|
|
/* report that the buffer was completely consumed */ |
339 |
|
327 |
return buf_size; |
340 |
|
|
} |
341 |
|
|
|
342 |
|
13 |
static av_cold int msvideo1_decode_end(AVCodecContext *avctx) |
343 |
|
|
{ |
344 |
|
13 |
Msvideo1Context *s = avctx->priv_data; |
345 |
|
|
|
346 |
|
13 |
av_frame_free(&s->frame); |
347 |
|
|
|
348 |
|
13 |
return 0; |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
AVCodec ff_msvideo1_decoder = { |
352 |
|
|
.name = "msvideo1", |
353 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Microsoft Video 1"), |
354 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
355 |
|
|
.id = AV_CODEC_ID_MSVIDEO1, |
356 |
|
|
.priv_data_size = sizeof(Msvideo1Context), |
357 |
|
|
.init = msvideo1_decode_init, |
358 |
|
|
.close = msvideo1_decode_end, |
359 |
|
|
.decode = msvideo1_decode_frame, |
360 |
|
|
.capabilities = AV_CODEC_CAP_DR1, |
361 |
|
|
}; |