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