Frame counters can overflow relatively easily (INT_MAX number of frames is slightly more than 1 year for 60 fps content), so make sure we use 64 bit values for them. Also deprecate the old 32 bit frame_number attribute. Signed-off-by: Marton Balint <cus@passwd.hu>
		
			
				
	
	
		
			606 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			606 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * QuickTime Graphics (SMC) Video Encoder
 | 
						|
 * Copyright (c) 2021 The FFmpeg project
 | 
						|
 *
 | 
						|
 * This file is part of FFmpeg.
 | 
						|
 *
 | 
						|
 * FFmpeg is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * FFmpeg is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with FFmpeg; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * @file smcenc.c
 | 
						|
 * QT SMC Video Encoder by Paul B. Mahol
 | 
						|
 */
 | 
						|
 | 
						|
#include "libavutil/common.h"
 | 
						|
 | 
						|
#include "avcodec.h"
 | 
						|
#include "codec_internal.h"
 | 
						|
#include "encode.h"
 | 
						|
#include "bytestream.h"
 | 
						|
 | 
						|
#define CPAIR 2
 | 
						|
#define CQUAD 4
 | 
						|
#define COCTET 8
 | 
						|
 | 
						|
#define COLORS_PER_TABLE 256
 | 
						|
 | 
						|
typedef struct SMCContext {
 | 
						|
    AVFrame *prev_frame;    // buffer for previous source frame
 | 
						|
 | 
						|
    uint8_t mono_value;
 | 
						|
    int nb_distinct;
 | 
						|
    int next_nb_distinct;
 | 
						|
    uint8_t distinct_values[16];
 | 
						|
    uint8_t next_distinct_values[16];
 | 
						|
 | 
						|
    uint8_t color_pairs[COLORS_PER_TABLE][CPAIR];
 | 
						|
    uint8_t color_quads[COLORS_PER_TABLE][CQUAD];
 | 
						|
    uint8_t color_octets[COLORS_PER_TABLE][COCTET];
 | 
						|
 | 
						|
    int key_frame;
 | 
						|
} SMCContext;
 | 
						|
 | 
						|
#define ADVANCE_BLOCK(pixel_ptr, row_ptr, nb_blocks) \
 | 
						|
{ \
 | 
						|
    for (int block = 0; block < nb_blocks && pixel_ptr && row_ptr; block++) { \
 | 
						|
        pixel_ptr += 4; \
 | 
						|
        cur_x += 4; \
 | 
						|
        if (pixel_ptr - row_ptr >= width) \
 | 
						|
        { \
 | 
						|
            row_ptr += stride * 4; \
 | 
						|
            pixel_ptr = row_ptr; \
 | 
						|
            cur_y += 4; \
 | 
						|
            cur_x  = 0; \
 | 
						|
        } \
 | 
						|
    } \
 | 
						|
}
 | 
						|
 | 
						|
static int smc_cmp_values(const void *a, const void *b)
 | 
						|
{
 | 
						|
    const uint8_t *aa = a, *bb = b;
 | 
						|
 | 
						|
    return FFDIFFSIGN(aa[0], bb[0]);
 | 
						|
}
 | 
						|
 | 
						|
static int count_distinct_items(const uint8_t *block_values,
 | 
						|
                                uint8_t *distinct_values,
 | 
						|
                                int size)
 | 
						|
{
 | 
						|
    int n = 1;
 | 
						|
 | 
						|
    distinct_values[0] = block_values[0];
 | 
						|
    for (int i = 1; i < size; i++) {
 | 
						|
        if (block_values[i] != block_values[i-1]) {
 | 
						|
            distinct_values[n] = block_values[i];
 | 
						|
            n++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return n;
 | 
						|
}
 | 
						|
 | 
						|
#define CACHE_PAIR(x) \
 | 
						|
    (s->color_pairs[i][0] == distinct_values[x] || \
 | 
						|
     s->color_pairs[i][1] == distinct_values[x])
 | 
						|
 | 
						|
#define CACHE_QUAD(x) \
 | 
						|
    (s->color_quads[i][0] == distinct_values[x] || \
 | 
						|
     s->color_quads[i][1] == distinct_values[x] || \
 | 
						|
     s->color_quads[i][2] == distinct_values[x] || \
 | 
						|
     s->color_quads[i][3] == distinct_values[x])
 | 
						|
 | 
						|
#define CACHE_OCTET(x) \
 | 
						|
    (s->color_octets[i][0] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][1] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][2] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][3] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][4] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][5] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][6] == distinct_values[x] || \
 | 
						|
     s->color_octets[i][7] == distinct_values[x])
 | 
						|
 | 
						|
