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