FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 *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 
136  opcode = bytestream2_get_byte(&s->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_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
155  opcode & 0xF0);
156  return;
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_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
189  opcode & 0xF0);
190  return;
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(&s->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(&s->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(&s->gb);
267 
268  while (n_blocks--) {
269  color_flags = bytestream2_get_be16(&s->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(&s->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(&s->gb);
309 
310  while (n_blocks--) {
311  color_flags = bytestream2_get_be32(&s->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(&s->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(&s->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(&s->gb);
360  int val2 = bytestream2_get_be16(&s->gb);
361  int val3 = bytestream2_get_be16(&s->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  n_blocks = (opcode & 0x0F) + 1;
391 
392  while (n_blocks--) {
393  block_ptr = row_ptr + pixel_ptr;
394  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
395  for (pixel_x = 0; pixel_x < 4; pixel_x++) {
396  pixels[block_ptr++] = bytestream2_get_byte(&s->gb);
397  }
398  block_ptr += row_inc;
399  }
400  ADVANCE_BLOCK();
401  }
402  break;
403 
404  case 0xF0:
405  avpriv_request_sample(s->avctx, "0xF0 opcode");
406  break;
407  }
408  }
409 
410  return;
411 }
412 
414 {
415  SmcContext *s = avctx->priv_data;
416 
417  s->avctx = avctx;
418  avctx->pix_fmt = AV_PIX_FMT_PAL8;
419 
420  s->frame = av_frame_alloc();
421  if (!s->frame)
422  return AVERROR(ENOMEM);
423 
424  return 0;
425 }
426 
428  void *data, int *got_frame,
429  AVPacket *avpkt)
430 {
431  const uint8_t *buf = avpkt->data;
432  int buf_size = avpkt->size;
433  SmcContext *s = avctx->priv_data;
434  const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
435  int ret;
436 
437  bytestream2_init(&s->gb, buf, buf_size);
438 
439  if ((ret = ff_reget_buffer(avctx, s->frame)) < 0)
440  return ret;
441 
442  if (pal) {
443  s->frame->palette_has_changed = 1;
444  memcpy(s->pal, pal, AVPALETTE_SIZE);
445  }
446 
448 
449  *got_frame = 1;
450  if ((ret = av_frame_ref(data, s->frame)) < 0)
451  return ret;
452 
453  /* always report that the buffer was completely consumed */
454  return buf_size;
455 }
456 
458 {
459  SmcContext *s = avctx->priv_data;
460 
461  av_frame_free(&s->frame);
462 
463  return 0;
464 }
465 
467  .name = "smc",
468  .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"),
469  .type = AVMEDIA_TYPE_VIDEO,
470  .id = AV_CODEC_ID_SMC,
471  .priv_data_size = sizeof(SmcContext),
475  .capabilities = CODEC_CAP_DR1,
476 };