static void smc_encode_stream(SMCContext *s, const AVFrame *frame,
 | 
						|
                              PutByteContext *pb)
 | 
						|
{
 | 
						|
    const uint8_t *src_pixels = (const uint8_t *)frame->data[0];
 | 
						|
    const ptrdiff_t stride = frame->linesize[0];
 | 
						|
    const uint8_t *prev_pixels = (const uint8_t *)s->prev_frame->data[0];
 | 
						|
    const ptrdiff_t prev_stride = s->prev_frame->linesize[0];
 | 
						|
    uint8_t *distinct_values = s->distinct_values;
 | 
						|
    const uint8_t *pixel_ptr, *row_ptr;
 | 
						|
    const int height = frame->height;
 | 
						|
    const int width = frame->width;
 | 
						|
    int block_counter = 0;
 | 
						|
    int color_pair_index = 0;
 | 
						|
    int color_quad_index = 0;
 | 
						|
    int color_octet_index = 0;
 | 
						|
    int color_table_index;  /* indexes to color pair, quad, or octet tables */
 | 
						|
    int total_blocks;
 | 
						|
    int cur_y = 0;
 | 
						|
    int cur_x = 0;
 | 
						|
 | 
						|
    /* Number of 4x4 blocks in frame. */
 | 
						|
    total_blocks = ((width + 3) / 4) * ((height + 3) / 4);
 | 
						|
 | 
						|
    pixel_ptr = row_ptr = src_pixels;
 | 
						|
 | 
						|
    while (block_counter < total_blocks) {
 | 
						|
        const uint8_t *xpixel_ptr = pixel_ptr;
 | 
						|
        const uint8_t *xrow_ptr = row_ptr;
 | 
						|
        int intra_skip_blocks = 0;
 | 
						|
        int inter_skip_blocks = 0;
 | 
						|
        int coded_distinct = 0;
 | 
						|
        int coded_blocks = 0;
 | 
						|
        int cache_index;
 | 
						|
        int distinct = 0;
 | 
						|
        int blocks = 0;
 | 
						|
        int frame_y = cur_y;
 | 
						|
        int frame_x = cur_x;
 | 
						|
 | 
						|
        while (prev_pixels && s->key_frame == 0 && block_counter + inter_skip_blocks < total_blocks) {
 | 
						|
            const int y_size = FFMIN(4, height - cur_y);
 | 
						|
            const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
            int compare = 0;
 | 
						|
 | 
						|
            for (int y = 0; y < y_size; y++) {
 | 
						|
                const uint8_t *prev_pixel_ptr = prev_pixels + (y + cur_y) * prev_stride + cur_x;
 | 
						|
 | 
						|
                compare |= !!memcmp(prev_pixel_ptr, pixel_ptr + y * stride, x_size);
 | 
						|
                if (compare)
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (compare)
 | 
						|
                break;
 | 
						|
 | 
						|
            inter_skip_blocks++;
 | 
						|
            if (inter_skip_blocks >= 256)
 | 
						|
                break;
 | 
						|
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
        }
 | 
						|
 | 
						|
        pixel_ptr = xpixel_ptr;
 | 
						|
        row_ptr = xrow_ptr;
 | 
						|
        cur_y = frame_y;
 | 
						|
        cur_x = frame_x;
 | 
						|
 | 
						|
        while (block_counter > 0 && block_counter + intra_skip_blocks < total_blocks) {
 | 
						|
            const int y_size = FFMIN(4, height - cur_y);
 | 
						|
            const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
            const ptrdiff_t offset = xpixel_ptr - src_pixels;
 | 
						|
            const int sy = offset / stride;
 | 
						|
            const int sx = offset % stride;
 | 
						|
            const int ny = sx < 4 ? sy - 4 : sy;
 | 
						|
            const int nx = sx < 4 ? width - 4 + (width & 3) : sx - 4;
 | 
						|
            const uint8_t *old_pixel_ptr = src_pixels + nx + ny * stride;
 | 
						|
            int compare = 0;
 | 
						|
 | 
						|
            for (int y = 0; y < y_size; y++) {
 | 
						|
                compare |= !!memcmp(old_pixel_ptr + y * stride, pixel_ptr + y * stride, x_size);
 | 
						|
                if (compare)
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (compare)
 | 
						|
                break;
 | 
						|
 | 
						|
            intra_skip_blocks++;
 | 
						|
            if (intra_skip_blocks >= 256)
 | 
						|
                break;
 | 
						|
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
        }
 | 
						|
 | 
						|
        pixel_ptr = xpixel_ptr;
 | 
						|
        row_ptr = xrow_ptr;
 | 
						|
        cur_y = frame_y;
 | 
						|
        cur_x = frame_x;
 | 
						|
 | 
						|
        while (block_counter + coded_blocks < total_blocks && coded_blocks < 256) {
 | 
						|
            const int y_size = FFMIN(4, height - cur_y);
 | 
						|
            const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
            const int nb_elements = x_size * y_size;
 | 
						|
            uint8_t block_values[16] = { 0 };
 | 
						|
            for (int y = 0; y < y_size; y++)
 | 
						|
                memcpy(block_values + y * x_size, pixel_ptr + y * stride, x_size);
 | 
						|
 | 
						|
            qsort(block_values, nb_elements, sizeof(block_values[0]), smc_cmp_values);
 | 
						|
            s->next_nb_distinct = count_distinct_items(block_values, s->next_distinct_values, nb_elements);
 | 
						|
            if (coded_blocks == 0) {
 | 
						|
                memcpy(distinct_values, s->next_distinct_values, sizeof(s->distinct_values));
 | 
						|
                s->nb_distinct = s->next_nb_distinct;
 | 
						|
            } else {
 | 
						|
                if (s->next_nb_distinct != s->nb_distinct ||
 | 
						|
                    memcmp(distinct_values, s->next_distinct_values, s->nb_distinct)) {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            s->mono_value = block_values[0];
 | 
						|
 | 
						|
            coded_distinct = s->nb_distinct;
 | 
						|
            coded_blocks++;
 | 
						|
            if (coded_distinct > 1 && coded_blocks >= 16)
 | 
						|
                break;
 | 
						|
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
        }
 | 
						|
 | 
						|
        pixel_ptr = xpixel_ptr;
 | 
						|
        row_ptr = xrow_ptr;
 | 
						|
        cur_y = frame_y;
 | 
						|
        cur_x = frame_x;
 | 
						|
 | 
						|
        blocks = coded_distinct <= 8 ? coded_blocks : 0;
 | 
						|
        distinct = coded_distinct;
 | 
						|
 | 
						|
        if (intra_skip_blocks >= blocks && intra_skip_blocks >= inter_skip_blocks) {
 | 
						|
            distinct = 17;
 | 
						|
            blocks = intra_skip_blocks;
 | 
						|
        }
 | 
						|
 | 
						|
        if (intra_skip_blocks > 16 && intra_skip_blocks >= inter_skip_blocks &&
 | 
						|
            intra_skip_blocks >= blocks) {
 | 
						|
            distinct = 18;
 | 
						|
            blocks = intra_skip_blocks;
 | 
						|
        }
 | 
						|
 | 
						|
        if (inter_skip_blocks >= blocks && inter_skip_blocks > intra_skip_blocks) {
 | 
						|
            distinct = 19;
 | 
						|
            blocks = inter_skip_blocks;
 | 
						|
        }
 | 
						|
 | 
						|
        if (inter_skip_blocks > 16 && inter_skip_blocks > intra_skip_blocks &&
 | 
						|
            inter_skip_blocks >= blocks) {
 | 
						|
            distinct = 20;
 | 
						|
            blocks = inter_skip_blocks;
 | 
						|
        }
 | 
						|
 | 
						|
        if (blocks == 0) {
 | 
						|
            blocks = coded_blocks;
 | 
						|
            distinct = coded_distinct;
 | 
						|
        }
 | 
						|
 | 
						|
        switch (distinct) {
 | 
						|
        case 1:
 | 
						|
            if (blocks <= 16) {
 | 
						|
                bytestream2_put_byte(pb, 0x60 | (blocks - 1));
 | 
						|
            } else {
 | 
						|
                bytestream2_put_byte(pb, 0x70);
 | 
						|
                bytestream2_put_byte(pb, blocks - 1);
 | 
						|
            }
 | 
						|
            bytestream2_put_byte(pb, s->mono_value);
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
 | 
						|
            break;
 | 
						|
        case 2:
 | 
						|
            cache_index = -1;
 | 
						|
            for (int i = 0; i < COLORS_PER_TABLE; i++) {
 | 
						|
                if (CACHE_PAIR(0) &&
 | 
						|
                    CACHE_PAIR(1)) {
 | 
						|
                    cache_index = i;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (cache_index >= 0) {
 | 
						|
                bytestream2_put_byte(pb, 0x90 | (blocks - 1));
 | 
						|
                bytestream2_put_byte(pb, cache_index);
 | 
						|
                color_table_index = cache_index;
 | 
						|
            } else {
 | 
						|
                bytestream2_put_byte(pb, 0x80 | (blocks - 1));
 | 
						|
 | 
						|
                color_table_index = color_pair_index;
 | 
						|
                for (int i = 0; i < CPAIR; i++) {
 | 
						|
                    s->color_pairs[color_table_index][i] = distinct_values[i];
 | 
						|
                    bytestream2_put_byte(pb, distinct_values[i]);
 | 
						|
                }
 | 
						|
 | 
						|
                color_pair_index++;
 | 
						|
                if (color_pair_index == COLORS_PER_TABLE)
 | 
						|
                    color_pair_index = 0;
 | 
						|
            }
 | 
						|
 | 
						|
            for (int i = 0; i < blocks; i++) {
 | 
						|
                const int y_size = FFMIN(4, height - cur_y);
 | 
						|
                const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
                uint8_t value = s->color_pairs[color_table_index][1];
 | 
						|
                uint16_t flags = 0;
 | 
						|
                int shift = 15;
 | 
						|
 | 
						|
                for (int y = 0; y < y_size; y++) {
 | 
						|
                    for (int x = 0; x < x_size; x++) {
 | 
						|
                        flags |= (value == pixel_ptr[x + y * stride]) << shift;
 | 
						|
                        shift--;
 | 
						|
                    }
 | 
						|
                    shift -= 4 - x_size;
 | 
						|
                }
 | 
						|
 | 
						|
                bytestream2_put_be16(pb, flags);
 | 
						|
 | 
						|
                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 3:
 | 
						|
        case 4:
 | 
						|
            cache_index = -1;
 | 
						|
            for (int i = 0; i < COLORS_PER_TABLE; i++) {
 | 
						|
                if (CACHE_QUAD(0) &&
 | 
						|
                    CACHE_QUAD(1) &&
 | 
						|
                    CACHE_QUAD(2) &&
 | 
						|
                    CACHE_QUAD(3)) {
 | 
						|
                    cache_index = i;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (cache_index >= 0) {
 | 
						|
                bytestream2_put_byte(pb, 0xB0 | (blocks - 1));
 | 
						|
                bytestream2_put_byte(pb, cache_index);
 | 
						|
                color_table_index = cache_index;
 | 
						|
            } else {
 | 
						|
                bytestream2_put_byte(pb, 0xA0 | (blocks - 1));
 | 
						|
 | 
						|
                color_table_index = color_quad_index;
 | 
						|
                for (int i = 0; i < CQUAD; i++) {
 | 
						|
                    s->color_quads[color_table_index][i] = distinct_values[i];
 | 
						|
                    bytestream2_put_byte(pb, distinct_values[i]);
 | 
						|
                }
 | 
						|
 | 
						|
                color_quad_index++;
 | 
						|
                if (color_quad_index == COLORS_PER_TABLE)
 | 
						|
                    color_quad_index = 0;
 | 
						|
            }
 | 
						|
 | 
						|
            for (int i = 0; i < blocks; i++) {
 | 
						|
                const int y_size = FFMIN(4, height - cur_y);
 | 
						|
                const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
                uint32_t flags = 0;
 | 
						|
                uint8_t quad[4];
 | 
						|
                int shift = 30;
 | 
						|
 | 
						|
                for (int k = 0; k < 4; k++)
 | 
						|
                    quad[k] = s->color_quads[color_table_index][k];
 | 
						|
 | 
						|
                for (int y = 0; y < y_size; y++) {
 | 
						|
                    for (int x = 0; x < x_size; x++) {
 | 
						|
                        int pixel = pixel_ptr[x + y * stride];
 | 
						|
                        uint32_t idx = 0;
 | 
						|
 | 
						|
                        for (int w = 0; w < CQUAD; w++) {
 | 
						|
                            if (quad[w] == pixel) {
 | 
						|
                                idx = w;
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        flags |= idx << shift;
 | 
						|
                        shift -= 2;
 | 
						|
                    }
 | 
						|
 | 
						|
                    shift -= 2 * (4 - x_size);
 | 
						|
                }
 | 
						|
 | 
						|
                bytestream2_put_be32(pb, flags);
 | 
						|
 | 
						|
                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 5:
 | 
						|
        case 6:
 | 
						|
        case 7:
 | 
						|
        case 8:
 | 
						|
            cache_index = -1;
 | 
						|
            for (int i = 0; i < COLORS_PER_TABLE; i++) {
 | 
						|
                if (CACHE_OCTET(0) &&
 | 
						|
                    CACHE_OCTET(1) &&
 | 
						|
                    CACHE_OCTET(2) &&
 | 
						|
                    CACHE_OCTET(3) &&
 | 
						|
                    CACHE_OCTET(4) &&
 | 
						|
                    CACHE_OCTET(5) &&
 | 
						|
                    CACHE_OCTET(6) &&
 | 
						|
                    CACHE_OCTET(7)) {
 | 
						|
                    cache_index = i;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (cache_index >= 0) {
 | 
						|
                bytestream2_put_byte(pb, 0xD0 | (blocks - 1));
 | 
						|
                bytestream2_put_byte(pb, cache_index);
 | 
						|
                color_table_index = cache_index;
 | 
						|
            } else {
 | 
						|
                bytestream2_put_byte(pb, 0xC0 | (blocks - 1));
 | 
						|
 | 
						|
                color_table_index = color_octet_index;
 | 
						|
                for (int i = 0; i < COCTET; i++) {
 | 
						|
                    s->color_octets[color_table_index][i] = distinct_values[i];
 | 
						|
                    bytestream2_put_byte(pb, distinct_values[i]);
 | 
						|
                }
 | 
						|
 | 
						|
                color_octet_index++;
 | 
						|
                if (color_octet_index == COLORS_PER_TABLE)
 | 
						|
                    color_octet_index = 0;
 | 
						|
            }
 | 
						|
 | 
						|
            for (int i = 0; i < blocks; i++) {
 | 
						|
                const int y_size = FFMIN(4, height - cur_y);
 | 
						|
                const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
                uint64_t flags = 0;
 | 
						|
                uint8_t octet[8];
 | 
						|
                int shift = 45;
 | 
						|
 | 
						|
                for (int k = 0; k < 8; k++)
 | 
						|
                    octet[k] = s->color_octets[color_table_index][k];
 | 
						|
 | 
						|
                for (int y = 0; y < y_size; y++) {
 | 
						|
                    for (int x = 0; x < x_size; x++) {
 | 
						|
                        int pixel = pixel_ptr[x + y * stride];
 | 
						|
                        uint64_t idx = 0;
 | 
						|
 | 
						|
                        for (int w = 0; w < COCTET; w++) {
 | 
						|
                            if (octet[w] == pixel) {
 | 
						|
                                idx = w;
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        flags |= idx << shift;
 | 
						|
                        shift -= 3;
 | 
						|
                    }
 | 
						|
 | 
						|
                    shift -= 3 * (4 - x_size);
 | 
						|
                }
 | 
						|
 | 
						|
                bytestream2_put_be16(pb, ((flags >> 32) & 0xFFF0) | ((flags >> 8) & 0xF));
 | 
						|
                bytestream2_put_be16(pb, ((flags >> 20) & 0xFFF0) | ((flags >> 4) & 0xF));
 | 
						|
                bytestream2_put_be16(pb, ((flags >>  8) & 0xFFF0) | ((flags >> 0) & 0xF));
 | 
						|
 | 
						|
                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            bytestream2_put_byte(pb, 0xE0 | (blocks - 1));
 | 
						|
            for (int i = 0; i < blocks; i++) {
 | 
						|
                const int y_size = FFMIN(4, height - cur_y);
 | 
						|
                const int x_size = FFMIN(4, width  - cur_x);
 | 
						|
                for (int y = 0; y < y_size; y++) {
 | 
						|
                    for (int x = 0; x < x_size; x++)
 | 
						|
                        bytestream2_put_byte(pb, pixel_ptr[x + y * stride]);
 | 
						|
                    for (int x = x_size; x < 4; x++)
 | 
						|
                        bytestream2_put_byte(pb, 0);
 | 
						|
                }
 | 
						|
 | 
						|
                for (int y = y_size; y < 4; y++) {
 | 
						|
                    for (int x = 0; x < 4; x++)
 | 
						|
                        bytestream2_put_byte(pb, 0);
 | 
						|
                }
 | 
						|
 | 
						|
                ADVANCE_BLOCK(pixel_ptr, row_ptr, 1)
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case 17:
 | 
						|
            bytestream2_put_byte(pb, 0x20 | (blocks - 1));
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
 | 
						|
            break;
 | 
						|
        case 18:
 | 
						|
            bytestream2_put_byte(pb, 0x30);
 | 
						|
            bytestream2_put_byte(pb, blocks - 1);
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
 | 
						|
            break;
 | 
						|
        case 19:
 | 
						|
            bytestream2_put_byte(pb, 0x00 | (blocks - 1));
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
 | 
						|
            break;
 | 
						|
        case 20:
 | 
						|
            bytestream2_put_byte(pb, 0x10);
 | 
						|
            bytestream2_put_byte(pb, blocks - 1);
 | 
						|
            ADVANCE_BLOCK(pixel_ptr, row_ptr, blocks)
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        block_counter += blocks;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int smc_encode_init(AVCodecContext *avctx)
 | 
						|
{
 | 
						|
    SMCContext *s = avctx->priv_data;
 | 
						|
 | 
						|
    avctx->bits_per_coded_sample = 8;
 | 
						|
 | 
						|
    s->prev_frame = av_frame_alloc();
 | 
						|
    if (!s->prev_frame)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
 | 
						|
                                const AVFrame *frame, int *got_packet)
 | 
						|
{
 | 
						|
    SMCContext *s = avctx->priv_data;
 | 
						|
    const AVFrame *pict = frame;
 | 
						|
    PutByteContext pb;
 | 
						|
    uint8_t *pal;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = ff_alloc_packet(avctx, pkt, 8LL * avctx->height * avctx->width);
 | 
						|
    if (ret < 0)
 | 
						|
        return ret;
 | 
						|
 | 
						|
    if (avctx->gop_size == 0 || !s->prev_frame->data[0] ||
 | 
						|
        (avctx->frame_num % avctx->gop_size) == 0) {
 | 
						|
        s->key_frame = 1;
 | 
						|
    } else {
 | 
						|
        s->key_frame = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    bytestream2_init_writer(&pb, pkt->data, pkt->size);
 | 
						|
 | 
						|
    bytestream2_put_be32(&pb, 0x00);
 | 
						|
 | 
						|
    pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
 | 
						|
    if (!pal)
 | 
						|
        return AVERROR(ENOMEM);
 | 
						|
    memcpy(pal, frame->data[1], AVPALETTE_SIZE);
 | 
						|
 | 
						|
    smc_encode_stream(s, pict, &pb);
 | 
						|
 | 
						|
    av_shrink_packet(pkt, bytestream2_tell_p(&pb));
 | 
						|
 | 
						|
    pkt->data[0] = 0x0;
 | 
						|
 | 
						|
    // write chunk length
 | 
						|
    AV_WB24(pkt->data + 1, pkt->size);
 | 
						|
 | 
						|
    av_frame_unref(s->prev_frame);
 | 
						|
    ret = av_frame_ref(s->prev_frame, frame);
 | 
						|
    if (ret < 0) {
 | 
						|
        av_log(avctx, AV_LOG_ERROR, "cannot add reference\n");
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    if (s->key_frame)
 | 
						|
        pkt->flags |= AV_PKT_FLAG_KEY;
 | 
						|
 | 
						|
    *got_packet = 1;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int smc_encode_end(AVCodecContext *avctx)
 | 
						|
{
 | 
						|
    SMCContext *s = (SMCContext *)avctx->priv_data;
 | 
						|
 | 
						|
    av_frame_free(&s->prev_frame);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
const FFCodec ff_smc_encoder = {
 | 
						|
    .p.name         = "smc",
 | 
						|
    CODEC_LONG_NAME("QuickTime Graphics (SMC)"),
 | 
						|
    .p.type         = AVMEDIA_TYPE_VIDEO,
 | 
						|
    .p.id           = AV_CODEC_ID_SMC,
 | 
						|
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
 | 
						|
    .priv_data_size = sizeof(SMCContext),
 | 
						|
    .init           = smc_encode_init,
 | 
						|
    FF_CODEC_ENCODE_CB(smc_encode_frame),
 | 
						|
    .close          = smc_encode_end,
 | 
						|
    .p.pix_fmts     = (const enum AVPixelFormat[]) { AV_PIX_FMT_PAL8,
 | 
						|
                                                     AV_PIX_FMT_NONE},
 | 
						|
};
 |