• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
48     AVCodecContext *avctx;
49     AVFrame *frame;
50 
51     GetByteContext gb;
52 
53     /* SMC color tables */
54     unsigned char color_pairs[COLORS_PER_TABLE * CPAIR];
55     unsigned char color_quads[COLORS_PER_TABLE * CQUAD];
56     unsigned char color_octets[COLORS_PER_TABLE * COCTET];
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 
smc_decode_stream(SmcContext * s)80 static void smc_decode_stream(SmcContext *s)
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 
smc_decode_init(AVCodecContext * avctx)417 static av_cold int smc_decode_init(AVCodecContext *avctx)
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 
smc_decode_frame(AVCodecContext * avctx,void * data,int * got_frame,AVPacket * avpkt)431 static int smc_decode_frame(AVCodecContext *avctx,
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     buffer_size_t 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)) < 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 
458     smc_decode_stream(s);
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 
smc_decode_end(AVCodecContext * avctx)468 static av_cold int smc_decode_end(AVCodecContext *avctx)
469 {
470     SmcContext *s = avctx->priv_data;
471 
472     av_frame_free(&s->frame);
473 
474     return 0;
475 }
476 
477 AVCodec ff_smc_decoder = {
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),
483     .init           = smc_decode_init,
484     .close          = smc_decode_end,
485     .decode         = smc_decode_frame,
486     .capabilities   = AV_CODEC_CAP_DR1,
487     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
488 };
489