FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sanm.c
Go to the documentation of this file.
1 /*
2  * LucasArts Smush video decoder
3  * Copyright (c) 2006 Cyril Zorin
4  * Copyright (c) 2011 Konstantin Shishkov
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 // #define DEBUG 1
24 
25 #include "avcodec.h"
26 #include "copy_block.h"
27 #include "bytestream.h"
28 #include "internal.h"
29 #include "libavutil/bswap.h"
30 #include "libavutil/imgutils.h"
31 #include "sanm_data.h"
32 #include "libavutil/avassert.h"
33 
34 #define NGLYPHS 256
35 
36 typedef struct {
39 
40  int version, subversion;
41  uint32_t pal[256];
42  int16_t delta_pal[768];
43 
44  int pitch;
45  int width, height;
46  int aligned_width, aligned_height;
47  int prev_seq;
48 
50  uint16_t *frm0, *frm1, *frm2;
52  uint32_t frm0_size, frm1_size, frm2_size;
54 
56  unsigned int rle_buf_size;
57 
59 
60  long npixels, buf_size;
61 
62  uint16_t codebook[256];
63  uint16_t small_codebook[4];
64 
65  int8_t p4x4glyphs[NGLYPHS][16];
66  int8_t p8x8glyphs[NGLYPHS][64];
68 
69 typedef struct {
70  int seq_num, codec, rotate_code, rle_output_size;
71 
72  uint16_t bg_color;
73  uint32_t width, height;
75 
76 enum GlyphEdge {
82 };
83 
84 enum GlyphDir {
90 };
91 
92 /**
93  * Return enum GlyphEdge of box where point (x, y) lies.
94  *
95  * @param x x point coordinate
96  * @param y y point coordinate
97  * @param edge_size box width/height.
98  */
99 static enum GlyphEdge which_edge(int x, int y, int edge_size)
100 {
101  const int edge_max = edge_size - 1;
102 
103  if (!y) {
104  return BOTTOM_EDGE;
105  } else if (y == edge_max) {
106  return TOP_EDGE;
107  } else if (!x) {
108  return LEFT_EDGE;
109  } else if (x == edge_max) {
110  return RIGHT_EDGE;
111  } else {
112  return NO_EDGE;
113  }
114 }
115 
116 static enum GlyphDir which_direction(enum GlyphEdge edge0, enum GlyphEdge edge1)
117 {
118  if ((edge0 == LEFT_EDGE && edge1 == RIGHT_EDGE) ||
119  (edge1 == LEFT_EDGE && edge0 == RIGHT_EDGE) ||
120  (edge0 == BOTTOM_EDGE && edge1 != TOP_EDGE) ||
121  (edge1 == BOTTOM_EDGE && edge0 != TOP_EDGE)) {
122  return DIR_UP;
123  } else if ((edge0 == TOP_EDGE && edge1 != BOTTOM_EDGE) ||
124  (edge1 == TOP_EDGE && edge0 != BOTTOM_EDGE)) {
125  return DIR_DOWN;
126  } else if ((edge0 == LEFT_EDGE && edge1 != RIGHT_EDGE) ||
127  (edge1 == LEFT_EDGE && edge0 != RIGHT_EDGE)) {
128  return DIR_LEFT;
129  } else if ((edge0 == TOP_EDGE && edge1 == BOTTOM_EDGE) ||
130  (edge1 == TOP_EDGE && edge0 == BOTTOM_EDGE) ||
131  (edge0 == RIGHT_EDGE && edge1 != LEFT_EDGE) ||
132  (edge1 == RIGHT_EDGE && edge0 != LEFT_EDGE)) {
133  return DIR_RIGHT;
134  }
135 
136  return NO_DIR;
137 }
138 
139 /**
140  * Interpolate two points.
141  */
142 static void interp_point(int8_t *points, int x0, int y0, int x1, int y1,
143  int pos, int npoints)
144 {
145  if (npoints) {
146  points[0] = (x0 * pos + x1 * (npoints - pos) + (npoints >> 1)) / npoints;
147  points[1] = (y0 * pos + y1 * (npoints - pos) + (npoints >> 1)) / npoints;
148  } else {
149  points[0] = x0;
150  points[1] = y0;
151  }
152 }
153 
154 /**
155  * Construct glyphs by iterating through vectors coordinates.
156  *
157  * @param pglyphs pointer to table where glyphs are stored
158  * @param xvec pointer to x component of vectors coordinates
159  * @param yvec pointer to y component of vectors coordinates
160  * @param side_length glyph width/height.
161  */
162 static void make_glyphs(int8_t *pglyphs, const int8_t *xvec, const int8_t *yvec,
163  const int side_length)
164 {
165  const int glyph_size = side_length * side_length;
166  int8_t *pglyph = pglyphs;
167 
168  int i, j;
169  for (i = 0; i < GLYPH_COORD_VECT_SIZE; i++) {
170  int x0 = xvec[i];
171  int y0 = yvec[i];
172  enum GlyphEdge edge0 = which_edge(x0, y0, side_length);
173 
174  for (j = 0; j < GLYPH_COORD_VECT_SIZE; j++, pglyph += glyph_size) {
175  int x1 = xvec[j];
176  int y1 = yvec[j];
177  enum GlyphEdge edge1 = which_edge(x1, y1, side_length);
178  enum GlyphDir dir = which_direction(edge0, edge1);
179  int npoints = FFMAX(FFABS(x1 - x0), FFABS(y1 - y0));
180  int ipoint;
181 
182  for (ipoint = 0; ipoint <= npoints; ipoint++) {
183  int8_t point[2];
184  int irow, icol;
185 
186  interp_point(point, x0, y0, x1, y1, ipoint, npoints);
187 
188  switch (dir) {
189  case DIR_UP:
190  for (irow = point[1]; irow >= 0; irow--)
191  pglyph[point[0] + irow * side_length] = 1;
192  break;
193 
194  case DIR_DOWN:
195  for (irow = point[1]; irow < side_length; irow++)
196  pglyph[point[0] + irow * side_length] = 1;
197  break;
198 
199  case DIR_LEFT:
200  for (icol = point[0]; icol >= 0; icol--)
201  pglyph[icol + point[1] * side_length] = 1;
202  break;
203 
204  case DIR_RIGHT:
205  for (icol = point[0]; icol < side_length; icol++)
206  pglyph[icol + point[1] * side_length] = 1;
207  break;
208  }
209  }
210  }
211  }
212 }
213 
214 static void init_sizes(SANMVideoContext *ctx, int width, int height)
215 {
216  ctx->width = width;
217  ctx->height = height;
218  ctx->npixels = width * height;
219 
220  ctx->aligned_width = FFALIGN(width, 8);
221  ctx->aligned_height = FFALIGN(height, 8);
222 
223  ctx->buf_size = ctx->aligned_width * ctx->aligned_height * sizeof(ctx->frm0[0]);
224  ctx->pitch = width;
225 }
226 
228 {
229  av_freep(&ctx->frm0);
230  av_freep(&ctx->frm1);
231  av_freep(&ctx->frm2);
232  av_freep(&ctx->stored_frame);
233  av_freep(&ctx->rle_buf);
234  ctx->frm0_size =
235  ctx->frm1_size =
236  ctx->frm2_size = 0;
237 }
238 
240 {
241  av_fast_padded_malloc(&ctx->frm0, &ctx->frm0_size, ctx->buf_size);
242  av_fast_padded_malloc(&ctx->frm1, &ctx->frm1_size, ctx->buf_size);
243  av_fast_padded_malloc(&ctx->frm2, &ctx->frm2_size, ctx->buf_size);
244  if (!ctx->version)
246 
247  if (!ctx->frm0 || !ctx->frm1 || !ctx->frm2 || (!ctx->stored_frame && !ctx->version)) {
248  destroy_buffers(ctx);
249  return AVERROR(ENOMEM);
250  }
251 
252  return 0;
253 }
254 
255 static void rotate_bufs(SANMVideoContext *ctx, int rotate_code)
256 {
257  av_dlog(ctx->avctx, "rotate %d\n", rotate_code);
258  if (rotate_code == 2)
259  FFSWAP(uint16_t*, ctx->frm1, ctx->frm2);
260  FFSWAP(uint16_t*, ctx->frm2, ctx->frm0);
261 }
262 
264 {
265  SANMVideoContext *ctx = avctx->priv_data;
266 
267  ctx->avctx = avctx;
268  ctx->version = !avctx->extradata_size;
269 
271 
272  init_sizes(ctx, avctx->width, avctx->height);
273  if (init_buffers(ctx)) {
274  av_log(avctx, AV_LOG_ERROR, "error allocating buffers\n");
275  return AVERROR(ENOMEM);
276  }
277  ctx->output = &ctx->frame;
278  ctx->output->data[0] = 0;
279 
280  make_glyphs(ctx->p4x4glyphs[0], glyph4_x, glyph4_y, 4);
281  make_glyphs(ctx->p8x8glyphs[0], glyph8_x, glyph8_y, 8);
282 
283  if (!ctx->version) {
284  int i;
285 
286  if (avctx->extradata_size < 1026) {
287  av_log(avctx, AV_LOG_ERROR, "not enough extradata\n");
288  return AVERROR_INVALIDDATA;
289  }
290 
291  ctx->subversion = AV_RL16(avctx->extradata);
292  for (i = 0; i < 256; i++)
293  ctx->pal[i] = 0xFFU << 24 | AV_RL32(avctx->extradata + 2 + i * 4);
294  }
295 
296  return 0;
297 }
298 
300 {
301  SANMVideoContext *ctx = avctx->priv_data;
302 
303  destroy_buffers(ctx);
304 
305  if (ctx->frame.data[0]) {
306  avctx->release_buffer(avctx, &ctx->frame);
307  ctx->frame.data[0] = 0;
308  }
309 
310  return 0;
311 }
312 
313 static int rle_decode(SANMVideoContext *ctx, uint8_t *dst, const int out_size)
314 {
315  int opcode, color, run_len, left = out_size;
316 
317  while (left > 0) {
318  opcode = bytestream2_get_byte(&ctx->gb);
319  run_len = (opcode >> 1) + 1;
320  if (run_len > left || bytestream2_get_bytes_left(&ctx->gb) <= 0)
321  return AVERROR_INVALIDDATA;
322 
323  if (opcode & 1) {
324  color = bytestream2_get_byte(&ctx->gb);
325  memset(dst, color, run_len);
326  } else {
327  if (bytestream2_get_bytes_left(&ctx->gb) < run_len)
328  return AVERROR_INVALIDDATA;
329  bytestream2_get_bufferu(&ctx->gb, dst, run_len);
330  }
331 
332  dst += run_len;
333  left -= run_len;
334  }
335 
336  return 0;
337 }
338 
339 static int old_codec1(SANMVideoContext *ctx, int top,
340  int left, int width, int height)
341 {
342  uint8_t *dst = ((uint8_t*)ctx->frm0) + left + top * ctx->pitch;
343  int i, j, len, flag, code, val, pos, end;
344 
345  for (i = 0; i < height; i++) {
346  pos = 0;
347 
348  if (bytestream2_get_bytes_left(&ctx->gb) < 2)
349  return AVERROR_INVALIDDATA;
350 
351  len = bytestream2_get_le16u(&ctx->gb);
352  end = bytestream2_tell(&ctx->gb) + len;
353 
354  while (bytestream2_tell(&ctx->gb) < end) {
355  if (bytestream2_get_bytes_left(&ctx->gb) < 2)
356  return AVERROR_INVALIDDATA;
357 
358  code = bytestream2_get_byteu(&ctx->gb);
359  flag = code & 1;
360  code = (code >> 1) + 1;
361  if (pos + code > width)
362  return AVERROR_INVALIDDATA;
363  if (flag) {
364  val = bytestream2_get_byteu(&ctx->gb);
365  if (val)
366  memset(dst + pos, val, code);
367  pos += code;
368  } else {
369  if (bytestream2_get_bytes_left(&ctx->gb) < code)
370  return AVERROR_INVALIDDATA;
371  for (j = 0; j < code; j++) {
372  val = bytestream2_get_byteu(&ctx->gb);
373  if (val)
374  dst[pos] = val;
375  pos++;
376  }
377  }
378  }
379  dst += ctx->pitch;
380  }
381  ctx->rotate_code = 0;
382 
383  return 0;
384 }
385 
386 static inline void codec37_mv(uint8_t *dst, const uint8_t *src,
387  int height, int stride, int x, int y)
388 {
389  int pos, i, j;
390 
391  pos = x + y * stride;
392  for (j = 0; j < 4; j++) {
393  for (i = 0; i < 4; i++) {
394  if ((pos + i) < 0 || (pos + i) >= height * stride)
395  dst[i] = 0;
396  else
397  dst[i] = src[i];
398  }
399  dst += stride;
400  src += stride;
401  pos += stride;
402  }
403 }
404 
405 static int old_codec37(SANMVideoContext *ctx, int top,
406  int left, int width, int height)
407 {
408  int stride = ctx->pitch;
409  int i, j, k, t;
410  int skip_run = 0;
411  int compr, mvoff, seq, flags;
412  uint32_t decoded_size;
413  uint8_t *dst, *prev;
414 
415  compr = bytestream2_get_byte(&ctx->gb);
416  mvoff = bytestream2_get_byte(&ctx->gb);
417  seq = bytestream2_get_le16(&ctx->gb);
418  decoded_size = bytestream2_get_le32(&ctx->gb);
419  bytestream2_skip(&ctx->gb, 4);
420  flags = bytestream2_get_byte(&ctx->gb);
421  bytestream2_skip(&ctx->gb, 3);
422 
423  if (decoded_size > ctx->height * stride - left - top * stride) {
424  decoded_size = ctx->height * stride - left - top * stride;
425  av_log(ctx->avctx, AV_LOG_WARNING, "decoded size is too large\n");
426  }
427 
428  ctx->rotate_code = 0;
429 
430  if (((seq & 1) || !(flags & 1)) && (compr && compr != 2))
431  rotate_bufs(ctx, 1);
432 
433  dst = ((uint8_t*)ctx->frm0) + left + top * stride;
434  prev = ((uint8_t*)ctx->frm2) + left + top * stride;
435 
436  if (mvoff > 2) {
437  av_log(ctx->avctx, AV_LOG_ERROR, "invalid motion base value %d\n", mvoff);
438  return AVERROR_INVALIDDATA;
439  }
440  av_dlog(ctx->avctx, "compression %d\n", compr);
441  switch (compr) {
442  case 0:
443  for (i = 0; i < height; i++) {
444  bytestream2_get_buffer(&ctx->gb, dst, width);
445  dst += stride;
446  }
447  memset(ctx->frm1, 0, ctx->height * stride);
448  memset(ctx->frm2, 0, ctx->height * stride);
449  break;
450  case 2:
451  if (rle_decode(ctx, dst, decoded_size))
452  return AVERROR_INVALIDDATA;
453  memset(ctx->frm1, 0, ctx->frm1_size);
454  memset(ctx->frm2, 0, ctx->frm2_size);
455  break;
456  case 3:
457  case 4:
458  if (flags & 4) {
459  for (j = 0; j < height; j += 4) {
460  for (i = 0; i < width; i += 4) {
461  int code;
462  if (skip_run) {
463  skip_run--;
464  copy_block4(dst + i, prev + i, stride, stride, 4);
465  continue;
466  }
467  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
468  return AVERROR_INVALIDDATA;
469  code = bytestream2_get_byteu(&ctx->gb);
470  switch (code) {
471  case 0xFF:
472  if (bytestream2_get_bytes_left(&ctx->gb) < 16)
473  return AVERROR_INVALIDDATA;
474  for (k = 0; k < 4; k++)
475  bytestream2_get_bufferu(&ctx->gb, dst + i + k * stride, 4);
476  break;
477  case 0xFE:
478  if (bytestream2_get_bytes_left(&ctx->gb) < 4)
479  return AVERROR_INVALIDDATA;
480  for (k = 0; k < 4; k++)
481  memset(dst + i + k * stride, bytestream2_get_byteu(&ctx->gb), 4);
482  break;
483  case 0xFD:
484  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
485  return AVERROR_INVALIDDATA;
486  t = bytestream2_get_byteu(&ctx->gb);
487  for (k = 0; k < 4; k++)
488  memset(dst + i + k * stride, t, 4);
489  break;
490  default:
491  if (compr == 4 && !code) {
492  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
493  return AVERROR_INVALIDDATA;
494  skip_run = bytestream2_get_byteu(&ctx->gb) + 1;
495  i -= 4;
496  } else {
497  int mx, my;
498 
499  mx = c37_mv[(mvoff * 255 + code) * 2 ];
500  my = c37_mv[(mvoff * 255 + code) * 2 + 1];
501  codec37_mv(dst + i, prev + i + mx + my * stride,
502  ctx->height, stride, i + mx, j + my);
503  }
504  }
505  }
506  dst += stride * 4;
507  prev += stride * 4;
508  }
509  } else {
510  for (j = 0; j < height; j += 4) {
511  for (i = 0; i < width; i += 4) {
512  int code;
513  if (skip_run) {
514  skip_run--;
515  copy_block4(dst + i, prev + i, stride, stride, 4);
516  continue;
517  }
518  code = bytestream2_get_byte(&ctx->gb);
519  if (code == 0xFF) {
520  if (bytestream2_get_bytes_left(&ctx->gb) < 16)
521  return AVERROR_INVALIDDATA;
522  for (k = 0; k < 4; k++)
523  bytestream2_get_bufferu(&ctx->gb, dst + i + k * stride, 4);
524  } else if (compr == 4 && !code) {
525  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
526  return AVERROR_INVALIDDATA;
527  skip_run = bytestream2_get_byteu(&ctx->gb) + 1;
528  i -= 4;
529  } else {
530  int mx, my;
531 
532  mx = c37_mv[(mvoff * 255 + code) * 2];
533  my = c37_mv[(mvoff * 255 + code) * 2 + 1];
534  codec37_mv(dst + i, prev + i + mx + my * stride,
535  ctx->height, stride, i + mx, j + my);
536  }
537  }
538  dst += stride * 4;
539  prev += stride * 4;
540  }
541  }
542  break;
543  default:
544  av_log(ctx->avctx, AV_LOG_ERROR,
545  "subcodec 37 compression %d not implemented\n", compr);
546  return AVERROR_PATCHWELCOME;
547  }
548 
549  return 0;
550 }
551 
553  uint8_t *prev2, int stride, int tbl, int size)
554 {
555  int code, k, t;
556  uint8_t colors[2];
557  int8_t *pglyph;
558 
559  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
560  return AVERROR_INVALIDDATA;
561 
562  code = bytestream2_get_byteu(&ctx->gb);
563  if (code >= 0xF8) {
564  switch (code) {
565  case 0xFF:
566  if (size == 2) {
567  if (bytestream2_get_bytes_left(&ctx->gb) < 4)
568  return AVERROR_INVALIDDATA;
569  dst[0] = bytestream2_get_byteu(&ctx->gb);
570  dst[1] = bytestream2_get_byteu(&ctx->gb);
571  dst[0+stride] = bytestream2_get_byteu(&ctx->gb);
572  dst[1+stride] = bytestream2_get_byteu(&ctx->gb);
573  } else {
574  size >>= 1;
575  if (process_block(ctx, dst, prev1, prev2, stride, tbl, size))
576  return AVERROR_INVALIDDATA;
577  if (process_block(ctx, dst + size, prev1 + size, prev2 + size,
578  stride, tbl, size))
579  return AVERROR_INVALIDDATA;
580  dst += size * stride;
581  prev1 += size * stride;
582  prev2 += size * stride;
583  if (process_block(ctx, dst, prev1, prev2, stride, tbl, size))
584  return AVERROR_INVALIDDATA;
585  if (process_block(ctx, dst + size, prev1 + size, prev2 + size,
586  stride, tbl, size))
587  return AVERROR_INVALIDDATA;
588  }
589  break;
590  case 0xFE:
591  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
592  return AVERROR_INVALIDDATA;
593 
594  t = bytestream2_get_byteu(&ctx->gb);
595  for (k = 0; k < size; k++)
596  memset(dst + k * stride, t, size);
597  break;
598  case 0xFD:
599  if (bytestream2_get_bytes_left(&ctx->gb) < 3)
600  return AVERROR_INVALIDDATA;
601 
602  code = bytestream2_get_byteu(&ctx->gb);
603  pglyph = (size == 8) ? ctx->p8x8glyphs[code] : ctx->p4x4glyphs[code];
604  bytestream2_get_bufferu(&ctx->gb, colors, 2);
605 
606  for (k = 0; k < size; k++)
607  for (t = 0; t < size; t++)
608  dst[t + k * stride] = colors[!*pglyph++];
609  break;
610  case 0xFC:
611  for (k = 0; k < size; k++)
612  memcpy(dst + k * stride, prev1 + k * stride, size);
613  break;
614  default:
615  k = bytestream2_tell(&ctx->gb);
616  bytestream2_seek(&ctx->gb, tbl + (code & 7), SEEK_SET);
617  t = bytestream2_get_byte(&ctx->gb);
618  bytestream2_seek(&ctx->gb, k, SEEK_SET);
619  for (k = 0; k < size; k++)
620  memset(dst + k * stride, t, size);
621  }
622  } else {
623  int mx = motion_vectors[code][0];
624  int my = motion_vectors[code][1];
625  int index = prev2 - (const uint8_t*)ctx->frm2;
626 
627  av_assert2(index >= 0 && index < (ctx->buf_size>>1));
628 
629  if (index < - mx - my*stride ||
630  (ctx->buf_size>>1) - index < mx + size + (my + size - 1)*stride) {
631  av_log(ctx->avctx, AV_LOG_ERROR, "MV is invalid \n");
632  return AVERROR_INVALIDDATA;
633  }
634 
635  for (k = 0; k < size; k++)
636  memcpy(dst + k * stride, prev2 + mx + (my + k) * stride, size);
637  }
638 
639  return 0;
640 }
641 
642 static int old_codec47(SANMVideoContext *ctx, int top,
643  int left, int width, int height)
644 {
645  int i, j, seq, compr, new_rot, tbl_pos, skip;
646  int stride = ctx->pitch;
647  uint8_t *dst = ((uint8_t*)ctx->frm0) + left + top * stride;
648  uint8_t *prev1 = (uint8_t*)ctx->frm1;
649  uint8_t *prev2 = (uint8_t*)ctx->frm2;
650  uint32_t decoded_size;
651 
652  tbl_pos = bytestream2_tell(&ctx->gb);
653  seq = bytestream2_get_le16(&ctx->gb);
654  compr = bytestream2_get_byte(&ctx->gb);
655  new_rot = bytestream2_get_byte(&ctx->gb);
656  skip = bytestream2_get_byte(&ctx->gb);
657  bytestream2_skip(&ctx->gb, 9);
658  decoded_size = bytestream2_get_le32(&ctx->gb);
659  bytestream2_skip(&ctx->gb, 8);
660 
661  if (decoded_size > ctx->height * stride - left - top * stride) {
662  decoded_size = ctx->height * stride - left - top * stride;
663  av_log(ctx->avctx, AV_LOG_WARNING, "decoded size is too large\n");
664  }
665 
666  if (skip & 1)
667  bytestream2_skip(&ctx->gb, 0x8080);
668  if (!seq) {
669  ctx->prev_seq = -1;
670  memset(prev1, 0, ctx->height * stride);
671  memset(prev2, 0, ctx->height * stride);
672  }
673  av_dlog(ctx->avctx, "compression %d\n", compr);
674  switch (compr) {
675  case 0:
676  if (bytestream2_get_bytes_left(&ctx->gb) < width * height)
677  return AVERROR_INVALIDDATA;
678  for (j = 0; j < height; j++) {
679  bytestream2_get_bufferu(&ctx->gb, dst, width);
680  dst += stride;
681  }
682  break;
683  case 1:
684  if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
685  return AVERROR_INVALIDDATA;
686  for (j = 0; j < height; j += 2) {
687  for (i = 0; i < width; i += 2) {
688  dst[i] = dst[i + 1] =
689  dst[stride + i] = dst[stride + i + 1] = bytestream2_get_byteu(&ctx->gb);
690  }
691  dst += stride * 2;
692  }
693  break;
694  case 2:
695  if (seq == ctx->prev_seq + 1) {
696  for (j = 0; j < height; j += 8) {
697  for (i = 0; i < width; i += 8) {
698  if (process_block(ctx, dst + i, prev1 + i, prev2 + i, stride,
699  tbl_pos + 8, 8))
700  return AVERROR_INVALIDDATA;
701  }
702  dst += stride * 8;
703  prev1 += stride * 8;
704  prev2 += stride * 8;
705  }
706  }
707  break;
708  case 3:
709  memcpy(ctx->frm0, ctx->frm2, ctx->pitch * ctx->height);
710  break;
711  case 4:
712  memcpy(ctx->frm0, ctx->frm1, ctx->pitch * ctx->height);
713  break;
714  case 5:
715  if (rle_decode(ctx, dst, decoded_size))
716  return AVERROR_INVALIDDATA;
717  break;
718  default:
719  av_log(ctx->avctx, AV_LOG_ERROR,
720  "subcodec 47 compression %d not implemented\n", compr);
721  return AVERROR_PATCHWELCOME;
722  }
723  if (seq == ctx->prev_seq + 1)
724  ctx->rotate_code = new_rot;
725  else
726  ctx->rotate_code = 0;
727  ctx->prev_seq = seq;
728 
729  return 0;
730 }
731 
733 {
734  uint16_t codec, top, left, w, h;
735 
736  codec = bytestream2_get_le16u(&ctx->gb);
737  left = bytestream2_get_le16u(&ctx->gb);
738  top = bytestream2_get_le16u(&ctx->gb);
739  w = bytestream2_get_le16u(&ctx->gb);
740  h = bytestream2_get_le16u(&ctx->gb);
741 
742  if (!w || !h) {
743  av_log(ctx->avctx, AV_LOG_ERROR, "dimensions are invalid\n");
744  return AVERROR_INVALIDDATA;
745  }
746 
747  if (ctx->width < left + w || ctx->height < top + h) {
748  if (av_image_check_size(FFMAX(left + w, ctx->width),
749  FFMAX(top + h, ctx->height), 0, ctx->avctx) < 0)
750  return AVERROR_INVALIDDATA;
751  avcodec_set_dimensions(ctx->avctx, FFMAX(left + w, ctx->width),
752  FFMAX(top + h, ctx->height));
753  init_sizes(ctx, FFMAX(left + w, ctx->width),
754  FFMAX(top + h, ctx->height));
755  if (init_buffers(ctx)) {
756  av_log(ctx->avctx, AV_LOG_ERROR, "error resizing buffers\n");
757  return AVERROR(ENOMEM);
758  }
759  }
760  bytestream2_skip(&ctx->gb, 4);
761 
762  av_dlog(ctx->avctx, "subcodec %d\n", codec);
763  switch (codec) {
764  case 1:
765  case 3:
766  return old_codec1(ctx, top, left, w, h);
767  break;
768  case 37:
769  return old_codec37(ctx, top, left, w, h);
770  break;
771  case 47:
772  return old_codec47(ctx, top, left, w, h);
773  break;
774  default:
775  av_log_ask_for_sample(ctx->avctx, "unknown subcodec %d\n", codec);
776  return AVERROR_PATCHWELCOME;
777  }
778 }
779 
780 static int decode_0(SANMVideoContext *ctx)
781 {
782  uint16_t *frm = ctx->frm0;
783  int x, y;
784 
785  if (bytestream2_get_bytes_left(&ctx->gb) < ctx->width * ctx->height * 2) {
786  av_log(ctx->avctx, AV_LOG_ERROR, "insufficient data for raw frame\n");
787  return AVERROR_INVALIDDATA;
788  }
789  for (y = 0; y < ctx->height; y++) {
790  for (x = 0; x < ctx->width; x++)
791  frm[x] = bytestream2_get_le16u(&ctx->gb);
792  frm += ctx->pitch;
793  }
794  return 0;
795 }
796 
797 static int decode_nop(SANMVideoContext *ctx)
798 {
799  av_log_ask_for_sample(ctx->avctx, "unknown/unsupported compression type\n");
800  return AVERROR_PATCHWELCOME;
801 }
802 
803 static void copy_block(uint16_t *pdest, uint16_t *psrc, int block_size, int pitch)
804 {
805  uint8_t *dst = (uint8_t *)pdest;
806  uint8_t *src = (uint8_t *)psrc;
807  int stride = pitch * 2;
808 
809  switch (block_size) {
810  case 2:
811  copy_block4(dst, src, stride, stride, 2);
812  break;
813  case 4:
814  copy_block8(dst, src, stride, stride, 4);
815  break;
816  case 8:
817  copy_block16(dst, src, stride, stride, 8);
818  break;
819  }
820 }
821 
822 static void fill_block(uint16_t *pdest, uint16_t color, int block_size, int pitch)
823 {
824  int x, y;
825 
826  pitch -= block_size;
827  for (y = 0; y < block_size; y++, pdest += pitch)
828  for (x = 0; x < block_size; x++)
829  *pdest++ = color;
830 }
831 
832 static int draw_glyph(SANMVideoContext *ctx, uint16_t *dst, int index, uint16_t fg_color,
833  uint16_t bg_color, int block_size, int pitch)
834 {
835  int8_t *pglyph;
836  uint16_t colors[2] = { fg_color, bg_color };
837  int x, y;
838 
839  if (index >= NGLYPHS) {
840  av_log(ctx->avctx, AV_LOG_ERROR, "ignoring nonexistent glyph #%u\n", index);
841  return AVERROR_INVALIDDATA;
842  }
843 
844  pglyph = block_size == 8 ? ctx->p8x8glyphs[index] : ctx->p4x4glyphs[index];
845  pitch -= block_size;
846 
847  for (y = 0; y < block_size; y++, dst += pitch)
848  for (x = 0; x < block_size; x++)
849  *dst++ = colors[*pglyph++];
850  return 0;
851 }
852 
853 static int opcode_0xf7(SANMVideoContext *ctx, int cx, int cy, int block_size, int pitch)
854 {
855  uint16_t *dst = ctx->frm0 + cx + cy * ctx->pitch;
856 
857  if (block_size == 2) {
858  uint32_t indices;
859 
860  if (bytestream2_get_bytes_left(&ctx->gb) < 4)
861  return AVERROR_INVALIDDATA;
862 
863  indices = bytestream2_get_le32u(&ctx->gb);
864  dst[0] = ctx->codebook[indices & 0xFF]; indices >>= 8;
865  dst[1] = ctx->codebook[indices & 0xFF]; indices >>= 8;
866  dst[pitch] = ctx->codebook[indices & 0xFF]; indices >>= 8;
867  dst[pitch + 1] = ctx->codebook[indices & 0xFF];
868  } else {
869  uint16_t fgcolor, bgcolor;
870  int glyph;
871 
872  if (bytestream2_get_bytes_left(&ctx->gb) < 3)
873  return AVERROR_INVALIDDATA;
874 
875  glyph = bytestream2_get_byteu(&ctx->gb);
876  bgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
877  fgcolor = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
878 
879  draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
880  }
881  return 0;
882 }
883 
884 static int opcode_0xf8(SANMVideoContext *ctx, int cx, int cy, int block_size, int pitch)
885 {
886  uint16_t *dst = ctx->frm0 + cx + cy * ctx->pitch;
887 
888  if (block_size == 2) {
889  if (bytestream2_get_bytes_left(&ctx->gb) < 8)
890  return AVERROR_INVALIDDATA;
891 
892  dst[0] = bytestream2_get_le16u(&ctx->gb);
893  dst[1] = bytestream2_get_le16u(&ctx->gb);
894  dst[pitch] = bytestream2_get_le16u(&ctx->gb);
895  dst[pitch + 1] = bytestream2_get_le16u(&ctx->gb);
896  } else {
897  uint16_t fgcolor, bgcolor;
898  int glyph;
899 
900  if (bytestream2_get_bytes_left(&ctx->gb) < 5)
901  return AVERROR_INVALIDDATA;
902 
903  glyph = bytestream2_get_byteu(&ctx->gb);
904  bgcolor = bytestream2_get_le16u(&ctx->gb);
905  fgcolor = bytestream2_get_le16u(&ctx->gb);
906 
907  draw_glyph(ctx, dst, glyph, fgcolor, bgcolor, block_size, pitch);
908  }
909  return 0;
910 }
911 
912 static int good_mvec(SANMVideoContext *ctx, int cx, int cy, int mx, int my,
913  int block_size)
914 {
915  int start_pos = cx + mx + (cy + my) * ctx->pitch;
916  int end_pos = start_pos + (block_size - 1) * (ctx->pitch + 1);
917 
918  int good = start_pos >= 0 && end_pos < (ctx->buf_size >> 1);
919 
920  if (!good) {
921  av_log(ctx->avctx, AV_LOG_ERROR, "ignoring invalid motion vector (%i, %i)->(%u, %u), block size = %u\n",
922  cx + mx, cy + my, cx, cy, block_size);
923  }
924 
925  return good;
926 }
927 
928 static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size)
929 {
930  int16_t mx, my, index;
931  int opcode;
932 
933  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
934  return AVERROR_INVALIDDATA;
935 
936  opcode = bytestream2_get_byteu(&ctx->gb);
937 
938  av_dlog(ctx->avctx, "opcode 0x%0X cx %d cy %d blk %d\n", opcode, cx, cy, blk_size);
939  switch (opcode) {
940  default:
941  mx = motion_vectors[opcode][0];
942  my = motion_vectors[opcode][1];
943 
944  if (good_mvec(ctx, cx, cy, mx, my, blk_size)) {
945  copy_block(ctx->frm0 + cx + ctx->pitch * cy,
946  ctx->frm2 + cx + mx + ctx->pitch * (cy + my),
947  blk_size, ctx->pitch);
948  }
949  break;
950  case 0xF5:
951  if (bytestream2_get_bytes_left(&ctx->gb) < 2)
952  return AVERROR_INVALIDDATA;
953  index = bytestream2_get_le16u(&ctx->gb);
954 
955  mx = index % ctx->width;
956  my = index / ctx->width;
957 
958  if (good_mvec(ctx, cx, cy, mx, my, blk_size)) {
959  copy_block(ctx->frm0 + cx + ctx->pitch * cy,
960  ctx->frm2 + cx + mx + ctx->pitch * (cy + my),
961  blk_size, ctx->pitch);
962  }
963  break;
964  case 0xF6:
965  copy_block(ctx->frm0 + cx + ctx->pitch * cy,
966  ctx->frm1 + cx + ctx->pitch * cy,
967  blk_size, ctx->pitch);
968  break;
969  case 0xF7:
970  opcode_0xf7(ctx, cx, cy, blk_size, ctx->pitch);
971  break;
972 
973  case 0xF8:
974  opcode_0xf8(ctx, cx, cy, blk_size, ctx->pitch);
975  break;
976  case 0xF9:
977  case 0xFA:
978  case 0xFB:
979  case 0xFC:
980  fill_block(ctx->frm0 + cx + cy * ctx->pitch,
981  ctx->small_codebook[opcode - 0xf9], blk_size, ctx->pitch);
982  break;
983  case 0xFD:
984  if (bytestream2_get_bytes_left(&ctx->gb) < 1)
985  return AVERROR_INVALIDDATA;
986  fill_block(ctx->frm0 + cx + cy * ctx->pitch,
987  ctx->codebook[bytestream2_get_byteu(&ctx->gb)], blk_size, ctx->pitch);
988  break;
989  case 0xFE:
990  if (bytestream2_get_bytes_left(&ctx->gb) < 2)
991  return AVERROR_INVALIDDATA;
992  fill_block(ctx->frm0 + cx + cy * ctx->pitch,
993  bytestream2_get_le16u(&ctx->gb), blk_size, ctx->pitch);
994  break;
995  case 0xFF:
996  if (blk_size == 2) {
997  opcode_0xf8(ctx, cx, cy, blk_size, ctx->pitch);
998  } else {
999  blk_size >>= 1;
1000  if (codec2subblock(ctx, cx , cy , blk_size))
1001  return AVERROR_INVALIDDATA;
1002  if (codec2subblock(ctx, cx + blk_size, cy , blk_size))
1003  return AVERROR_INVALIDDATA;
1004  if (codec2subblock(ctx, cx , cy + blk_size, blk_size))
1005  return AVERROR_INVALIDDATA;
1006  if (codec2subblock(ctx, cx + blk_size, cy + blk_size, blk_size))
1007  return AVERROR_INVALIDDATA;
1008  }
1009  break;
1010  }
1011  return 0;
1012 }
1013 
1014 static int decode_2(SANMVideoContext *ctx)
1015 {
1016  int cx, cy, ret;
1017 
1018  for (cy = 0; cy < ctx->aligned_height; cy += 8) {
1019  for (cx = 0; cx < ctx->aligned_width; cx += 8) {
1020  if (ret = codec2subblock(ctx, cx, cy, 8))
1021  return ret;
1022  }
1023  }
1024 
1025  return 0;
1026 }
1027 
1028 static int decode_3(SANMVideoContext *ctx)
1029 {
1030  memcpy(ctx->frm0, ctx->frm2, ctx->frm2_size);
1031  return 0;
1032 }
1033 
1034 static int decode_4(SANMVideoContext *ctx)
1035 {
1036  memcpy(ctx->frm0, ctx->frm1, ctx->frm1_size);
1037  return 0;
1038 }
1039 
1040 static int decode_5(SANMVideoContext *ctx)
1041 {
1042 #if HAVE_BIGENDIAN
1043  uint16_t *frm;
1044  int npixels;
1045 #endif
1046  uint8_t *dst = (uint8_t*)ctx->frm0;
1047 
1048  if (rle_decode(ctx, dst, ctx->buf_size))
1049  return AVERROR_INVALIDDATA;
1050 
1051 #if HAVE_BIGENDIAN
1052  npixels = ctx->npixels;
1053  frm = ctx->frm0;
1054  while (npixels--)
1055  *frm++ = av_bswap16(*frm);
1056 #endif
1057 
1058  return 0;
1059 }
1060 
1061 static int decode_6(SANMVideoContext *ctx)
1062 {
1063  int npixels = ctx->npixels;
1064  uint16_t *frm = ctx->frm0;
1065 
1066  if (bytestream2_get_bytes_left(&ctx->gb) < npixels) {
1067  av_log(ctx->avctx, AV_LOG_ERROR, "insufficient data for frame\n");
1068  return AVERROR_INVALIDDATA;
1069  }
1070  while (npixels--)
1071  *frm++ = ctx->codebook[bytestream2_get_byteu(&ctx->gb)];
1072 
1073  return 0;
1074 }
1075 
1076 static int decode_8(SANMVideoContext *ctx)
1077 {
1078  uint16_t *pdest = ctx->frm0;
1079  uint8_t *rsrc;
1080  long npixels = ctx->npixels;
1081 
1082  av_fast_malloc(&ctx->rle_buf, &ctx->rle_buf_size, npixels);
1083  if (!ctx->rle_buf) {
1084  av_log(ctx->avctx, AV_LOG_ERROR, "RLE buffer allocation failed\n");
1085  return AVERROR(ENOMEM);
1086  }
1087  rsrc = ctx->rle_buf;
1088 
1089  if (rle_decode(ctx, rsrc, npixels))
1090  return AVERROR_INVALIDDATA;
1091 
1092  while (npixels--)
1093  *pdest++ = ctx->codebook[*rsrc++];
1094 
1095  return 0;
1096 }
1097 
1098 typedef int (*frm_decoder)(SANMVideoContext *ctx);
1099 
1100 static const frm_decoder v1_decoders[] = {
1103 };
1104 
1106 {
1107  int i, ret;
1108 
1109  if ((ret = bytestream2_get_bytes_left(&ctx->gb)) < 560) {
1110  av_log(ctx->avctx, AV_LOG_ERROR, "too short input frame (%d bytes)\n",
1111  ret);
1112  return AVERROR_INVALIDDATA;
1113  }
1114  bytestream2_skip(&ctx->gb, 8); // skip pad
1115 
1116  hdr->width = bytestream2_get_le32u(&ctx->gb);
1117  hdr->height = bytestream2_get_le32u(&ctx->gb);
1118 
1119  if (hdr->width != ctx->width || hdr->height != ctx->height) {
1120  av_log(ctx->avctx, AV_LOG_ERROR, "variable size frames are not implemented\n");
1121  return AVERROR_PATCHWELCOME;
1122  }
1123 
1124  hdr->seq_num = bytestream2_get_le16u(&ctx->gb);
1125  hdr->codec = bytestream2_get_byteu(&ctx->gb);
1126  hdr->rotate_code = bytestream2_get_byteu(&ctx->gb);
1127 
1128  bytestream2_skip(&ctx->gb, 4); // skip pad
1129 
1130  for (i = 0; i < 4; i++)
1131  ctx->small_codebook[i] = bytestream2_get_le16u(&ctx->gb);
1132  hdr->bg_color = bytestream2_get_le16u(&ctx->gb);
1133 
1134  bytestream2_skip(&ctx->gb, 2); // skip pad
1135 
1136  hdr->rle_output_size = bytestream2_get_le32u(&ctx->gb);
1137  for (i = 0; i < 256; i++)
1138  ctx->codebook[i] = bytestream2_get_le16u(&ctx->gb);
1139 
1140  bytestream2_skip(&ctx->gb, 8); // skip pad
1141 
1142  av_dlog(ctx->avctx, "subcodec %d\n", hdr->codec);
1143  return 0;
1144 }
1145 
1146 static void fill_frame(uint16_t *pbuf, int buf_size, uint16_t color)
1147 {
1148  while (buf_size--)
1149  *pbuf++ = color;
1150 }
1151 
1153 {
1154  uint8_t *dst;
1155  const uint8_t *src = (uint8_t*) ctx->frm0;
1156  int ret, dstpitch, height = ctx->height;
1157  int srcpitch = ctx->pitch * (hdr ? sizeof(ctx->frm0[0]) : 1);
1158 
1159  if ((ret = ff_get_buffer(ctx->avctx, ctx->output)) < 0) {
1160  av_log(ctx->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
1161  return ret;
1162  }
1163 
1164  dst = ctx->output->data[0];
1165  dstpitch = ctx->output->linesize[0];
1166 
1167  while (height--) {
1168  memcpy(dst, src, srcpitch);
1169  src += srcpitch;
1170  dst += dstpitch;
1171  }
1172 
1173  return 0;
1174 }
1175 
1176 static int decode_frame(AVCodecContext *avctx, void *data,
1177  int *got_frame_ptr, AVPacket *pkt)
1178 {
1179  SANMVideoContext *ctx = avctx->priv_data;
1180  int i, ret;
1181 
1182  bytestream2_init(&ctx->gb, pkt->data, pkt->size);
1183  if (ctx->output->data[0])
1184  avctx->release_buffer(avctx, ctx->output);
1185 
1186  if (!ctx->version) {
1187  int to_store = 0;
1188 
1189  while (bytestream2_get_bytes_left(&ctx->gb) >= 8) {
1190  uint32_t sig, size;
1191  int pos;
1192 
1193  sig = bytestream2_get_be32u(&ctx->gb);
1194  size = bytestream2_get_be32u(&ctx->gb);
1195  pos = bytestream2_tell(&ctx->gb);
1196 
1197  if (bytestream2_get_bytes_left(&ctx->gb) < size) {
1198  av_log(avctx, AV_LOG_ERROR, "incorrect chunk size %d\n", size);
1199  break;
1200  }
1201  switch (sig) {
1202  case MKBETAG('N', 'P', 'A', 'L'):
1203  if (size != 256 * 3) {
1204  av_log(avctx, AV_LOG_ERROR, "incorrect palette block size %d\n",
1205  size);
1206  return AVERROR_INVALIDDATA;
1207  }
1208  for (i = 0; i < 256; i++)
1209  ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
1210  break;
1211  case MKBETAG('F', 'O', 'B', 'J'):
1212  if (size < 16)
1213  return AVERROR_INVALIDDATA;
1214  if (ret = process_frame_obj(ctx))
1215  return ret;
1216  break;
1217  case MKBETAG('X', 'P', 'A', 'L'):
1218  if (size == 6 || size == 4) {
1219  uint8_t tmp[3];
1220  int j;
1221 
1222  for (i = 0; i < 256; i++) {
1223  for (j = 0; j < 3; j++) {
1224  int t = (ctx->pal[i] >> (16 - j * 8)) & 0xFF;
1225  tmp[j] = av_clip_uint8((t * 129 + ctx->delta_pal[i * 3 + j]) >> 7);
1226  }
1227  ctx->pal[i] = 0xFFU << 24 | AV_RB24(tmp);
1228  }
1229  } else {
1230  if (size < 768 * 2 + 4) {
1231  av_log(avctx, AV_LOG_ERROR, "incorrect palette change block size %d\n",
1232  size);
1233  return AVERROR_INVALIDDATA;
1234  }
1235  bytestream2_skipu(&ctx->gb, 4);
1236  for (i = 0; i < 768; i++)
1237  ctx->delta_pal[i] = bytestream2_get_le16u(&ctx->gb);
1238  if (size >= 768 * 5 + 4) {
1239  for (i = 0; i < 256; i++)
1240  ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24u(&ctx->gb);
1241  } else {
1242  memset(ctx->pal, 0, sizeof(ctx->pal));
1243  }
1244  }
1245  break;
1246  case MKBETAG('S', 'T', 'O', 'R'):
1247  to_store = 1;
1248  break;
1249  case MKBETAG('F', 'T', 'C', 'H'):
1250  memcpy(ctx->frm0, ctx->stored_frame, ctx->buf_size);
1251  break;
1252  default:
1253  bytestream2_skip(&ctx->gb, size);
1254  av_log(avctx, AV_LOG_DEBUG, "unknown/unsupported chunk %x\n", sig);
1255  break;
1256  }
1257 
1258  bytestream2_seek(&ctx->gb, pos + size, SEEK_SET);
1259  if (size & 1)
1260  bytestream2_skip(&ctx->gb, 1);
1261  }
1262  if (to_store)
1263  memcpy(ctx->stored_frame, ctx->frm0, ctx->buf_size);
1264  if ((ret = copy_output(ctx, NULL)))
1265  return ret;
1266  memcpy(ctx->output->data[1], ctx->pal, 1024);
1267  } else {
1268  SANMFrameHeader header;
1269 
1270  if ((ret = read_frame_header(ctx, &header)))
1271  return ret;
1272 
1273  ctx->rotate_code = header.rotate_code;
1274  if ((ctx->output->key_frame = !header.seq_num)) {
1276  fill_frame(ctx->frm1, ctx->npixels, header.bg_color);
1277  fill_frame(ctx->frm2, ctx->npixels, header.bg_color);
1278  } else {
1280  }
1281 
1282  if (header.codec < FF_ARRAY_ELEMS(v1_decoders)) {
1283  if ((ret = v1_decoders[header.codec](ctx))) {
1284  av_log(avctx, AV_LOG_ERROR,
1285  "subcodec %d: error decoding frame\n", header.codec);
1286  return ret;
1287  }
1288  } else {
1289  av_log_ask_for_sample(avctx, "subcodec %d is not implemented\n",
1290  header.codec);
1291  return AVERROR_PATCHWELCOME;
1292  }
1293 
1294  if ((ret = copy_output(ctx, &header)))
1295  return ret;
1296  }
1297  if (ctx->rotate_code)
1298  rotate_bufs(ctx, ctx->rotate_code);
1299 
1300  *got_frame_ptr = 1;
1301  *(AVFrame*)data = *ctx->output;
1302 
1303  return pkt->size;
1304 }
1305 
1307  .name = "sanm",
1308  .type = AVMEDIA_TYPE_VIDEO,
1309  .id = AV_CODEC_ID_SANM,
1310  .priv_data_size = sizeof(SANMVideoContext),
1311  .init = decode_init,
1312  .close = decode_end,
1313  .decode = decode_frame,
1314  .capabilities = CODEC_CAP_DR1,
1315  .long_name = NULL_IF_CONFIG_SMALL("LucasArts SMUSH video"),
1316 };