1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Flash Screen Video Version 2 encoder
3cabdff1aSopenharmony_ci * Copyright (C) 2009 Joshua Warner
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci/**
23cabdff1aSopenharmony_ci * @file
24cabdff1aSopenharmony_ci * Flash Screen Video Version 2 encoder
25cabdff1aSopenharmony_ci * @author Joshua Warner
26cabdff1aSopenharmony_ci */
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_ci/* Differences from version 1 stream:
29cabdff1aSopenharmony_ci * NOTE: Currently, the only player that supports version 2 streams is Adobe Flash Player itself.
30cabdff1aSopenharmony_ci * * Supports sending only a range of scanlines in a block,
31cabdff1aSopenharmony_ci *   indicating a difference from the corresponding block in the last keyframe.
32cabdff1aSopenharmony_ci * * Supports initializing the zlib dictionary with data from the corresponding
33cabdff1aSopenharmony_ci *   block in the last keyframe, to improve compression.
34cabdff1aSopenharmony_ci * * Supports a hybrid 15-bit rgb / 7-bit palette color space.
35cabdff1aSopenharmony_ci */
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_ci/* TODO:
38cabdff1aSopenharmony_ci * Don't keep Block structures for both current frame and keyframe.
39cabdff1aSopenharmony_ci * Make better heuristics for deciding stream parameters (optimum_* functions).  Currently these return constants.
40cabdff1aSopenharmony_ci * Figure out how to encode palette information in the stream, choose an optimum palette at each keyframe.
41cabdff1aSopenharmony_ci * Figure out how the zlibPrimeCompressCurrent flag works, implement support.
42cabdff1aSopenharmony_ci * Find other sample files (that weren't generated here), develop a decoder.
43cabdff1aSopenharmony_ci */
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_ci#include <stdio.h>
46cabdff1aSopenharmony_ci#include <stdlib.h>
47cabdff1aSopenharmony_ci#include <zlib.h>
48cabdff1aSopenharmony_ci
49cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
50cabdff1aSopenharmony_ci#include "avcodec.h"
51cabdff1aSopenharmony_ci#include "codec_internal.h"
52cabdff1aSopenharmony_ci#include "encode.h"
53cabdff1aSopenharmony_ci#include "put_bits.h"
54cabdff1aSopenharmony_ci#include "bytestream.h"
55cabdff1aSopenharmony_ci#include "zlib_wrapper.h"
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_ci#define HAS_IFRAME_IMAGE 0x02
58cabdff1aSopenharmony_ci#define HAS_PALLET_INFO 0x01
59cabdff1aSopenharmony_ci
60cabdff1aSopenharmony_ci#define COLORSPACE_BGR 0x00
61cabdff1aSopenharmony_ci#define COLORSPACE_15_7 0x10
62cabdff1aSopenharmony_ci#define HAS_DIFF_BLOCKS 0x04
63cabdff1aSopenharmony_ci#define ZLIB_PRIME_COMPRESS_CURRENT 0x02
64cabdff1aSopenharmony_ci#define ZLIB_PRIME_COMPRESS_PREVIOUS 0x01
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci// Disables experimental "smart" parameter-choosing code, as well as the statistics that it depends on.
67cabdff1aSopenharmony_ci// At the moment, the "smart" code is a great example of how the parameters *shouldn't* be chosen.
68cabdff1aSopenharmony_ci#define FLASHSV2_DUMB
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_citypedef struct Block {
71cabdff1aSopenharmony_ci    uint8_t *enc;
72cabdff1aSopenharmony_ci    uint8_t *sl_begin, *sl_end;
73cabdff1aSopenharmony_ci    int enc_size;
74cabdff1aSopenharmony_ci    uint8_t *data;
75cabdff1aSopenharmony_ci    unsigned long data_size;
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    uint8_t start, len;
78cabdff1aSopenharmony_ci    uint8_t dirty;
79cabdff1aSopenharmony_ci    uint8_t col, row, width, height;
80cabdff1aSopenharmony_ci    uint8_t flags;
81cabdff1aSopenharmony_ci} Block;
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_citypedef struct Palette {
84cabdff1aSopenharmony_ci    unsigned colors[128];
85cabdff1aSopenharmony_ci    uint8_t index[1 << 15];
86cabdff1aSopenharmony_ci} Palette;
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_citypedef struct FlashSV2Context {
89cabdff1aSopenharmony_ci    AVCodecContext *avctx;
90cabdff1aSopenharmony_ci    uint8_t *current_frame;
91cabdff1aSopenharmony_ci    uint8_t *key_frame;
92cabdff1aSopenharmony_ci    uint8_t *encbuffer;
93cabdff1aSopenharmony_ci    uint8_t *keybuffer;
94cabdff1aSopenharmony_ci    uint8_t *databuffer;
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci    uint8_t *blockbuffer;
97cabdff1aSopenharmony_ci    int blockbuffer_size;
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_ci    Block *frame_blocks;
100cabdff1aSopenharmony_ci    Block *key_blocks;
101cabdff1aSopenharmony_ci    int frame_size;
102cabdff1aSopenharmony_ci    int blocks_size;
103cabdff1aSopenharmony_ci
104cabdff1aSopenharmony_ci    int use15_7, dist, comp;
105cabdff1aSopenharmony_ci
106cabdff1aSopenharmony_ci    int rows, cols;
107cabdff1aSopenharmony_ci
108cabdff1aSopenharmony_ci    int last_key_frame;
109cabdff1aSopenharmony_ci
110cabdff1aSopenharmony_ci    int image_width, image_height;
111cabdff1aSopenharmony_ci    int block_width, block_height;
112cabdff1aSopenharmony_ci    uint8_t flags;
113cabdff1aSopenharmony_ci    uint8_t use_custom_palette;
114cabdff1aSopenharmony_ci    uint8_t palette_type;       ///< 0=>default, 1=>custom - changed when palette regenerated.
115cabdff1aSopenharmony_ci    Palette palette;
116cabdff1aSopenharmony_ci    FFZStream zstream;
117cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
118cabdff1aSopenharmony_ci    double tot_blocks;          ///< blocks encoded since last keyframe
119cabdff1aSopenharmony_ci    double diff_blocks;         ///< blocks that were different since last keyframe
120cabdff1aSopenharmony_ci    double tot_lines;           ///< total scanlines in image since last keyframe
121cabdff1aSopenharmony_ci    double diff_lines;          ///< scanlines that were different since last keyframe
122cabdff1aSopenharmony_ci    double raw_size;            ///< size of raw frames since last keyframe
123cabdff1aSopenharmony_ci    double comp_size;           ///< size of compressed data since last keyframe
124cabdff1aSopenharmony_ci    double uncomp_size;         ///< size of uncompressed data since last keyframe
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    double total_bits;          ///< total bits written to stream so far
127cabdff1aSopenharmony_ci#endif
128cabdff1aSopenharmony_ci} FlashSV2Context;
129cabdff1aSopenharmony_ci
130cabdff1aSopenharmony_cistatic av_cold void cleanup(FlashSV2Context * s)
131cabdff1aSopenharmony_ci{
132cabdff1aSopenharmony_ci    av_freep(&s->encbuffer);
133cabdff1aSopenharmony_ci    av_freep(&s->keybuffer);
134cabdff1aSopenharmony_ci    av_freep(&s->databuffer);
135cabdff1aSopenharmony_ci    av_freep(&s->blockbuffer);
136cabdff1aSopenharmony_ci    av_freep(&s->current_frame);
137cabdff1aSopenharmony_ci    av_freep(&s->key_frame);
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci    av_freep(&s->frame_blocks);
140cabdff1aSopenharmony_ci    av_freep(&s->key_blocks);
141cabdff1aSopenharmony_ci    ff_deflate_end(&s->zstream);
142cabdff1aSopenharmony_ci}
143cabdff1aSopenharmony_ci
144cabdff1aSopenharmony_cistatic void init_blocks(FlashSV2Context * s, Block * blocks,
145cabdff1aSopenharmony_ci                        uint8_t * encbuf, uint8_t * databuf)
146cabdff1aSopenharmony_ci{
147cabdff1aSopenharmony_ci    int row, col;
148cabdff1aSopenharmony_ci    Block *b;
149cabdff1aSopenharmony_ci    memset(blocks, 0, s->cols * s->rows * sizeof(*blocks));
150cabdff1aSopenharmony_ci    for (col = 0; col < s->cols; col++) {
151cabdff1aSopenharmony_ci        for (row = 0; row < s->rows; row++) {
152cabdff1aSopenharmony_ci            b = blocks + (col + row * s->cols);
153cabdff1aSopenharmony_ci            b->width = (col < s->cols - 1) ?
154cabdff1aSopenharmony_ci                s->block_width :
155cabdff1aSopenharmony_ci                s->image_width - col * s->block_width;
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_ci            b->height = (row < s->rows - 1) ?
158cabdff1aSopenharmony_ci                s->block_height :
159cabdff1aSopenharmony_ci                s->image_height - row * s->block_height;
160cabdff1aSopenharmony_ci
161cabdff1aSopenharmony_ci            b->row   = row;
162cabdff1aSopenharmony_ci            b->col   = col;
163cabdff1aSopenharmony_ci            b->enc   = encbuf;
164cabdff1aSopenharmony_ci            b->data  = databuf;
165cabdff1aSopenharmony_ci            encbuf  += b->width * b->height * 3;
166cabdff1aSopenharmony_ci            databuf  = databuf ? databuf + b->width * b->height * 6 : NULL;
167cabdff1aSopenharmony_ci        }
168cabdff1aSopenharmony_ci    }
169cabdff1aSopenharmony_ci}
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_cistatic void reset_stats(FlashSV2Context * s)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
174cabdff1aSopenharmony_ci    s->diff_blocks = 0.1;
175cabdff1aSopenharmony_ci    s->tot_blocks = 1;
176cabdff1aSopenharmony_ci    s->diff_lines = 0.1;
177cabdff1aSopenharmony_ci    s->tot_lines = 1;
178cabdff1aSopenharmony_ci    s->raw_size = s->comp_size = s->uncomp_size = 10;
179cabdff1aSopenharmony_ci#endif
180cabdff1aSopenharmony_ci}
181cabdff1aSopenharmony_ci
182cabdff1aSopenharmony_cistatic int update_block_dimensions(FlashSV2Context *s, int block_width, int block_height)
183cabdff1aSopenharmony_ci{
184cabdff1aSopenharmony_ci    s->block_width  = block_width;
185cabdff1aSopenharmony_ci    s->block_height = block_height;
186cabdff1aSopenharmony_ci    s->rows = (s->image_height + s->block_height - 1) / s->block_height;
187cabdff1aSopenharmony_ci    s->cols = (s->image_width  + s->block_width  - 1) / s->block_width;
188cabdff1aSopenharmony_ci    if (s->rows * s->cols > s->blocks_size / sizeof(Block)) {
189cabdff1aSopenharmony_ci        s->frame_blocks = av_realloc_array(s->frame_blocks, s->rows, s->cols * sizeof(Block));
190cabdff1aSopenharmony_ci        s->key_blocks = av_realloc_array(s->key_blocks, s->cols, s->rows * sizeof(Block));
191cabdff1aSopenharmony_ci        if (!s->frame_blocks || !s->key_blocks) {
192cabdff1aSopenharmony_ci            av_log(s->avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
193cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
194cabdff1aSopenharmony_ci        }
195cabdff1aSopenharmony_ci        s->blocks_size = s->rows * s->cols * sizeof(Block);
196cabdff1aSopenharmony_ci    }
197cabdff1aSopenharmony_ci    init_blocks(s, s->frame_blocks, s->encbuffer, s->databuffer);
198cabdff1aSopenharmony_ci    init_blocks(s, s->key_blocks, s->keybuffer, 0);
199cabdff1aSopenharmony_ci
200cabdff1aSopenharmony_ci    av_fast_malloc(&s->blockbuffer, &s->blockbuffer_size, block_width * block_height * 6);
201cabdff1aSopenharmony_ci    if (!s->blockbuffer) {
202cabdff1aSopenharmony_ci        av_log(s->avctx, AV_LOG_ERROR, "Could not allocate block buffer.\n");
203cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
204cabdff1aSopenharmony_ci    }
205cabdff1aSopenharmony_ci    return 0;
206cabdff1aSopenharmony_ci}
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci
209cabdff1aSopenharmony_cistatic av_cold int flashsv2_encode_init(AVCodecContext * avctx)
210cabdff1aSopenharmony_ci{
211cabdff1aSopenharmony_ci    FlashSV2Context *s = avctx->priv_data;
212cabdff1aSopenharmony_ci    int ret;
213cabdff1aSopenharmony_ci
214cabdff1aSopenharmony_ci    s->avctx = avctx;
215cabdff1aSopenharmony_ci
216cabdff1aSopenharmony_ci    s->comp = avctx->compression_level;
217cabdff1aSopenharmony_ci    if (s->comp == -1)
218cabdff1aSopenharmony_ci        s->comp = 9;
219cabdff1aSopenharmony_ci    if (s->comp < 0 || s->comp > 9) {
220cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR,
221cabdff1aSopenharmony_ci               "Compression level should be 0-9, not %d\n", s->comp);
222cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
223cabdff1aSopenharmony_ci    }
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ci    if ((avctx->width > 4095) || (avctx->height > 4095)) {
227cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR,
228cabdff1aSopenharmony_ci               "Input dimensions too large, input must be max 4095x4095 !\n");
229cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
230cabdff1aSopenharmony_ci    }
231cabdff1aSopenharmony_ci    if ((avctx->width < 16) || (avctx->height < 16)) {
232cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR,
233cabdff1aSopenharmony_ci               "Input dimensions too small, input must be at least 16x16 !\n");
234cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
235cabdff1aSopenharmony_ci    }
236cabdff1aSopenharmony_ci
237cabdff1aSopenharmony_ci    if ((ret = av_image_check_size(avctx->width, avctx->height, 0, avctx)) < 0)
238cabdff1aSopenharmony_ci        return ret;
239cabdff1aSopenharmony_ci
240cabdff1aSopenharmony_ci    ret = ff_deflate_init(&s->zstream, s->comp, avctx);
241cabdff1aSopenharmony_ci    if (ret < 0)
242cabdff1aSopenharmony_ci        return ret;
243cabdff1aSopenharmony_ci    s->last_key_frame = 0;
244cabdff1aSopenharmony_ci
245cabdff1aSopenharmony_ci    s->image_width  = avctx->width;
246cabdff1aSopenharmony_ci    s->image_height = avctx->height;
247cabdff1aSopenharmony_ci
248cabdff1aSopenharmony_ci    s->frame_size  = s->image_width * s->image_height * 3;
249cabdff1aSopenharmony_ci
250cabdff1aSopenharmony_ci    s->encbuffer     = av_mallocz(s->frame_size);
251cabdff1aSopenharmony_ci    s->keybuffer     = av_mallocz(s->frame_size);
252cabdff1aSopenharmony_ci    s->databuffer    = av_mallocz(s->frame_size * 6);
253cabdff1aSopenharmony_ci    s->current_frame = av_mallocz(s->frame_size);
254cabdff1aSopenharmony_ci    s->key_frame     = av_mallocz(s->frame_size);
255cabdff1aSopenharmony_ci    if (!s->encbuffer || !s->keybuffer || !s->databuffer
256cabdff1aSopenharmony_ci        || !s->current_frame || !s->key_frame) {
257cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
258cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
259cabdff1aSopenharmony_ci    }
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_ci    reset_stats(s);
262cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
263cabdff1aSopenharmony_ci    s->total_bits = 1;
264cabdff1aSopenharmony_ci#endif
265cabdff1aSopenharmony_ci
266cabdff1aSopenharmony_ci    s->use_custom_palette =  0;
267cabdff1aSopenharmony_ci    s->palette_type       = -1;        // so that the palette will be generated in reconfigure_at_keyframe
268cabdff1aSopenharmony_ci
269cabdff1aSopenharmony_ci    return update_block_dimensions(s, 64, 64);
270cabdff1aSopenharmony_ci}
271cabdff1aSopenharmony_ci
272cabdff1aSopenharmony_cistatic int new_key_frame(FlashSV2Context * s)
273cabdff1aSopenharmony_ci{
274cabdff1aSopenharmony_ci    int i;
275cabdff1aSopenharmony_ci    memcpy(s->key_blocks, s->frame_blocks, s->blocks_size);
276cabdff1aSopenharmony_ci    memcpy(s->key_frame, s->current_frame, s->frame_size);
277cabdff1aSopenharmony_ci
278cabdff1aSopenharmony_ci    for (i = 0; i < s->rows * s->cols; i++) {
279cabdff1aSopenharmony_ci        s->key_blocks[i].enc += (s->keybuffer - s->encbuffer);
280cabdff1aSopenharmony_ci        s->key_blocks[i].sl_begin = 0;
281cabdff1aSopenharmony_ci        s->key_blocks[i].sl_end   = 0;
282cabdff1aSopenharmony_ci        s->key_blocks[i].data     = 0;
283cabdff1aSopenharmony_ci    }
284cabdff1aSopenharmony_ci    memcpy(s->keybuffer, s->encbuffer, s->frame_size);
285cabdff1aSopenharmony_ci
286cabdff1aSopenharmony_ci    return 0;
287cabdff1aSopenharmony_ci}
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_cistatic int write_palette(FlashSV2Context * s, uint8_t * buf, int buf_size)
290cabdff1aSopenharmony_ci{
291cabdff1aSopenharmony_ci    //this isn't implemented yet!  Default palette only!
292cabdff1aSopenharmony_ci    return -1;
293cabdff1aSopenharmony_ci}
294cabdff1aSopenharmony_ci
295cabdff1aSopenharmony_cistatic int write_header(FlashSV2Context * s, uint8_t * buf, int buf_size)
296cabdff1aSopenharmony_ci{
297cabdff1aSopenharmony_ci    PutBitContext pb;
298cabdff1aSopenharmony_ci    int buf_pos, len;
299cabdff1aSopenharmony_ci
300cabdff1aSopenharmony_ci    if (buf_size < 5)
301cabdff1aSopenharmony_ci        return -1;
302cabdff1aSopenharmony_ci
303cabdff1aSopenharmony_ci    init_put_bits(&pb, buf, buf_size);
304cabdff1aSopenharmony_ci
305cabdff1aSopenharmony_ci    put_bits(&pb, 4, (s->block_width  >> 4) - 1);
306cabdff1aSopenharmony_ci    put_bits(&pb, 12, s->image_width);
307cabdff1aSopenharmony_ci    put_bits(&pb, 4, (s->block_height >> 4) - 1);
308cabdff1aSopenharmony_ci    put_bits(&pb, 12, s->image_height);
309cabdff1aSopenharmony_ci
310cabdff1aSopenharmony_ci    flush_put_bits(&pb);
311cabdff1aSopenharmony_ci    buf_pos = 4;
312cabdff1aSopenharmony_ci
313cabdff1aSopenharmony_ci    buf[buf_pos++] = s->flags;
314cabdff1aSopenharmony_ci
315cabdff1aSopenharmony_ci    if (s->flags & HAS_PALLET_INFO) {
316cabdff1aSopenharmony_ci        len = write_palette(s, buf + buf_pos, buf_size - buf_pos);
317cabdff1aSopenharmony_ci        if (len < 0)
318cabdff1aSopenharmony_ci            return -1;
319cabdff1aSopenharmony_ci        buf_pos += len;
320cabdff1aSopenharmony_ci    }
321cabdff1aSopenharmony_ci
322cabdff1aSopenharmony_ci    return buf_pos;
323cabdff1aSopenharmony_ci}
324cabdff1aSopenharmony_ci
325cabdff1aSopenharmony_cistatic int write_block(Block * b, uint8_t * buf, int buf_size)
326cabdff1aSopenharmony_ci{
327cabdff1aSopenharmony_ci    int buf_pos = 0;
328cabdff1aSopenharmony_ci    unsigned block_size = b->data_size;
329cabdff1aSopenharmony_ci
330cabdff1aSopenharmony_ci    if (b->flags & HAS_DIFF_BLOCKS)
331cabdff1aSopenharmony_ci        block_size += 2;
332cabdff1aSopenharmony_ci    if (b->flags & ZLIB_PRIME_COMPRESS_CURRENT)
333cabdff1aSopenharmony_ci        block_size += 2;
334cabdff1aSopenharmony_ci    if (block_size > 0)
335cabdff1aSopenharmony_ci        block_size += 1;
336cabdff1aSopenharmony_ci    if (buf_size < block_size + 2)
337cabdff1aSopenharmony_ci        return -1;
338cabdff1aSopenharmony_ci
339cabdff1aSopenharmony_ci    buf[buf_pos++] = block_size >> 8;
340cabdff1aSopenharmony_ci    buf[buf_pos++] = block_size;
341cabdff1aSopenharmony_ci
342cabdff1aSopenharmony_ci    if (block_size == 0)
343cabdff1aSopenharmony_ci        return buf_pos;
344cabdff1aSopenharmony_ci
345cabdff1aSopenharmony_ci    buf[buf_pos++] = b->flags;
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_ci    if (b->flags & HAS_DIFF_BLOCKS) {
348cabdff1aSopenharmony_ci        buf[buf_pos++] = (b->start);
349cabdff1aSopenharmony_ci        buf[buf_pos++] = (b->len);
350cabdff1aSopenharmony_ci    }
351cabdff1aSopenharmony_ci
352cabdff1aSopenharmony_ci    if (b->flags & ZLIB_PRIME_COMPRESS_CURRENT) {
353cabdff1aSopenharmony_ci        //This feature of the format is poorly understood, and as of now, unused.
354cabdff1aSopenharmony_ci        buf[buf_pos++] = (b->col);
355cabdff1aSopenharmony_ci        buf[buf_pos++] = (b->row);
356cabdff1aSopenharmony_ci    }
357cabdff1aSopenharmony_ci
358cabdff1aSopenharmony_ci    memcpy(buf + buf_pos, b->data, b->data_size);
359cabdff1aSopenharmony_ci
360cabdff1aSopenharmony_ci    buf_pos += b->data_size;
361cabdff1aSopenharmony_ci
362cabdff1aSopenharmony_ci    return buf_pos;
363cabdff1aSopenharmony_ci}
364cabdff1aSopenharmony_ci
365cabdff1aSopenharmony_cistatic int encode_zlib(Block *b, uint8_t *buf, unsigned long *buf_size,
366cabdff1aSopenharmony_ci                       z_stream *zstream)
367cabdff1aSopenharmony_ci{
368cabdff1aSopenharmony_ci    int res;
369cabdff1aSopenharmony_ci
370cabdff1aSopenharmony_ci    if (deflateReset(zstream) != Z_OK)
371cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
372cabdff1aSopenharmony_ci    zstream->next_out  = buf;
373cabdff1aSopenharmony_ci    zstream->avail_out = *buf_size;
374cabdff1aSopenharmony_ci    zstream->next_in   = b->sl_begin;
375cabdff1aSopenharmony_ci    zstream->avail_in  = b->sl_end - b->sl_begin;
376cabdff1aSopenharmony_ci    res = deflate(zstream, Z_FINISH);
377cabdff1aSopenharmony_ci    if (res != Z_STREAM_END)
378cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
379cabdff1aSopenharmony_ci    *buf_size -= zstream->avail_out;
380cabdff1aSopenharmony_ci    return 0;
381cabdff1aSopenharmony_ci}
382cabdff1aSopenharmony_ci
383cabdff1aSopenharmony_cistatic int encode_zlibprime(Block * b, Block * prime, uint8_t * buf,
384cabdff1aSopenharmony_ci                            int *buf_size, z_stream *zstream)
385cabdff1aSopenharmony_ci{
386cabdff1aSopenharmony_ci    int res;
387cabdff1aSopenharmony_ci
388cabdff1aSopenharmony_ci    if (deflateReset(zstream) != Z_OK)
389cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
390cabdff1aSopenharmony_ci    zstream->next_in  = prime->enc;
391cabdff1aSopenharmony_ci    zstream->avail_in = prime->enc_size;
392cabdff1aSopenharmony_ci    while (zstream->avail_in > 0) {
393cabdff1aSopenharmony_ci        zstream->next_out  = buf;
394cabdff1aSopenharmony_ci        zstream->avail_out = *buf_size;
395cabdff1aSopenharmony_ci        res = deflate(zstream, Z_SYNC_FLUSH);
396cabdff1aSopenharmony_ci        if (res < 0)
397cabdff1aSopenharmony_ci            return -1;
398cabdff1aSopenharmony_ci    }
399cabdff1aSopenharmony_ci
400cabdff1aSopenharmony_ci    zstream->next_in   = b->sl_begin;
401cabdff1aSopenharmony_ci    zstream->avail_in  = b->sl_end - b->sl_begin;
402cabdff1aSopenharmony_ci    zstream->next_out  = buf;
403cabdff1aSopenharmony_ci    zstream->avail_out = *buf_size;
404cabdff1aSopenharmony_ci    res = deflate(zstream, Z_FINISH);
405cabdff1aSopenharmony_ci    *buf_size -= zstream->avail_out;
406cabdff1aSopenharmony_ci    if (res != Z_STREAM_END)
407cabdff1aSopenharmony_ci        return -1;
408cabdff1aSopenharmony_ci    return 0;
409cabdff1aSopenharmony_ci}
410cabdff1aSopenharmony_ci
411cabdff1aSopenharmony_cistatic int encode_bgr(Block * b, const uint8_t * src, int stride)
412cabdff1aSopenharmony_ci{
413cabdff1aSopenharmony_ci    int i;
414cabdff1aSopenharmony_ci    uint8_t *ptr = b->enc;
415cabdff1aSopenharmony_ci    for (i = 0; i < b->start; i++)
416cabdff1aSopenharmony_ci        memcpy(ptr + i * b->width * 3, src + i * stride, b->width * 3);
417cabdff1aSopenharmony_ci    b->sl_begin = ptr + i * b->width * 3;
418cabdff1aSopenharmony_ci    for (; i < b->start + b->len; i++)
419cabdff1aSopenharmony_ci        memcpy(ptr + i * b->width * 3, src + i * stride, b->width * 3);
420cabdff1aSopenharmony_ci    b->sl_end = ptr + i * b->width * 3;
421cabdff1aSopenharmony_ci    for (; i < b->height; i++)
422cabdff1aSopenharmony_ci        memcpy(ptr + i * b->width * 3, src + i * stride, b->width * 3);
423cabdff1aSopenharmony_ci    b->enc_size = ptr + i * b->width * 3 - b->enc;
424cabdff1aSopenharmony_ci    return b->enc_size;
425cabdff1aSopenharmony_ci}
426cabdff1aSopenharmony_ci
427cabdff1aSopenharmony_cistatic inline unsigned pixel_color15(const uint8_t * src)
428cabdff1aSopenharmony_ci{
429cabdff1aSopenharmony_ci    return (src[0] >> 3) | ((src[1] & 0xf8) << 2) | ((src[2] & 0xf8) << 7);
430cabdff1aSopenharmony_ci}
431cabdff1aSopenharmony_ci
432cabdff1aSopenharmony_cistatic inline unsigned int chroma_diff(unsigned int c1, unsigned int c2)
433cabdff1aSopenharmony_ci{
434cabdff1aSopenharmony_ci#define ABSDIFF(a,b) (abs((int)(a)-(int)(b)))
435cabdff1aSopenharmony_ci
436cabdff1aSopenharmony_ci    unsigned int t1 = (c1 & 0x000000ff) + ((c1 & 0x0000ff00) >> 8) + ((c1 & 0x00ff0000) >> 16);
437cabdff1aSopenharmony_ci    unsigned int t2 = (c2 & 0x000000ff) + ((c2 & 0x0000ff00) >> 8) + ((c2 & 0x00ff0000) >> 16);
438cabdff1aSopenharmony_ci
439cabdff1aSopenharmony_ci    return ABSDIFF(t1, t2) + ABSDIFF(c1 & 0x000000ff, c2 & 0x000000ff) +
440cabdff1aSopenharmony_ci        ABSDIFF((c1 & 0x0000ff00) >> 8 , (c2 & 0x0000ff00) >> 8) +
441cabdff1aSopenharmony_ci        ABSDIFF((c1 & 0x00ff0000) >> 16, (c2 & 0x00ff0000) >> 16);
442cabdff1aSopenharmony_ci}
443cabdff1aSopenharmony_ci
444cabdff1aSopenharmony_cistatic inline int pixel_color7_fast(Palette * palette, unsigned c15)
445cabdff1aSopenharmony_ci{
446cabdff1aSopenharmony_ci    return palette->index[c15];
447cabdff1aSopenharmony_ci}
448cabdff1aSopenharmony_ci
449cabdff1aSopenharmony_cistatic int pixel_color7_slow(Palette * palette, unsigned color)
450cabdff1aSopenharmony_ci{
451cabdff1aSopenharmony_ci    int i, min = 0x7fffffff;
452cabdff1aSopenharmony_ci    int minc = -1;
453cabdff1aSopenharmony_ci    for (i = 0; i < 128; i++) {
454cabdff1aSopenharmony_ci        int c1 = palette->colors[i];
455cabdff1aSopenharmony_ci        int diff = chroma_diff(c1, color);
456cabdff1aSopenharmony_ci        if (diff < min) {
457cabdff1aSopenharmony_ci            min = diff;
458cabdff1aSopenharmony_ci            minc = i;
459cabdff1aSopenharmony_ci        }
460cabdff1aSopenharmony_ci    }
461cabdff1aSopenharmony_ci    return minc;
462cabdff1aSopenharmony_ci}
463cabdff1aSopenharmony_ci
464cabdff1aSopenharmony_cistatic inline unsigned pixel_bgr(const uint8_t * src)
465cabdff1aSopenharmony_ci{
466cabdff1aSopenharmony_ci    return (src[0]) | (src[1] << 8) | (src[2] << 16);
467cabdff1aSopenharmony_ci}
468cabdff1aSopenharmony_ci
469cabdff1aSopenharmony_cistatic int write_pixel_15_7(Palette * palette, uint8_t * dest, const uint8_t * src,
470cabdff1aSopenharmony_ci                            int dist)
471cabdff1aSopenharmony_ci{
472cabdff1aSopenharmony_ci    unsigned c15 = pixel_color15(src);
473cabdff1aSopenharmony_ci    unsigned color = pixel_bgr(src);
474cabdff1aSopenharmony_ci    int d15 = chroma_diff(color, color & 0x00f8f8f8);
475cabdff1aSopenharmony_ci    int c7 = pixel_color7_fast(palette, c15);
476cabdff1aSopenharmony_ci    int d7 = chroma_diff(color, palette->colors[c7]);
477cabdff1aSopenharmony_ci    if (dist + d15 >= d7) {
478cabdff1aSopenharmony_ci        dest[0] = c7;
479cabdff1aSopenharmony_ci        return 1;
480cabdff1aSopenharmony_ci    } else {
481cabdff1aSopenharmony_ci        dest[0] = 0x80 | (c15 >> 8);
482cabdff1aSopenharmony_ci        dest[1] = c15 & 0xff;
483cabdff1aSopenharmony_ci        return 2;
484cabdff1aSopenharmony_ci    }
485cabdff1aSopenharmony_ci}
486cabdff1aSopenharmony_ci
487cabdff1aSopenharmony_cistatic int update_palette_index(Palette * palette)
488cabdff1aSopenharmony_ci{
489cabdff1aSopenharmony_ci    int r, g, b;
490cabdff1aSopenharmony_ci    unsigned int bgr, c15, index;
491cabdff1aSopenharmony_ci    for (r = 4; r < 256; r += 8) {
492cabdff1aSopenharmony_ci        for (g = 4; g < 256; g += 8) {
493cabdff1aSopenharmony_ci            for (b = 4; b < 256; b += 8) {
494cabdff1aSopenharmony_ci                bgr = b | (g << 8) | (r << 16);
495cabdff1aSopenharmony_ci                c15 = (b >> 3) | ((g & 0xf8) << 2) | ((r & 0xf8) << 7);
496cabdff1aSopenharmony_ci                index = pixel_color7_slow(palette, bgr);
497cabdff1aSopenharmony_ci
498cabdff1aSopenharmony_ci                palette->index[c15] = index;
499cabdff1aSopenharmony_ci            }
500cabdff1aSopenharmony_ci        }
501cabdff1aSopenharmony_ci    }
502cabdff1aSopenharmony_ci    return 0;
503cabdff1aSopenharmony_ci}
504cabdff1aSopenharmony_ci
505cabdff1aSopenharmony_cistatic const unsigned int default_screen_video_v2_palette[128] = {
506cabdff1aSopenharmony_ci    0x00000000, 0x00333333, 0x00666666, 0x00999999, 0x00CCCCCC, 0x00FFFFFF,
507cabdff1aSopenharmony_ci    0x00330000, 0x00660000, 0x00990000, 0x00CC0000, 0x00FF0000, 0x00003300,
508cabdff1aSopenharmony_ci    0x00006600, 0x00009900, 0x0000CC00, 0x0000FF00, 0x00000033, 0x00000066,
509cabdff1aSopenharmony_ci    0x00000099, 0x000000CC, 0x000000FF, 0x00333300, 0x00666600, 0x00999900,
510cabdff1aSopenharmony_ci    0x00CCCC00, 0x00FFFF00, 0x00003333, 0x00006666, 0x00009999, 0x0000CCCC,
511cabdff1aSopenharmony_ci    0x0000FFFF, 0x00330033, 0x00660066, 0x00990099, 0x00CC00CC, 0x00FF00FF,
512cabdff1aSopenharmony_ci    0x00FFFF33, 0x00FFFF66, 0x00FFFF99, 0x00FFFFCC, 0x00FF33FF, 0x00FF66FF,
513cabdff1aSopenharmony_ci    0x00FF99FF, 0x00FFCCFF, 0x0033FFFF, 0x0066FFFF, 0x0099FFFF, 0x00CCFFFF,
514cabdff1aSopenharmony_ci    0x00CCCC33, 0x00CCCC66, 0x00CCCC99, 0x00CCCCFF, 0x00CC33CC, 0x00CC66CC,
515cabdff1aSopenharmony_ci    0x00CC99CC, 0x00CCFFCC, 0x0033CCCC, 0x0066CCCC, 0x0099CCCC, 0x00FFCCCC,
516cabdff1aSopenharmony_ci    0x00999933, 0x00999966, 0x009999CC, 0x009999FF, 0x00993399, 0x00996699,
517cabdff1aSopenharmony_ci    0x0099CC99, 0x0099FF99, 0x00339999, 0x00669999, 0x00CC9999, 0x00FF9999,
518cabdff1aSopenharmony_ci    0x00666633, 0x00666699, 0x006666CC, 0x006666FF, 0x00663366, 0x00669966,
519cabdff1aSopenharmony_ci    0x0066CC66, 0x0066FF66, 0x00336666, 0x00996666, 0x00CC6666, 0x00FF6666,
520cabdff1aSopenharmony_ci    0x00333366, 0x00333399, 0x003333CC, 0x003333FF, 0x00336633, 0x00339933,
521cabdff1aSopenharmony_ci    0x0033CC33, 0x0033FF33, 0x00663333, 0x00993333, 0x00CC3333, 0x00FF3333,
522cabdff1aSopenharmony_ci    0x00003366, 0x00336600, 0x00660033, 0x00006633, 0x00330066, 0x00663300,
523cabdff1aSopenharmony_ci    0x00336699, 0x00669933, 0x00993366, 0x00339966, 0x00663399, 0x00996633,
524cabdff1aSopenharmony_ci    0x006699CC, 0x0099CC66, 0x00CC6699, 0x0066CC99, 0x009966CC, 0x00CC9966,
525cabdff1aSopenharmony_ci    0x0099CCFF, 0x00CCFF99, 0x00FF99CC, 0x0099FFCC, 0x00CC99FF, 0x00FFCC99,
526cabdff1aSopenharmony_ci    0x00111111, 0x00222222, 0x00444444, 0x00555555, 0x00AAAAAA, 0x00BBBBBB,
527cabdff1aSopenharmony_ci    0x00DDDDDD, 0x00EEEEEE
528cabdff1aSopenharmony_ci};
529cabdff1aSopenharmony_ci
530cabdff1aSopenharmony_cistatic int generate_default_palette(Palette * palette)
531cabdff1aSopenharmony_ci{
532cabdff1aSopenharmony_ci    memcpy(palette->colors, default_screen_video_v2_palette,
533cabdff1aSopenharmony_ci           sizeof(default_screen_video_v2_palette));
534cabdff1aSopenharmony_ci
535cabdff1aSopenharmony_ci    return update_palette_index(palette);
536cabdff1aSopenharmony_ci}
537cabdff1aSopenharmony_ci
538cabdff1aSopenharmony_cistatic int generate_optimum_palette(Palette * palette, const uint8_t * image,
539cabdff1aSopenharmony_ci                                   int width, int height, int stride)
540cabdff1aSopenharmony_ci{
541cabdff1aSopenharmony_ci    //this isn't implemented yet!  Default palette only!
542cabdff1aSopenharmony_ci    return -1;
543cabdff1aSopenharmony_ci}
544cabdff1aSopenharmony_ci
545cabdff1aSopenharmony_cistatic inline int encode_15_7_sl(Palette * palette, uint8_t * dest,
546cabdff1aSopenharmony_ci                                 const uint8_t * src, int width, int dist)
547cabdff1aSopenharmony_ci{
548cabdff1aSopenharmony_ci    int len = 0, x;
549cabdff1aSopenharmony_ci    for (x = 0; x < width; x++) {
550cabdff1aSopenharmony_ci        len += write_pixel_15_7(palette, dest + len, src + 3 * x, dist);
551cabdff1aSopenharmony_ci    }
552cabdff1aSopenharmony_ci    return len;
553cabdff1aSopenharmony_ci}
554cabdff1aSopenharmony_ci
555cabdff1aSopenharmony_cistatic int encode_15_7(Palette * palette, Block * b, const uint8_t * src,
556cabdff1aSopenharmony_ci                       int stride, int dist)
557cabdff1aSopenharmony_ci{
558cabdff1aSopenharmony_ci    int i;
559cabdff1aSopenharmony_ci    uint8_t *ptr = b->enc;
560cabdff1aSopenharmony_ci    for (i = 0; i < b->start; i++)
561cabdff1aSopenharmony_ci        ptr += encode_15_7_sl(palette, ptr, src + i * stride, b->width, dist);
562cabdff1aSopenharmony_ci    b->sl_begin = ptr;
563cabdff1aSopenharmony_ci    for (; i < b->start + b->len; i++)
564cabdff1aSopenharmony_ci        ptr += encode_15_7_sl(palette, ptr, src + i * stride, b->width, dist);
565cabdff1aSopenharmony_ci    b->sl_end = ptr;
566cabdff1aSopenharmony_ci    for (; i < b->height; i++)
567cabdff1aSopenharmony_ci        ptr += encode_15_7_sl(palette, ptr, src + i * stride, b->width, dist);
568cabdff1aSopenharmony_ci    b->enc_size = ptr - b->enc;
569cabdff1aSopenharmony_ci    return b->enc_size;
570cabdff1aSopenharmony_ci}
571cabdff1aSopenharmony_ci
572cabdff1aSopenharmony_cistatic int encode_block(FlashSV2Context *s, Palette * palette, Block * b,
573cabdff1aSopenharmony_ci                        Block *prev, const uint8_t *src, int stride,
574cabdff1aSopenharmony_ci                        int dist, int keyframe)
575cabdff1aSopenharmony_ci{
576cabdff1aSopenharmony_ci    unsigned buf_size = b->width * b->height * 6;
577cabdff1aSopenharmony_ci    uint8_t *buf = s->blockbuffer;
578cabdff1aSopenharmony_ci    int res;
579cabdff1aSopenharmony_ci
580cabdff1aSopenharmony_ci    if (b->flags & COLORSPACE_15_7) {
581cabdff1aSopenharmony_ci        encode_15_7(palette, b, src, stride, dist);
582cabdff1aSopenharmony_ci    } else {
583cabdff1aSopenharmony_ci        encode_bgr(b, src, stride);
584cabdff1aSopenharmony_ci    }
585cabdff1aSopenharmony_ci
586cabdff1aSopenharmony_ci    if (b->len > 0) {
587cabdff1aSopenharmony_ci        b->data_size = buf_size;
588cabdff1aSopenharmony_ci        res = encode_zlib(b, b->data, &b->data_size, &s->zstream.zstream);
589cabdff1aSopenharmony_ci        if (res)
590cabdff1aSopenharmony_ci            return res;
591cabdff1aSopenharmony_ci
592cabdff1aSopenharmony_ci        if (!keyframe) {
593cabdff1aSopenharmony_ci            res = encode_zlibprime(b, prev, buf, &buf_size, &s->zstream.zstream);
594cabdff1aSopenharmony_ci            if (res)
595cabdff1aSopenharmony_ci                return res;
596cabdff1aSopenharmony_ci
597cabdff1aSopenharmony_ci            if (buf_size < b->data_size) {
598cabdff1aSopenharmony_ci                b->data_size = buf_size;
599cabdff1aSopenharmony_ci                memcpy(b->data, buf, buf_size);
600cabdff1aSopenharmony_ci                b->flags |= ZLIB_PRIME_COMPRESS_PREVIOUS;
601cabdff1aSopenharmony_ci            }
602cabdff1aSopenharmony_ci        }
603cabdff1aSopenharmony_ci    } else {
604cabdff1aSopenharmony_ci        b->data_size = 0;
605cabdff1aSopenharmony_ci    }
606cabdff1aSopenharmony_ci    return 0;
607cabdff1aSopenharmony_ci}
608cabdff1aSopenharmony_ci
609cabdff1aSopenharmony_cistatic int compare_sl(FlashSV2Context * s, Block * b, const uint8_t * src,
610cabdff1aSopenharmony_ci                      uint8_t * frame, uint8_t * key, int y, int keyframe)
611cabdff1aSopenharmony_ci{
612cabdff1aSopenharmony_ci    if (memcmp(src, frame, b->width * 3) != 0) {
613cabdff1aSopenharmony_ci        b->dirty = 1;
614cabdff1aSopenharmony_ci        memcpy(frame, src, b->width * 3);
615cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
616cabdff1aSopenharmony_ci        s->diff_lines++;
617cabdff1aSopenharmony_ci#endif
618cabdff1aSopenharmony_ci    }
619cabdff1aSopenharmony_ci    if (memcmp(src, key, b->width * 3) != 0) {
620cabdff1aSopenharmony_ci        if (b->len == 0)
621cabdff1aSopenharmony_ci            b->start = y;
622cabdff1aSopenharmony_ci        b->len = y + 1 - b->start;
623cabdff1aSopenharmony_ci    }
624cabdff1aSopenharmony_ci    return 0;
625cabdff1aSopenharmony_ci}
626cabdff1aSopenharmony_ci
627cabdff1aSopenharmony_cistatic int mark_all_blocks(FlashSV2Context * s, const uint8_t * src, int stride,
628cabdff1aSopenharmony_ci                           int keyframe)
629cabdff1aSopenharmony_ci{
630cabdff1aSopenharmony_ci    int sl, rsl, col, pos, possl;
631cabdff1aSopenharmony_ci    Block *b;
632cabdff1aSopenharmony_ci    for (sl = s->image_height - 1; sl >= 0; sl--) {
633cabdff1aSopenharmony_ci        for (col = 0; col < s->cols; col++) {
634cabdff1aSopenharmony_ci            rsl = s->image_height - sl - 1;
635cabdff1aSopenharmony_ci            b = s->frame_blocks + col + rsl / s->block_height * s->cols;
636cabdff1aSopenharmony_ci            possl = stride * sl + col * s->block_width * 3;
637cabdff1aSopenharmony_ci            pos = s->image_width * rsl * 3 + col * s->block_width * 3;
638cabdff1aSopenharmony_ci            compare_sl(s, b, src + possl, s->current_frame + pos,
639cabdff1aSopenharmony_ci                       s->key_frame + pos, rsl % s->block_height, keyframe);
640cabdff1aSopenharmony_ci        }
641cabdff1aSopenharmony_ci    }
642cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
643cabdff1aSopenharmony_ci    s->tot_lines += s->image_height * s->cols;
644cabdff1aSopenharmony_ci#endif
645cabdff1aSopenharmony_ci    return 0;
646cabdff1aSopenharmony_ci}
647cabdff1aSopenharmony_ci
648cabdff1aSopenharmony_cistatic int encode_all_blocks(FlashSV2Context * s, int keyframe)
649cabdff1aSopenharmony_ci{
650cabdff1aSopenharmony_ci    int row, col, res;
651cabdff1aSopenharmony_ci    uint8_t *data;
652cabdff1aSopenharmony_ci    Block *b, *prev;
653cabdff1aSopenharmony_ci    for (row = 0; row < s->rows; row++) {
654cabdff1aSopenharmony_ci        for (col = 0; col < s->cols; col++) {
655cabdff1aSopenharmony_ci            b = s->frame_blocks + (row * s->cols + col);
656cabdff1aSopenharmony_ci            prev = s->key_blocks + (row * s->cols + col);
657cabdff1aSopenharmony_ci            b->flags = s->use15_7 ? COLORSPACE_15_7 : 0;
658cabdff1aSopenharmony_ci            if (keyframe) {
659cabdff1aSopenharmony_ci                b->start = 0;
660cabdff1aSopenharmony_ci                b->len = b->height;
661cabdff1aSopenharmony_ci            } else if (!b->dirty) {
662cabdff1aSopenharmony_ci                b->start = 0;
663cabdff1aSopenharmony_ci                b->len = 0;
664cabdff1aSopenharmony_ci                b->data_size = 0;
665cabdff1aSopenharmony_ci                continue;
666cabdff1aSopenharmony_ci            } else if (b->start != 0 || b->len != b->height) {
667cabdff1aSopenharmony_ci                b->flags |= HAS_DIFF_BLOCKS;
668cabdff1aSopenharmony_ci            }
669cabdff1aSopenharmony_ci            data = s->current_frame + s->image_width * 3 * s->block_height * row + s->block_width * col * 3;
670cabdff1aSopenharmony_ci            res = encode_block(s, &s->palette, b, prev, data,
671cabdff1aSopenharmony_ci                               s->image_width * 3, s->dist, keyframe);
672cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
673cabdff1aSopenharmony_ci            if (b->dirty)
674cabdff1aSopenharmony_ci                s->diff_blocks++;
675cabdff1aSopenharmony_ci            s->comp_size += b->data_size;
676cabdff1aSopenharmony_ci            s->uncomp_size += b->enc_size;
677cabdff1aSopenharmony_ci#endif
678cabdff1aSopenharmony_ci            if (res)
679cabdff1aSopenharmony_ci                return res;
680cabdff1aSopenharmony_ci        }
681cabdff1aSopenharmony_ci    }
682cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
683cabdff1aSopenharmony_ci    s->raw_size += s->image_width * s->image_height * 3;
684cabdff1aSopenharmony_ci    s->tot_blocks += s->rows * s->cols;
685cabdff1aSopenharmony_ci#endif
686cabdff1aSopenharmony_ci    return 0;
687cabdff1aSopenharmony_ci}
688cabdff1aSopenharmony_ci
689cabdff1aSopenharmony_cistatic int write_all_blocks(FlashSV2Context * s, uint8_t * buf,
690cabdff1aSopenharmony_ci                            int buf_size)
691cabdff1aSopenharmony_ci{
692cabdff1aSopenharmony_ci    int row, col, buf_pos = 0, len;
693cabdff1aSopenharmony_ci    Block *b;
694cabdff1aSopenharmony_ci    for (row = 0; row < s->rows; row++) {
695cabdff1aSopenharmony_ci        for (col = 0; col < s->cols; col++) {
696cabdff1aSopenharmony_ci            b = s->frame_blocks + row * s->cols + col;
697cabdff1aSopenharmony_ci            len = write_block(b, buf + buf_pos, buf_size - buf_pos);
698cabdff1aSopenharmony_ci            b->start = b->len = b->dirty = 0;
699cabdff1aSopenharmony_ci            if (len < 0)
700cabdff1aSopenharmony_ci                return len;
701cabdff1aSopenharmony_ci            buf_pos += len;
702cabdff1aSopenharmony_ci        }
703cabdff1aSopenharmony_ci    }
704cabdff1aSopenharmony_ci    return buf_pos;
705cabdff1aSopenharmony_ci}
706cabdff1aSopenharmony_ci
707cabdff1aSopenharmony_cistatic int write_bitstream(FlashSV2Context * s, const uint8_t * src, int stride,
708cabdff1aSopenharmony_ci                           uint8_t * buf, int buf_size, int keyframe)
709cabdff1aSopenharmony_ci{
710cabdff1aSopenharmony_ci    int buf_pos, res;
711cabdff1aSopenharmony_ci
712cabdff1aSopenharmony_ci    res = mark_all_blocks(s, src, stride, keyframe);
713cabdff1aSopenharmony_ci    if (res)
714cabdff1aSopenharmony_ci        return res;
715cabdff1aSopenharmony_ci    res = encode_all_blocks(s, keyframe);
716cabdff1aSopenharmony_ci    if (res)
717cabdff1aSopenharmony_ci        return res;
718cabdff1aSopenharmony_ci
719cabdff1aSopenharmony_ci    res = write_header(s, buf, buf_size);
720cabdff1aSopenharmony_ci    if (res < 0) {
721cabdff1aSopenharmony_ci        return res;
722cabdff1aSopenharmony_ci    } else {
723cabdff1aSopenharmony_ci        buf_pos = res;
724cabdff1aSopenharmony_ci    }
725cabdff1aSopenharmony_ci    res = write_all_blocks(s, buf + buf_pos, buf_size - buf_pos);
726cabdff1aSopenharmony_ci    if (res < 0)
727cabdff1aSopenharmony_ci        return res;
728cabdff1aSopenharmony_ci    buf_pos += res;
729cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
730cabdff1aSopenharmony_ci    s->total_bits += ((double) buf_pos) * 8.0;
731cabdff1aSopenharmony_ci#endif
732cabdff1aSopenharmony_ci
733cabdff1aSopenharmony_ci    return buf_pos;
734cabdff1aSopenharmony_ci}
735cabdff1aSopenharmony_ci
736cabdff1aSopenharmony_cistatic void recommend_keyframe(FlashSV2Context * s, int *keyframe)
737cabdff1aSopenharmony_ci{
738cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
739cabdff1aSopenharmony_ci    double block_ratio, line_ratio, enc_ratio, comp_ratio, data_ratio;
740cabdff1aSopenharmony_ci    if (s->avctx->gop_size > 0) {
741cabdff1aSopenharmony_ci        block_ratio = s->diff_blocks / s->tot_blocks;
742cabdff1aSopenharmony_ci        line_ratio = s->diff_lines / s->tot_lines;
743cabdff1aSopenharmony_ci        enc_ratio = s->uncomp_size / s->raw_size;
744cabdff1aSopenharmony_ci        comp_ratio = s->comp_size / s->uncomp_size;
745cabdff1aSopenharmony_ci        data_ratio = s->comp_size / s->raw_size;
746cabdff1aSopenharmony_ci
747cabdff1aSopenharmony_ci        if ((block_ratio >= 0.5 && line_ratio / block_ratio <= 0.5) || line_ratio >= 0.95) {
748cabdff1aSopenharmony_ci            *keyframe = 1;
749cabdff1aSopenharmony_ci            return;
750cabdff1aSopenharmony_ci        }
751cabdff1aSopenharmony_ci    }
752cabdff1aSopenharmony_ci#else
753cabdff1aSopenharmony_ci    return;
754cabdff1aSopenharmony_ci#endif
755cabdff1aSopenharmony_ci}
756cabdff1aSopenharmony_ci
757cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
758cabdff1aSopenharmony_cistatic const double block_size_fraction = 1.0 / 300;
759cabdff1aSopenharmony_cistatic const double use15_7_threshold = 8192;
760cabdff1aSopenharmony_cistatic const double color15_7_factor = 100;
761cabdff1aSopenharmony_ci#endif
762cabdff1aSopenharmony_cistatic int optimum_block_width(FlashSV2Context * s)
763cabdff1aSopenharmony_ci{
764cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
765cabdff1aSopenharmony_ci    double save = (1-pow(s->diff_lines/s->diff_blocks/s->block_height, 0.5)) * s->comp_size/s->tot_blocks;
766cabdff1aSopenharmony_ci    double width = block_size_fraction * sqrt(0.5 * save * s->rows * s->cols) * s->image_width;
767cabdff1aSopenharmony_ci    int pwidth = ((int) width);
768cabdff1aSopenharmony_ci    return FFCLIP(pwidth & ~15, 256, 16);
769cabdff1aSopenharmony_ci#else
770cabdff1aSopenharmony_ci    return 64;
771cabdff1aSopenharmony_ci#endif
772cabdff1aSopenharmony_ci}
773cabdff1aSopenharmony_ci
774cabdff1aSopenharmony_cistatic int optimum_block_height(FlashSV2Context * s)
775cabdff1aSopenharmony_ci{
776cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
777cabdff1aSopenharmony_ci    double save = (1-pow(s->diff_lines/s->diff_blocks/s->block_height, 0.5)) * s->comp_size/s->tot_blocks;
778cabdff1aSopenharmony_ci    double height = block_size_fraction * sqrt(0.5 * save * s->rows * s->cols) * s->image_height;
779cabdff1aSopenharmony_ci    int pheight = ((int) height);
780cabdff1aSopenharmony_ci    return FFCLIP(pheight & ~15, 256, 16);
781cabdff1aSopenharmony_ci#else
782cabdff1aSopenharmony_ci    return 64;
783cabdff1aSopenharmony_ci#endif
784cabdff1aSopenharmony_ci}
785cabdff1aSopenharmony_ci
786cabdff1aSopenharmony_cistatic int optimum_use15_7(FlashSV2Context * s)
787cabdff1aSopenharmony_ci{
788cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
789cabdff1aSopenharmony_ci    double ideal = ((double)(s->avctx->bit_rate * s->avctx->time_base.den * s->avctx->ticks_per_frame)) /
790cabdff1aSopenharmony_ci        ((double) s->avctx->time_base.num) * s->avctx->frame_number;
791cabdff1aSopenharmony_ci    if (ideal + use15_7_threshold < s->total_bits) {
792cabdff1aSopenharmony_ci        return 1;
793cabdff1aSopenharmony_ci    } else {
794cabdff1aSopenharmony_ci        return 0;
795cabdff1aSopenharmony_ci    }
796cabdff1aSopenharmony_ci#else
797cabdff1aSopenharmony_ci    return s->avctx->global_quality == 0;
798cabdff1aSopenharmony_ci#endif
799cabdff1aSopenharmony_ci}
800cabdff1aSopenharmony_ci
801cabdff1aSopenharmony_cistatic int optimum_dist(FlashSV2Context * s)
802cabdff1aSopenharmony_ci{
803cabdff1aSopenharmony_ci#ifndef FLASHSV2_DUMB
804cabdff1aSopenharmony_ci    double ideal =
805cabdff1aSopenharmony_ci        s->avctx->bit_rate * s->avctx->time_base.den *
806cabdff1aSopenharmony_ci        s->avctx->ticks_per_frame;
807cabdff1aSopenharmony_ci    int dist = pow((s->total_bits / ideal) * color15_7_factor, 3);
808cabdff1aSopenharmony_ci    av_log(s->avctx, AV_LOG_DEBUG, "dist: %d\n", dist);
809cabdff1aSopenharmony_ci    return dist;
810cabdff1aSopenharmony_ci#else
811cabdff1aSopenharmony_ci    return 15;
812cabdff1aSopenharmony_ci#endif
813cabdff1aSopenharmony_ci}
814cabdff1aSopenharmony_ci
815cabdff1aSopenharmony_ci
816cabdff1aSopenharmony_cistatic int reconfigure_at_keyframe(FlashSV2Context * s, const uint8_t * image,
817cabdff1aSopenharmony_ci                                   int stride)
818cabdff1aSopenharmony_ci{
819cabdff1aSopenharmony_ci    int update_palette = 0;
820cabdff1aSopenharmony_ci    int res;
821cabdff1aSopenharmony_ci    int block_width  = optimum_block_width (s);
822cabdff1aSopenharmony_ci    int block_height = optimum_block_height(s);
823cabdff1aSopenharmony_ci
824cabdff1aSopenharmony_ci    if (block_width != s->block_width || block_height != s->block_height) {
825cabdff1aSopenharmony_ci        res = update_block_dimensions(s, block_width, block_height);
826cabdff1aSopenharmony_ci        if (res < 0)
827cabdff1aSopenharmony_ci            return res;
828cabdff1aSopenharmony_ci    }
829cabdff1aSopenharmony_ci
830cabdff1aSopenharmony_ci    s->use15_7 = optimum_use15_7(s);
831cabdff1aSopenharmony_ci    if (s->use15_7) {
832cabdff1aSopenharmony_ci        if ((s->use_custom_palette && s->palette_type != 1) || update_palette) {
833cabdff1aSopenharmony_ci            res = generate_optimum_palette(&s->palette, image, s->image_width, s->image_height, stride);
834cabdff1aSopenharmony_ci            if (res)
835cabdff1aSopenharmony_ci                return res;
836cabdff1aSopenharmony_ci            s->palette_type = 1;
837cabdff1aSopenharmony_ci            av_log(s->avctx, AV_LOG_DEBUG, "Generated optimum palette\n");
838cabdff1aSopenharmony_ci        } else if (!s->use_custom_palette && s->palette_type != 0) {
839cabdff1aSopenharmony_ci            res = generate_default_palette(&s->palette);
840cabdff1aSopenharmony_ci            if (res)
841cabdff1aSopenharmony_ci                return res;
842cabdff1aSopenharmony_ci            s->palette_type = 0;
843cabdff1aSopenharmony_ci            av_log(s->avctx, AV_LOG_DEBUG, "Generated default palette\n");
844cabdff1aSopenharmony_ci        }
845cabdff1aSopenharmony_ci    }
846cabdff1aSopenharmony_ci
847cabdff1aSopenharmony_ci
848cabdff1aSopenharmony_ci    reset_stats(s);
849cabdff1aSopenharmony_ci
850cabdff1aSopenharmony_ci    return 0;
851cabdff1aSopenharmony_ci}
852cabdff1aSopenharmony_ci
853cabdff1aSopenharmony_cistatic int flashsv2_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
854cabdff1aSopenharmony_ci                                 const AVFrame *p, int *got_packet)
855cabdff1aSopenharmony_ci{
856cabdff1aSopenharmony_ci    FlashSV2Context *const s = avctx->priv_data;
857cabdff1aSopenharmony_ci    int res;
858cabdff1aSopenharmony_ci    int keyframe = 0;
859cabdff1aSopenharmony_ci
860cabdff1aSopenharmony_ci    if ((res = ff_alloc_packet(avctx, pkt, s->frame_size + AV_INPUT_BUFFER_MIN_SIZE)) < 0)
861cabdff1aSopenharmony_ci        return res;
862cabdff1aSopenharmony_ci
863cabdff1aSopenharmony_ci    /* First frame needs to be a keyframe */
864cabdff1aSopenharmony_ci    if (avctx->frame_number == 0)
865cabdff1aSopenharmony_ci        keyframe = 1;
866cabdff1aSopenharmony_ci
867cabdff1aSopenharmony_ci    /* Check the placement of keyframes */
868cabdff1aSopenharmony_ci    if (avctx->gop_size > 0) {
869cabdff1aSopenharmony_ci        if (avctx->frame_number >= s->last_key_frame + avctx->gop_size)
870cabdff1aSopenharmony_ci            keyframe = 1;
871cabdff1aSopenharmony_ci    }
872cabdff1aSopenharmony_ci
873cabdff1aSopenharmony_ci    if (!keyframe
874cabdff1aSopenharmony_ci        && avctx->frame_number > s->last_key_frame + avctx->keyint_min) {
875cabdff1aSopenharmony_ci        recommend_keyframe(s, &keyframe);
876cabdff1aSopenharmony_ci        if (keyframe)
877cabdff1aSopenharmony_ci            av_log(avctx, AV_LOG_DEBUG, "Recommending key frame at frame %d\n", avctx->frame_number);
878cabdff1aSopenharmony_ci    }
879cabdff1aSopenharmony_ci
880cabdff1aSopenharmony_ci    if (keyframe) {
881cabdff1aSopenharmony_ci        res = reconfigure_at_keyframe(s, p->data[0], p->linesize[0]);
882cabdff1aSopenharmony_ci        if (res)
883cabdff1aSopenharmony_ci            return res;
884cabdff1aSopenharmony_ci    }
885cabdff1aSopenharmony_ci
886cabdff1aSopenharmony_ci    if (s->use15_7)
887cabdff1aSopenharmony_ci        s->dist = optimum_dist(s);
888cabdff1aSopenharmony_ci
889cabdff1aSopenharmony_ci    res = write_bitstream(s, p->data[0], p->linesize[0], pkt->data, pkt->size, keyframe);
890cabdff1aSopenharmony_ci
891cabdff1aSopenharmony_ci    if (keyframe) {
892cabdff1aSopenharmony_ci        new_key_frame(s);
893cabdff1aSopenharmony_ci        s->last_key_frame = avctx->frame_number;
894cabdff1aSopenharmony_ci        pkt->flags |= AV_PKT_FLAG_KEY;
895cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_DEBUG, "Inserting key frame at frame %d\n", avctx->frame_number);
896cabdff1aSopenharmony_ci    }
897cabdff1aSopenharmony_ci
898cabdff1aSopenharmony_ci    pkt->size = res;
899cabdff1aSopenharmony_ci    *got_packet = 1;
900cabdff1aSopenharmony_ci
901cabdff1aSopenharmony_ci    return 0;
902cabdff1aSopenharmony_ci}
903cabdff1aSopenharmony_ci
904cabdff1aSopenharmony_cistatic av_cold int flashsv2_encode_end(AVCodecContext * avctx)
905cabdff1aSopenharmony_ci{
906cabdff1aSopenharmony_ci    FlashSV2Context *s = avctx->priv_data;
907cabdff1aSopenharmony_ci
908cabdff1aSopenharmony_ci    cleanup(s);
909cabdff1aSopenharmony_ci
910cabdff1aSopenharmony_ci    return 0;
911cabdff1aSopenharmony_ci}
912cabdff1aSopenharmony_ci
913cabdff1aSopenharmony_ciconst FFCodec ff_flashsv2_encoder = {
914cabdff1aSopenharmony_ci    .p.name         = "flashsv2",
915cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("Flash Screen Video Version 2"),
916cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
917cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_FLASHSV2,
918cabdff1aSopenharmony_ci    .priv_data_size = sizeof(FlashSV2Context),
919cabdff1aSopenharmony_ci    .init           = flashsv2_encode_init,
920cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_CB(flashsv2_encode_frame),
921cabdff1aSopenharmony_ci    .close          = flashsv2_encode_end,
922cabdff1aSopenharmony_ci    .p.pix_fmts     = (const enum AVPixelFormat[]){ AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE },
923cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
924cabdff1aSopenharmony_ci};
925