xref: /third_party/ffmpeg/libavcodec/smcenc.c (revision cabdff1a)
1/*
2 * QuickTime Graphics (SMC) Video Encoder
3 * Copyright (c) 2021 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 smcenc.c
24 * QT SMC Video Encoder by Paul B. Mahol
25 */
26
27#include "libavutil/common.h"
28
29#include "avcodec.h"
30#include "codec_internal.h"
31#include "encode.h"
32#include "bytestream.h"
33
34#define CPAIR 2
35#define CQUAD 4
36#define COCTET 8
37
38#define COLORS_PER_TABLE 256
39
40typedef struct SMCContext {
41    AVFrame *prev_frame;    // buffer for previous source frame
42
43    uint8_t mono_value;
44    int nb_distinct;
45    int next_nb_distinct;
46    uint8_t distinct_values[16];
47    uint8_t next_distinct_values[16];
48
49    uint8_t color_pairs[COLORS_PER_TABLE][CPAIR];
50    uint8_t color_quads[COLORS_PER_TABLE][CQUAD];
51    uint8_t color_octets[COLORS_PER_TABLE][COCTET];
52
53    int key_frame;
54} SMCContext;
55
56#define ADVANCE_BLOCK(pixel_ptr, row_ptr, nb_blocks) \
57{ \
58    for (int block = 0; block < nb_blocks && pixel_ptr && row_ptr; block++) { \
59        pixel_ptr += 4; \
60        if (pixel_ptr - row_ptr >= width) \
61        { \
62            row_ptr += stride * 4; \
63            pixel_ptr = row_ptr; \
64            cur_y += 4; \
65        } \
66    } \
67}
68
69static int smc_cmp_values(const void *a, const void *b)
70{
71    const uint8_t *aa = a, *bb = b;
72
73    return FFDIFFSIGN(aa[0], bb[0]);
74}
75
76static int count_distinct_items(const uint8_t *block_values,
77                                uint8_t *distinct_values,
78                                int size)
79{
80    int n = 1;
81
82    distinct_values[0] = block_values[0];
83    for (int i = 1; i < size; i++) {
84        if (block_values[i] != block_values[i-1]) {
85            distinct_values[n] = block_values[i];
86            n++;
87        }
88    }
89
90    return n;
91}
92
93#define CACHE_PAIR(x) \
94    (s->color_pairs[i][0] == distinct_values[x] || \
95     s->color_pairs[i][1] == distinct_values[x])
96
97#define CACHE_QUAD(x) \
98    (s->color_quads[i][0] == distinct_values[x] || \
99     s->color_quads[i][1] == distinct_values[x] || \
100     s->color_quads[i][2] == distinct_values[x] || \
101     s->color_quads[i][3] == distinct_values[x])
102
103#define CACHE_OCTET(x) \
104    (s->color_octets[i][0] == distinct_values[x] || \
105     s->color_octets[i][1] == distinct_values[x] || \
106     s->color_octets[i][2] == distinct_values[x] || \
107     s->color_octets[i][3] == distinct_values[x] || \
108     s->color_octets[i][4] == distinct_values[x] || \
109     s->color_octets[i][5] == distinct_values[x] || \
110     s->color_octets[i][6] == distinct_values[x] || \
111     s->color_octets[i][7] == distinct_values[x])
112
113static void smc_encode_stream(SMCContext *s, const AVFrame *frame,
114                              PutByteContext *pb)
115{
116    const uint8_t *src_pixels = (const uint8_t *)frame->data[0];
117    const int stride = frame->linesize[0];
118    const uint8_t *prev_pixels = (const uint8_t *)s->prev_frame->data[0];
119    uint8_t *distinct_values = s->distinct_values;
120    const uint8_t *pixel_ptr, *row_ptr;
121    const int height = frame->height;
122    const int width = frame->width;
123    uint8_t block_values[16];
124    int block_counter = 0;
125    int color_pair_index = 0;
126    int color_quad_index = 0;
127    int color_octet_index = 0;
128    int color_table_index;  /* indexes to color pair, quad, or octet tables */
129    int total_blocks;
130    int cur_y = 0;
131
132    memset(s->color_pairs, 0, sizeof(s->color_pairs));
133    memset(s->color_quads, 0, sizeof(s->color_quads));
134    memset(s->color_octets, 0, sizeof(s->color_octets));
135
136    /* Number of 4x4 blocks in frame. */
137    total_blocks = ((width + 3) / 4) * ((height + 3) / 4);
138
139    pixel_ptr = row_ptr = src_pixels;
140
141    while (block_counter < total_blocks) {
142        const uint8_t *xpixel_ptr = pixel_ptr;
143        const uint8_t *xrow_ptr = row_ptr;
144        int intra_skip_blocks = 0;
145        int inter_skip_blocks = 0;
146        int coded_distinct = 0;
147        int coded_blocks = 0;
148        int cache_index;
149        int distinct = 0;
150        int blocks = 0;
151        int frame_y = cur_y;
152
153        while (prev_pixels && s->key_frame == 0 && block_counter + inter_skip_blocks < total_blocks) {
154            const int y_size = FFMIN(4, height - cur_y);
155            int compare = 0;
156
157            for (int y = 0; y < y_size; y++) {
158                const ptrdiff_t offset = pixel_ptr - src_pixels;
159                const uint8_t *prev_pixel_ptr = prev_pixels + offset;
160
161                compare |= memcmp(prev_pixel_ptr + y * stride, pixel_ptr + y * stride, 4);
162                if (compare)
163                    break;
164            }
165
166            if (compare)
167                break;
168
169            if (inter_skip_blocks >= 256)
170                break;
171            inter_skip_blocks++;
172
173            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
174        }
175
176        pixel_ptr = xpixel_ptr;
177        row_ptr = xrow_ptr;
178        cur_y = frame_y;
179
180        while (block_counter > 0 && block_counter + intra_skip_blocks < total_blocks) {
181            const int y_size = FFMIN(4, height - cur_y);
182            const ptrdiff_t offset = pixel_ptr - src_pixels;
183            const int sy = offset / stride;
184            const int sx = offset % stride;
185            const int ny = sx < 4 ? sy - 4 : sy;
186            const int nx = sx < 4 ? width - 4 : sx - 4;
187            const uint8_t *old_pixel_ptr = src_pixels + nx + ny * stride;
188            int compare = 0;
189
190            for (int y = 0; y < y_size; y++) {
191                compare |= memcmp(old_pixel_ptr + y * stride, pixel_ptr + y * stride, 4);
192                if (compare)
193                    break;
194            }
195
196            if (compare)
197                break;
198
199            if (intra_skip_blocks >= 256)
200                break;
201            intra_skip_blocks++;
202            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
203        }
204
205        pixel_ptr = xpixel_ptr;
206        row_ptr = xrow_ptr;
207        cur_y = frame_y;
208
209        while (block_counter + coded_blocks < total_blocks && coded_blocks < 256) {
210            const int y_size = FFMIN(4, height - cur_y);
211            for (int y = 0; y < y_size; y++)
212                memcpy(block_values + y * 4, pixel_ptr + y * stride, 4);
213
214            qsort(block_values, 16, sizeof(block_values[0]), smc_cmp_values);
215            s->next_nb_distinct = count_distinct_items(block_values, s->next_distinct_values, 16);
216            if (coded_blocks == 0) {
217                memcpy(distinct_values, s->next_distinct_values, sizeof(s->distinct_values));
218                s->nb_distinct = s->next_nb_distinct;
219            } else {
220                if (s->next_nb_distinct != s->nb_distinct ||
221                    memcmp(distinct_values, s->next_distinct_values, s->nb_distinct)) {
222                    break;
223                }
224            }
225            s->mono_value = block_values[0];
226
227            coded_distinct = s->nb_distinct;
228            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
229            coded_blocks++;
230            if (coded_distinct > 1 && coded_blocks >= 16)
231                break;
232        }
233
234        pixel_ptr = xpixel_ptr;
235        row_ptr = xrow_ptr;
236        cur_y = frame_y;
237
238        blocks = coded_blocks;
239        distinct = coded_distinct;
240
241        if (intra_skip_blocks > 0 && intra_skip_blocks >= inter_skip_blocks &&
242            intra_skip_blocks > 0) {
243            distinct = 17;
244            blocks = intra_skip_blocks;
245        }
246
247        if (intra_skip_blocks > 16 && intra_skip_blocks >= inter_skip_blocks &&
248            intra_skip_blocks > 0) {
249            distinct = 18;
250            blocks = intra_skip_blocks;
251        }
252
253        if (inter_skip_blocks > 0 && inter_skip_blocks > intra_skip_blocks &&
254            inter_skip_blocks > 0) {
255            distinct = 19;
256            blocks = inter_skip_blocks;
257        }
258
259        if (inter_skip_blocks > 16 && inter_skip_blocks > intra_skip_blocks &&
260            inter_skip_blocks > 0) {
261            distinct = 20;
262            blocks = inter_skip_blocks;
263        }
264
265        switch (distinct) {
266        case 1:
267            if (blocks <= 16) {
268                bytestream2_put_byte(pb, 0x60 | (blocks - 1));
269            } else {
270                bytestream2_put_byte(pb, 0x70);
271                bytestream2_put_byte(pb, blocks - 1);
272            }
273            bytestream2_put_byte(pb, s->mono_value);
274            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
275            break;
276        case 2:
277            cache_index = -1;
278            for (int i = 0; i < COLORS_PER_TABLE; i++) {
279                if (CACHE_PAIR(0) &&
280                    CACHE_PAIR(1)) {
281                    cache_index = i;
282                    break;
283                }
284            }
285
286            if (cache_index >= 0) {
287                bytestream2_put_byte(pb, 0x90 | (blocks - 1));
288                bytestream2_put_byte(pb, cache_index);
289                color_table_index = cache_index;
290            } else {
291                bytestream2_put_byte(pb, 0x80 | (blocks - 1));
292
293                color_table_index = color_pair_index;
294                for (int i = 0; i < CPAIR; i++) {
295                    s->color_pairs[color_table_index][i] = distinct_values[i];
296                    bytestream2_put_byte(pb, distinct_values[i]);
297                }
298
299                color_pair_index++;
300                if (color_pair_index == COLORS_PER_TABLE)
301                    color_pair_index = 0;
302            }
303
304            for (int i = 0; i < blocks; i++) {
305                uint8_t value = s->color_pairs[color_table_index][1];
306                uint16_t flags = 0;
307                int shift = 15;
308
309                for (int y = 0; y < 4; y++) {
310                    for (int x = 0; x < 4; x++) {
311                        flags |= (value == pixel_ptr[x + y * stride]) << shift;
312                        shift--;
313                    }
314                }
315
316                bytestream2_put_be16(pb, flags);
317
318                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
319            }
320            break;
321        case 3:
322        case 4:
323            cache_index = -1;
324            for (int i = 0; i < COLORS_PER_TABLE; i++) {
325                if (CACHE_QUAD(0) &&
326                    CACHE_QUAD(1) &&
327                    CACHE_QUAD(2) &&
328                    CACHE_QUAD(3)) {
329                    cache_index = i;
330                    break;
331                }
332            }
333
334            if (cache_index >= 0) {
335                bytestream2_put_byte(pb, 0xB0 | (blocks - 1));
336                bytestream2_put_byte(pb, cache_index);
337                color_table_index = cache_index;
338            } else {
339                bytestream2_put_byte(pb, 0xA0 | (blocks - 1));
340
341                color_table_index = color_quad_index;
342                for (int i = 0; i < CQUAD; i++) {
343                    s->color_quads[color_table_index][i] = distinct_values[i];
344                    bytestream2_put_byte(pb, distinct_values[i]);
345                }
346
347                color_quad_index++;
348                if (color_quad_index == COLORS_PER_TABLE)
349                    color_quad_index = 0;
350            }
351
352            for (int i = 0; i < blocks; i++) {
353                uint32_t flags = 0;
354                uint8_t quad[4];
355                int shift = 30;
356
357                for (int k = 0; k < 4; k++)
358                    quad[k] = s->color_quads[color_table_index][k];
359
360                for (int y = 0; y < 4; y++) {
361                    for (int x = 0; x < 4; x++) {
362                        int pixel = pixel_ptr[x + y * stride];
363                        uint32_t idx = 0;
364
365                        for (int w = 0; w < CQUAD; w++) {
366                            if (quad[w] == pixel) {
367                                idx = w;
368                                break;
369                            }
370                        }
371
372                        flags |= idx << shift;
373                        shift -= 2;
374                    }
375                }
376
377                bytestream2_put_be32(pb, flags);
378
379                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
380            }
381            break;
382        case 5:
383        case 6:
384        case 7:
385        case 8:
386            cache_index = -1;
387            for (int i = 0; i < COLORS_PER_TABLE; i++) {
388                if (CACHE_OCTET(0) &&
389                    CACHE_OCTET(1) &&
390                    CACHE_OCTET(2) &&
391                    CACHE_OCTET(3) &&
392                    CACHE_OCTET(4) &&
393                    CACHE_OCTET(5) &&
394                    CACHE_OCTET(6) &&
395                    CACHE_OCTET(7)) {
396                    cache_index = i;
397                    break;
398                }
399            }
400
401            if (cache_index >= 0) {
402                bytestream2_put_byte(pb, 0xD0 | (blocks - 1));
403                bytestream2_put_byte(pb, cache_index);
404                color_table_index = cache_index;
405            } else {
406                bytestream2_put_byte(pb, 0xC0 | (blocks - 1));
407
408                color_table_index = color_octet_index;
409                for (int i = 0; i < COCTET; i++) {
410                    s->color_octets[color_table_index][i] = distinct_values[i];
411                    bytestream2_put_byte(pb, distinct_values[i]);
412                }
413
414                color_octet_index++;
415                if (color_octet_index == COLORS_PER_TABLE)
416                    color_octet_index = 0;
417            }
418
419            for (int i = 0; i < blocks; i++) {
420                uint64_t flags = 0;
421                uint8_t octet[8];
422                int shift = 45;
423
424                for (int k = 0; k < 8; k++)
425                    octet[k] = s->color_octets[color_table_index][k];
426
427                for (int y = 0; y < 4; y++) {
428                    for (int x = 0; x < 4; x++) {
429                        int pixel = pixel_ptr[x + y * stride];
430                        uint64_t idx = 0;
431
432                        for (int w = 0; w < COCTET; w++) {
433                            if (octet[w] == pixel) {
434                                idx = w;
435                                break;
436                            }
437                        }
438
439                        flags |= idx << shift;
440                        shift -= 3;
441                    }
442                }
443
444                bytestream2_put_be16(pb, ((flags >> 32) & 0xFFF0) | ((flags >> 8) & 0xF));
445                bytestream2_put_be16(pb, ((flags >> 20) & 0xFFF0) | ((flags >> 4) & 0xF));
446                bytestream2_put_be16(pb, ((flags >>  8) & 0xFFF0) | ((flags >> 0) & 0xF));
447
448                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
449            }
450            break;
451        default:
452            bytestream2_put_byte(pb, 0xE0 | (blocks - 1));
453            for (int i = 0; i < blocks; i++) {
454                for (int y = 0; y < 4; y++) {
455                    for (int x = 0; x < 4; x++)
456                        bytestream2_put_byte(pb, pixel_ptr[x + y * stride]);
457                }
458
459                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
460            }
461            break;
462        case 17:
463            bytestream2_put_byte(pb, 0x20 | (blocks - 1));
464            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
465            break;
466        case 18:
467            bytestream2_put_byte(pb, 0x30);
468            bytestream2_put_byte(pb, blocks - 1);
469            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
470            break;
471        case 19:
472            bytestream2_put_byte(pb, 0x00 | (blocks - 1));
473            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
474            break;
475        case 20:
476            bytestream2_put_byte(pb, 0x10);
477            bytestream2_put_byte(pb, blocks - 1);
478            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
479            break;
480        }
481
482        block_counter += blocks;
483    }
484}
485
486static int smc_encode_init(AVCodecContext *avctx)
487{
488    SMCContext *s = avctx->priv_data;
489
490    avctx->bits_per_coded_sample = 8;
491
492    s->prev_frame = av_frame_alloc();
493    if (!s->prev_frame)
494        return AVERROR(ENOMEM);
495
496    return 0;
497}
498
499static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
500                                const AVFrame *frame, int *got_packet)
501{
502    SMCContext *s = avctx->priv_data;
503    const AVFrame *pict = frame;
504    PutByteContext pb;
505    uint8_t *pal;
506    int ret;
507
508    ret = ff_alloc_packet(avctx, pkt, 8LL * avctx->height * avctx->width);
509    if (ret < 0)
510        return ret;
511
512    if (avctx->gop_size == 0 || !s->prev_frame->data[0] ||
513        (avctx->frame_number % avctx->gop_size) == 0) {
514        s->key_frame = 1;
515    } else {
516        s->key_frame = 0;
517    }
518
519    bytestream2_init_writer(&pb, pkt->data, pkt->size);
520
521    bytestream2_put_be32(&pb, 0x00);
522
523    pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
524    if (!pal)
525        return AVERROR(ENOMEM);
526    memcpy(pal, frame->data[1], AVPALETTE_SIZE);
527
528    smc_encode_stream(s, pict, &pb);
529
530    av_shrink_packet(pkt, bytestream2_tell_p(&pb));
531
532    pkt->data[0] = 0x0;
533
534    // write chunk length
535    AV_WB24(pkt->data + 1, pkt->size);
536
537    av_frame_unref(s->prev_frame);
538    ret = av_frame_ref(s->prev_frame, frame);
539    if (ret < 0) {
540        av_log(avctx, AV_LOG_ERROR, "cannot add reference\n");
541        return ret;
542    }
543
544    if (s->key_frame)
545        pkt->flags |= AV_PKT_FLAG_KEY;
546
547    *got_packet = 1;
548
549    return 0;
550}
551
552static int smc_encode_end(AVCodecContext *avctx)
553{
554    SMCContext *s = (SMCContext *)avctx->priv_data;
555
556    av_frame_free(&s->prev_frame);
557
558    return 0;
559}
560
561const FFCodec ff_smc_encoder = {
562    .p.name         = "smc",
563    .p.long_name    = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"),
564    .p.type         = AVMEDIA_TYPE_VIDEO,
565    .p.id           = AV_CODEC_ID_SMC,
566    .priv_data_size = sizeof(SMCContext),
567    .init           = smc_encode_init,
568    FF_CODEC_ENCODE_CB(smc_encode_frame),
569    .close          = smc_encode_end,
570    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
571    .p.pix_fmts     = (const enum AVPixelFormat[]) { AV_PIX_FMT_PAL8,
572                                                     AV_PIX_FMT_NONE},
573};
574