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