Commit | Line | Data |
---|---|---|
3ef8be2b MM |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | * | |
19 | */ | |
20 | ||
21 | /** | |
186447f8 MM |
22 | * @file interplayvideo.c |
23 | * Interplay MVE Video Decoder by Mike Melanson (melanson@pcisys.net) | |
3ef8be2b | 24 | * For more information about the Interplay MVE format, visit: |
186447f8 MM |
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. | |
3ef8be2b MM |
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 | ||
186447f8 MM |
45 | #define PALETTE_COUNT 256 |
46 | ||
47 | /* debugging support */ | |
48 | #define DEBUG_INTERPLAY 0 | |
49 | #if DEBUG_INTERPLAY | |
50 | #define debug_interplay printf | |
51 | #else | |
52 | static inline void debug_interplay(const char *format, ...) { } | |
53 | #endif | |
54 | ||
3ef8be2b MM |
55 | typedef struct IpvideoContext { |
56 | ||
57 | AVCodecContext *avctx; | |
58 | DSPContext dsp; | |
d86e8568 | 59 | AVFrame second_last_frame; |
3ef8be2b MM |
60 | AVFrame last_frame; |
61 | AVFrame current_frame; | |
3ef8be2b MM |
62 | unsigned char *decoding_map; |
63 | int decoding_map_size; | |
64 | ||
65 | unsigned char *buf; | |
66 | int size; | |
67 | ||
bf649a9d MM |
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 | ||
3ef8be2b MM |
75 | } IpvideoContext; |
76 | ||
186447f8 | 77 | #define CHECK_STREAM_PTR(n) \ |
bf649a9d | 78 | if ((s->stream_ptr + n) > s->stream_end) { \ |
9b879566 | 79 | av_log(s->avctx, AV_LOG_ERROR, "Interplay video warning: stream_ptr out of bounds (%p >= %p)\n", \ |
bf649a9d | 80 | s->stream_ptr + n, s->stream_end); \ |
186447f8 MM |
81 | return -1; \ |
82 | } | |
83 | ||
186447f8 MM |
84 | #define COPY_FROM_CURRENT() \ |
85 | motion_offset = current_offset; \ | |
bf649a9d | 86 | motion_offset += y * s->stride; \ |
186447f8 MM |
87 | motion_offset += x; \ |
88 | if (motion_offset < 0) { \ | |
9b879566 | 89 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \ |
186447f8 | 90 | return -1; \ |
bf649a9d | 91 | } else if (motion_offset > s->upper_motion_limit_offset) { \ |
9b879566 | 92 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \ |
bf649a9d | 93 | motion_offset, s->upper_motion_limit_offset); \ |
186447f8 MM |
94 | return -1; \ |
95 | } \ | |
bf649a9d MM |
96 | s->dsp.put_pixels_tab[0][0](s->pixel_ptr, \ |
97 | s->current_frame.data[0] + motion_offset, s->stride, 8); | |
186447f8 MM |
98 | |
99 | #define COPY_FROM_PREVIOUS() \ | |
100 | motion_offset = current_offset; \ | |
bf649a9d | 101 | motion_offset += y * s->stride; \ |
186447f8 MM |
102 | motion_offset += x; \ |
103 | if (motion_offset < 0) { \ | |
9b879566 | 104 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \ |
186447f8 | 105 | return -1; \ |
bf649a9d | 106 | } else if (motion_offset > s->upper_motion_limit_offset) { \ |
9b879566 | 107 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \ |
bf649a9d | 108 | motion_offset, s->upper_motion_limit_offset); \ |
186447f8 MM |
109 | return -1; \ |
110 | } \ | |
bf649a9d MM |
111 | s->dsp.put_pixels_tab[0][0](s->pixel_ptr, \ |
112 | s->last_frame.data[0] + motion_offset, s->stride, 8); | |
186447f8 | 113 | |
d86e8568 MM |
114 | #define COPY_FROM_SECOND_LAST() \ |
115 | motion_offset = current_offset; \ | |
bf649a9d | 116 | motion_offset += y * s->stride; \ |
d86e8568 MM |
117 | motion_offset += x; \ |
118 | if (motion_offset < 0) { \ | |
9b879566 | 119 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset); \ |
d86e8568 | 120 | return -1; \ |
bf649a9d | 121 | } else if (motion_offset > s->upper_motion_limit_offset) { \ |
9b879566 | 122 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n", \ |
bf649a9d | 123 | motion_offset, s->upper_motion_limit_offset); \ |
d86e8568 MM |
124 | return -1; \ |
125 | } \ | |
bf649a9d MM |
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(); | |
d86e8568 | 138 | |
bf649a9d MM |
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) | |
186447f8 MM |
158 | { |
159 | unsigned char B; | |
160 | int x, y; | |
161 | int motion_offset; | |
bf649a9d | 162 | int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
186447f8 | 163 | |
bf649a9d | 164 | /* copy block from 2 frames ago using a motion vector; need 1 more byte */ |
186447f8 | 165 | CHECK_STREAM_PTR(1); |
bf649a9d | 166 | B = *s->stream_ptr++; |
186447f8 MM |
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); | |
d86e8568 | 177 | COPY_FROM_SECOND_LAST(); |
186447f8 MM |
178 | |
179 | /* report success */ | |
180 | return 0; | |
181 | } | |
182 | ||
bf649a9d | 183 | static int ipvideo_decode_block_opcode_0x3(IpvideoContext *s) |
186447f8 MM |
184 | { |
185 | unsigned char B; | |
186 | int x, y; | |
187 | int motion_offset; | |
bf649a9d | 188 | int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
186447f8 MM |
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); | |
bf649a9d | 194 | B = *s->stream_ptr++; |
186447f8 MM |
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 | ||
bf649a9d | 211 | static int ipvideo_decode_block_opcode_0x4(IpvideoContext *s) |
186447f8 MM |
212 | { |
213 | int x, y; | |
214 | unsigned char B, BL, BH; | |
215 | int motion_offset; | |
bf649a9d | 216 | int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
186447f8 MM |
217 | |
218 | /* copy a block from the previous frame; need 1 more byte */ | |
219 | CHECK_STREAM_PTR(1); | |
220 | ||
bf649a9d | 221 | B = *s->stream_ptr++; |
186447f8 MM |
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 | ||
bf649a9d | 234 | static int ipvideo_decode_block_opcode_0x5(IpvideoContext *s) |
186447f8 MM |
235 | { |
236 | signed char x, y; | |
237 | int motion_offset; | |
bf649a9d | 238 | int current_offset = s->pixel_ptr - s->current_frame.data[0]; |
186447f8 MM |
239 | |
240 | /* copy a block from the previous frame using an expanded range; | |
241 | * need 2 more bytes */ | |
242 | CHECK_STREAM_PTR(2); | |
243 | ||
bf649a9d MM |
244 | x = *s->stream_ptr++; |
245 | y = *s->stream_ptr++; | |
186447f8 MM |
246 | |
247 | debug_interplay (" motion bytes = %d, %d\n", x, y); | |
248 | COPY_FROM_PREVIOUS(); | |
249 | ||
250 | /* report success */ | |
251 | return 0; | |
252 | } | |
253 | ||
bf649a9d | 254 | static int ipvideo_decode_block_opcode_0x6(IpvideoContext *s) |
186447f8 MM |
255 | { |
256 | /* mystery opcode? skip multiple blocks? */ | |
9b879566 | 257 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: Help! Mystery opcode 0x6 seen\n"); |
186447f8 MM |
258 | |
259 | /* report success */ | |
260 | return 0; | |
261 | } | |
262 | ||
bf649a9d | 263 | static int ipvideo_decode_block_opcode_0x7(IpvideoContext *s) |
186447f8 MM |
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 | ||
bf649a9d MM |
274 | P0 = *s->stream_ptr++; |
275 | P1 = *s->stream_ptr++; | |
186447f8 MM |
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++) | |
bf649a9d | 282 | B[y] = *s->stream_ptr++; |
186447f8 MM |
283 | |
284 | for (y = 0; y < 8; y++) { | |
285 | flags = B[y]; | |
75816852 | 286 | for (x = 0x01; x <= 0x80; x <<= 1) { |
186447f8 | 287 | if (flags & x) |
bf649a9d | 288 | *s->pixel_ptr++ = P1; |
186447f8 | 289 | else |
bf649a9d | 290 | *s->pixel_ptr++ = P0; |
186447f8 | 291 | } |
bf649a9d | 292 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
293 | } |
294 | ||
295 | } else { | |
296 | ||
297 | /* need 2 more bytes from the stream */ | |
298 | CHECK_STREAM_PTR(2); | |
bf649a9d MM |
299 | B[0] = *s->stream_ptr++; |
300 | B[1] = *s->stream_ptr++; | |
186447f8 | 301 | |
75816852 MM |
302 | flags = (B[1] << 8) | B[0]; |
303 | bitmask = 0x0001; | |
186447f8 | 304 | for (y = 0; y < 8; y += 2) { |
75816852 | 305 | for (x = 0; x < 8; x += 2, bitmask <<= 1) { |
186447f8 | 306 | if (flags & bitmask) { |
bf649a9d MM |
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; | |
d86e8568 | 311 | } else { |
bf649a9d MM |
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; | |
186447f8 MM |
316 | } |
317 | } | |
bf649a9d | 318 | s->pixel_ptr += s->stride * 2; |
186447f8 MM |
319 | } |
320 | } | |
321 | ||
322 | /* report success */ | |
323 | return 0; | |
324 | } | |
325 | ||
bf649a9d | 326 | static int ipvideo_decode_block_opcode_0x8(IpvideoContext *s) |
186447f8 MM |
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 | ||
bf649a9d MM |
340 | P[0] = *s->stream_ptr++; |
341 | P[1] = *s->stream_ptr++; | |
186447f8 MM |
342 | |
343 | if (P[0] <= P[1]) { | |
344 | ||
345 | /* need 12 more bytes */ | |
346 | CHECK_STREAM_PTR(12); | |
bf649a9d MM |
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++; | |
186447f8 MM |
354 | |
355 | for (y = 0; y < 8; y++) { | |
356 | ||
357 | /* time to reload flags? */ | |
358 | if (y == 0) { | |
359 | flags = | |
75816852 MM |
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; | |
186447f8 MM |
365 | lower_half = 0; /* still on top half */ |
366 | } else if (y == 4) { | |
367 | flags = | |
75816852 MM |
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; | |
d86e8568 | 373 | lower_half = 2; |
186447f8 MM |
374 | } |
375 | ||
75816852 | 376 | for (x = 0; x < 8; x++, bitmask <<= 1) { |
186447f8 MM |
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) { | |
d86e8568 MM |
382 | P0 = P[lower_half + 4]; |
383 | P1 = P[lower_half + 5]; | |
186447f8 MM |
384 | } |
385 | ||
386 | if (flags & bitmask) | |
bf649a9d | 387 | *s->pixel_ptr++ = P1; |
186447f8 | 388 | else |
bf649a9d | 389 | *s->pixel_ptr++ = P0; |
186447f8 | 390 | } |
bf649a9d | 391 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
392 | } |
393 | ||
394 | } else { | |
395 | ||
396 | /* need 10 more bytes */ | |
397 | CHECK_STREAM_PTR(10); | |
bf649a9d MM |
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++; | |
186447f8 MM |
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 = | |
75816852 MM |
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; | |
186447f8 MM |
418 | } else if (y == 4) { |
419 | flags = | |
75816852 MM |
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; | |
186447f8 MM |
425 | } |
426 | ||
75816852 | 427 | for (x = 0; x < 8; x++, bitmask <<= 1) { |
186447f8 MM |
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) | |
bf649a9d | 438 | *s->pixel_ptr++ = P1; |
d86e8568 | 439 | else |
bf649a9d | 440 | *s->pixel_ptr++ = P0; |
186447f8 | 441 | } |
bf649a9d | 442 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
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 | ||
75816852 | 460 | for (bitmask = 0x01; bitmask <= 0x80; bitmask <<= 1) { |
186447f8 MM |
461 | |
462 | if (flags & bitmask) | |
bf649a9d | 463 | *s->pixel_ptr++ = P1; |
d86e8568 | 464 | else |
bf649a9d | 465 | *s->pixel_ptr++ = P0; |
186447f8 | 466 | } |
bf649a9d | 467 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
468 | } |
469 | } | |
470 | } | |
471 | ||
472 | /* report success */ | |
473 | return 0; | |
474 | } | |
475 | ||
bf649a9d | 476 | static int ipvideo_decode_block_opcode_0x9(IpvideoContext *s) |
186447f8 MM |
477 | { |
478 | int x, y; | |
479 | unsigned char P[4]; | |
75816852 | 480 | unsigned char B[4]; |
186447f8 MM |
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++) | |
bf649a9d | 489 | P[y] = *s->stream_ptr++; |
186447f8 MM |
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 */ | |
bf649a9d MM |
498 | flags = (s->stream_ptr[1] << 8) | s->stream_ptr[0]; |
499 | s->stream_ptr += 2; | |
75816852 | 500 | for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
bf649a9d | 501 | *s->pixel_ptr++ = P[(flags >> shifter) & 0x03]; |
186447f8 | 502 | } |
bf649a9d | 503 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
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 | ||
bf649a9d MM |
511 | B[0] = *s->stream_ptr++; |
512 | B[1] = *s->stream_ptr++; | |
513 | B[2] = *s->stream_ptr++; | |
514 | B[3] = *s->stream_ptr++; | |
75816852 MM |
515 | flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
516 | shifter = 0; | |
186447f8 MM |
517 | |
518 | for (y = 0; y < 8; y += 2) { | |
75816852 | 519 | for (x = 0; x < 8; x += 2, shifter += 2) { |
186447f8 | 520 | pix = P[(flags >> shifter) & 0x03]; |
bf649a9d MM |
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; | |
186447f8 | 525 | } |
bf649a9d | 526 | s->pixel_ptr += s->stride * 2; |
186447f8 MM |
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)) { | |
bf649a9d MM |
537 | B[0] = *s->stream_ptr++; |
538 | B[1] = *s->stream_ptr++; | |
539 | B[2] = *s->stream_ptr++; | |
540 | B[3] = *s->stream_ptr++; | |
75816852 MM |
541 | flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
542 | shifter = 0; | |
186447f8 | 543 | } |
75816852 | 544 | for (x = 0; x < 8; x += 2, shifter += 2) { |
186447f8 | 545 | pix = P[(flags >> shifter) & 0x03]; |
bf649a9d MM |
546 | *(s->pixel_ptr + x) = pix; |
547 | *(s->pixel_ptr + x + 1) = pix; | |
186447f8 | 548 | } |
bf649a9d | 549 | s->pixel_ptr += s->stride; |
186447f8 MM |
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)) { | |
bf649a9d MM |
560 | B[0] = *s->stream_ptr++; |
561 | B[1] = *s->stream_ptr++; | |
562 | B[2] = *s->stream_ptr++; | |
563 | B[3] = *s->stream_ptr++; | |
75816852 MM |
564 | flags = (B[3] << 24) | (B[2] << 16) | (B[1] << 8) | B[0]; |
565 | shifter = 0; | |
186447f8 | 566 | } |
75816852 | 567 | for (x = 0; x < 8; x++, shifter += 2) { |
186447f8 | 568 | pix = P[(flags >> shifter) & 0x03]; |
bf649a9d MM |
569 | *(s->pixel_ptr + x) = pix; |
570 | *(s->pixel_ptr + s->stride + x) = pix; | |
186447f8 | 571 | } |
bf649a9d | 572 | s->pixel_ptr += s->stride * 2; |
186447f8 MM |
573 | } |
574 | } | |
575 | ||
576 | /* report success */ | |
577 | return 0; | |
578 | } | |
579 | ||
bf649a9d | 580 | static int ipvideo_decode_block_opcode_0xA(IpvideoContext *s) |
186447f8 MM |
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++) | |
bf649a9d | 596 | P[y] = *s->stream_ptr++; |
186447f8 MM |
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++) | |
bf649a9d | 604 | B[y] = *s->stream_ptr++; |
186447f8 MM |
605 | for (y = 4; y < 16; y += 4) { |
606 | for (x = y; x < y + 4; x++) | |
bf649a9d | 607 | P[x] = *s->stream_ptr++; |
186447f8 | 608 | for (x = y; x < y + 4; x++) |
bf649a9d | 609 | B[x] = *s->stream_ptr++; |
186447f8 MM |
610 | } |
611 | ||
612 | for (y = 0; y < 8; y++) { | |
613 | ||
614 | lower_half = (y >= 4) ? 4 : 0; | |
75816852 | 615 | flags = (B[y + 8] << 8) | B[y]; |
186447f8 | 616 | |
75816852 | 617 | for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
186447f8 MM |
618 | split = (x >= 4) ? 8 : 0; |
619 | index = split + lower_half + ((flags >> shifter) & 0x03); | |
bf649a9d | 620 | *s->pixel_ptr++ = P[index]; |
186447f8 MM |
621 | } |
622 | ||
bf649a9d | 623 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
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++) | |
bf649a9d | 633 | B[y] = *s->stream_ptr++; |
186447f8 | 634 | for (y = 4; y < 8; y++) |
bf649a9d | 635 | P[y] = *s->stream_ptr++; |
186447f8 | 636 | for (y = 8; y < 16; y++) |
bf649a9d | 637 | B[y] = *s->stream_ptr++; |
186447f8 MM |
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 | ||
75816852 | 644 | flags = (B[y + 8] << 8) | B[y]; |
186447f8 MM |
645 | split = 0; |
646 | ||
75816852 | 647 | for (x = 0, shifter = 0; x < 8; x++, shifter += 2) { |
186447f8 MM |
648 | if (x == 4) |
649 | split = 4; | |
bf649a9d | 650 | *s->pixel_ptr++ = P[split + ((flags >> shifter) & 0x03)]; |
186447f8 MM |
651 | } |
652 | ||
bf649a9d | 653 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
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 | ||
75816852 | 662 | flags = (B[y * 2 + 1] << 8) | B[y * 2]; |
186447f8 MM |
663 | if (y == 4) |
664 | split = 4; | |
665 | ||
75816852 | 666 | for (x = 0, shifter = 0; x < 8; x++, shifter += 2) |
bf649a9d | 667 | *s->pixel_ptr++ = P[split + ((flags >> shifter) & 0x03)]; |
186447f8 | 668 | |
bf649a9d | 669 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
670 | } |
671 | } | |
672 | } | |
673 | ||
674 | /* report success */ | |
675 | return 0; | |
676 | } | |
677 | ||
bf649a9d | 678 | static int ipvideo_decode_block_opcode_0xB(IpvideoContext *s) |
186447f8 MM |
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++) { | |
bf649a9d | 687 | *s->pixel_ptr++ = *s->stream_ptr++; |
186447f8 | 688 | } |
bf649a9d | 689 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
690 | } |
691 | ||
692 | /* report success */ | |
693 | return 0; | |
694 | } | |
695 | ||
bf649a9d | 696 | static int ipvideo_decode_block_opcode_0xC(IpvideoContext *s) |
186447f8 MM |
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) { | |
bf649a9d MM |
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; | |
186447f8 | 711 | } |
bf649a9d | 712 | s->pixel_ptr += s->stride * 2; |
186447f8 MM |
713 | } |
714 | ||
715 | /* report success */ | |
716 | return 0; | |
717 | } | |
718 | ||
bf649a9d | 719 | static int ipvideo_decode_block_opcode_0xD(IpvideoContext *s) |
186447f8 MM |
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++) | |
bf649a9d | 729 | P[y] = *s->stream_ptr++; |
186447f8 MM |
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++; | |
bf649a9d | 740 | *s->pixel_ptr++ = P[index]; |
186447f8 | 741 | } |
bf649a9d | 742 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
743 | } |
744 | ||
745 | /* report success */ | |
746 | return 0; | |
747 | } | |
748 | ||
bf649a9d | 749 | static int ipvideo_decode_block_opcode_0xE(IpvideoContext *s) |
186447f8 MM |
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); | |
bf649a9d | 756 | pix = *s->stream_ptr++; |
186447f8 MM |
757 | |
758 | for (y = 0; y < 8; y++) { | |
759 | for (x = 0; x < 8; x++) { | |
bf649a9d | 760 | *s->pixel_ptr++ = pix; |
186447f8 | 761 | } |
bf649a9d | 762 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
763 | } |
764 | ||
765 | /* report success */ | |
766 | return 0; | |
767 | } | |
768 | ||
bf649a9d | 769 | static int ipvideo_decode_block_opcode_0xF(IpvideoContext *s) |
186447f8 MM |
770 | { |
771 | int x, y; | |
772 | unsigned char sample0, sample1; | |
773 | ||
774 | /* dithered encoding */ | |
775 | CHECK_STREAM_PTR(2); | |
bf649a9d MM |
776 | sample0 = *s->stream_ptr++; |
777 | sample1 = *s->stream_ptr++; | |
186447f8 MM |
778 | |
779 | for (y = 0; y < 8; y++) { | |
780 | for (x = 0; x < 8; x += 2) { | |
781 | if (y & 1) { | |
bf649a9d MM |
782 | *s->pixel_ptr++ = sample1; |
783 | *s->pixel_ptr++ = sample0; | |
186447f8 | 784 | } else { |
bf649a9d MM |
785 | *s->pixel_ptr++ = sample0; |
786 | *s->pixel_ptr++ = sample1; | |
186447f8 MM |
787 | } |
788 | } | |
bf649a9d | 789 | s->pixel_ptr += s->line_inc; |
186447f8 MM |
790 | } |
791 | ||
792 | /* report success */ | |
793 | return 0; | |
794 | } | |
795 | ||
bf649a9d | 796 | static int (*ipvideo_decode_block[16])(IpvideoContext *s); |
186447f8 MM |
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 */ | |
2a2bbcb0 | 814 | memcpy(s->current_frame.data[1], s->avctx->palctrl->palette, PALETTE_COUNT * 4); |
bf649a9d MM |
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) { | |
9b879566 | 841 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode problem on frame %d, @ block (%d, %d)\n", |
bf649a9d MM |
842 | frame, x - y, y / s->stride); |
843 | return; | |
186447f8 MM |
844 | } |
845 | } | |
186447f8 | 846 | } |
bf649a9d MM |
847 | if ((s->stream_ptr != s->stream_end) && |
848 | (s->stream_ptr + 1 != s->stream_end)) { | |
9b879566 | 849 | av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode finished with %d bytes left over\n", |
bf649a9d MM |
850 | s->stream_end - s->stream_ptr); |
851 | } | |
186447f8 MM |
852 | } |
853 | ||
3ef8be2b MM |
854 | static int ipvideo_decode_init(AVCodecContext *avctx) |
855 | { | |
856 | IpvideoContext *s = avctx->priv_data; | |
857 | ||
858 | s->avctx = avctx; | |
186447f8 | 859 | |
2a2bbcb0 | 860 | if (s->avctx->palctrl == NULL) { |
9b879566 | 861 | av_log(avctx, AV_LOG_ERROR, " Interplay video: palette expected.\n"); |
186447f8 MM |
862 | return -1; |
863 | } | |
864 | ||
865 | avctx->pix_fmt = PIX_FMT_PAL8; | |
3ef8be2b MM |
866 | avctx->has_b_frames = 0; |
867 | dsputil_init(&s->dsp, avctx); | |
868 | ||
3ef8be2b MM |
869 | /* decoding map contains 4 bits of information per 8x8 block */ |
870 | s->decoding_map_size = avctx->width * avctx->height / (8 * 8 * 2); | |
186447f8 MM |
871 | |
872 | /* assign block decode functions */ | |
d86e8568 MM |
873 | ipvideo_decode_block[0x0] = ipvideo_decode_block_opcode_0x0; |
874 | ipvideo_decode_block[0x1] = ipvideo_decode_block_opcode_0x1; | |
186447f8 MM |
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; | |
3ef8be2b | 889 | |
d86e8568 MM |
890 | s->current_frame.data[0] = s->last_frame.data[0] = |
891 | s->second_last_frame.data[0] = NULL; | |
892 | ||
3ef8be2b MM |
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; | |
2a2bbcb0 | 901 | AVPaletteControl *palette_control = avctx->palctrl; |
3ef8be2b | 902 | |
18a508c5 MM |
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 | ||
186447f8 MM |
908 | s->decoding_map = buf; |
909 | s->buf = buf + s->decoding_map_size; | |
910 | s->size = buf_size - s->decoding_map_size; | |
3ef8be2b | 911 | |
d86e8568 | 912 | s->current_frame.reference = 3; |
186447f8 | 913 | if (avctx->get_buffer(avctx, &s->current_frame)) { |
9b879566 | 914 | av_log(avctx, AV_LOG_ERROR, " Interplay Video: get_buffer() failed\n"); |
186447f8 MM |
915 | return -1; |
916 | } | |
3ef8be2b | 917 | |
186447f8 | 918 | ipvideo_decode_opcodes(s); |
3ef8be2b | 919 | |
2a2bbcb0 MM |
920 | if (palette_control->palette_changed) { |
921 | palette_control->palette_changed = 0; | |
922 | s->current_frame.palette_has_changed = 1; | |
923 | } | |
924 | ||
d86e8568 MM |
925 | *data_size = sizeof(AVFrame); |
926 | *(AVFrame*)data = s->current_frame; | |
3ef8be2b | 927 | |
186447f8 | 928 | /* shuffle frames */ |
d86e8568 MM |
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; | |
186447f8 | 932 | s->last_frame = s->current_frame; |
d86e8568 | 933 | s->current_frame.data[0] = NULL; /* catch any access attempts */ |
3ef8be2b | 934 | |
186447f8 | 935 | /* report that the buffer was completely consumed */ |
3ef8be2b MM |
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 */ | |
d86e8568 MM |
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); | |
3ef8be2b | 948 | |
3ef8be2b MM |
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 | }; |