FFmpeg
smc.c
Go to the documentation of this file.
1 /*
2  * Quicktime Graphics (SMC) Video Decoder
3  * Copyright (C) 2003 The FFmpeg project
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /**
23  * @file
24  * QT SMC Video Decoder by Mike Melanson (melanson@pcisys.net)
25  * For more information about the SMC format, visit:
26  * http://www.pcisys.net/~melanson/codecs/
27  *
28  * The SMC decoder outputs PAL8 colorspace data.
29  */
30 
31 #include <string.h>
32 
33 #include "avcodec.h"
34 #include "bytestream.h"
35 #include "codec_internal.h"
36 #include "decode.h"
37 
38 #define CPAIR 2
39 #define CQUAD 4
40 #define COCTET 8
41 
42 #define COLORS_PER_TABLE 256
43 
44 typedef struct SmcContext {
45 
48 
49  /* SMC color tables */
53 
54  uint32_t pal[256];
55 } SmcContext;
56 
57 #define GET_BLOCK_COUNT() \
58  (opcode & 0x10) ? (1 + bytestream2_get_byte(gb)) : 1 + (opcode & 0x0F);
59 
60 #define ADVANCE_BLOCK() \
61 { \
62  pixel_ptr += 4; \
63  if (pixel_ptr >= width) \
64  { \
65  pixel_ptr = 0; \
66  row_ptr += stride * 4; \
67  } \
68  total_blocks--; \
69  if (total_blocks < !!n_blocks) \
70  { \
71  av_log(s->avctx, AV_LOG_ERROR, "block counter just went negative (this should not happen)\n"); \
72  return AVERROR_INVALIDDATA; \
73  } \
74 }
75 
77 {
78  int width = s->avctx->width;
79  int height = s->avctx->height;
80  int stride = s->frame->linesize[0];
81  int i;
82  int chunk_size;
83  int buf_size = bytestream2_size(gb);
84  uint8_t opcode;
85  int n_blocks;
86  unsigned int color_flags;
87  unsigned int color_flags_a;
88  unsigned int color_flags_b;
89  unsigned int flag_mask;
90 
91  uint8_t * const pixels = s->frame->data[0];
92 
93  int image_size = height * s->frame->linesize[0];
94  int row_ptr = 0;
95  int pixel_ptr = 0;
96  int pixel_x, pixel_y;
97  int row_inc = stride - 4;
98  int block_ptr;
99  int prev_block_ptr;
100  int prev_block_ptr1, prev_block_ptr2;
101  int prev_block_flag;
102  int total_blocks;
103  int color_table_index; /* indexes to color pair, quad, or octet tables */
104  int pixel;
105 
106  int color_pair_index = 0;
107  int color_quad_index = 0;
108  int color_octet_index = 0;
109 
110  /* make the palette available */
111  memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE);
112 
113  bytestream2_skip(gb, 1);
114  chunk_size = bytestream2_get_be24(gb);
115  if (chunk_size != buf_size)
116  av_log(s->avctx, AV_LOG_WARNING, "MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n",
117  chunk_size, buf_size);
118 
119  chunk_size = buf_size;
120  total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
121 
122  /* traverse through the blocks */
123  while (total_blocks) {
124  /* sanity checks */
125  /* make sure the row pointer hasn't gone wild */
126  if (row_ptr >= image_size) {
127  av_log(s->avctx, AV_LOG_ERROR, "just went out of bounds (row ptr = %d, height = %d)\n",
128  row_ptr, image_size);
129  return AVERROR_INVALIDDATA;
130  }
131  if (bytestream2_get_bytes_left(gb) < 1) {
132  av_log(s->avctx, AV_LOG_ERROR, "input too small\n");
133  return AVERROR_INVALIDDATA;
134  }
135 
136  opcode = bytestream2_get_byteu(gb);
137  switch (opcode & 0xF0) {
138  /* skip n blocks */
139  case 0x00:
140  case 0x10:
141  n_blocks = GET_BLOCK_COUNT();
142  while (n_blocks--) {
143  ADVANCE_BLOCK();
144  }
145  break;
146 
147  /* repeat last block n times */
148  case 0x20:
149  case 0x30:
150  n_blocks = GET_BLOCK_COUNT();
151 
152  /* sanity check */
153  if ((row_ptr == 0) && (pixel_ptr == 0)) {
154  av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
155  opcode & 0xF0);
156  return AVERROR_INVALIDDATA;
157  }
158 
159  /* figure out where the previous block started */
160  if (pixel_ptr == 0)
161  prev_block_ptr1 =
162  (row_ptr - s->avctx->width * 4) + s->avctx->width - 4;
163  else
164  prev_block_ptr1 = row_ptr + pixel_ptr - 4;
165 
166  while (n_blocks--) {
167  block_ptr = row_ptr + pixel_ptr;
168  prev_block_ptr = prev_block_ptr1;
169  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
170  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
171  pixels[block_ptr++] = pixels[prev_block_ptr++];
172  }
173  block_ptr += row_inc;
174  prev_block_ptr += row_inc;
175  }
176  ADVANCE_BLOCK();
177  }
178  break;
179 
180  /* repeat previous pair of blocks n times */
181  case 0x40:
182  case 0x50:
183  n_blocks = GET_BLOCK_COUNT();
184  n_blocks *= 2;
185 
186  /* sanity check */
187  if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) {
188  av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
189  opcode & 0xF0);
190  return AVERROR_INVALIDDATA;
191  }
192 
193  /* figure out where the previous 2 blocks started */
194  if (pixel_ptr == 0)
195  prev_block_ptr1 = (row_ptr - s->avctx->width * 4) +
196  s->avctx->width - 4 * 2;
197  else if (pixel_ptr == 4)
198  prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc;
199  else
200  prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2;
201 
202  if (pixel_ptr == 0)
203  prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc;
204  else
205  prev_block_ptr2 = row_ptr + pixel_ptr - 4;
206 
207  prev_block_flag = 0;
208  while (n_blocks--) {
209  block_ptr = row_ptr + pixel_ptr;
210  if (prev_block_flag)
211  prev_block_ptr = prev_block_ptr2;
212  else
213  prev_block_ptr = prev_block_ptr1;
214  prev_block_flag = !prev_block_flag;
215 
216  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
217  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
218  pixels[block_ptr++] = pixels[prev_block_ptr++];
219  }
220  block_ptr += row_inc;
221  prev_block_ptr += row_inc;
222  }
223  ADVANCE_BLOCK();
224  }
225  break;
226 
227  /* 1-color block encoding */
228  case 0x60:
229  case 0x70:
230  n_blocks = GET_BLOCK_COUNT();
231  pixel = bytestream2_get_byte(gb);
232 
233  while (n_blocks--) {
234  block_ptr = row_ptr + pixel_ptr;
235  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
236  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
237  pixels[block_ptr++] = pixel;
238  }
239  block_ptr += row_inc;
240  }
241  ADVANCE_BLOCK();
242  }
243  break;
244 
245  /* 2-color block encoding */
246  case 0x80:
247  case 0x90:
248  n_blocks = (opcode & 0x0F) + 1;
249 
250  /* figure out which color pair to use to paint the 2-color block */
251  if ((opcode & 0xF0) == 0x80) {
252  /* fetch the next 2 colors from bytestream and store in next
253  * available entry in the color pair table */
254  for (i = 0; i < CPAIR; i++) {
255  pixel = bytestream2_get_byte(gb);
256  color_table_index = CPAIR * color_pair_index + i;
257  s->color_pairs[color_table_index] = pixel;
258  }
259  /* this is the base index to use for this block */
260  color_table_index = CPAIR * color_pair_index;
261  color_pair_index++;
262  /* wraparound */
263  if (color_pair_index == COLORS_PER_TABLE)
264  color_pair_index = 0;
265  } else
266  color_table_index = CPAIR * bytestream2_get_byte(gb);
267 
268  while (n_blocks--) {
269  color_flags = bytestream2_get_be16(gb);
270  flag_mask = 0x8000;
271  block_ptr = row_ptr + pixel_ptr;
272  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
273  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
274  if (color_flags & flag_mask)
275  pixel = color_table_index + 1;
276  else
277  pixel = color_table_index;
278  flag_mask >>= 1;
279  pixels[block_ptr++] = s->color_pairs[pixel];
280  }
281  block_ptr += row_inc;
282  }
283  ADVANCE_BLOCK();
284  }
285  break;
286 
287  /* 4-color block encoding */
288  case 0xA0:
289  case 0xB0:
290  n_blocks = (opcode & 0x0F) + 1;
291 
292  /* figure out which color quad to use to paint the 4-color block */
293  if ((opcode & 0xF0) == 0xA0) {
294  /* fetch the next 4 colors from bytestream and store in next
295  * available entry in the color quad table */
296  for (i = 0; i < CQUAD; i++) {
297  pixel = bytestream2_get_byte(gb);
298  color_table_index = CQUAD * color_quad_index + i;
299  s->color_quads[color_table_index] = pixel;
300  }
301  /* this is the base index to use for this block */
302  color_table_index = CQUAD * color_quad_index;
303  color_quad_index++;
304  /* wraparound */
305  if (color_quad_index == COLORS_PER_TABLE)
306  color_quad_index = 0;
307  } else
308  color_table_index = CQUAD * bytestream2_get_byte(gb);
309 
310  while (n_blocks--) {
311  color_flags = bytestream2_get_be32(gb);
312  /* flag mask actually acts as a bit shift count here */
313  flag_mask = 30;
314  block_ptr = row_ptr + pixel_ptr;
315  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
316  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
317  pixel = color_table_index +
318  ((color_flags >> flag_mask) & 0x03);
319  flag_mask -= 2;
320  pixels[block_ptr++] = s->color_quads[pixel];
321  }
322  block_ptr += row_inc;
323  }
324  ADVANCE_BLOCK();
325  }
326  break;
327 
328  /* 8-color block encoding */
329  case 0xC0:
330  case 0xD0:
331  n_blocks = (opcode & 0x0F) + 1;
332 
333  /* figure out which color octet to use to paint the 8-color block */
334  if ((opcode & 0xF0) == 0xC0) {
335  /* fetch the next 8 colors from bytestream and store in next
336  * available entry in the color octet table */
337  for (i = 0; i < COCTET; i++) {
338  pixel = bytestream2_get_byte(gb);
339  color_table_index = COCTET * color_octet_index + i;
340  s->color_octets[color_table_index] = pixel;
341  }
342  /* this is the base index to use for this block */
343  color_table_index = COCTET * color_octet_index;
344  color_octet_index++;
345  /* wraparound */
346  if (color_octet_index == COLORS_PER_TABLE)
347  color_octet_index = 0;
348  } else
349  color_table_index = COCTET * bytestream2_get_byte(gb);
350 
351  while (n_blocks--) {
352  /*
353  For this input of 6 hex bytes:
354  01 23 45 67 89 AB
355  Mangle it to this output:
356  flags_a = xx012456, flags_b = xx89A37B
357  */
358  /* build the color flags */
359  int val1 = bytestream2_get_be16(gb);
360  int val2 = bytestream2_get_be16(gb);
361  int val3 = bytestream2_get_be16(gb);
362  color_flags_a = ((val1 & 0xFFF0) << 8) | (val2 >> 4);
363  color_flags_b = ((val3 & 0xFFF0) << 8) |
364  ((val1 & 0x0F) << 8) | ((val2 & 0x0F) << 4) | (val3 & 0x0F);
365 
366  color_flags = color_flags_a;
367  /* flag mask actually acts as a bit shift count here */
368  flag_mask = 21;
369  block_ptr = row_ptr + pixel_ptr;
370  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
371  /* reload flags at third row (iteration pixel_y == 2) */
372  if (pixel_y == 2) {
373  color_flags = color_flags_b;
374  flag_mask = 21;
375  }
376  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
377  pixel = color_table_index +
378  ((color_flags >> flag_mask) & 0x07);
379  flag_mask -= 3;
380  pixels[block_ptr++] = s->color_octets[pixel];
381  }
382  block_ptr += row_inc;
383  }
384  ADVANCE_BLOCK();
385  }
386  break;
387 
388  /* 16-color block encoding (every pixel is a different color) */
389  case 0xE0:
390  case 0xF0:
391  n_blocks = (opcode & 0x0F) + 1;
392 
393  while (n_blocks--) {
394  block_ptr = row_ptr + pixel_ptr;
395  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
396  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
397  pixels[block_ptr++] = bytestream2_get_byte(gb);
398  }
399  block_ptr += row_inc;
400  }
401  ADVANCE_BLOCK();
402  }
403  break;
404  }
405  }
406 
407  return 0;
408 }
409 
411 {
412  SmcContext *s = avctx->priv_data;
413 
414  s->avctx = avctx;
415  avctx->pix_fmt = AV_PIX_FMT_PAL8;
416 
417  s->frame = av_frame_alloc();
418  if (!s->frame)
419  return AVERROR(ENOMEM);
420 
421  return 0;
422 }
423 
424 static int smc_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
425  int *got_frame, AVPacket *avpkt)
426 {
427  const uint8_t *buf = avpkt->data;
428  int buf_size = avpkt->size;
429  SmcContext *s = avctx->priv_data;
430  GetByteContext gb;
431  int ret;
432  int total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
433 
434  if (total_blocks / 1024 > avpkt->size)
435  return AVERROR_INVALIDDATA;
436 
437  if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
438  return ret;
439 
440 #if FF_API_PALETTE_HAS_CHANGED
442  s->frame->palette_has_changed =
443 #endif
444  ff_copy_palette(s->pal, avpkt, avctx);
445 #if FF_API_PALETTE_HAS_CHANGED
447 #endif
448 
449  bytestream2_init(&gb, buf, buf_size);
450  ret = smc_decode_stream(s, &gb);
451  if (ret < 0)
452  return ret;
453 
454  *got_frame = 1;
455  if ((ret = av_frame_ref(rframe, s->frame)) < 0)
456  return ret;
457 
458  /* always report that the buffer was completely consumed */
459  return buf_size;
460 }
461 
463 {
464  SmcContext *s = avctx->priv_data;
465 
466  av_frame_free(&s->frame);
467 
468  return 0;
469 }
470 
472  .p.name = "smc",
473  CODEC_LONG_NAME("QuickTime Graphics (SMC)"),
474  .p.type = AVMEDIA_TYPE_VIDEO,
475  .p.id = AV_CODEC_ID_SMC,
476  .priv_data_size = sizeof(SmcContext),
478  .close = smc_decode_end,
480  .p.capabilities = AV_CODEC_CAP_DR1,
481 };
FF_ENABLE_DEPRECATION_WARNINGS
#define FF_ENABLE_DEPRECATION_WARNINGS
Definition: internal.h:73
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SmcContext
Definition: smc.c:44
GET_BLOCK_COUNT
#define GET_BLOCK_COUNT()
Definition: smc.c:57
GetByteContext
Definition: bytestream.h:33
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:100
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:340
AVPacket::data
uint8_t * data
Definition: packet.h:491
FFCodec
Definition: codec_internal.h:127
SmcContext::frame
AVFrame * frame
Definition: smc.c:47
bytestream2_skip
static av_always_inline void bytestream2_skip(GetByteContext *g, unsigned int size)
Definition: bytestream.h:168
SmcContext::pal
uint32_t pal[256]
Definition: smc.c:54
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:131
smc_decode_frame
static int smc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, AVPacket *avpkt)
Definition: smc.c:424
smc_decode_init
static av_cold int smc_decode_init(AVCodecContext *avctx)
Definition: smc.c:410
AV_CODEC_ID_SMC
@ AV_CODEC_ID_SMC
Definition: codec_id.h:101
ADVANCE_BLOCK
#define ADVANCE_BLOCK()
Definition: smc.c:60
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:88
SmcContext::avctx
AVCodecContext * avctx
Definition: smc.c:46
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
av_cold
#define av_cold
Definition: attributes.h:90
width
#define width
FF_CODEC_DECODE_CB
#define FF_CODEC_DECODE_CB(func)
Definition: codec_internal.h:306
s
#define s(width, name)
Definition: cbs_vp9.c:198
CPAIR
#define CPAIR
Definition: smc.c:38
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts_bsf.c:365
decode.h
CQUAD
#define CQUAD
Definition: smc.c:39
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:272
if
if(ret)
Definition: filter_design.txt:179
pixel
uint8_t pixel
Definition: tiny_ssim.c:41
AVPALETTE_SIZE
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
bytestream2_get_bytes_left
static av_always_inline int bytestream2_get_bytes_left(GetByteContext *g)
Definition: bytestream.h:158
AV_CODEC_CAP_DR1
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() or get_encode_buffer() for allocating buffers and supports custom allocators.
Definition: codec.h:52
AVPacket::size
int size
Definition: packet.h:492
bytestream2_size
static av_always_inline int bytestream2_size(GetByteContext *g)
Definition: bytestream.h:202
av_frame_ref
int av_frame_ref(AVFrame *dst, const AVFrame *src)
Set up a new reference to the data described by the source frame.
Definition: frame.c:361
codec_internal.h
SmcContext::color_quads
uint8_t color_quads[COLORS_PER_TABLE *CQUAD]
Definition: smc.c:51
height
#define height
ff_smc_decoder
const FFCodec ff_smc_decoder
Definition: smc.c:471
smc_decode_stream
static int smc_decode_stream(SmcContext *s, GetByteContext *gb)
Definition: smc.c:76
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:255
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:194
AVCodecContext::pix_fmt
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:658
avcodec.h
stride
#define stride
Definition: h264pred_template.c:537
AV_PIX_FMT_PAL8
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:77
ff_reget_buffer
int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
Identical in function to ff_get_buffer(), except it reuses the existing buffer if available.
Definition: decode.c:1735
ret
ret
Definition: filter_design.txt:187
SmcContext::color_pairs
uint8_t color_pairs[COLORS_PER_TABLE *CPAIR]
Definition: smc.c:50
AVCodecContext
main external API structure.
Definition: avcodec.h:441
COCTET
#define COCTET
Definition: smc.c:40
COLORS_PER_TABLE
#define COLORS_PER_TABLE
Definition: smc.c:42
FF_DISABLE_DEPRECATION_WARNINGS
#define FF_DISABLE_DEPRECATION_WARNINGS
Definition: internal.h:72
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
AVPacket
This structure stores compressed data.
Definition: packet.h:468
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:468
bytestream.h
bytestream2_init
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition: bytestream.h:137
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
ff_copy_palette
int ff_copy_palette(void *dst, const AVPacket *src, void *logctx)
Check whether the side-data of src contains a palette of size AVPALETTE_SIZE; if so,...
Definition: decode.c:1823
smc_decode_end
static av_cold int smc_decode_end(AVCodecContext *avctx)
Definition: smc.c:462
SmcContext::color_octets
uint8_t color_octets[COLORS_PER_TABLE *COCTET]
Definition: smc.c:52