• 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 "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 
50     AVCodecContext *avctx;
51     AVFrame *frame;
52 
53     GetByteContext gb;
54 
55     /* SMC color tables */
56     uint8_t color_pairs[COLORS_PER_TABLE * CPAIR];
57     uint8_t color_quads[COLORS_PER_TABLE * CQUAD];
58     uint8_t color_octets[COLORS_PER_TABLE * COCTET];
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 
smc_decode_stream(SmcContext * s)82 static int smc_decode_stream(SmcContext *s)
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 
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,AVFrame * rframe,int * got_frame,AVPacket * avpkt)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 
450     ret = smc_decode_stream(s);
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 
smc_decode_end(AVCodecContext * avctx)462 static av_cold int smc_decode_end(AVCodecContext *avctx)
463 {
464     SmcContext *s = avctx->priv_data;
465 
466     av_frame_free(&s->frame);
467 
468     return 0;
469 }
470 
471 const FFCodec ff_smc_decoder = {
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),
477     .init           = smc_decode_init,
478     .close          = smc_decode_end,
479     FF_CODEC_DECODE_CB(smc_decode_frame),
480     .p.capabilities = AV_CODEC_CAP_DR1,
481     .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
482 };
483