ffmpeg / libavcodec / interplayvideo.c @ 5509bffa
History | View | Annotate | Download (28 KB)
1 |
/*
|
---|---|
2 |
* Interplay MVE Video Decoder
|
3 |
* Copyright (C) 2003 the ffmpeg project
|
4 |
*
|
5 |
* This library is free software; you can redistribute it and/or
|
6 |
* modify it under the terms of the GNU Lesser General Public
|
7 |
* License as published by the Free Software Foundation; either
|
8 |
* version 2 of the License, or (at your option) any later version.
|
9 |
*
|
10 |
* This library is distributed in the hope that it will be useful,
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 |
* Lesser General Public License for more details.
|
14 |
*
|
15 |
* You should have received a copy of the GNU Lesser General Public
|
16 |
* License along with this library; if not, write to the Free Software
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18 |
*
|
19 |
*/
|
20 |
|
21 |
/**
|
22 |
* @file interplayvideo.c
|
23 |
* Interplay MVE Video Decoder by Mike Melanson (melanson@pcisys.net)
|
24 |
* For more information about the Interplay MVE format, visit:
|
25 |
* http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
|
26 |
* This code is written in such a way that the identifiers match up
|
27 |
* with the encoding descriptions in the document.
|
28 |
*
|
29 |
* This decoder presently only supports a PAL8 output colorspace.
|
30 |
*
|
31 |
* An Interplay video frame consists of 2 parts: The decoding map and
|
32 |
* the video data. A demuxer must load these 2 parts together in a single
|
33 |
* buffer before sending it through the stream to this decoder.
|
34 |
*/
|
35 |
|
36 |
#include <stdio.h> |
37 |
#include <stdlib.h> |
38 |
#include <string.h> |
39 |
#include <unistd.h> |
40 |
|
41 |
#include "common.h" |
42 |
#include "avcodec.h" |
43 |
#include "dsputil.h" |
44 |
|
45 |
#define PALETTE_COUNT 256 |
46 |
|
47 |
/* debugging support */
|
48 |
#define DEBUG_INTERPLAY 0 |
49 |
#if DEBUG_INTERPLAY
|
50 |
#define debug_interplay(x,...) av_log(NULL, AV_LOG_DEBUG, x, __VA_ARGS__) |
51 |
#else
|
52 |
static inline void debug_interplay(const char *format, ...) { } |
53 |
#endif
|
54 |
|
55 |
typedef struct IpvideoContext { |
56 |
|
57 |
AVCodecContext *avctx; |
58 |
DSPContext dsp; |
59 |
AVFrame second_last_frame; |
60 |
AVFrame last_frame; |
61 |
AVFrame current_frame; |
62 |
unsigned char *decoding_map; |
63 |
int decoding_map_size;
|
64 |
|
65 |
unsigned char *buf; |
66 |
int size;
|
67 |
|
68 |
unsigned char *stream_ptr; |
69 |
unsigned char *stream_end; |
70 |
unsigned char *pixel_ptr; |
71 |
int line_inc;
|
72 |
int stride;
|
73 |
int upper_motion_limit_offset;
|
74 |
|
75 |
} IpvideoContext; |
76 |
|
77 |
#define CHECK_STREAM_PTR(n) \
|
78 |
if ((s->stream_ptr + n) > s->stream_end) { \
|
79 |
av_log(s->avctx, AV_LOG_ERROR, "Interplay video warning: stream_ptr out of bounds (%p >= %p)\n", \
|
80 |
s->stream_ptr + n, s->stream_end); \ |
81 |
return -1; \ |
82 |
} |
83 |
|
84 |
#define COPY_FROM_CURRENT() \
|
85 |
motion_offset = current_offset; \ |
86 |
motion_offset += y * s->stride; \ |
87 |
motion_offset += x; \ |
88 |
if (motion_offset < 0) { \ |
89 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \
|
90 |
return -1; \ |
91 |
} else if (motion_offset > s->upper_motion_limit_offset) { \ |
92 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \
|
93 |
motion_offset, s->upper_motion_limit_offset); \ |
94 |
return -1; \ |
95 |
} \ |
96 |
s->dsp.put_pixels_tab[0][0](s->pixel_ptr, \ |
97 |
s->current_frame.data[0] + motion_offset, s->stride, 8); |
98 |
|
99 |
#define COPY_FROM_PREVIOUS() \
|
100 |
motion_offset = current_offset; \ |
101 |
motion_offset += y * s->stride; \ |
102 |
motion_offset += x; \ |
103 |
if (motion_offset < 0) { \ |
104 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \
|
105 |
return -1; \ |
106 |
} else if (motion_offset > s->upper_motion_limit_offset) { \ |
107 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \
|
108 |
motion_offset, s->upper_motion_limit_offset); \ |
109 |
return -1; \ |
110 |
} \ |
111 |
s->dsp.put_pixels_tab[0][0](s->pixel_ptr, \ |
112 |
s->last_frame.data[0] + motion_offset, s->stride, 8); |
113 |
|
114 |
#define COPY_FROM_SECOND_LAST() \
|
115 |
motion_offset = current_offset; \ |
116 |
motion_offset += y * s->stride; \ |
117 |
motion_offset += x; \ |
118 |
if (motion_offset < 0) { \ |
119 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \
|
120 |
return -1; \ |
121 |
} else if (motion_offset > s->upper_motion_limit_offset) { \ |
122 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \
|
123 |
motion_offset, s->upper_motion_limit_offset); \ |
124 |
return -1; \ |
125 |
} \ |
126 |
s->dsp.put_pixels_tab[0][0](s->pixel_ptr, \ |
127 |
s->second_last_frame.data[0] + motion_offset, s->stride, 8); |
128 |
|
129 |
static int ipvideo_decode_block_opcode_0x0(IpvideoContext *s) |
130 |
{ |
131 |
int x, y;
|
132 |
int motion_offset;
|
133 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
134 |
|
135 |
/* copy a block from the previous frame */
|
136 |
x = y = 0;
|
137 |
COPY_FROM_PREVIOUS(); |
138 |
|
139 |
/* report success */
|
140 |
return 0; |
141 |
} |
142 |
|
143 |
static int ipvideo_decode_block_opcode_0x1(IpvideoContext *s) |
144 |
{ |
145 |
int x, y;
|
146 |
int motion_offset;
|
147 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
148 |
|
149 |
/* copy block from 2 frames ago */
|
150 |
x = y = 0;
|
151 |
COPY_FROM_SECOND_LAST(); |
152 |
|
153 |
/* report success */
|
154 |
return 0; |
155 |
} |
156 |
|
157 |
static int ipvideo_decode_block_opcode_0x2(IpvideoContext *s) |
158 |
{ |
159 |
unsigned char B; |
160 |
int x, y;
|
161 |
int motion_offset;
|
162 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
163 |
|
164 |
/* copy block from 2 frames ago using a motion vector; need 1 more byte */
|
165 |
CHECK_STREAM_PTR(1);
|
166 |
B = *s->stream_ptr++; |
167 |
|
168 |
if (B < 56) { |
169 |
x = 8 + (B % 7); |
170 |
y = B / 7;
|
171 |
} else {
|
172 |
x = -14 + ((B - 56) % 29); |
173 |
y = 8 + ((B - 56) / 29); |
174 |
} |
175 |
|
176 |
debug_interplay (" motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
|
177 |
COPY_FROM_SECOND_LAST(); |
178 |
|
179 |
/* report success */
|
180 |
return 0; |
181 |
} |
182 |
|
183 |
static int ipvideo_decode_block_opcode_0x3(IpvideoContext *s) |
184 |
{ |
185 |
unsigned char B; |
186 |
int x, y;
|
187 |
int motion_offset;
|
188 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
189 |
|
190 |
/* copy 8x8 block from current frame from an up/left block */
|
191 |
|
192 |
/* need 1 more byte for motion */
|
193 |
CHECK_STREAM_PTR(1);
|
194 |
B = *s->stream_ptr++; |
195 |
|
196 |
if (B < 56) { |
197 |
x = -(8 + (B % 7)); |
198 |
y = -(B / 7);
|
199 |
} else {
|
200 |
x = -(-14 + ((B - 56) % 29)); |
201 |
y = -( 8 + ((B - 56) / 29)); |
202 |
} |
203 |
|
204 |
debug_interplay (" motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
|
205 |
COPY_FROM_CURRENT(); |
206 |
|
207 |
/* report success */
|
208 |
return 0; |
209 |
} |
210 |
|
211 |
static int ipvideo_decode_block_opcode_0x4(IpvideoContext *s) |
212 |
{ |
213 |
int x, y;
|
214 |
unsigned char B, BL, BH; |
215 |
int motion_offset;
|
216 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
217 |
|
218 |
/* copy a block from the previous frame; need 1 more byte */
|
219 |
CHECK_STREAM_PTR(1);
|
220 |
|
221 |
B = *s->stream_ptr++; |
222 |
BL = B & 0x0F;
|
223 |
BH = (B >> 4) & 0x0F; |
224 |
x = -8 + BL;
|
225 |
y = -8 + BH;
|
226 |
|
227 |
debug_interplay (" motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
|
228 |
COPY_FROM_PREVIOUS(); |
229 |
|
230 |
/* report success */
|
231 |
return 0; |
232 |
} |
233 |
|
234 |
static int ipvideo_decode_block_opcode_0x5(IpvideoContext *s) |
235 |
{ |
236 |
signed char x, y; |
237 |
int motion_offset;
|
238 |
int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
239 |
|
240 |
/* copy a block from the previous frame using an expanded range;
|
241 |
* need 2 more bytes */
|
242 |
CHECK_STREAM_PTR(2);
|
243 |
|
244 |
x = *s->stream_ptr++; |
245 |
y = *s->stream_ptr++; |
246 |
|
247 |
debug_interplay (" motion bytes = %d, %d\n", x, y);
|
248 |
COPY_FROM_PREVIOUS(); |
249 |
|
250 |
/* report success */
|
251 |
return 0; |
252 |
} |
253 |
|
254 |
static int ipvideo_decode_block_opcode_0x6(IpvideoContext *s) |
255 |
{ |
256 |
/* mystery opcode? skip multiple blocks? */
|
257 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: Help! Mystery opcode 0x6 seen\n");
|
258 |
|
259 |
/* report success */
|
260 |
return 0; |
261 |
} |
262 |
|
263 |
static int ipvideo_decode_block_opcode_0x7(IpvideoContext *s) |
264 |
{ |
265 |
int x, y;
|
266 |
unsigned char P0, P1; |
267 |
unsigned char B[8]; |
268 |
unsigned int flags; |
269 |
int bitmask;
|
270 |
|
271 |
/* 2-color encoding */
|
272 |
CHECK_STREAM_PTR(2);
|
273 |
|
274 |
P0 = *s->stream_ptr++; |
275 |
P1 = *s->stream_ptr++; |
276 |
|
277 |
if (P0 <= P1) {
|
278 |
|
279 |
/* need 8 more bytes from the stream */
|
280 |
CHECK_STREAM_PTR(8);
|
281 |
for (y = 0; y < 8; y++) |
282 |
B[y] = *s->stream_ptr++; |
283 |
|
284 |
for (y = 0; y < 8; y++) { |
285 |
flags = B[y]; |
286 |
for (x = 0x01; x <= 0x80; x <<= 1) { |
287 |
if (flags & x)
|
288 |
*s->pixel_ptr++ = P1; |
289 |
else
|
290 |
*s->pixel_ptr++ = P0; |
291 |
} |
292 |
s->pixel_ptr += s->line_inc; |
293 |
} |
294 |
|
295 |
} else {
|
296 |
|
297 |
/* need 2 more bytes from the stream */
|
298 |
CHECK_STREAM_PTR(2);
|
299 |
B[0] = *s->stream_ptr++;
|
300 |
B[1] = *s->stream_ptr++;
|
301 |
|
302 |
flags = (B[1] << 8) | B[0]; |
303 |
bitmask = 0x0001;
|
304 |
for (y = 0; y < 8; y += 2) { |
305 |
for (x = 0; x < 8; x += 2, bitmask <<= 1) { |
306 |
if (flags & bitmask) {
|
307 |
*(s->pixel_ptr + x) = P1; |
308 |
*(s->pixel_ptr + x + 1) = P1;
|
309 |
*(s->pixel_ptr + s->stride + x) = P1; |
310 |
*(s->pixel_ptr + s->stride + x + 1) = P1;
|
311 |
} else {
|
312 |
*(s->pixel_ptr + x) = P0; |
313 |
*(s->pixel_ptr + x + 1) = P0;
|
314 |
*(s->pixel_ptr + s->stride + x) = P0; |
315 |
*(s->pixel_ptr + s->stride + x + 1) = P0;
|
316 |
} |
317 |
} |
318 |
s->pixel_ptr += s->stride * 2;
|
319 |
} |
320 |
} |
321 |
|
322 |
/* report success */
|
323 |
return 0; |
324 |
} |
325 |
|
326 |
static int ipvideo_decode_block_opcode_0x8(IpvideoContext *s) |
327 |
{ |
328 |
int x, y;
|
329 |
unsigned char P[8]; |
330 |
unsigned char B[8]; |
331 |
unsigned int flags = 0; |
332 |
unsigned int bitmask = 0; |
333 |
unsigned char P0 = 0, P1 = 0; |
334 |
int lower_half = 0; |
335 |
|
336 |
/* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
|
337 |
* either top and bottom or left and right halves */
|
338 |
CHECK_STREAM_PTR(2);
|
339 |
|
340 |
P[0] = *s->stream_ptr++;
|
341 |
P[1] = *s->stream_ptr++;
|
342 |
|
343 |
if (P[0] <= P[1]) { |
344 |
|
345 |
/* need 12 more bytes */
|
346 |
CHECK_STREAM_PTR(12);
|
347 |
B[0] = *s->stream_ptr++; B[1] = *s->stream_ptr++; |
348 |
P[2] = *s->stream_ptr++; P[3] = *s->stream_ptr++; |
349 |
B[2] = *s->stream_ptr++; B[3] = *s->stream_ptr++; |
350 |
P[4] = *s->stream_ptr++; P[5] = *s->stream_ptr++; |
351 |
B[4] = *s->stream_ptr++; B[5] = *s->stream_ptr++; |
352 |
P[6] = *s->stream_ptr++; P[7] = *s->stream_ptr++; |
353 |
B[6] = *s->stream_ptr++; B[7] = *s->stream_ptr++; |
354 |
|
355 |
for (y = 0; y < 8; y++) { |
356 |
|
357 |
/* time to reload flags? */
|
358 |
if (y == 0) { |
359 |
flags = |
360 |
((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) | |
361 |
((B[0] & 0x0F) ) | ((B[4] & 0x0F) << 4) | |
362 |
((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) | |
363 |
((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20); |
364 |
bitmask = 0x00000001;
|
365 |
lower_half = 0; /* still on top half */ |
366 |
} else if (y == 4) { |
367 |
flags = |
368 |
((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) | |
369 |
((B[2] & 0x0F) ) | ((B[6] & 0x0F) << 4) | |
370 |
((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) | |
371 |
((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20); |
372 |
bitmask = 0x00000001;
|
373 |
lower_half = 2;
|
374 |
} |
375 |
|
376 |
for (x = 0; x < 8; x++, bitmask <<= 1) { |
377 |
/* get the pixel values ready for this quadrant */
|
378 |
if (x == 0) { |
379 |
P0 = P[lower_half + 0];
|
380 |
P1 = P[lower_half + 1];
|
381 |
} else if (x == 4) { |
382 |
P0 = P[lower_half + 4];
|
383 |
P1 = P[lower_half + 5];
|
384 |
} |
385 |
|
386 |
if (flags & bitmask)
|
387 |
*s->pixel_ptr++ = P1; |
388 |
else
|
389 |
*s->pixel_ptr++ = P0; |
390 |
} |
391 |
s->pixel_ptr += s->line_inc; |
392 |
} |
393 |
|
394 |
} else {
|
395 |
|
396 |
/* need 10 more bytes */
|
397 |
CHECK_STREAM_PTR(10);
|
398 |
B[0] = *s->stream_ptr++; B[1] = *s->stream_ptr++; |
399 |
B[2] = *s->stream_ptr++; B[3] = *s->stream_ptr++; |
400 |
P[2] = *s->stream_ptr++; P[3] = *s->stream_ptr++; |
401 |
B[4] = *s->stream_ptr++; B[5] = *s->stream_ptr++; |
402 |
B[6] = *s->stream_ptr++; B[7] = *s->stream_ptr++; |
403 |
|
404 |
if (P[2] <= P[3]) { |
405 |
|
406 |
/* vertical split; left & right halves are 2-color encoded */
|
407 |
|
408 |
for (y = 0; y < 8; y++) { |
409 |
|
410 |
/* time to reload flags? */
|
411 |
if (y == 0) { |
412 |
flags = |
413 |
((B[0] & 0xF0) << 4) | ((B[4] & 0xF0) << 8) | |
414 |
((B[0] & 0x0F) ) | ((B[4] & 0x0F) << 4) | |
415 |
((B[1] & 0xF0) << 20) | ((B[5] & 0xF0) << 24) | |
416 |
((B[1] & 0x0F) << 16) | ((B[5] & 0x0F) << 20); |
417 |
bitmask = 0x00000001;
|
418 |
} else if (y == 4) { |
419 |
flags = |
420 |
((B[2] & 0xF0) << 4) | ((B[6] & 0xF0) << 8) | |
421 |
((B[2] & 0x0F) ) | ((B[6] & 0x0F) << 4) | |
422 |
((B[3] & 0xF0) << 20) | ((B[7] & 0xF0) << 24) | |
423 |
((B[3] & 0x0F) << 16) | ((B[7] & 0x0F) << 20); |
424 |
bitmask = 0x00000001;
|
425 |
} |
426 |
|
427 |
for (x = 0; x < 8; x++, bitmask <<= 1) { |
428 |
/* get the pixel values ready for this half */
|
429 |
if (x == 0) { |
430 |
P0 = P[0];
|
431 |
P1 = P[1];
|
432 |
} else if (x == 4) { |
433 |
P0 = P[2];
|
434 |
P1 = P[3];
|
435 |
} |
436 |
|
437 |
if (flags & bitmask)
|
438 |
*s->pixel_ptr++ = P1; |
439 |
else
|
440 |
*s->pixel_ptr++ = P0; |
441 |
} |
442 |
s->pixel_ptr += s->line_inc; |
443 |
} |
444 |
|
445 |
} else {
|
446 |
|
447 |
/* horizontal split; top & bottom halves are 2-color encoded */
|
448 |
|
449 |
for (y = 0; y < 8; y++) { |
450 |
|
451 |
flags = B[y]; |
452 |
if (y == 0) { |
453 |
P0 = P[0];
|
454 |
P1 = P[1];
|
455 |
} else if (y == 4) { |
456 |
P0 = P[2];
|
457 |
P1 = P[3];
|
458 |
} |
459 |
|
460 |
for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) { |
461 |
|
462 |
if (flags & bitmask)
|
463 |
*s->pixel_ptr++ = P1; |
464 |
else
|
465 |
*s->pixel_ptr++ = P0; |
466 |
} |
467 |
s->pixel_ptr += s->line_inc; |
468 |
} |
469 |
} |
470 |
} |
471 |
|
472 |
/* report success */
|
473 |
return 0; |
474 |
} |
475 |
|
476 |
static int ipvideo_decode_block_opcode_0x9(IpvideoContext *s) |
477 |
{ |
478 |
int x, y;
|
479 |
unsigned char P[4]; |
480 |
unsigned char B[4]; |
481 |
unsigned int flags = 0; |
482 |
int shifter = 0; |
483 |
unsigned char pix; |
484 |
|
485 |
/* 4-color encoding */
|
486 |
CHECK_STREAM_PTR(4);
|
487 |
|
488 |
for (y = 0; y < 4; y++) |
489 |
P[y] = *s->stream_ptr++; |
490 |
|
491 |
if ((P[0] <= P[1]) && (P[2] <= P[3])) { |
492 |
|
493 |
/* 1 of 4 colors for each pixel, need 16 more bytes */
|
494 |
CHECK_STREAM_PTR(16);
|
495 |
|
496 |
for (y = 0; y < 8; y++) { |
497 |
/* get the next set of 8 2-bit flags */
|
498 |
flags = (s->stream_ptr[1] << 8) | s->stream_ptr[0]; |
499 |
s->stream_ptr += 2;
|
500 |
for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
501 |
*s->pixel_ptr++ = P[(flags >> shifter) & 0x03];
|
502 |
} |
503 |
s->pixel_ptr += s->line_inc; |
504 |
} |
505 |
|
506 |
} else if ((P[0] <= P[1]) && (P[2] > P[3])) { |
507 |
|
508 |
/* 1 of 4 colors for each 2x2 block, need 4 more bytes */
|
509 |
CHECK_STREAM_PTR(4);
|
510 |
|
511 |
B[0] = *s->stream_ptr++;
|
512 |
B[1] = *s->stream_ptr++;
|
513 |
B[2] = *s->stream_ptr++;
|
514 |
B[3] = *s->stream_ptr++;
|
515 |
flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
516 |
shifter = 0;
|
517 |
|
518 |
for (y = 0; y < 8; y += 2) { |
519 |
for (x = 0; x < 8; x += 2, shifter += 2) { |
520 |
pix = P[(flags >> shifter) & 0x03];
|
521 |
*(s->pixel_ptr + x) = pix; |
522 |
*(s->pixel_ptr + x + 1) = pix;
|
523 |
*(s->pixel_ptr + s->stride + x) = pix; |
524 |
*(s->pixel_ptr + s->stride + x + 1) = pix;
|
525 |
} |
526 |
s->pixel_ptr += s->stride * 2;
|
527 |
} |
528 |
|
529 |
} else if ((P[0] > P[1]) && (P[2] <= P[3])) { |
530 |
|
531 |
/* 1 of 4 colors for each 2x1 block, need 8 more bytes */
|
532 |
CHECK_STREAM_PTR(8);
|
533 |
|
534 |
for (y = 0; y < 8; y++) { |
535 |
/* time to reload flags? */
|
536 |
if ((y == 0) || (y == 4)) { |
537 |
B[0] = *s->stream_ptr++;
|
538 |
B[1] = *s->stream_ptr++;
|
539 |
B[2] = *s->stream_ptr++;
|
540 |
B[3] = *s->stream_ptr++;
|
541 |
flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
542 |
shifter = 0;
|
543 |
} |
544 |
for (x = 0; x < 8; x += 2, shifter += 2) { |
545 |
pix = P[(flags >> shifter) & 0x03];
|
546 |
*(s->pixel_ptr + x) = pix; |
547 |
*(s->pixel_ptr + x + 1) = pix;
|
548 |
} |
549 |
s->pixel_ptr += s->stride; |
550 |
} |
551 |
|
552 |
} else {
|
553 |
|
554 |
/* 1 of 4 colors for each 1x2 block, need 8 more bytes */
|
555 |
CHECK_STREAM_PTR(8);
|
556 |
|
557 |
for (y = 0; y < 8; y += 2) { |
558 |
/* time to reload flags? */
|
559 |
if ((y == 0) || (y == 4)) { |
560 |
B[0] = *s->stream_ptr++;
|
561 |
B[1] = *s->stream_ptr++;
|
562 |
B[2] = *s->stream_ptr++;
|
563 |
B[3] = *s->stream_ptr++;
|
564 |
flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
565 |
shifter = 0;
|
566 |
} |
567 |
for (x = 0; x < 8; x++, shifter += 2) { |
568 |
pix = P[(flags >> shifter) & 0x03];
|
569 |
*(s->pixel_ptr + x) = pix; |
570 |
*(s->pixel_ptr + s->stride + x) = pix; |
571 |
} |
572 |
s->pixel_ptr += s->stride * 2;
|
573 |
} |
574 |
} |
575 |
|
576 |
/* report success */
|
577 |
return 0; |
578 |
} |
579 |
|
580 |
static int ipvideo_decode_block_opcode_0xA(IpvideoContext *s) |
581 |
{ |
582 |
int x, y;
|
583 |
unsigned char P[16]; |
584 |
unsigned char B[16]; |
585 |
int flags = 0; |
586 |
int shifter = 0; |
587 |
int index;
|
588 |
int split;
|
589 |
int lower_half;
|
590 |
|
591 |
/* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
|
592 |
* either top and bottom or left and right halves */
|
593 |
CHECK_STREAM_PTR(4);
|
594 |
|
595 |
for (y = 0; y < 4; y++) |
596 |
P[y] = *s->stream_ptr++; |
597 |
|
598 |
if (P[0] <= P[1]) { |
599 |
|
600 |
/* 4-color encoding for each quadrant; need 28 more bytes */
|
601 |
CHECK_STREAM_PTR(28);
|
602 |
|
603 |
for (y = 0; y < 4; y++) |
604 |
B[y] = *s->stream_ptr++; |
605 |
for (y = 4; y < 16; y += 4) { |
606 |
for (x = y; x < y + 4; x++) |
607 |
P[x] = *s->stream_ptr++; |
608 |
for (x = y; x < y + 4; x++) |
609 |
B[x] = *s->stream_ptr++; |
610 |
} |
611 |
|
612 |
for (y = 0; y < 8; y++) { |
613 |
|
614 |
lower_half = (y >= 4) ? 4 : 0; |
615 |
flags = (B[y + 8] << 8) | B[y]; |
616 |
|
617 |
for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
618 |
split = (x >= 4) ? 8 : 0; |
619 |
index = split + lower_half + ((flags >> shifter) & 0x03);
|
620 |
*s->pixel_ptr++ = P[index]; |
621 |
} |
622 |
|
623 |
s->pixel_ptr += s->line_inc; |
624 |
} |
625 |
|
626 |
} else {
|
627 |
|
628 |
/* 4-color encoding for either left and right or top and bottom
|
629 |
* halves; need 20 more bytes */
|
630 |
CHECK_STREAM_PTR(20);
|
631 |
|
632 |
for (y = 0; y < 8; y++) |
633 |
B[y] = *s->stream_ptr++; |
634 |
for (y = 4; y < 8; y++) |
635 |
P[y] = *s->stream_ptr++; |
636 |
for (y = 8; y < 16; y++) |
637 |
B[y] = *s->stream_ptr++; |
638 |
|
639 |
if (P[4] <= P[5]) { |
640 |
|
641 |
/* block is divided into left and right halves */
|
642 |
for (y = 0; y < 8; y++) { |
643 |
|
644 |
flags = (B[y + 8] << 8) | B[y]; |
645 |
split = 0;
|
646 |
|
647 |
for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
648 |
if (x == 4) |
649 |
split = 4;
|
650 |
*s->pixel_ptr++ = P[split + ((flags >> shifter) & 0x03)];
|
651 |
} |
652 |
|
653 |
s->pixel_ptr += s->line_inc; |
654 |
} |
655 |
|
656 |
} else {
|
657 |
|
658 |
/* block is divided into top and bottom halves */
|
659 |
split = 0;
|
660 |
for (y = 0; y < 8; y++) { |
661 |
|
662 |
flags = (B[y * 2 + 1] << 8) | B[y * 2]; |
663 |
if (y == 4) |
664 |
split = 4;
|
665 |
|
666 |
for (x = 0, shifter = 0; x < 8; x++, shifter += 2) |
667 |
*s->pixel_ptr++ = P[split + ((flags >> shifter) & 0x03)];
|
668 |
|
669 |
s->pixel_ptr += s->line_inc; |
670 |
} |
671 |
} |
672 |
} |
673 |
|
674 |
/* report success */
|
675 |
return 0; |
676 |
} |
677 |
|
678 |
static int ipvideo_decode_block_opcode_0xB(IpvideoContext *s) |
679 |
{ |
680 |
int x, y;
|
681 |
|
682 |
/* 64-color encoding (each pixel in block is a different color) */
|
683 |
CHECK_STREAM_PTR(64);
|
684 |
|
685 |
for (y = 0; y < 8; y++) { |
686 |
for (x = 0; x < 8; x++) { |
687 |
*s->pixel_ptr++ = *s->stream_ptr++; |
688 |
} |
689 |
s->pixel_ptr += s->line_inc; |
690 |
} |
691 |
|
692 |
/* report success */
|
693 |
return 0; |
694 |
} |
695 |
|
696 |
static int ipvideo_decode_block_opcode_0xC(IpvideoContext *s) |
697 |
{ |
698 |
int x, y;
|
699 |
unsigned char pix; |
700 |
|
701 |
/* 16-color block encoding: each 2x2 block is a different color */
|
702 |
CHECK_STREAM_PTR(16);
|
703 |
|
704 |
for (y = 0; y < 8; y += 2) { |
705 |
for (x = 0; x < 8; x += 2) { |
706 |
pix = *s->stream_ptr++; |
707 |
*(s->pixel_ptr + x) = pix; |
708 |
*(s->pixel_ptr + x + 1) = pix;
|
709 |
*(s->pixel_ptr + s->stride + x) = pix; |
710 |
*(s->pixel_ptr + s->stride + x + 1) = pix;
|
711 |
} |
712 |
s->pixel_ptr += s->stride * 2;
|
713 |
} |
714 |
|
715 |
/* report success */
|
716 |
return 0; |
717 |
} |
718 |
|
719 |
static int ipvideo_decode_block_opcode_0xD(IpvideoContext *s) |
720 |
{ |
721 |
int x, y;
|
722 |
unsigned char P[4]; |
723 |
unsigned char index = 0; |
724 |
|
725 |
/* 4-color block encoding: each 4x4 block is a different color */
|
726 |
CHECK_STREAM_PTR(4);
|
727 |
|
728 |
for (y = 0; y < 4; y++) |
729 |
P[y] = *s->stream_ptr++; |
730 |
|
731 |
for (y = 0; y < 8; y++) { |
732 |
if (y < 4) |
733 |
index = 0;
|
734 |
else
|
735 |
index = 2;
|
736 |
|
737 |
for (x = 0; x < 8; x++) { |
738 |
if (x == 4) |
739 |
index++; |
740 |
*s->pixel_ptr++ = P[index]; |
741 |
} |
742 |
s->pixel_ptr += s->line_inc; |
743 |
} |
744 |
|
745 |
/* report success */
|
746 |
return 0; |
747 |
} |
748 |
|
749 |
static int ipvideo_decode_block_opcode_0xE(IpvideoContext *s) |
750 |
{ |
751 |
int x, y;
|
752 |
unsigned char pix; |
753 |
|
754 |
/* 1-color encoding: the whole block is 1 solid color */
|
755 |
CHECK_STREAM_PTR(1);
|
756 |
pix = *s->stream_ptr++; |
757 |
|
758 |
for (y = 0; y < 8; y++) { |
759 |
for (x = 0; x < 8; x++) { |
760 |
*s->pixel_ptr++ = pix; |
761 |
} |
762 |
s->pixel_ptr += s->line_inc; |
763 |
} |
764 |
|
765 |
/* report success */
|
766 |
return 0; |
767 |
} |
768 |
|
769 |
static int ipvideo_decode_block_opcode_0xF(IpvideoContext *s) |
770 |
{ |
771 |
int x, y;
|
772 |
unsigned char sample0, sample1; |
773 |
|
774 |
/* dithered encoding */
|
775 |
CHECK_STREAM_PTR(2);
|
776 |
sample0 = *s->stream_ptr++; |
777 |
sample1 = *s->stream_ptr++; |
778 |
|
779 |
for (y = 0; y < 8; y++) { |
780 |
for (x = 0; x < 8; x += 2) { |
781 |
if (y & 1) { |
782 |
*s->pixel_ptr++ = sample1; |
783 |
*s->pixel_ptr++ = sample0; |
784 |
} else {
|
785 |
*s->pixel_ptr++ = sample0; |
786 |
*s->pixel_ptr++ = sample1; |
787 |
} |
788 |
} |
789 |
s->pixel_ptr += s->line_inc; |
790 |
} |
791 |
|
792 |
/* report success */
|
793 |
return 0; |
794 |
} |
795 |
|
796 |
static int (*ipvideo_decode_block[16])(IpvideoContext *s); |
797 |
|
798 |
static void ipvideo_decode_opcodes(IpvideoContext *s) |
799 |
{ |
800 |
int x, y;
|
801 |
int index = 0; |
802 |
unsigned char opcode; |
803 |
int ret;
|
804 |
int code_counts[16]; |
805 |
static int frame = 0; |
806 |
|
807 |
debug_interplay("------------------ frame %d\n", frame);
|
808 |
frame++; |
809 |
|
810 |
for (x = 0; x < 16; x++) |
811 |
code_counts[x] = 0;
|
812 |
|
813 |
/* this is PAL8, so make the palette available */
|
814 |
memcpy(s->current_frame.data[1], s->avctx->palctrl->palette, PALETTE_COUNT * 4); |
815 |
|
816 |
s->stride = s->current_frame.linesize[0];
|
817 |
s->stream_ptr = s->buf + 14; /* data starts 14 bytes in */ |
818 |
s->stream_end = s->buf + s->size; |
819 |
s->line_inc = s->stride - 8;
|
820 |
s->upper_motion_limit_offset = (s->avctx->height - 8) * s->stride
|
821 |
+ s->avctx->width - 8;
|
822 |
s->dsp = s->dsp; |
823 |
|
824 |
for (y = 0; y < (s->stride * s->avctx->height); y += s->stride * 8) { |
825 |
for (x = y; x < y + s->avctx->width; x += 8) { |
826 |
/* bottom nibble first, then top nibble (which makes it
|
827 |
* hard to use a GetBitcontext) */
|
828 |
if (index & 1) |
829 |
opcode = s->decoding_map[index >> 1] >> 4; |
830 |
else
|
831 |
opcode = s->decoding_map[index >> 1] & 0xF; |
832 |
index++; |
833 |
|
834 |
debug_interplay(" block @ (%3d, %3d): encoding 0x%X, data ptr @ %p\n",
|
835 |
x - y, y / s->stride, opcode, s->stream_ptr); |
836 |
code_counts[opcode]++; |
837 |
|
838 |
s->pixel_ptr = s->current_frame.data[0] + x;
|
839 |
ret = ipvideo_decode_block[opcode](s); |
840 |
if (ret != 0) { |
841 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode problem on frame %d, @ block (%d, %d)\n",
|
842 |
frame, x - y, y / s->stride); |
843 |
return;
|
844 |
} |
845 |
} |
846 |
} |
847 |
if ((s->stream_ptr != s->stream_end) &&
|
848 |
(s->stream_ptr + 1 != s->stream_end)) {
|
849 |
av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode finished with %td bytes left over\n",
|
850 |
s->stream_end - s->stream_ptr); |
851 |
} |
852 |
} |
853 |
|
854 |
static int ipvideo_decode_init(AVCodecContext *avctx) |
855 |
{ |
856 |
IpvideoContext *s = avctx->priv_data; |
857 |
|
858 |
s->avctx = avctx; |
859 |
|
860 |
if (s->avctx->palctrl == NULL) { |
861 |
av_log(avctx, AV_LOG_ERROR, " Interplay video: palette expected.\n");
|
862 |
return -1; |
863 |
} |
864 |
|
865 |
avctx->pix_fmt = PIX_FMT_PAL8; |
866 |
avctx->has_b_frames = 0;
|
867 |
dsputil_init(&s->dsp, avctx); |
868 |
|
869 |
/* decoding map contains 4 bits of information per 8x8 block */
|
870 |
s->decoding_map_size = avctx->width * avctx->height / (8 * 8 * 2); |
871 |
|
872 |
/* assign block decode functions */
|
873 |
ipvideo_decode_block[0x0] = ipvideo_decode_block_opcode_0x0;
|
874 |
ipvideo_decode_block[0x1] = ipvideo_decode_block_opcode_0x1;
|
875 |
ipvideo_decode_block[0x2] = ipvideo_decode_block_opcode_0x2;
|
876 |
ipvideo_decode_block[0x3] = ipvideo_decode_block_opcode_0x3;
|
877 |
ipvideo_decode_block[0x4] = ipvideo_decode_block_opcode_0x4;
|
878 |
ipvideo_decode_block[0x5] = ipvideo_decode_block_opcode_0x5;
|
879 |
ipvideo_decode_block[0x6] = ipvideo_decode_block_opcode_0x6;
|
880 |
ipvideo_decode_block[0x7] = ipvideo_decode_block_opcode_0x7;
|
881 |
ipvideo_decode_block[0x8] = ipvideo_decode_block_opcode_0x8;
|
882 |
ipvideo_decode_block[0x9] = ipvideo_decode_block_opcode_0x9;
|
883 |
ipvideo_decode_block[0xA] = ipvideo_decode_block_opcode_0xA;
|
884 |
ipvideo_decode_block[0xB] = ipvideo_decode_block_opcode_0xB;
|
885 |
ipvideo_decode_block[0xC] = ipvideo_decode_block_opcode_0xC;
|
886 |
ipvideo_decode_block[0xD] = ipvideo_decode_block_opcode_0xD;
|
887 |
ipvideo_decode_block[0xE] = ipvideo_decode_block_opcode_0xE;
|
888 |
ipvideo_decode_block[0xF] = ipvideo_decode_block_opcode_0xF;
|
889 |
|
890 |
s->current_frame.data[0] = s->last_frame.data[0] = |
891 |
s->second_last_frame.data[0] = NULL; |
892 |
|
893 |
return 0; |
894 |
} |
895 |
|
896 |
static int ipvideo_decode_frame(AVCodecContext *avctx, |
897 |
void *data, int *data_size, |
898 |
uint8_t *buf, int buf_size)
|
899 |
{ |
900 |
IpvideoContext *s = avctx->priv_data; |
901 |
AVPaletteControl *palette_control = avctx->palctrl; |
902 |
|
903 |
/* compressed buffer needs to be large enough to at least hold an entire
|
904 |
* decoding map */
|
905 |
if (buf_size < s->decoding_map_size)
|
906 |
return buf_size;
|
907 |
|
908 |
s->decoding_map = buf; |
909 |
s->buf = buf + s->decoding_map_size; |
910 |
s->size = buf_size - s->decoding_map_size; |
911 |
|
912 |
s->current_frame.reference = 3;
|
913 |
if (avctx->get_buffer(avctx, &s->current_frame)) {
|
914 |
av_log(avctx, AV_LOG_ERROR, " Interplay Video: get_buffer() failed\n");
|
915 |
return -1; |
916 |
} |
917 |
|
918 |
ipvideo_decode_opcodes(s); |
919 |
|
920 |
if (palette_control->palette_changed) {
|
921 |
palette_control->palette_changed = 0;
|
922 |
s->current_frame.palette_has_changed = 1;
|
923 |
} |
924 |
|
925 |
*data_size = sizeof(AVFrame);
|
926 |
*(AVFrame*)data = s->current_frame; |
927 |
|
928 |
/* shuffle frames */
|
929 |
if (s->second_last_frame.data[0]) |
930 |
avctx->release_buffer(avctx, &s->second_last_frame); |
931 |
s->second_last_frame = s->last_frame; |
932 |
s->last_frame = s->current_frame; |
933 |
s->current_frame.data[0] = NULL; /* catch any access attempts */ |
934 |
|
935 |
/* report that the buffer was completely consumed */
|
936 |
return buf_size;
|
937 |
} |
938 |
|
939 |
static int ipvideo_decode_end(AVCodecContext *avctx) |
940 |
{ |
941 |
IpvideoContext *s = avctx->priv_data; |
942 |
|
943 |
/* release the last frame */
|
944 |
if (s->last_frame.data[0]) |
945 |
avctx->release_buffer(avctx, &s->last_frame); |
946 |
if (s->second_last_frame.data[0]) |
947 |
avctx->release_buffer(avctx, &s->second_last_frame); |
948 |
|
949 |
return 0; |
950 |
} |
951 |
|
952 |
AVCodec interplay_video_decoder = { |
953 |
"interplayvideo",
|
954 |
CODEC_TYPE_VIDEO, |
955 |
CODEC_ID_INTERPLAY_VIDEO, |
956 |
sizeof(IpvideoContext),
|
957 |
ipvideo_decode_init, |
958 |
NULL,
|
959 |
ipvideo_decode_end, |
960 |
ipvideo_decode_frame, |
961 |
CODEC_CAP_DR1, |
962 |
}; |