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 <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "libavutil/intreadwrite.h"
36 #include "avcodec.h"
37 #include "bytestream.h"
38 #include "decode.h"
39 #include "internal.h"
40 
41 #define CPAIR 2
42 #define CQUAD 4
43 #define COCTET 8
44 
45 #define COLORS_PER_TABLE 256
46 
47 typedef struct SmcContext {
48 
51 
53 
54  /* SMC color tables */
58 
59  uint32_t pal[256];
60 } SmcContext;
61 
62 #define GET_BLOCK_COUNT() \
63  (opcode & 0x10) ? (1 + bytestream2_get_byte(gb)) : 1 + (opcode & 0x0F);
64 
65 #define ADVANCE_BLOCK() \
66 { \
67  pixel_ptr += 4; \
68  if (pixel_ptr >= width) \
69  { \
70  pixel_ptr = 0; \
71  row_ptr += stride * 4; \
72  } \
73  total_blocks--; \
74  if (total_blocks < !!n_blocks) \
75  { \
76  av_log(s->avctx, AV_LOG_ERROR, "block counter just went negative (this should not happen)\n"); \
77  return AVERROR_INVALIDDATA; \
78  } \
79 }
80 
82 {
83  GetByteContext *gb = &s->gb;
84  int width = s->avctx->width;
85  int height = s->avctx->height;
86  int stride = s->frame->linesize[0];
87  int i;
88  int chunk_size;
89  int buf_size = bytestream2_size(gb);
90  uint8_t opcode;
91  int n_blocks;
92  unsigned int color_flags;
93  unsigned int color_flags_a;
94  unsigned int color_flags_b;
95  unsigned int flag_mask;
96 
97  uint8_t * const pixels = s->frame->data[0];
98 
99  int image_size = height * s->frame->linesize[0];
100  int row_ptr = 0;
101  int pixel_ptr = 0;
102  int pixel_x, pixel_y;
103  int row_inc = stride - 4;
104  int block_ptr;
105  int prev_block_ptr;
106  int prev_block_ptr1, prev_block_ptr2;
107  int prev_block_flag;
108  int total_blocks;
109  int color_table_index; /* indexes to color pair, quad, or octet tables */
110  int pixel;
111 
112  int color_pair_index = 0;
113  int color_quad_index = 0;
114  int color_octet_index = 0;
115 
116  /* make the palette available */
117  memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE);
118 
119  bytestream2_skip(gb, 1);
120  chunk_size = bytestream2_get_be24(gb);
121  if (chunk_size != buf_size)
122  av_log(s->avctx, AV_LOG_WARNING, "MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n",
123  chunk_size, buf_size);
124 
125  chunk_size = buf_size;
126  total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
127 
128  /* traverse through the blocks */
129  while (total_blocks) {
130  /* sanity checks */
131  /* make sure the row pointer hasn't gone wild */
132  if (row_ptr >= image_size) {
133  av_log(s->avctx, AV_LOG_ERROR, "just went out of bounds (row ptr = %d, height = %d)\n",
134  row_ptr, image_size);
135  return AVERROR_INVALIDDATA;
136  }
137  if (bytestream2_get_bytes_left(gb) < 1) {
138  av_log(s->avctx, AV_LOG_ERROR, "input too small\n");
139  return AVERROR_INVALIDDATA;
140  }
141 
142  opcode = bytestream2_get_byteu(gb);
143  switch (opcode & 0xF0) {
144  /* skip n blocks */
145  case 0x00:
146  case 0x10:
147  n_blocks = GET_BLOCK_COUNT();
148  while (n_blocks--) {
149  ADVANCE_BLOCK();
150  }
151  break;
152 
153  /* repeat last block n times */
154  case 0x20:
155  case 0x30:
156  n_blocks = GET_BLOCK_COUNT();
157 
158  /* sanity check */
159  if ((row_ptr == 0) && (pixel_ptr == 0)) {
160  av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
161  opcode & 0xF0);
162  return AVERROR_INVALIDDATA;
163  }
164 
165  /* figure out where the previous block started */
166  if (pixel_ptr == 0)
167  prev_block_ptr1 =
168  (row_ptr - s->avctx->width * 4) + s->avctx->width - 4;
169  else
170  prev_block_ptr1 = row_ptr + pixel_ptr - 4;
171 
172  while (n_blocks--) {
173  block_ptr = row_ptr + pixel_ptr;
174  prev_block_ptr = prev_block_ptr1;
175  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
176  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
177  pixels[block_ptr++] = pixels[prev_block_ptr++];
178  }
179  block_ptr += row_inc;
180  prev_block_ptr += row_inc;
181  }
182  ADVANCE_BLOCK();
183  }
184  break;
185 
186  /* repeat previous pair of blocks n times */
187  case 0x40:
188  case 0x50:
189  n_blocks = GET_BLOCK_COUNT();
190  n_blocks *= 2;
191 
192  /* sanity check */
193  if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) {
194  av_log(s->avctx, AV_LOG_ERROR, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
195  opcode & 0xF0);
196  return AVERROR_INVALIDDATA;
197  }
198 
199  /* figure out where the previous 2 blocks started */
200  if (pixel_ptr == 0)
201  prev_block_ptr1 = (row_ptr - s->avctx->width * 4) +
202  s->avctx->width - 4 * 2;
203  else if (pixel_ptr == 4)
204  prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc;
205  else
206  prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2;
207 
208  if (pixel_ptr == 0)
209  prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc;
210  else
211  prev_block_ptr2 = row_ptr + pixel_ptr - 4;
212 
213  prev_block_flag = 0;
214  while (n_blocks--) {
215  block_ptr = row_ptr + pixel_ptr;
216  if (prev_block_flag)
217  prev_block_ptr = prev_block_ptr2;
218  else
219  prev_block_ptr = prev_block_ptr1;
220  prev_block_flag = !prev_block_flag;
221 
222  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
223  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
224  pixels[block_ptr++] = pixels[prev_block_ptr++];
225  }
226  block_ptr += row_inc;
227  prev_block_ptr += row_inc;
228  }
229  ADVANCE_BLOCK();
230  }
231  break;
232 
233  /* 1-color block encoding */
234  case 0x60:
235  case 0x70:
236  n_blocks = GET_BLOCK_COUNT();
237  pixel = bytestream2_get_byte(gb);
238 
239  while (n_blocks--) {
240  block_ptr = row_ptr + pixel_ptr;
241  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
242  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
243  pixels[block_ptr++] = pixel;
244  }
245  block_ptr += row_inc;
246  }
247  ADVANCE_BLOCK();
248  }
249  break;
250 
251  /* 2-color block encoding */
252  case 0x80:
253  case 0x90:
254  n_blocks = (opcode & 0x0F) + 1;
255 
256  /* figure out which color pair to use to paint the 2-color block */
257  if ((opcode & 0xF0) == 0x80) {
258  /* fetch the next 2 colors from bytestream and store in next
259  * available entry in the color pair table */
260  for (i = 0; i < CPAIR; i++) {
261  pixel = bytestream2_get_byte(gb);
262  color_table_index = CPAIR * color_pair_index + i;
263  s->color_pairs[color_table_index] = pixel;
264  }
265  /* this is the base index to use for this block */
266  color_table_index = CPAIR * color_pair_index;
267  color_pair_index++;
268  /* wraparound */
269  if (color_pair_index == COLORS_PER_TABLE)
270  color_pair_index = 0;
271  } else
272  color_table_index = CPAIR * bytestream2_get_byte(gb);
273 
274  while (n_blocks--) {
275  color_flags = bytestream2_get_be16(gb);
276  flag_mask = 0x8000;
277  block_ptr = row_ptr + pixel_ptr;
278  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
279  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
280  if (color_flags & flag_mask)
281  pixel = color_table_index + 1;
282  else
283  pixel = color_table_index;
284  flag_mask >>= 1;
285  pixels[block_ptr++] = s->color_pairs[pixel];
286  }
287  block_ptr += row_inc;
288  }
289  ADVANCE_BLOCK();
290  }
291  break;
292 
293  /* 4-color block encoding */
294  case 0xA0:
295  case 0xB0:
296  n_blocks = (opcode & 0x0F) + 1;
297 
298  /* figure out which color quad to use to paint the 4-color block */
299  if ((opcode & 0xF0) == 0xA0) {
300  /* fetch the next 4 colors from bytestream and store in next
301  * available entry in the color quad table */
302  for (i = 0; i < CQUAD; i++) {
303  pixel = bytestream2_get_byte(gb);
304  color_table_index = CQUAD * color_quad_index + i;
305  s->color_quads[color_table_index] = pixel;
306  }
307  /* this is the base index to use for this block */
308  color_table_index = CQUAD * color_quad_index;
309  color_quad_index++;
310  /* wraparound */
311  if (color_quad_index == COLORS_PER_TABLE)
312  color_quad_index = 0;
313  } else
314  color_table_index = CQUAD * bytestream2_get_byte(gb);
315 
316  while (n_blocks--) {
317  color_flags = bytestream2_get_be32(gb);
318  /* flag mask actually acts as a bit shift count here */
319  flag_mask = 30;
320  block_ptr = row_ptr + pixel_ptr;
321  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
322  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
323  pixel = color_table_index +
324  ((color_flags >> flag_mask) & 0x03);
325  flag_mask -= 2;
326  pixels[block_ptr++] = s->color_quads[pixel];
327  }
328  block_ptr += row_inc;
329  }
330  ADVANCE_BLOCK();
331  }
332  break;
333 
334  /* 8-color block encoding */
335  case 0xC0:
336  case 0xD0:
337  n_blocks = (opcode & 0x0F) + 1;
338 
339  /* figure out which color octet to use to paint the 8-color block */
340  if ((opcode & 0xF0) == 0xC0) {
341  /* fetch the next 8 colors from bytestream and store in next
342  * available entry in the color octet table */
343  for (i = 0; i < COCTET; i++) {
344  pixel = bytestream2_get_byte(gb);
345  color_table_index = COCTET * color_octet_index + i;
346  s->color_octets[color_table_index] = pixel;
347  }
348  /* this is the base index to use for this block */
349  color_table_index = COCTET * color_octet_index;
350  color_octet_index++;
351  /* wraparound */
352  if (color_octet_index == COLORS_PER_TABLE)
353  color_octet_index = 0;
354  } else
355  color_table_index = COCTET * bytestream2_get_byte(gb);
356 
357  while (n_blocks--) {
358  /*
359  For this input of 6 hex bytes:
360  01 23 45 67 89 AB
361  Mangle it to this output:
362  flags_a = xx012456, flags_b = xx89A37B
363  */
364  /* build the color flags */
365  int val1 = bytestream2_get_be16(gb);
366  int val2 = bytestream2_get_be16(gb);
367  int val3 = bytestream2_get_be16(gb);
368  color_flags_a = ((val1 & 0xFFF0) << 8) | (val2 >> 4);
369  color_flags_b = ((val3 & 0xFFF0) << 8) |
370  ((val1 & 0x0F) << 8) | ((val2 & 0x0F) << 4) | (val3 & 0x0F);
371 
372  color_flags = color_flags_a;
373  /* flag mask actually acts as a bit shift count here */
374  flag_mask = 21;
375  block_ptr = row_ptr + pixel_ptr;
376  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
377  /* reload flags at third row (iteration pixel_y == 2) */
378  if (pixel_y == 2) {
379  color_flags = color_flags_b;
380  flag_mask = 21;
381  }
382  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
383  pixel = color_table_index +
384  ((color_flags >> flag_mask) & 0x07);
385  flag_mask -= 3;
386  pixels[block_ptr++] = s->color_octets[pixel];
387  }
388  block_ptr += row_inc;
389  }
390  ADVANCE_BLOCK();
391  }
392  break;
393 
394  /* 16-color block encoding (every pixel is a different color) */
395  case 0xE0:
396  case 0xF0:
397  n_blocks = (opcode & 0x0F) + 1;
398 
399  while (n_blocks--) {
400  block_ptr = row_ptr + pixel_ptr;
401  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
402  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
403  pixels[block_ptr++] = bytestream2_get_byte(gb);
404  }
405  block_ptr += row_inc;
406  }
407  ADVANCE_BLOCK();
408  }
409  break;
410  }
411  }
412 
413  return 0;
414 }
415 
417 {
418  SmcContext *s = avctx->priv_data;
419 
420  s->avctx = avctx;
421  avctx->pix_fmt = AV_PIX_FMT_PAL8;
422 
423  s->frame = av_frame_alloc();
424  if (!s->frame)
425  return AVERROR(ENOMEM);
426 
427  return 0;
428 }
429 
431  void *data, int *got_frame,
432  AVPacket *avpkt)
433 {
434  const uint8_t *buf = avpkt->data;
435  int buf_size = avpkt->size;
436  SmcContext *s = avctx->priv_data;
437  int ret;
438  int total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
439 
440  if (total_blocks / 1024 > avpkt->size)
441  return AVERROR_INVALIDDATA;
442 
443  bytestream2_init(&s->gb, buf, buf_size);
444 
445  if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
446  return ret;
447 
448  s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx);
449 
451  if (ret < 0)
452  return ret;
453 
454  *got_frame = 1;
455  if ((ret = av_frame_ref(data, 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  .name = "smc",
473  .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"),
474  .type = AVMEDIA_TYPE_VIDEO,
475  .id = AV_CODEC_ID_SMC,
476  .priv_data_size = sizeof(SmcContext),
478  .close = smc_decode_end,
480  .capabilities = AV_CODEC_CAP_DR1,
481  .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
482 };
AVCodec
AVCodec.
Definition: codec.h:202
stride
int stride
Definition: mace.c:144
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
FF_CODEC_CAP_INIT_THREADSAFE
#define FF_CODEC_CAP_INIT_THREADSAFE
The codec does not modify any global variables in the init function, allowing to call the init functi...
Definition: internal.h:42
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:47
GET_BLOCK_COUNT
#define GET_BLOCK_COUNT()
Definition: smc.c:62
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:109
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:317
ff_smc_decoder
const AVCodec ff_smc_decoder
Definition: smc.c:471
internal.h
AVPacket::data
uint8_t * data
Definition: packet.h:373
data
const char data[16]
Definition: mxf.c:143
SmcContext::frame
AVFrame * frame
Definition: smc.c:50
init
static int init
Definition: av_tx.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:59
smc_decode_init
static av_cold int smc_decode_init(AVCodecContext *avctx)
Definition: smc.c:416
AV_CODEC_ID_SMC
@ AV_CODEC_ID_SMC
Definition: codec_id.h:99
ADVANCE_BLOCK
#define ADVANCE_BLOCK()
Definition: smc.c:65
smc_decode_frame
static int smc_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt)
Definition: smc.c:430
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:97
SmcContext::avctx
AVCodecContext * avctx
Definition: smc.c:49
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
decode
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile)
Definition: decode_audio.c:71
width
#define width
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:257
CPAIR
#define CPAIR
Definition: smc.c:41
decode.h
CQUAD
#define CQUAD
Definition: smc.c:42
if
if(ret)
Definition: filter_design.txt:179
pixel
uint8_t pixel
Definition: tiny_ssim.c:42
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
SmcContext::gb
GetByteContext gb
Definition: smc.c:52
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:374
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
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:325
SmcContext::color_quads
uint8_t color_quads[COLORS_PER_TABLE *CQUAD]
Definition: smc.c:56
height
#define height
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:271
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:209
AVCodecContext::pix_fmt
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:593
avcodec.h
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:1759
ret
ret
Definition: filter_design.txt:187
SmcContext::color_pairs
uint8_t color_pairs[COLORS_PER_TABLE *CPAIR]
Definition: smc.c:55
AVCodecContext
main external API structure.
Definition: avcodec.h:383
COCTET
#define COCTET
Definition: smc.c:43
smc_decode_stream
static int smc_decode_stream(SmcContext *s)
Definition: smc.c:81
COLORS_PER_TABLE
#define COLORS_PER_TABLE
Definition: smc.c:45
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
AVPacket
This structure stores compressed data.
Definition: packet.h:350
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:410
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:28
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:1854
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:57