00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "avcodec.h"
00026 #include "bytestream.h"
00027 #include "libavutil/bswap.h"
00028 #include "libavcodec/dsputil.h"
00029 #include "sanm_data.h"
00030
00031 #define NGLYPHS 256
00032
00033 typedef struct {
00034 AVCodecContext *avctx;
00035 GetByteContext gb;
00036
00037 int version, subversion;
00038 uint32_t pal[256];
00039 int16_t delta_pal[768];
00040
00041 int pitch;
00042 int width, height;
00043 int aligned_width, aligned_height;
00044 int prev_seq;
00045
00046 AVFrame frame, *output;
00047 uint16_t *frm0, *frm1, *frm2;
00048 uint8_t *stored_frame;
00049 uint32_t frm0_size, frm1_size, frm2_size;
00050 uint32_t stored_frame_size;
00051
00052 uint8_t *rle_buf;
00053 unsigned int rle_buf_size;
00054
00055 int rotate_code;
00056
00057 long npixels, buf_size;
00058
00059 uint16_t codebook[256];
00060 uint16_t small_codebook[4];
00061
00062 int8_t p4x4glyphs[NGLYPHS][16];
00063 int8_t p8x8glyphs[NGLYPHS][64];
00064 } SANMVideoContext;
00065
00066 typedef struct {
00067 int seq_num, codec, rotate_code, rle_output_size;
00068
00069 uint16_t bg_color;
00070 uint32_t width, height;
00071 } SANMFrameHeader;
00072
00073 enum GlyphEdge {
00074 LEFT_EDGE,
00075 TOP_EDGE,
00076 RIGHT_EDGE,
00077 BOTTOM_EDGE,
00078 NO_EDGE
00079 };
00080
00081 enum GlyphDir {
00082 DIR_LEFT,
00083 DIR_UP,
00084 DIR_RIGHT,
00085 DIR_DOWN,
00086 NO_DIR
00087 };
00088
00096 static enum GlyphEdge which_edge(int x, int y, int edge_size)
00097 {
00098 const int edge_max = edge_size - 1;
00099
00100 if (!y) {
00101 return BOTTOM_EDGE;
00102 } else if (y == edge_max) {
00103 return TOP_EDGE;
00104 } else if (!x) {
00105 return LEFT_EDGE;
00106 } else if (x == edge_max) {
00107 return RIGHT_EDGE;
00108 } else {
00109 return NO_EDGE;
00110 }
00111 }
00112
00113 static enum GlyphDir which_direction(enum GlyphEdge edge0, enum GlyphEdge edge1)
00114 {
00115 if ((edge0 == LEFT_EDGE && edge1 == RIGHT_EDGE) ||
00116 (edge1 == LEFT_EDGE && edge0 == RIGHT_EDGE) ||
00117 (edge0 == BOTTOM_EDGE && edge1 != TOP_EDGE) ||
00118 (edge1 == BOTTOM_EDGE && edge0 != TOP_EDGE)) {
00119 return DIR_UP;
00120 } else if ((edge0 == TOP_EDGE && edge1 != BOTTOM_EDGE) ||
00121 (edge1 == TOP_EDGE && edge0 != BOTTOM_EDGE)) {
00122 return DIR_DOWN;
00123 } else if ((edge0 == LEFT_EDGE && edge1 != RIGHT_EDGE) ||
00124 (edge1 == LEFT_EDGE && edge0 != RIGHT_EDGE)) {
00125 return DIR_LEFT;
00126 } else if ((edge0 == TOP_EDGE && edge1 == BOTTOM_EDGE) ||
00127 (edge1 == TOP_EDGE && edge0 == BOTTOM_EDGE) ||
00128 (edge0 == RIGHT_EDGE && edge1 != LEFT_EDGE) ||
00129 (edge1 == RIGHT_EDGE && edge0 != LEFT_EDGE)) {
00130 return DIR_RIGHT;
00131 }
00132
00133 return NO_DIR;
00134 }
00135
00139 static void interp_point(int8_t *points, int x0, int y0, int x1, int y1,
00140 int pos, int npoints)
00141 {
00142 if (npoints) {
00143 points[0] = (x0 * pos + x1 * (npoints - pos) + (npoints >> 1)) / npoints;
00144 points[1] = (y0 * pos + y1 * (npoints - pos) + (npoints >> 1)) / npoints;
00145 } else {
00146 points[0] = x0;
00147 points[1] = y0;
00148 }
00149 }
00150
00159 static void make_glyphs(int8_t *pglyphs, const int8_t *xvec, const int8_t *yvec,
00160 const int side_length)
00161 {
00162 const int glyph_size = side_length * side_length;
00163 int8_t *pglyph = pglyphs;
00164
00165 int i, j;
00166 for (i = 0; i < GLYPH_COORD_VECT_SIZE; i++) {
00167 int x0 = xvec[i];
00168 int y0 = yvec[i];
00169 enum GlyphEdge edge0 = which_edge(x0, y0, side_length);
00170
00171 for (j = 0; j < GLYPH_COORD_VECT_SIZE; j++, pglyph += glyph_size) {
00172 int x1 = xvec[j];
00173 int y1 = yvec[j];
00174 enum GlyphEdge edge1 = which_edge(x1, y1, side_length);
00175 enum GlyphDir dir = which_direction(edge0, edge1);
00176 int npoints = FFMAX(FFABS(x1 - x0), FFABS(y1 - y0));
00177 int ipoint;
00178
00179 for (ipoint = 0; ipoint <= npoints; ipoint++) {
00180 int8_t point[2];
00181 int irow, icol;
00182
00183 interp_point(point, x0, y0, x1, y1, ipoint, npoints);
00184
00185 switch (dir) {
00186 case DIR_UP:
00187 for (irow = point[1]; irow >= 0; irow--)
00188 pglyph[point[0] + irow * side_length] = 1;
00189 break;
00190
00191 case DIR_DOWN:
00192 for (irow = point[1]; irow < side_length; irow++)
00193 pglyph[point[0] + irow * side_length] = 1;
00194 break;
00195
00196 case DIR_LEFT:
00197 for (icol = point[0]; icol >= 0; icol--)
00198 pglyph[icol + point[1] * side_length] = 1;
00199 break;
00200
00201 case DIR_RIGHT:
00202 for (icol = point[0]; icol < side_length; icol++)
00203 pglyph[icol + point[1] * side_length] = 1;
00204 break;
00205 }
00206 }
00207 }
00208 }
00209 }
00210
00211 static void init_sizes(SANMVideoContext *ctx, int width, int height)
00212 {
00213 ctx->width = width;
00214 ctx->height = height;
00215 ctx->npixels = width * height;
00216
00217 ctx->aligned_width = FFALIGN(width, 8);
00218 ctx->aligned_height = FFALIGN(height, 8);
00219
00220 ctx->buf_size = ctx->aligned_width * ctx->aligned_height * sizeof(ctx->frm0[0]);
00221 ctx->pitch = width;
00222 }
00223
00224 static void destroy_buffers(SANMVideoContext *ctx)
00225 {
00226 av_freep(&ctx->frm0);
00227 av_freep(&ctx->frm1);
00228 av_freep(&ctx->frm2);
00229 av_freep(&ctx->stored_frame);
00230 av_freep(&ctx->rle_buf);
00231 }
00232
00233 static av_cold int init_buffers(SANMVideoContext *ctx)
00234 {
00235 av_fast_padded_malloc(&ctx->frm0, &ctx->frm0_size, ctx->buf_size);
00236 av_fast_padded_malloc(&ctx->frm1, &ctx->frm1_size, ctx->buf_size);
00237 av_fast_padded_malloc(&ctx->frm2, &ctx->frm2_size, ctx->buf_size);
00238 if (!ctx->version)
00239 av_fast_padded_malloc(&ctx->stored_frame, &ctx->stored_frame_size, ctx->buf_size);
00240
00241 if (!ctx->frm0 || !ctx->frm1 || !ctx->frm2 || (!ctx->stored_frame && !ctx->version)) {
00242 destroy_buffers(ctx);
00243 return AVERROR(ENOMEM);
00244 }
00245
00246 return 0;
00247 }
00248
00249 static void rotate_bufs(SANMVideoContext *ctx, int rotate_code)
00250 {
00251 av_dlog(ctx->avctx, "rotate %d\n", rotate_code);
00252 if (rotate_code == 2)
00253 FFSWAP(uint16_t*, ctx->frm1, ctx->frm2);
00254 FFSWAP(uint16_t*, ctx->frm2, ctx->frm0);
00255 }
00256
00257 static av_cold int decode_init(AVCodecContext *avctx)
00258 {
00259 SANMVideoContext *ctx = avctx->priv_data;
00260
00261 ctx->avctx = avctx;
00262 ctx->version = !avctx->extradata_size;
00263
00264 avctx->pix_fmt = ctx->version ? PIX_FMT_RGB565 : PIX_FMT_PAL8;
00265
00266 init_sizes(ctx, avctx->width, avctx->height);
00267 if (init_buffers(ctx)) {
00268 av_log(avctx, AV_LOG_ERROR, "error allocating buffers\n");
00269 return AVERROR(ENOMEM);
00270 }
00271 ctx->output = &ctx->frame;
00272 ctx->output->data[0] = 0;
00273
00274 make_glyphs(ctx->p4x4glyphs[0], glyph4_x, glyph4_y, 4);
00275 make_glyphs(ctx->p8x8glyphs[0], glyph8_x, glyph8_y, 8);
00276
00277 if (!ctx->version) {
00278 int i;
00279
00280 if (avctx->extradata_size < 1026) {
00281 av_log(avctx, AV_LOG_ERROR, "not enough extradata\n");
00282 return AVERROR_INVALIDDATA;
00283 }
00284
00285 ctx->subversion = AV_RL16(avctx->extradata);
00286 for (i = 0; i < 256; i++)
00287 ctx->pal[i] = 0xFF << 24 | AV_RL32(avctx->extradata + 2 + i * 4);
00288 }
00289
00290 return 0;
00291 }
00292
00293 static av_cold int decode_end(AVCodecContext *avctx)
00294 {
00295 SANMVideoContext *ctx = avctx->priv_data;
00296
00297 destroy_buffers(ctx);
00298
00299 if (ctx->frame.data[0]) {
00300 avctx->release_buffer(avctx, &ctx->frame);
00301 ctx->frame.data[0] = 0;
00302 }
00303
00304 return 0;
00305 }
00306
00307 static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
00308 {
00309 int opcode, color, run_len, left = out_size;
00310
00311 while (left > 0) {
00312 opcode = bytestream2_get_byte(&ctx->gb);
00313 run_len = (opcode >> 1) + 1;
00314 if (run_len > left || bytestream2_get_bytes_left(&ctx->gb) <= 0)
00315 return AVERROR_INVALIDDATA;
00316
00317 if (opcode & 1) {
00318 color = bytestream2_get_byte(&ctx->gb);
00319 memset(dst, color, run_len);
00320 } else {
00321 if (bytestream2_get_bytes_left(&ctx->gb) < run_len)
00322 return AVERROR_INVALIDDATA;
00323 bytestream2_get_bufferu(&ctx->gb, dst, run_len);
00324 }
00325
00326 dst += run_len;
00327 left -= run_len;
00328 }
00329
00330 return 0;
00331 }
00332
00333 static int old_codec1(SANMVideoContext *ctx, int top,
00334 int left, int width, int height)
00335 {
00336 uint8_t *dst = ((uint8_t*)ctx->frm0) + left + top * ctx->pitch;
00337 int i, j, len, flag, code, val, pos, end;
00338
00339 for (i = 0; i < height; i++) {
00340 pos = 0;
00341
00342 if (bytestream2_get_bytes_left(&ctx->gb) < 2)
00343 return AVERROR_INVALIDDATA;
00344
00345 len = bytestream2_get_le16u(&ctx->gb);
00346 end = bytestream2_tell(&ctx->gb) + len;
00347
00348 while (bytestream2_tell(&ctx->gb) < end) {
00349 if (bytestream2_get_bytes_left(&ctx->gb) < 2)
00350 return AVERROR_INVALIDDATA;
00351
00352 code = bytestream2_get_byteu(&ctx->gb);
00353 flag = code & 1;
00354 code = (code >> 1) + 1;
00355 if (pos + code > width)
00356 return AVERROR_INVALIDDATA;
00357 if (flag) {
00358 val = bytestream2_get_byteu(&ctx->gb);
00359 if (val)
00360 memset(dst + pos, val, code);
00361 pos += code;
00362 } else {
00363 if (bytestream2_get_bytes_left(&ctx->gb) < code)
00364 return AVERROR_INVALIDDATA;
00365 for (j = 0; j < code; j++) {
00366 val = bytestream2_get_byteu(&ctx->gb);
00367 if (val)
00368 dst[pos] = val;
00369 pos++;
00370 }
00371 }
00372 }
00373 dst += ctx->pitch;
00374 }
00375 ctx->rotate_code = 0;
00376
00377 return 0;
00378 }
00379
00380 static inline void codec37_mv(uint8_t *dst, const uint8_t *src,
00381 int height, int stride, int x, int y)
00382 {
00383 int pos, i, j;
00384
00385 pos = x + y * stride;
00386 for (j = 0; j < 4; j++) {
00387 for (i = 0; i < 4; i++) {
00388 if ((pos + i) < 0 || (pos + i) >= height * stride)
00389 dst[i] = 0;
00390 else
00391 dst[i] = src[i];
00392 }
00393 dst += stride;
00394 src += stride;
00395 pos += stride;
00396 }
00397 }
00398
00399 static int old_codec37(SANMVideoContext *ctx, int top,
00400 int left, int width, int height)
00401 {
00402 int stride = ctx->pitch;
00403 int i, j, k, t;
00404 int skip_run = 0;
00405 int compr, mvoff, seq, flags;
00406 uint32_t decoded_size;
00407 uint8_t *dst, *prev;
00408
00409 compr = bytestream2_get_byte(&ctx->gb);
00410 mvoff = bytestream2_get_byte(&ctx->gb);
00411 seq = bytestream2_get_le16(&ctx->gb);
00412 decoded_size = bytestream2_get_le32(&ctx->gb);
00413 bytestream2_skip(&ctx->gb, 4);
00414 flags = bytestream2_get_byte(&ctx->gb);
00415 bytestream2_skip(&ctx->gb, 3);
00416
00417 ctx->rotate_code = 0;
00418
00419 if (((seq & 1) || !(flags & 1)) && (compr && compr != 2))
00420 rotate_bufs(ctx, 1);
00421
00422 dst = ((uint8_t*)ctx->frm0) + left + top * stride;
00423 prev = ((uint8_t*)ctx->frm2) + left + top * stride;
00424
00425 if (mvoff > 2) {
00426 av_log(ctx->avctx, AV_LOG_ERROR, "invalid motion base value %d\n", mvoff);
00427 return AVERROR_INVALIDDATA;
00428 }
00429 av_dlog(ctx->avctx, "compression %d\n", compr);
00430 switch (compr) {
00431 case 0:
00432 for (i = 0; i < height; i++) {
00433 bytestream2_get_buffer(&ctx->gb, dst, width);
00434 dst += stride;
00435 }
00436 memset(ctx->frm1, 0, ctx->height * stride);
00437 memset(ctx->frm2, 0, ctx->height * stride);
00438 break;
00439 case 2:
00440 if (rle_decode(ctx, dst, decoded_size))
00441 return AVERROR_INVALIDDATA;
00442 memset(ctx->frm1, 0, ctx->frm1_size);
00443 memset(ctx->frm2, 0, ctx->frm2_size);
00444 break;
00445 case 3:
00446 case 4:
00447 if (flags & 4) {
00448 for (j = 0; j < height; j += 4) {
00449 for (i = 0; i < width; i += 4) {
00450 int code;
00451 if (skip_run) {
00452 skip_run--;
00453 copy_block4(dst + i, prev + i, stride, stride, 4);
00454 continue;
00455 }
00456 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00457 return AVERROR_INVALIDDATA;
00458 code = bytestream2_get_byteu(&ctx->gb);
00459 switch (code) {
00460 case 0xFF:
00461 if (bytestream2_get_bytes_left(&ctx->gb) < 16)
00462 return AVERROR_INVALIDDATA;
00463 for (k = 0; k < 4; k++)
00464 bytestream2_get_bufferu(&ctx->gb, dst + i + k * stride, 4);
00465 break;
00466 case 0xFE:
00467 if (bytestream2_get_bytes_left(&ctx->gb) < 4)
00468 return AVERROR_INVALIDDATA;
00469 for (k = 0; k < 4; k++)
00470 memset(dst + i + k * stride, bytestream2_get_byteu(&ctx->gb), 4);
00471 break;
00472 case 0xFD:
00473 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00474 return AVERROR_INVALIDDATA;
00475 t = bytestream2_get_byteu(&ctx->gb);
00476 for (k = 0; k < 4; k++)
00477 memset(dst + i + k * stride, t, 4);
00478 break;
00479 default:
00480 if (compr == 4 && !code) {
00481 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00482 return AVERROR_INVALIDDATA;
00483 skip_run = bytestream2_get_byteu(&ctx->gb) + 1;
00484 i -= 4;
00485 } else {
00486 int mx, my;
00487
00488 mx = c37_mv[(mvoff * 255 + code) * 2 ];
00489 my = c37_mv[(mvoff * 255 + code) * 2 + 1];
00490 codec37_mv(dst + i, prev + i + mx + my * stride,
00491 ctx->height, stride, i + mx, j + my);
00492 }
00493 }
00494 }
00495 dst += stride * 4;
00496 prev += stride * 4;
00497 }
00498 } else {
00499 for (j = 0; j < height; j += 4) {
00500 for (i = 0; i < width; i += 4) {
00501 int code;
00502 if (skip_run) {
00503 skip_run--;
00504 copy_block4(dst + i, prev + i, stride, stride, 4);
00505 continue;
00506 }
00507 code = bytestream2_get_byte(&ctx->gb);
00508 if (code == 0xFF) {
00509 if (bytestream2_get_bytes_left(&ctx->gb) < 16)
00510 return AVERROR_INVALIDDATA;
00511 for (k = 0; k < 4; k++)
00512 bytestream2_get_bufferu(&ctx->gb, dst + i + k * stride, 4);
00513 } else if (compr == 4 && !code) {
00514 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00515 return AVERROR_INVALIDDATA;
00516 skip_run = bytestream2_get_byteu(&ctx->gb) + 1;
00517 i -= 4;
00518 } else {
00519 int mx, my;
00520
00521 mx = c37_mv[(mvoff * 255 + code) * 2];
00522 my = c37_mv[(mvoff * 255 + code) * 2 + 1];
00523 codec37_mv(dst + i, prev + i + mx + my * stride,
00524 ctx->height, stride, i + mx, j + my);
00525 }
00526 }
00527 dst += stride * 4;
00528 prev += stride * 4;
00529 }
00530 }
00531 break;
00532 default:
00533 av_log(ctx->avctx, AV_LOG_ERROR,
00534 "subcodec 37 compression %d not implemented\n", compr);
00535 return AVERROR_PATCHWELCOME;
00536 }
00537
00538 return 0;
00539 }
00540
00541 static int process_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *prev1,
00542 uint8_t *prev2, int stride, int tbl, int size)
00543 {
00544 int code, k, t;
00545 uint8_t colors[2];
00546 int8_t *pglyph;
00547
00548 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00549 return AVERROR_INVALIDDATA;
00550
00551 code = bytestream2_get_byteu(&ctx->gb);
00552 if (code >= 0xF8) {
00553 switch (code) {
00554 case 0xFF:
00555 if (size == 2) {
00556 if (bytestream2_get_bytes_left(&ctx->gb) < 4)
00557 return AVERROR_INVALIDDATA;
00558 dst[0] = bytestream2_get_byteu(&ctx->gb);
00559 dst[1] = bytestream2_get_byteu(&ctx->gb);
00560 dst[0+stride] = bytestream2_get_byteu(&ctx->gb);
00561 dst[1+stride] = bytestream2_get_byteu(&ctx->gb);
00562 } else {
00563 size >>= 1;
00564 if (process_block(ctx, dst, prev1, prev2, stride, tbl, size))
00565 return AVERROR_INVALIDDATA;
00566 if (process_block(ctx, dst + size, prev1 + size, prev2 + size,
00567 stride, tbl, size))
00568 return AVERROR_INVALIDDATA;
00569 dst += size * stride;
00570 prev1 += size * stride;
00571 prev2 += size * stride;
00572 if (process_block(ctx, dst, prev1, prev2, stride, tbl, size))
00573 return AVERROR_INVALIDDATA;
00574 if (process_block(ctx, dst + size, prev1 + size, prev2 + size,
00575 stride, tbl, size))
00576 return AVERROR_INVALIDDATA;
00577 }
00578 break;
00579 case 0xFE:
00580 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00581 return AVERROR_INVALIDDATA;
00582
00583 t = bytestream2_get_byteu(&ctx->gb);
00584 for (k = 0; k < size; k++)
00585 memset(dst + k * stride, t, size);
00586 break;
00587 case 0xFD:
00588 if (bytestream2_get_bytes_left(&ctx->gb) < 3)
00589 return AVERROR_INVALIDDATA;
00590
00591 code = bytestream2_get_byteu(&ctx->gb);
00592 pglyph = (size == 8) ? ctx->p8x8glyphs[code] : ctx->p4x4glyphs[code];
00593 bytestream2_get_bufferu(&ctx->gb, colors, 2);
00594
00595 for (k = 0; k < size; k++)
00596 for (t = 0; t < size; t++)
00597 dst[t + k * stride] = colors[!*pglyph++];
00598 break;
00599 case 0xFC:
00600 for (k = 0; k < size; k++)
00601 memcpy(dst + k * stride, prev1 + k * stride, size);
00602 break;
00603 default:
00604 k = bytestream2_tell(&ctx->gb);
00605 bytestream2_seek(&ctx->gb, tbl + (code & 7), SEEK_SET);
00606 t = bytestream2_get_byte(&ctx->gb);
00607 bytestream2_seek(&ctx->gb, k, SEEK_SET);
00608 for (k = 0; k < size; k++)
00609 memset(dst + k * stride, t, size);
00610 }
00611 } else {
00612 int mx = motion_vectors[code][0];
00613 int my = motion_vectors[code][1];
00614 for (k = 0; k < size; k++)
00615 memcpy(dst + k * stride, prev2 + mx + (my + k) * stride, size);
00616 }
00617
00618 return 0;
00619 }
00620
00621 static int old_codec47(SANMVideoContext *ctx, int top,
00622 int left, int width, int height)
00623 {
00624 int i, j, seq, compr, new_rot, tbl_pos, skip;
00625 int stride = ctx->pitch;
00626 uint8_t *dst = ((uint8_t*)ctx->frm0) + left + top * stride;
00627 uint8_t *prev1 = (uint8_t*)ctx->frm1;
00628 uint8_t *prev2 = (uint8_t*)ctx->frm2;
00629 uint32_t decoded_size;
00630
00631 tbl_pos = bytestream2_tell(&ctx->gb);
00632 seq = bytestream2_get_le16(&ctx->gb);
00633 compr = bytestream2_get_byte(&ctx->gb);
00634 new_rot = bytestream2_get_byte(&ctx->gb);
00635 skip = bytestream2_get_byte(&ctx->gb);
00636 bytestream2_skip(&ctx->gb, 9);
00637 decoded_size = bytestream2_get_le32(&ctx->gb);
00638 bytestream2_skip(&ctx->gb, 8);
00639
00640 if (skip & 1)
00641 bytestream2_skip(&ctx->gb, 0x8080);
00642 if (!seq) {
00643 ctx->prev_seq = -1;
00644 memset(prev1, 0, ctx->height * stride);
00645 memset(prev2, 0, ctx->height * stride);
00646 }
00647 av_dlog(ctx->avctx, "compression %d\n", compr);
00648 switch (compr) {
00649 case 0:
00650 if (bytestream2_get_bytes_left(&ctx->gb) < width * height)
00651 return AVERROR_INVALIDDATA;
00652 for (j = 0; j < height; j++) {
00653 for (i = 0; i < width; i++)
00654 bytestream2_get_bufferu(&ctx->gb, dst, width);
00655 dst += stride;
00656 }
00657 break;
00658 case 1:
00659 if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
00660 return AVERROR_INVALIDDATA;
00661 for (j = 0; j < height; j += 2) {
00662 for (i = 0; i < width; i += 2) {
00663 dst[i] = dst[i + 1] =
00664 dst[stride + i] = dst[stride + i + 1] = bytestream2_get_byteu(&ctx->gb);
00665 }
00666 dst += stride * 2;
00667 }
00668 break;
00669 case 2:
00670 if (seq == ctx->prev_seq + 1) {
00671 for (j = 0; j < height; j += 8) {
00672 for (i = 0; i < width; i += 8) {
00673 if (process_block(ctx, dst + i, prev1 + i, prev2 + i, stride,
00674 tbl_pos + 8, 8))
00675 return AVERROR_INVALIDDATA;
00676 }
00677 dst += stride * 8;
00678 prev1 += stride * 8;
00679 prev2 += stride * 8;
00680 }
00681 }
00682 break;
00683 case 3:
00684 memcpy(ctx->frm0, ctx->frm2, ctx->pitch * ctx->height);
00685 break;
00686 case 4:
00687 memcpy(ctx->frm0, ctx->frm1, ctx->pitch * ctx->height);
00688 break;
00689 case 5:
00690 if (rle_decode(ctx, dst, decoded_size))
00691 return AVERROR_INVALIDDATA;
00692 break;
00693 default:
00694 av_log(ctx->avctx, AV_LOG_ERROR,
00695 "subcodec 47 compression %d not implemented\n", compr);
00696 return AVERROR_PATCHWELCOME;
00697 }
00698 if (seq == ctx->prev_seq + 1)
00699 ctx->rotate_code = new_rot;
00700 else
00701 ctx->rotate_code = 0;
00702 ctx->prev_seq = seq;
00703
00704 return 0;
00705 }
00706
00707 static int process_frame_obj(SANMVideoContext *ctx)
00708 {
00709 uint16_t codec, top, left, w, h;
00710
00711 codec = bytestream2_get_le16u(&ctx->gb);
00712 left = bytestream2_get_le16u(&ctx->gb);
00713 top = bytestream2_get_le16u(&ctx->gb);
00714 w = bytestream2_get_le16u(&ctx->gb);
00715 h = bytestream2_get_le16u(&ctx->gb);
00716
00717 if (ctx->width < left + w || ctx->height < top + h) {
00718 ctx->avctx->width = FFMAX(left + w, ctx->width);
00719 ctx->avctx->height = FFMAX(top + h, ctx->height);
00720 init_sizes(ctx, left + w, top + h);
00721 if (init_buffers(ctx)) {
00722 av_log(ctx->avctx, AV_LOG_ERROR, "error resizing buffers\n");
00723 return AVERROR(ENOMEM);
00724 }
00725 }
00726 bytestream2_skip(&ctx->gb, 4);
00727
00728 av_dlog(ctx->avctx, "subcodec %d\n", codec);
00729 switch (codec) {
00730 case 1:
00731 case 3:
00732 return old_codec1(ctx, top, left, w, h);
00733 break;
00734 case 37:
00735 return old_codec37(ctx, top, left, w, h);
00736 break;
00737 case 47:
00738 return old_codec47(ctx, top, left, w, h);
00739 break;
00740 default:
00741 av_log_ask_for_sample(ctx->avctx, "unknown subcodec %d\n", codec);
00742 return AVERROR_PATCHWELCOME;
00743 }
00744 }
00745
00746 static int decode_0(SANMVideoContext *ctx)
00747 {
00748 uint16_t *frm = ctx->frm0;
00749 int x, y;
00750
00751 if (bytestream2_get_bytes_left(&ctx->gb) < ctx->width * ctx->height * 2) {
00752 av_log(ctx->avctx, AV_LOG_ERROR, "insufficient data for raw frame\n");
00753 return AVERROR_INVALIDDATA;
00754 }
00755 for (y = 0; y < ctx->height; y++) {
00756 for (x = 0; x < ctx->width; x++)
00757 frm[x] = bytestream2_get_le16u(&ctx->gb);
00758 frm += ctx->pitch;
00759 }
00760 return 0;
00761 }
00762
00763 static int decode_nop(SANMVideoContext *ctx)
00764 {
00765 av_log_ask_for_sample(ctx->avctx, "unknown/unsupported compression type\n");
00766 return AVERROR_PATCHWELCOME;
00767 }
00768
00769 static void copy_block(uint16_t *pdest, uint16_t *psrc, int block_size, int pitch)
00770 {
00771 uint8_t *dst = (uint8_t *)pdest;
00772 uint8_t *src = (uint8_t *)psrc;
00773 int stride = pitch * 2;
00774
00775 switch (block_size) {
00776 case 2:
00777 copy_block4(dst, src, stride, stride, 2);
00778 break;
00779 case 4:
00780 copy_block8(dst, src, stride, stride, 4);
00781 break;
00782 case 8:
00783 copy_block16(dst, src, stride, stride, 8);
00784 break;
00785 }
00786 }
00787
00788 static void fill_block(uint16_t *pdest, uint16_t color, int block_size, int pitch)
00789 {
00790 int x, y;
00791
00792 pitch -= block_size;
00793 for (y = 0; y < block_size; y++, pdest += pitch)
00794 for (x = 0; x < block_size; x++)
00795 *pdest++ = color;
00796 }
00797
00798 static int draw_glyph(SANMVideoContext *ctx, uint16_t *dst, int index, uint16_t fg_color,
00799 uint16_t bg_color, int block_size, int pitch)
00800 {
00801 int8_t *pglyph;
00802 uint16_t colors[2] = { fg_color, bg_color };
00803 int x, y;
00804
00805 if (index > NGLYPHS) {
00806 av_log(ctx->avctx, AV_LOG_ERROR, "ignoring nonexistent glyph #%u\n", index);
00807 return AVERROR_INVALIDDATA;
00808 }
00809
00810 pglyph = block_size == 8 ? ctx->p8x8glyphs[index] : ctx->p4x4glyphs[index];
00811 pitch -= block_size;
00812
00813 for (y = 0; y < block_size; y++, dst += pitch)
00814 for (x = 0; x < block_size; x++)
00815 *dst++ = colors[*pglyph++];
00816 return 0;
00817 }
00818
00819 static int opcode_0xf7(SANMVideoContext *ctx, int cx, int cy, int block_size, int pitch)
00820 {
00821 uint16_t *dst = ctx->frm0 + cx + cy * ctx->pitch;
00822
00823 if (block_size == 2) {
00824 uint32_t indices;
00825
00826 if (bytestream2_get_bytes_left(&ctx->gb) < 4)
00827 return AVERROR_INVALIDDATA;
00828
00829 indices = bytestream2_get_le32u(&ctx->gb);
00830 dst[0] = ctx->codebook[indices & 0xFF]; indices >>= 8;
00831 dst[1] = ctx->codebook[indices & 0xFF]; indices >>= 8;
00832 dst[pitch] = ctx->codebook[indices & 0xFF]; indices >>= 8;
00833 dst[pitch + 1] = ctx->codebook[indices & 0xFF];
00834 } else {
00835 uint16_t fgcolor, bgcolor;
00836 int glyph;
00837
00838 if (bytestream2_get_bytes_left(&ctx->gb) < 3)
00839 return AVERROR_INVALIDDATA;
00840
00841 glyph = bytestream2_get_byteu(&ctx->gb);
00842 bgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
00843 fgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
00844
00845 draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
00846 }
00847 return 0;
00848 }
00849
00850 static int opcode_0xf8(SANMVideoContext *ctx, int cx, int cy, int block_size, int pitch)
00851 {
00852 uint16_t *dst = ctx->frm0 + cx + cy * ctx->pitch;
00853
00854 if (block_size == 2) {
00855 if (bytestream2_get_bytes_left(&ctx->gb) < 8)
00856 return AVERROR_INVALIDDATA;
00857
00858 dst[0] = bytestream2_get_le16u(&ctx->gb);
00859 dst[1] = bytestream2_get_le16u(&ctx->gb);
00860 dst[pitch] = bytestream2_get_le16u(&ctx->gb);
00861 dst[pitch + 1] = bytestream2_get_le16u(&ctx->gb);
00862 } else {
00863 uint16_t fgcolor, bgcolor;
00864 int glyph;
00865
00866 if (bytestream2_get_bytes_left(&ctx->gb) < 5)
00867 return AVERROR_INVALIDDATA;
00868
00869 glyph = bytestream2_get_byteu(&ctx->gb);
00870 bgcolor = bytestream2_get_le16u(&ctx->gb);
00871 fgcolor = bytestream2_get_le16u(&ctx->gb);
00872
00873 draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
00874 }
00875 return 0;
00876 }
00877
00878 static int good_mvec(SANMVideoContext *ctx, int cx, int cy, int mx, int my,
00879 int block_size)
00880 {
00881 int start_pos = cx + mx + (cy + my) * ctx->pitch;
00882 int end_pos = start_pos + (block_size - 1) * (ctx->pitch + 1);
00883
00884 int good = start_pos >= 0 && end_pos < (ctx->buf_size >> 1);
00885
00886 if (!good) {
00887 av_log(ctx->avctx, AV_LOG_ERROR, "ignoring invalid motion vector (%i, %i)->(%u, %u), block size = %u\n",
00888 cx + mx, cy + my, cx, cy, block_size);
00889 }
00890
00891 return good;
00892 }
00893
00894 static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size)
00895 {
00896 int16_t mx, my, index;
00897 int opcode;
00898
00899 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00900 return AVERROR_INVALIDDATA;
00901
00902 opcode = bytestream2_get_byteu(&ctx->gb);
00903
00904 av_dlog(ctx->avctx, "opcode 0x%0X cx %d cy %d blk %d\n", opcode, cx, cy, blk_size);
00905 switch (opcode) {
00906 default:
00907 mx = motion_vectors[opcode][0];
00908 my = motion_vectors[opcode][1];
00909
00910 if (good_mvec(ctx, cx, cy, mx, my, blk_size)) {
00911 copy_block(ctx->frm0 + cx + ctx->pitch * cy,
00912 ctx->frm2 + cx + mx + ctx->pitch * (cy + my),
00913 blk_size, ctx->pitch);
00914 }
00915 break;
00916 case 0xF5:
00917 if (bytestream2_get_bytes_left(&ctx->gb) < 2)
00918 return AVERROR_INVALIDDATA;
00919 index = bytestream2_get_le16u(&ctx->gb);
00920
00921 mx = index % ctx->width;
00922 my = index / ctx->width;
00923
00924 if (good_mvec(ctx, cx, cy, mx, my, blk_size)) {
00925 copy_block(ctx->frm0 + cx + ctx->pitch * cy,
00926 ctx->frm2 + cx + mx + ctx->pitch * (cy + my),
00927 blk_size, ctx->pitch);
00928 }
00929 break;
00930 case 0xF6:
00931 copy_block(ctx->frm0 + cx + ctx->pitch * cy,
00932 ctx->frm1 + cx + ctx->pitch * cy,
00933 blk_size, ctx->pitch);
00934 break;
00935 case 0xF7:
00936 opcode_0xf7(ctx, cx, cy, blk_size, ctx->pitch);
00937 break;
00938
00939 case 0xF8:
00940 opcode_0xf8(ctx, cx, cy, blk_size, ctx->pitch);
00941 break;
00942 case 0xF9:
00943 case 0xFA:
00944 case 0xFB:
00945 case 0xFC:
00946 fill_block(ctx->frm0 + cx + cy * ctx->pitch,
00947 ctx->small_codebook[opcode - 0xf9], blk_size, ctx->pitch);
00948 break;
00949 case 0xFD:
00950 if (bytestream2_get_bytes_left(&ctx->gb) < 1)
00951 return AVERROR_INVALIDDATA;
00952 fill_block(ctx->frm0 + cx + cy * ctx->pitch,
00953 ctx->codebook[bytestream2_get_byteu(&ctx->gb)], blk_size, ctx->pitch);
00954 break;
00955 case 0xFE:
00956 if (bytestream2_get_bytes_left(&ctx->gb) < 2)
00957 return AVERROR_INVALIDDATA;
00958 fill_block(ctx->frm0 + cx + cy * ctx->pitch,
00959 bytestream2_get_le16u(&ctx->gb), blk_size, ctx->pitch);
00960 break;
00961 case 0xFF:
00962 if (blk_size == 2) {
00963 opcode_0xf8(ctx, cx, cy, blk_size, ctx->pitch);
00964 } else {
00965 blk_size >>= 1;
00966 if (codec2subblock(ctx, cx , cy , blk_size))
00967 return AVERROR_INVALIDDATA;
00968 if (codec2subblock(ctx, cx + blk_size, cy , blk_size))
00969 return AVERROR_INVALIDDATA;
00970 if (codec2subblock(ctx, cx , cy + blk_size, blk_size))
00971 return AVERROR_INVALIDDATA;
00972 if (codec2subblock(ctx, cx + blk_size, cy + blk_size, blk_size))
00973 return AVERROR_INVALIDDATA;
00974 }
00975 break;
00976 }
00977 return 0;
00978 }
00979
00980 static int decode_2(SANMVideoContext *ctx)
00981 {
00982 int cx, cy, ret;
00983
00984 for (cy = 0; cy < ctx->aligned_height; cy += 8) {
00985 for (cx = 0; cx < ctx->aligned_width; cx += 8) {
00986 if (ret = codec2subblock(ctx, cx, cy, 8))
00987 return ret;
00988 }
00989 }
00990
00991 return 0;
00992 }
00993
00994 static int decode_3(SANMVideoContext *ctx)
00995 {
00996 memcpy(ctx->frm0, ctx->frm2, ctx->frm2_size);
00997 return 0;
00998 }
00999
01000 static int decode_4(SANMVideoContext *ctx)
01001 {
01002 memcpy(ctx->frm0, ctx->frm1, ctx->frm1_size);
01003 return 0;
01004 }
01005
01006 static int decode_5(SANMVideoContext *ctx)
01007 {
01008 #if HAVE_BIGENDIAN
01009 uint16_t *frm;
01010 int npixels;
01011 #endif
01012 uint8_t *dst = (uint8_t*)ctx->frm0;
01013
01014 if (rle_decode(ctx, dst, ctx->buf_size))
01015 return AVERROR_INVALIDDATA;
01016
01017 #if HAVE_BIGENDIAN
01018 npixels = ctx->npixels;
01019 frm = ctx->frm0;
01020 while (npixels--)
01021 *frm++ = av_bswap16(*frm);
01022 #endif
01023
01024 return 0;
01025 }
01026
01027 static int decode_6(SANMVideoContext *ctx)
01028 {
01029 int npixels = ctx->npixels;
01030 uint16_t *frm = ctx->frm0;
01031
01032 if (bytestream2_get_bytes_left(&ctx->gb) < npixels) {
01033 av_log(ctx->avctx, AV_LOG_ERROR, "insufficient data for frame\n");
01034 return AVERROR_INVALIDDATA;
01035 }
01036 while (npixels--)
01037 *frm++ = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
01038
01039 return 0;
01040 }
01041
01042 static int decode_8(SANMVideoContext *ctx)
01043 {
01044 uint16_t *pdest = ctx->frm0;
01045 uint8_t *rsrc;
01046 long npixels = ctx->npixels;
01047
01048 av_fast_malloc(&ctx->rle_buf, &ctx->rle_buf_size, npixels);
01049 if (!ctx->rle_buf) {
01050 av_log(ctx->avctx, AV_LOG_ERROR, "RLE buffer allocation failed\n");
01051 return AVERROR(ENOMEM);
01052 }
01053 rsrc = ctx->rle_buf;
01054
01055 if (rle_decode(ctx, rsrc, npixels))
01056 return AVERROR_INVALIDDATA;
01057
01058 while (npixels--)
01059 *pdest++ = ctx->codebook[*rsrc++];
01060
01061 return 0;
01062 }
01063
01064 typedef int (*frm_decoder)(SANMVideoContext *ctx);
01065
01066 static const frm_decoder v1_decoders[] = {
01067 decode_0, decode_nop, decode_2, decode_3, decode_4, decode_5,
01068 decode_6, decode_nop, decode_8
01069 };
01070
01071 static int read_frame_header(SANMVideoContext *ctx, SANMFrameHeader *hdr)
01072 {
01073 int i, ret;
01074
01075 if ((ret = bytestream2_get_bytes_left(&ctx->gb)) < 560) {
01076 av_log(ctx->avctx, AV_LOG_ERROR, "too short input frame (%d bytes)\n",
01077 ret);
01078 return AVERROR_INVALIDDATA;
01079 }
01080 bytestream2_skip(&ctx->gb, 8);
01081
01082 hdr->width = bytestream2_get_le32u(&ctx->gb);
01083 hdr->height = bytestream2_get_le32u(&ctx->gb);
01084
01085 if (hdr->width != ctx->width || hdr->height != ctx->height) {
01086 av_log(ctx->avctx, AV_LOG_ERROR, "variable size frames are not implemented\n");
01087 return AVERROR_PATCHWELCOME;
01088 }
01089
01090 hdr->seq_num = bytestream2_get_le16u(&ctx->gb);
01091 hdr->codec = bytestream2_get_byteu(&ctx->gb);
01092 hdr->rotate_code = bytestream2_get_byteu(&ctx->gb);
01093
01094 bytestream2_skip(&ctx->gb, 4);
01095
01096 for (i = 0; i < 4; i++)
01097 ctx->small_codebook[i] = bytestream2_get_le16u(&ctx->gb);
01098 hdr->bg_color = bytestream2_get_le16u(&ctx->gb);
01099
01100 bytestream2_skip(&ctx->gb, 2);
01101
01102 hdr->rle_output_size = bytestream2_get_le32u(&ctx->gb);
01103 for (i = 0; i < 256; i++)
01104 ctx->codebook[i] = bytestream2_get_le16u(&ctx->gb);
01105
01106 bytestream2_skip(&ctx->gb, 8);
01107
01108 av_dlog(ctx->avctx, "subcodec %d\n", hdr->codec);
01109 return 0;
01110 }
01111
01112 static void fill_frame(uint16_t *pbuf, int buf_size, uint16_t color)
01113 {
01114 while (buf_size--)
01115 *pbuf++ = color;
01116 }
01117
01118 static int copy_output(SANMVideoContext *ctx, SANMFrameHeader *hdr)
01119 {
01120 uint8_t *dst;
01121 const uint8_t *src = (uint8_t*) ctx->frm0;
01122 int ret, dstpitch, height = ctx->height;
01123 int srcpitch = ctx->pitch * (hdr ? sizeof(ctx->frm0[0]) : 1);
01124
01125 if ((ret = ctx->avctx->get_buffer(ctx->avctx, ctx->output)) < 0) {
01126 av_log(ctx->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
01127 return ret;
01128 }
01129
01130 dst = ctx->output->data[0];
01131 dstpitch = ctx->output->linesize[0];
01132
01133 while (height--) {
01134 memcpy(dst, src, srcpitch);
01135 src += srcpitch;
01136 dst += dstpitch;
01137 }
01138
01139 return 0;
01140 }
01141
01142 static int decode_frame(AVCodecContext *avctx, void *data,
01143 int *got_frame_ptr, AVPacket *pkt)
01144 {
01145 SANMVideoContext *ctx = avctx->priv_data;
01146 int i, ret;
01147
01148 bytestream2_init(&ctx->gb, pkt->data, pkt->size);
01149 if (ctx->output->data[0])
01150 avctx->release_buffer(avctx, ctx->output);
01151
01152 if (!ctx->version) {
01153 int to_store = 0;
01154
01155 while (bytestream2_get_bytes_left(&ctx->gb) >= 8) {
01156 uint32_t sig, size;
01157 int pos;
01158
01159 sig = bytestream2_get_be32u(&ctx->gb);
01160 size = bytestream2_get_be32u(&ctx->gb);
01161 pos = bytestream2_tell(&ctx->gb);
01162
01163 if (bytestream2_get_bytes_left(&ctx->gb) < size) {
01164 av_log(avctx, AV_LOG_ERROR, "incorrect chunk size %d\n", size);
01165 break;
01166 }
01167 switch (sig) {
01168 case MKBETAG('N', 'P', 'A', 'L'):
01169 if (size != 256 * 3) {
01170 av_log(avctx, AV_LOG_ERROR, "incorrect palette block size %d\n",
01171 size);
01172 return AVERROR_INVALIDDATA;
01173 }
01174 for (i = 0; i < 256; i++)
01175 ctx->pal[i] = 0xFF << 24 | bytestream2_get_be24u(&ctx->gb);
01176 break;
01177 case MKBETAG('F', 'O', 'B', 'J'):
01178 if (size < 16)
01179 return AVERROR_INVALIDDATA;
01180 if (ret = process_frame_obj(ctx))
01181 return ret;
01182 break;
01183 case MKBETAG('X', 'P', 'A', 'L'):
01184 if (size == 6 || size == 4) {
01185 uint8_t tmp[3];
01186 int j;
01187
01188 for (i = 0; i < 256; i++) {
01189 for (j = 0; j < 3; j++) {
01190 int t = (ctx->pal[i] >> (16 - j * 8)) & 0xFF;
01191 tmp[j] = av_clip_uint8((t * 129 + ctx->delta_pal[i * 3 + j]) >> 7);
01192 }
01193 ctx->pal[i] = 0xFF << 24 | AV_RB24(tmp);
01194 }
01195 } else {
01196 if (size < 768 * 2 + 4) {
01197 av_log(avctx, AV_LOG_ERROR, "incorrect palette change block size %d\n",
01198 size);
01199 return AVERROR_INVALIDDATA;
01200 }
01201 bytestream2_skipu(&ctx->gb, 4);
01202 for (i = 0; i < 768; i++)
01203 ctx->delta_pal[i] = bytestream2_get_le16u(&ctx->gb);
01204 if (size >= 768 * 5 + 4) {
01205 for (i = 0; i < 256; i++)
01206 ctx->pal[i] = 0xFF << 24 | bytestream2_get_be24u(&ctx->gb);
01207 } else {
01208 memset(ctx->pal, 0, sizeof(ctx->pal));
01209 }
01210 }
01211 break;
01212 case MKBETAG('S', 'T', 'O', 'R'):
01213 to_store = 1;
01214 break;
01215 case MKBETAG('F', 'T', 'C', 'H'):
01216 memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
01217 break;
01218 default:
01219 bytestream2_skip(&ctx->gb, size);
01220 av_log(avctx, AV_LOG_DEBUG, "unknown/unsupported chunk %x\n", sig);
01221 break;
01222 }
01223
01224 bytestream2_seek(&ctx->gb, pos + size, SEEK_SET);
01225 if (size & 1)
01226 bytestream2_skip(&ctx->gb, 1);
01227 }
01228 if (to_store)
01229 memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
01230 if ((ret = copy_output(ctx, NULL)))
01231 return ret;
01232 memcpy(ctx->output->data[1], ctx->pal, 1024);
01233 } else {
01234 SANMFrameHeader header;
01235
01236 if ((ret = read_frame_header(ctx, &header)))
01237 return ret;
01238
01239 ctx->rotate_code = header.rotate_code;
01240 if ((ctx->output->key_frame = !header.seq_num)) {
01241 ctx->output->pict_type = AV_PICTURE_TYPE_I;
01242 fill_frame(ctx->frm1, ctx->npixels, header.bg_color);
01243 fill_frame(ctx->frm2, ctx->npixels, header.bg_color);
01244 } else {
01245 ctx->output->pict_type = AV_PICTURE_TYPE_P;
01246 }
01247
01248 if (header.codec < FF_ARRAY_ELEMS(v1_decoders)) {
01249 if ((ret = v1_decoders[header.codec](ctx))) {
01250 av_log(avctx, AV_LOG_ERROR,
01251 "subcodec %d: error decoding frame\n", header.codec);
01252 return ret;
01253 }
01254 } else {
01255 av_log_ask_for_sample(avctx, "subcodec %d is not implemented\n",
01256 header.codec);
01257 return AVERROR_PATCHWELCOME;
01258 }
01259
01260 if ((ret = copy_output(ctx, &header)))
01261 return ret;
01262 }
01263 if (ctx->rotate_code)
01264 rotate_bufs(ctx, ctx->rotate_code);
01265
01266 *got_frame_ptr = 1;
01267 *(AVFrame*)data = *ctx->output;
01268
01269 return pkt->size;
01270 }
01271
01272 AVCodec ff_sanm_decoder = {
01273 .name = "sanm",
01274 .type = AVMEDIA_TYPE_VIDEO,
01275 .id = AV_CODEC_ID_SANM,
01276 .priv_data_size = sizeof(SANMVideoContext),
01277 .init = decode_init,
01278 .close = decode_end,
01279 .decode = decode_frame,
01280 .capabilities = CODEC_CAP_DR1,
01281 .long_name = NULL_IF_CONFIG_SMALL("LucasArts SMUSH video"),
01282 };