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