ffmpeg / libavcodec / smc.c @ 5509bffa
History | View | Annotate | Download (16.4 KB)
1 |
/*
|
---|---|
2 |
* Quicktime Graphics (SMC) 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 smc.c
|
23 |
* QT SMC Video Decoder by Mike Melanson (melanson@pcisys.net)
|
24 |
* For more information about the SMC format, visit:
|
25 |
* http://www.pcisys.net/~melanson/codecs/
|
26 |
*
|
27 |
* The SMC decoder outputs PAL8 colorspace data.
|
28 |
*/
|
29 |
|
30 |
#include <stdio.h> |
31 |
#include <stdlib.h> |
32 |
#include <string.h> |
33 |
#include <unistd.h> |
34 |
|
35 |
#include "common.h" |
36 |
#include "avcodec.h" |
37 |
#include "dsputil.h" |
38 |
|
39 |
#define CPAIR 2 |
40 |
#define CQUAD 4 |
41 |
#define COCTET 8 |
42 |
|
43 |
#define COLORS_PER_TABLE 256 |
44 |
|
45 |
typedef struct SmcContext { |
46 |
|
47 |
AVCodecContext *avctx; |
48 |
DSPContext dsp; |
49 |
AVFrame frame; |
50 |
|
51 |
unsigned char *buf; |
52 |
int size;
|
53 |
|
54 |
/* SMC color tables */
|
55 |
unsigned char color_pairs[COLORS_PER_TABLE * CPAIR]; |
56 |
unsigned char color_quads[COLORS_PER_TABLE * CQUAD]; |
57 |
unsigned char color_octets[COLORS_PER_TABLE * COCTET]; |
58 |
|
59 |
} SmcContext; |
60 |
|
61 |
#define GET_BLOCK_COUNT() \
|
62 |
(opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F); |
63 |
|
64 |
#define ADVANCE_BLOCK() \
|
65 |
{ \ |
66 |
pixel_ptr += 4; \
|
67 |
if (pixel_ptr >= width) \
|
68 |
{ \ |
69 |
pixel_ptr = 0; \
|
70 |
row_ptr += stride * 4; \
|
71 |
} \ |
72 |
total_blocks--; \ |
73 |
if (total_blocks < 0) \ |
74 |
{ \ |
75 |
av_log(s->avctx, AV_LOG_INFO, "warning: block counter just went negative (this should not happen)\n"); \
|
76 |
return; \
|
77 |
} \ |
78 |
} |
79 |
|
80 |
static void smc_decode_stream(SmcContext *s) |
81 |
{ |
82 |
int width = s->avctx->width;
|
83 |
int height = s->avctx->height;
|
84 |
int stride = s->frame.linesize[0]; |
85 |
int i;
|
86 |
int stream_ptr = 0; |
87 |
int chunk_size;
|
88 |
unsigned char opcode; |
89 |
int n_blocks;
|
90 |
unsigned int color_flags; |
91 |
unsigned int color_flags_a; |
92 |
unsigned int color_flags_b; |
93 |
unsigned int flag_mask; |
94 |
|
95 |
unsigned char *pixels = s->frame.data[0]; |
96 |
|
97 |
int image_size = height * s->frame.linesize[0]; |
98 |
int row_ptr = 0; |
99 |
int pixel_ptr = 0; |
100 |
int pixel_x, pixel_y;
|
101 |
int row_inc = stride - 4; |
102 |
int block_ptr;
|
103 |
int prev_block_ptr;
|
104 |
int prev_block_ptr1, prev_block_ptr2;
|
105 |
int prev_block_flag;
|
106 |
int total_blocks;
|
107 |
int color_table_index; /* indexes to color pair, quad, or octet tables */ |
108 |
int pixel;
|
109 |
|
110 |
int color_pair_index = 0; |
111 |
int color_quad_index = 0; |
112 |
int color_octet_index = 0; |
113 |
|
114 |
/* make the palette available */
|
115 |
memcpy(s->frame.data[1], s->avctx->palctrl->palette, AVPALETTE_SIZE);
|
116 |
if (s->avctx->palctrl->palette_changed) {
|
117 |
s->frame.palette_has_changed = 1;
|
118 |
s->avctx->palctrl->palette_changed = 0;
|
119 |
} |
120 |
|
121 |
chunk_size = BE_32(&s->buf[stream_ptr]) & 0x00FFFFFF;
|
122 |
stream_ptr += 4;
|
123 |
if (chunk_size != s->size)
|
124 |
av_log(s->avctx, AV_LOG_INFO, "warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n",
|
125 |
chunk_size, s->size); |
126 |
|
127 |
chunk_size = s->size; |
128 |
total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); |
129 |
|
130 |
/* traverse through the blocks */
|
131 |
while (total_blocks) {
|
132 |
/* sanity checks */
|
133 |
/* make sure stream ptr hasn't gone out of bounds */
|
134 |
if (stream_ptr > chunk_size) {
|
135 |
av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)\n",
|
136 |
stream_ptr, chunk_size); |
137 |
return;
|
138 |
} |
139 |
/* make sure the row pointer hasn't gone wild */
|
140 |
if (row_ptr >= image_size) {
|
141 |
av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n",
|
142 |
row_ptr, image_size); |
143 |
return;
|
144 |
} |
145 |
|
146 |
opcode = s->buf[stream_ptr++]; |
147 |
switch (opcode & 0xF0) { |
148 |
/* skip n blocks */
|
149 |
case 0x00: |
150 |
case 0x10: |
151 |
n_blocks = GET_BLOCK_COUNT(); |
152 |
while (n_blocks--) {
|
153 |
ADVANCE_BLOCK(); |
154 |
} |
155 |
break;
|
156 |
|
157 |
/* repeat last block n times */
|
158 |
case 0x20: |
159 |
case 0x30: |
160 |
n_blocks = GET_BLOCK_COUNT(); |
161 |
|
162 |
/* sanity check */
|
163 |
if ((row_ptr == 0) && (pixel_ptr == 0)) { |
164 |
av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
|
165 |
opcode & 0xF0);
|
166 |
break;
|
167 |
} |
168 |
|
169 |
/* figure out where the previous block started */
|
170 |
if (pixel_ptr == 0) |
171 |
prev_block_ptr1 = |
172 |
(row_ptr - s->avctx->width * 4) + s->avctx->width - 4; |
173 |
else
|
174 |
prev_block_ptr1 = row_ptr + pixel_ptr - 4;
|
175 |
|
176 |
while (n_blocks--) {
|
177 |
block_ptr = row_ptr + pixel_ptr; |
178 |
prev_block_ptr = prev_block_ptr1; |
179 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
180 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
181 |
pixels[block_ptr++] = pixels[prev_block_ptr++]; |
182 |
} |
183 |
block_ptr += row_inc; |
184 |
prev_block_ptr += row_inc; |
185 |
} |
186 |
ADVANCE_BLOCK(); |
187 |
} |
188 |
break;
|
189 |
|
190 |
/* repeat previous pair of blocks n times */
|
191 |
case 0x40: |
192 |
case 0x50: |
193 |
n_blocks = GET_BLOCK_COUNT(); |
194 |
n_blocks *= 2;
|
195 |
|
196 |
/* sanity check */
|
197 |
if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) { |
198 |
av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
|
199 |
opcode & 0xF0);
|
200 |
break;
|
201 |
} |
202 |
|
203 |
/* figure out where the previous 2 blocks started */
|
204 |
if (pixel_ptr == 0) |
205 |
prev_block_ptr1 = (row_ptr - s->avctx->width * 4) +
|
206 |
s->avctx->width - 4 * 2; |
207 |
else if (pixel_ptr == 4) |
208 |
prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc;
|
209 |
else
|
210 |
prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2; |
211 |
|
212 |
if (pixel_ptr == 0) |
213 |
prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc;
|
214 |
else
|
215 |
prev_block_ptr2 = row_ptr + pixel_ptr - 4;
|
216 |
|
217 |
prev_block_flag = 0;
|
218 |
while (n_blocks--) {
|
219 |
block_ptr = row_ptr + pixel_ptr; |
220 |
if (prev_block_flag)
|
221 |
prev_block_ptr = prev_block_ptr2; |
222 |
else
|
223 |
prev_block_ptr = prev_block_ptr1; |
224 |
prev_block_flag = !prev_block_flag; |
225 |
|
226 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
227 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
228 |
pixels[block_ptr++] = pixels[prev_block_ptr++]; |
229 |
} |
230 |
block_ptr += row_inc; |
231 |
prev_block_ptr += row_inc; |
232 |
} |
233 |
ADVANCE_BLOCK(); |
234 |
} |
235 |
break;
|
236 |
|
237 |
/* 1-color block encoding */
|
238 |
case 0x60: |
239 |
case 0x70: |
240 |
n_blocks = GET_BLOCK_COUNT(); |
241 |
pixel = s->buf[stream_ptr++]; |
242 |
|
243 |
while (n_blocks--) {
|
244 |
block_ptr = row_ptr + pixel_ptr; |
245 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
246 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
247 |
pixels[block_ptr++] = pixel; |
248 |
} |
249 |
block_ptr += row_inc; |
250 |
} |
251 |
ADVANCE_BLOCK(); |
252 |
} |
253 |
break;
|
254 |
|
255 |
/* 2-color block encoding */
|
256 |
case 0x80: |
257 |
case 0x90: |
258 |
n_blocks = (opcode & 0x0F) + 1; |
259 |
|
260 |
/* figure out which color pair to use to paint the 2-color block */
|
261 |
if ((opcode & 0xF0) == 0x80) { |
262 |
/* fetch the next 2 colors from bytestream and store in next
|
263 |
* available entry in the color pair table */
|
264 |
for (i = 0; i < CPAIR; i++) { |
265 |
pixel = s->buf[stream_ptr++]; |
266 |
color_table_index = CPAIR * color_pair_index + i; |
267 |
s->color_pairs[color_table_index] = pixel; |
268 |
} |
269 |
/* this is the base index to use for this block */
|
270 |
color_table_index = CPAIR * color_pair_index; |
271 |
color_pair_index++; |
272 |
/* wraparound */
|
273 |
if (color_pair_index == COLORS_PER_TABLE)
|
274 |
color_pair_index = 0;
|
275 |
} else
|
276 |
color_table_index = CPAIR * s->buf[stream_ptr++]; |
277 |
|
278 |
while (n_blocks--) {
|
279 |
color_flags = BE_16(&s->buf[stream_ptr]); |
280 |
stream_ptr += 2;
|
281 |
flag_mask = 0x8000;
|
282 |
block_ptr = row_ptr + pixel_ptr; |
283 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
284 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
285 |
if (color_flags & flag_mask)
|
286 |
pixel = color_table_index + 1;
|
287 |
else
|
288 |
pixel = color_table_index; |
289 |
flag_mask >>= 1;
|
290 |
pixels[block_ptr++] = s->color_pairs[pixel]; |
291 |
} |
292 |
block_ptr += row_inc; |
293 |
} |
294 |
ADVANCE_BLOCK(); |
295 |
} |
296 |
break;
|
297 |
|
298 |
/* 4-color block encoding */
|
299 |
case 0xA0: |
300 |
case 0xB0: |
301 |
n_blocks = (opcode & 0x0F) + 1; |
302 |
|
303 |
/* figure out which color quad to use to paint the 4-color block */
|
304 |
if ((opcode & 0xF0) == 0xA0) { |
305 |
/* fetch the next 4 colors from bytestream and store in next
|
306 |
* available entry in the color quad table */
|
307 |
for (i = 0; i < CQUAD; i++) { |
308 |
pixel = s->buf[stream_ptr++]; |
309 |
color_table_index = CQUAD * color_quad_index + i; |
310 |
s->color_quads[color_table_index] = pixel; |
311 |
} |
312 |
/* this is the base index to use for this block */
|
313 |
color_table_index = CQUAD * color_quad_index; |
314 |
color_quad_index++; |
315 |
/* wraparound */
|
316 |
if (color_quad_index == COLORS_PER_TABLE)
|
317 |
color_quad_index = 0;
|
318 |
} else
|
319 |
color_table_index = CQUAD * s->buf[stream_ptr++]; |
320 |
|
321 |
while (n_blocks--) {
|
322 |
color_flags = BE_32(&s->buf[stream_ptr]); |
323 |
stream_ptr += 4;
|
324 |
/* flag mask actually acts as a bit shift count here */
|
325 |
flag_mask = 30;
|
326 |
block_ptr = row_ptr + pixel_ptr; |
327 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
328 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
329 |
pixel = color_table_index + |
330 |
((color_flags >> flag_mask) & 0x03);
|
331 |
flag_mask -= 2;
|
332 |
pixels[block_ptr++] = s->color_quads[pixel]; |
333 |
} |
334 |
block_ptr += row_inc; |
335 |
} |
336 |
ADVANCE_BLOCK(); |
337 |
} |
338 |
break;
|
339 |
|
340 |
/* 8-color block encoding */
|
341 |
case 0xC0: |
342 |
case 0xD0: |
343 |
n_blocks = (opcode & 0x0F) + 1; |
344 |
|
345 |
/* figure out which color octet to use to paint the 8-color block */
|
346 |
if ((opcode & 0xF0) == 0xC0) { |
347 |
/* fetch the next 8 colors from bytestream and store in next
|
348 |
* available entry in the color octet table */
|
349 |
for (i = 0; i < COCTET; i++) { |
350 |
pixel = s->buf[stream_ptr++]; |
351 |
color_table_index = COCTET * color_octet_index + i; |
352 |
s->color_octets[color_table_index] = pixel; |
353 |
} |
354 |
/* this is the base index to use for this block */
|
355 |
color_table_index = COCTET * color_octet_index; |
356 |
color_octet_index++; |
357 |
/* wraparound */
|
358 |
if (color_octet_index == COLORS_PER_TABLE)
|
359 |
color_octet_index = 0;
|
360 |
} else
|
361 |
color_table_index = COCTET * s->buf[stream_ptr++]; |
362 |
|
363 |
while (n_blocks--) {
|
364 |
/*
|
365 |
For this input of 6 hex bytes:
|
366 |
01 23 45 67 89 AB
|
367 |
Mangle it to this output:
|
368 |
flags_a = xx012456, flags_b = xx89A37B
|
369 |
*/
|
370 |
/* build the color flags */
|
371 |
color_flags_a = color_flags_b = 0;
|
372 |
color_flags_a = |
373 |
(s->buf[stream_ptr + 0] << 16) | |
374 |
((s->buf[stream_ptr + 1] & 0xF0) << 8) | |
375 |
((s->buf[stream_ptr + 2] & 0xF0) << 4) | |
376 |
((s->buf[stream_ptr + 2] & 0x0F) << 4) | |
377 |
((s->buf[stream_ptr + 3] & 0xF0) >> 4); |
378 |
color_flags_b = |
379 |
(s->buf[stream_ptr + 4] << 16) | |
380 |
((s->buf[stream_ptr + 5] & 0xF0) << 8) | |
381 |
((s->buf[stream_ptr + 1] & 0x0F) << 8) | |
382 |
((s->buf[stream_ptr + 3] & 0x0F) << 4) | |
383 |
(s->buf[stream_ptr + 5] & 0x0F); |
384 |
stream_ptr += 6;
|
385 |
|
386 |
color_flags = color_flags_a; |
387 |
/* flag mask actually acts as a bit shift count here */
|
388 |
flag_mask = 21;
|
389 |
block_ptr = row_ptr + pixel_ptr; |
390 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
391 |
/* reload flags at third row (iteration pixel_y == 2) */
|
392 |
if (pixel_y == 2) { |
393 |
color_flags = color_flags_b; |
394 |
flag_mask = 21;
|
395 |
} |
396 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
397 |
pixel = color_table_index + |
398 |
((color_flags >> flag_mask) & 0x07);
|
399 |
flag_mask -= 3;
|
400 |
pixels[block_ptr++] = s->color_octets[pixel]; |
401 |
} |
402 |
block_ptr += row_inc; |
403 |
} |
404 |
ADVANCE_BLOCK(); |
405 |
} |
406 |
break;
|
407 |
|
408 |
/* 16-color block encoding (every pixel is a different color) */
|
409 |
case 0xE0: |
410 |
n_blocks = (opcode & 0x0F) + 1; |
411 |
|
412 |
while (n_blocks--) {
|
413 |
block_ptr = row_ptr + pixel_ptr; |
414 |
for (pixel_y = 0; pixel_y < 4; pixel_y++) { |
415 |
for (pixel_x = 0; pixel_x < 4; pixel_x++) { |
416 |
pixels[block_ptr++] = s->buf[stream_ptr++]; |
417 |
} |
418 |
block_ptr += row_inc; |
419 |
} |
420 |
ADVANCE_BLOCK(); |
421 |
} |
422 |
break;
|
423 |
|
424 |
case 0xF0: |
425 |
av_log(s->avctx, AV_LOG_INFO, "0xF0 opcode seen in SMC chunk (contact the developers)\n");
|
426 |
break;
|
427 |
} |
428 |
} |
429 |
} |
430 |
|
431 |
static int smc_decode_init(AVCodecContext *avctx) |
432 |
{ |
433 |
SmcContext *s = (SmcContext *)avctx->priv_data; |
434 |
|
435 |
s->avctx = avctx; |
436 |
avctx->pix_fmt = PIX_FMT_PAL8; |
437 |
avctx->has_b_frames = 0;
|
438 |
dsputil_init(&s->dsp, avctx); |
439 |
|
440 |
s->frame.data[0] = NULL; |
441 |
|
442 |
return 0; |
443 |
} |
444 |
|
445 |
static int smc_decode_frame(AVCodecContext *avctx, |
446 |
void *data, int *data_size, |
447 |
uint8_t *buf, int buf_size)
|
448 |
{ |
449 |
SmcContext *s = (SmcContext *)avctx->priv_data; |
450 |
|
451 |
s->buf = buf; |
452 |
s->size = buf_size; |
453 |
|
454 |
s->frame.reference = 1;
|
455 |
s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | |
456 |
FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE; |
457 |
if (avctx->reget_buffer(avctx, &s->frame)) {
|
458 |
av_log(s->avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
|
459 |
return -1; |
460 |
} |
461 |
|
462 |
smc_decode_stream(s); |
463 |
|
464 |
*data_size = sizeof(AVFrame);
|
465 |
*(AVFrame*)data = s->frame; |
466 |
|
467 |
/* always report that the buffer was completely consumed */
|
468 |
return buf_size;
|
469 |
} |
470 |
|
471 |
static int smc_decode_end(AVCodecContext *avctx) |
472 |
{ |
473 |
SmcContext *s = (SmcContext *)avctx->priv_data; |
474 |
|
475 |
if (s->frame.data[0]) |
476 |
avctx->release_buffer(avctx, &s->frame); |
477 |
|
478 |
return 0; |
479 |
} |
480 |
|
481 |
AVCodec smc_decoder = { |
482 |
"smc",
|
483 |
CODEC_TYPE_VIDEO, |
484 |
CODEC_ID_SMC, |
485 |
sizeof(SmcContext),
|
486 |
smc_decode_init, |
487 |
NULL,
|
488 |
smc_decode_end, |
489 |
smc_decode_frame, |
490 |
CODEC_CAP_DR1, |
491 |
}; |