Libav
|
00001 /* 00002 * Quicktime Graphics (SMC) Video Decoder 00003 * Copyright (C) 2003 the ffmpeg project 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 #include <string.h> 00034 00035 #include "libavutil/intreadwrite.h" 00036 #include "avcodec.h" 00037 00038 #define CPAIR 2 00039 #define CQUAD 4 00040 #define COCTET 8 00041 00042 #define COLORS_PER_TABLE 256 00043 00044 typedef struct SmcContext { 00045 00046 AVCodecContext *avctx; 00047 AVFrame frame; 00048 00049 const unsigned char *buf; 00050 int size; 00051 00052 /* SMC color tables */ 00053 unsigned char color_pairs[COLORS_PER_TABLE * CPAIR]; 00054 unsigned char color_quads[COLORS_PER_TABLE * CQUAD]; 00055 unsigned char color_octets[COLORS_PER_TABLE * COCTET]; 00056 00057 } SmcContext; 00058 00059 #define GET_BLOCK_COUNT() \ 00060 (opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F); 00061 00062 #define ADVANCE_BLOCK() \ 00063 { \ 00064 pixel_ptr += 4; \ 00065 if (pixel_ptr >= width) \ 00066 { \ 00067 pixel_ptr = 0; \ 00068 row_ptr += stride * 4; \ 00069 } \ 00070 total_blocks--; \ 00071 if (total_blocks < 0) \ 00072 { \ 00073 av_log(s->avctx, AV_LOG_INFO, "warning: block counter just went negative (this should not happen)\n"); \ 00074 return; \ 00075 } \ 00076 } 00077 00078 static void smc_decode_stream(SmcContext *s) 00079 { 00080 int width = s->avctx->width; 00081 int height = s->avctx->height; 00082 int stride = s->frame.linesize[0]; 00083 int i; 00084 int stream_ptr = 0; 00085 int chunk_size; 00086 unsigned char opcode; 00087 int n_blocks; 00088 unsigned int color_flags; 00089 unsigned int color_flags_a; 00090 unsigned int color_flags_b; 00091 unsigned int flag_mask; 00092 00093 unsigned char *pixels = s->frame.data[0]; 00094 00095 int image_size = height * s->frame.linesize[0]; 00096 int row_ptr = 0; 00097 int pixel_ptr = 0; 00098 int pixel_x, pixel_y; 00099 int row_inc = stride - 4; 00100 int block_ptr; 00101 int prev_block_ptr; 00102 int prev_block_ptr1, prev_block_ptr2; 00103 int prev_block_flag; 00104 int total_blocks; 00105 int color_table_index; /* indexes to color pair, quad, or octet tables */ 00106 int pixel; 00107 00108 int color_pair_index = 0; 00109 int color_quad_index = 0; 00110 int color_octet_index = 0; 00111 00112 /* make the palette available */ 00113 memcpy(s->frame.data[1], s->avctx->palctrl->palette, AVPALETTE_SIZE); 00114 if (s->avctx->palctrl->palette_changed) { 00115 s->frame.palette_has_changed = 1; 00116 s->avctx->palctrl->palette_changed = 0; 00117 } 00118 00119 chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF; 00120 stream_ptr += 4; 00121 if (chunk_size != s->size) 00122 av_log(s->avctx, AV_LOG_INFO, "warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n", 00123 chunk_size, s->size); 00124 00125 chunk_size = s->size; 00126 total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); 00127 00128 /* traverse through the blocks */ 00129 while (total_blocks) { 00130 /* sanity checks */ 00131 /* make sure stream ptr hasn't gone out of bounds */ 00132 if (stream_ptr > chunk_size) { 00133 av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)\n", 00134 stream_ptr, chunk_size); 00135 return; 00136 } 00137 /* make sure the row pointer hasn't gone wild */ 00138 if (row_ptr >= image_size) { 00139 av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n", 00140 row_ptr, image_size); 00141 return; 00142 } 00143 00144 opcode = s->buf[stream_ptr++]; 00145 switch (opcode & 0xF0) { 00146 /* skip n blocks */ 00147 case 0x00: 00148 case 0x10: 00149 n_blocks = GET_BLOCK_COUNT(); 00150 while (n_blocks--) { 00151 ADVANCE_BLOCK(); 00152 } 00153 break; 00154 00155 /* repeat last block n times */ 00156 case 0x20: 00157 case 0x30: 00158 n_blocks = GET_BLOCK_COUNT(); 00159 00160 /* sanity check */ 00161 if ((row_ptr == 0) && (pixel_ptr == 0)) { 00162 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n", 00163 opcode & 0xF0); 00164 break; 00165 } 00166 00167 /* figure out where the previous block started */ 00168 if (pixel_ptr == 0) 00169 prev_block_ptr1 = 00170 (row_ptr - s->avctx->width * 4) + s->avctx->width - 4; 00171 else 00172 prev_block_ptr1 = row_ptr + pixel_ptr - 4; 00173 00174 while (n_blocks--) { 00175 block_ptr = row_ptr + pixel_ptr; 00176 prev_block_ptr = prev_block_ptr1; 00177 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00178 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00179 pixels[block_ptr++] = pixels[prev_block_ptr++]; 00180 } 00181 block_ptr += row_inc; 00182 prev_block_ptr += row_inc; 00183 } 00184 ADVANCE_BLOCK(); 00185 } 00186 break; 00187 00188 /* repeat previous pair of blocks n times */ 00189 case 0x40: 00190 case 0x50: 00191 n_blocks = GET_BLOCK_COUNT(); 00192 n_blocks *= 2; 00193 00194 /* sanity check */ 00195 if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) { 00196 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n", 00197 opcode & 0xF0); 00198 break; 00199 } 00200 00201 /* figure out where the previous 2 blocks started */ 00202 if (pixel_ptr == 0) 00203 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + 00204 s->avctx->width - 4 * 2; 00205 else if (pixel_ptr == 4) 00206 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc; 00207 else 00208 prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2; 00209 00210 if (pixel_ptr == 0) 00211 prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc; 00212 else 00213 prev_block_ptr2 = row_ptr + pixel_ptr - 4; 00214 00215 prev_block_flag = 0; 00216 while (n_blocks--) { 00217 block_ptr = row_ptr + pixel_ptr; 00218 if (prev_block_flag) 00219 prev_block_ptr = prev_block_ptr2; 00220 else 00221 prev_block_ptr = prev_block_ptr1; 00222 prev_block_flag = !prev_block_flag; 00223 00224 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00225 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00226 pixels[block_ptr++] = pixels[prev_block_ptr++]; 00227 } 00228 block_ptr += row_inc; 00229 prev_block_ptr += row_inc; 00230 } 00231 ADVANCE_BLOCK(); 00232 } 00233 break; 00234 00235 /* 1-color block encoding */ 00236 case 0x60: 00237 case 0x70: 00238 n_blocks = GET_BLOCK_COUNT(); 00239 pixel = s->buf[stream_ptr++]; 00240 00241 while (n_blocks--) { 00242 block_ptr = row_ptr + pixel_ptr; 00243 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00244 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00245 pixels[block_ptr++] = pixel; 00246 } 00247 block_ptr += row_inc; 00248 } 00249 ADVANCE_BLOCK(); 00250 } 00251 break; 00252 00253 /* 2-color block encoding */ 00254 case 0x80: 00255 case 0x90: 00256 n_blocks = (opcode & 0x0F) + 1; 00257 00258 /* figure out which color pair to use to paint the 2-color block */ 00259 if ((opcode & 0xF0) == 0x80) { 00260 /* fetch the next 2 colors from bytestream and store in next 00261 * available entry in the color pair table */ 00262 for (i = 0; i < CPAIR; i++) { 00263 pixel = s->buf[stream_ptr++]; 00264 color_table_index = CPAIR * color_pair_index + i; 00265 s->color_pairs[color_table_index] = pixel; 00266 } 00267 /* this is the base index to use for this block */ 00268 color_table_index = CPAIR * color_pair_index; 00269 color_pair_index++; 00270 /* wraparound */ 00271 if (color_pair_index == COLORS_PER_TABLE) 00272 color_pair_index = 0; 00273 } else 00274 color_table_index = CPAIR * s->buf[stream_ptr++]; 00275 00276 while (n_blocks--) { 00277 color_flags = AV_RB16(&s->buf[stream_ptr]); 00278 stream_ptr += 2; 00279 flag_mask = 0x8000; 00280 block_ptr = row_ptr + pixel_ptr; 00281 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00282 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00283 if (color_flags & flag_mask) 00284 pixel = color_table_index + 1; 00285 else 00286 pixel = color_table_index; 00287 flag_mask >>= 1; 00288 pixels[block_ptr++] = s->color_pairs[pixel]; 00289 } 00290 block_ptr += row_inc; 00291 } 00292 ADVANCE_BLOCK(); 00293 } 00294 break; 00295 00296 /* 4-color block encoding */ 00297 case 0xA0: 00298 case 0xB0: 00299 n_blocks = (opcode & 0x0F) + 1; 00300 00301 /* figure out which color quad to use to paint the 4-color block */ 00302 if ((opcode & 0xF0) == 0xA0) { 00303 /* fetch the next 4 colors from bytestream and store in next 00304 * available entry in the color quad table */ 00305 for (i = 0; i < CQUAD; i++) { 00306 pixel = s->buf[stream_ptr++]; 00307 color_table_index = CQUAD * color_quad_index + i; 00308 s->color_quads[color_table_index] = pixel; 00309 } 00310 /* this is the base index to use for this block */ 00311 color_table_index = CQUAD * color_quad_index; 00312 color_quad_index++; 00313 /* wraparound */ 00314 if (color_quad_index == COLORS_PER_TABLE) 00315 color_quad_index = 0; 00316 } else 00317 color_table_index = CQUAD * s->buf[stream_ptr++]; 00318 00319 while (n_blocks--) { 00320 color_flags = AV_RB32(&s->buf[stream_ptr]); 00321 stream_ptr += 4; 00322 /* flag mask actually acts as a bit shift count here */ 00323 flag_mask = 30; 00324 block_ptr = row_ptr + pixel_ptr; 00325 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00326 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00327 pixel = color_table_index + 00328 ((color_flags >> flag_mask) & 0x03); 00329 flag_mask -= 2; 00330 pixels[block_ptr++] = s->color_quads[pixel]; 00331 } 00332 block_ptr += row_inc; 00333 } 00334 ADVANCE_BLOCK(); 00335 } 00336 break; 00337 00338 /* 8-color block encoding */ 00339 case 0xC0: 00340 case 0xD0: 00341 n_blocks = (opcode & 0x0F) + 1; 00342 00343 /* figure out which color octet to use to paint the 8-color block */ 00344 if ((opcode & 0xF0) == 0xC0) { 00345 /* fetch the next 8 colors from bytestream and store in next 00346 * available entry in the color octet table */ 00347 for (i = 0; i < COCTET; i++) { 00348 pixel = s->buf[stream_ptr++]; 00349 color_table_index = COCTET * color_octet_index + i; 00350 s->color_octets[color_table_index] = pixel; 00351 } 00352 /* this is the base index to use for this block */ 00353 color_table_index = COCTET * color_octet_index; 00354 color_octet_index++; 00355 /* wraparound */ 00356 if (color_octet_index == COLORS_PER_TABLE) 00357 color_octet_index = 0; 00358 } else 00359 color_table_index = COCTET * s->buf[stream_ptr++]; 00360 00361 while (n_blocks--) { 00362 /* 00363 For this input of 6 hex bytes: 00364 01 23 45 67 89 AB 00365 Mangle it to this output: 00366 flags_a = xx012456, flags_b = xx89A37B 00367 */ 00368 /* build the color flags */ 00369 color_flags_a = 00370 ((AV_RB16(s->buf + stream_ptr ) & 0xFFF0) << 8) | 00371 (AV_RB16(s->buf + stream_ptr + 2) >> 4); 00372 color_flags_b = 00373 ((AV_RB16(s->buf + stream_ptr + 4) & 0xFFF0) << 8) | 00374 ((s->buf[stream_ptr + 1] & 0x0F) << 8) | 00375 ((s->buf[stream_ptr + 3] & 0x0F) << 4) | 00376 (s->buf[stream_ptr + 5] & 0x0F); 00377 stream_ptr += 6; 00378 00379 color_flags = color_flags_a; 00380 /* flag mask actually acts as a bit shift count here */ 00381 flag_mask = 21; 00382 block_ptr = row_ptr + pixel_ptr; 00383 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00384 /* reload flags at third row (iteration pixel_y == 2) */ 00385 if (pixel_y == 2) { 00386 color_flags = color_flags_b; 00387 flag_mask = 21; 00388 } 00389 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00390 pixel = color_table_index + 00391 ((color_flags >> flag_mask) & 0x07); 00392 flag_mask -= 3; 00393 pixels[block_ptr++] = s->color_octets[pixel]; 00394 } 00395 block_ptr += row_inc; 00396 } 00397 ADVANCE_BLOCK(); 00398 } 00399 break; 00400 00401 /* 16-color block encoding (every pixel is a different color) */ 00402 case 0xE0: 00403 n_blocks = (opcode & 0x0F) + 1; 00404 00405 while (n_blocks--) { 00406 block_ptr = row_ptr + pixel_ptr; 00407 for (pixel_y = 0; pixel_y < 4; pixel_y++) { 00408 for (pixel_x = 0; pixel_x < 4; pixel_x++) { 00409 pixels[block_ptr++] = s->buf[stream_ptr++]; 00410 } 00411 block_ptr += row_inc; 00412 } 00413 ADVANCE_BLOCK(); 00414 } 00415 break; 00416 00417 case 0xF0: 00418 av_log(s->avctx, AV_LOG_INFO, "0xF0 opcode seen in SMC chunk (contact the developers)\n"); 00419 break; 00420 } 00421 } 00422 } 00423 00424 static av_cold int smc_decode_init(AVCodecContext *avctx) 00425 { 00426 SmcContext *s = avctx->priv_data; 00427 00428 s->avctx = avctx; 00429 avctx->pix_fmt = PIX_FMT_PAL8; 00430 00431 s->frame.data[0] = NULL; 00432 00433 return 0; 00434 } 00435 00436 static int smc_decode_frame(AVCodecContext *avctx, 00437 void *data, int *data_size, 00438 AVPacket *avpkt) 00439 { 00440 const uint8_t *buf = avpkt->data; 00441 int buf_size = avpkt->size; 00442 SmcContext *s = avctx->priv_data; 00443 00444 s->buf = buf; 00445 s->size = buf_size; 00446 00447 s->frame.reference = 1; 00448 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | 00449 FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE; 00450 if (avctx->reget_buffer(avctx, &s->frame)) { 00451 av_log(s->avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 00452 return -1; 00453 } 00454 00455 smc_decode_stream(s); 00456 00457 *data_size = sizeof(AVFrame); 00458 *(AVFrame*)data = s->frame; 00459 00460 /* always report that the buffer was completely consumed */ 00461 return buf_size; 00462 } 00463 00464 static av_cold int smc_decode_end(AVCodecContext *avctx) 00465 { 00466 SmcContext *s = avctx->priv_data; 00467 00468 if (s->frame.data[0]) 00469 avctx->release_buffer(avctx, &s->frame); 00470 00471 return 0; 00472 } 00473 00474 AVCodec smc_decoder = { 00475 "smc", 00476 AVMEDIA_TYPE_VIDEO, 00477 CODEC_ID_SMC, 00478 sizeof(SmcContext), 00479 smc_decode_init, 00480 NULL, 00481 smc_decode_end, 00482 smc_decode_frame, 00483 CODEC_CAP_DR1, 00484 .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"), 00485 };