1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * a64 video encoder - multicolor modes
3cabdff1aSopenharmony_ci * Copyright (c) 2009 Tobias Bindhammer
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 * a64 video encoder - multicolor modes
25cabdff1aSopenharmony_ci */
26cabdff1aSopenharmony_ci
27cabdff1aSopenharmony_ci#include "config_components.h"
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_ci#include "a64colors.h"
30cabdff1aSopenharmony_ci#include "a64tables.h"
31cabdff1aSopenharmony_ci#include "codec_internal.h"
32cabdff1aSopenharmony_ci#include "elbg.h"
33cabdff1aSopenharmony_ci#include "encode.h"
34cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
35cabdff1aSopenharmony_ci#include "libavutil/common.h"
36cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
37cabdff1aSopenharmony_ci
38cabdff1aSopenharmony_ci#define DITHERSTEPS   8
39cabdff1aSopenharmony_ci#define CHARSET_CHARS 256
40cabdff1aSopenharmony_ci#define INTERLACED    1
41cabdff1aSopenharmony_ci#define CROP_SCREENS  1
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_ci#define C64XRES 320
44cabdff1aSopenharmony_ci#define C64YRES 200
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_citypedef struct A64Context {
47cabdff1aSopenharmony_ci    /* variables for multicolor modes */
48cabdff1aSopenharmony_ci    struct ELBGContext *elbg;
49cabdff1aSopenharmony_ci    AVLFG randctx;
50cabdff1aSopenharmony_ci    int mc_lifetime;
51cabdff1aSopenharmony_ci    int mc_use_5col;
52cabdff1aSopenharmony_ci    unsigned mc_frame_counter;
53cabdff1aSopenharmony_ci    int *mc_meta_charset;
54cabdff1aSopenharmony_ci    int *mc_charmap;
55cabdff1aSopenharmony_ci    int *mc_best_cb;
56cabdff1aSopenharmony_ci    int mc_luma_vals[5];
57cabdff1aSopenharmony_ci    uint8_t *mc_colram;
58cabdff1aSopenharmony_ci    uint8_t *mc_palette;
59cabdff1aSopenharmony_ci    int mc_pal_size;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_ci    /* pts of the next packet that will be output */
62cabdff1aSopenharmony_ci    int64_t next_pts;
63cabdff1aSopenharmony_ci} A64Context;
64cabdff1aSopenharmony_ci
65cabdff1aSopenharmony_ci/* gray gradient */
66cabdff1aSopenharmony_cistatic const uint8_t mc_colors[5]={0x0,0xb,0xc,0xf,0x1};
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci/* other possible gradients - to be tested */
69cabdff1aSopenharmony_ci//static const uint8_t mc_colors[5]={0x0,0x8,0xa,0xf,0x7};
70cabdff1aSopenharmony_ci//static const uint8_t mc_colors[5]={0x0,0x9,0x8,0xa,0x3};
71cabdff1aSopenharmony_ci
72cabdff1aSopenharmony_cistatic void to_meta_with_crop(AVCodecContext *avctx,
73cabdff1aSopenharmony_ci                              const AVFrame *p, int *dest)
74cabdff1aSopenharmony_ci{
75cabdff1aSopenharmony_ci    int blockx, blocky, x, y;
76cabdff1aSopenharmony_ci    int luma = 0;
77cabdff1aSopenharmony_ci    int height = FFMIN(avctx->height, C64YRES);
78cabdff1aSopenharmony_ci    int width  = FFMIN(avctx->width , C64XRES);
79cabdff1aSopenharmony_ci    uint8_t *src = p->data[0];
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci    for (blocky = 0; blocky < C64YRES; blocky += 8) {
82cabdff1aSopenharmony_ci        for (blockx = 0; blockx < C64XRES; blockx += 8) {
83cabdff1aSopenharmony_ci            for (y = blocky; y < blocky + 8 && y < C64YRES; y++) {
84cabdff1aSopenharmony_ci                for (x = blockx; x < blockx + 8 && x < C64XRES; x += 2) {
85cabdff1aSopenharmony_ci                    if(x < width && y < height) {
86cabdff1aSopenharmony_ci                        if (x + 1 < width) {
87cabdff1aSopenharmony_ci                            /* build average over 2 pixels */
88cabdff1aSopenharmony_ci                            luma = (src[(x + 0 + y * p->linesize[0])] +
89cabdff1aSopenharmony_ci                                    src[(x + 1 + y * p->linesize[0])]) / 2;
90cabdff1aSopenharmony_ci                        } else {
91cabdff1aSopenharmony_ci                            luma = src[(x + y * p->linesize[0])];
92cabdff1aSopenharmony_ci                        }
93cabdff1aSopenharmony_ci                        /* write blocks as linear data now so they are suitable for elbg */
94cabdff1aSopenharmony_ci                        dest[0] = luma;
95cabdff1aSopenharmony_ci                    }
96cabdff1aSopenharmony_ci                    dest++;
97cabdff1aSopenharmony_ci                }
98cabdff1aSopenharmony_ci            }
99cabdff1aSopenharmony_ci        }
100cabdff1aSopenharmony_ci    }
101cabdff1aSopenharmony_ci}
102cabdff1aSopenharmony_ci
103cabdff1aSopenharmony_cistatic void render_charset(AVCodecContext *avctx, uint8_t *charset,
104cabdff1aSopenharmony_ci                           uint8_t *colrammap)
105cabdff1aSopenharmony_ci{
106cabdff1aSopenharmony_ci    A64Context *c = avctx->priv_data;
107cabdff1aSopenharmony_ci    uint8_t row1, row2;
108cabdff1aSopenharmony_ci    int charpos, x, y;
109cabdff1aSopenharmony_ci    int a, b;
110cabdff1aSopenharmony_ci    uint8_t pix;
111cabdff1aSopenharmony_ci    int lowdiff, highdiff;
112cabdff1aSopenharmony_ci    int *best_cb = c->mc_best_cb;
113cabdff1aSopenharmony_ci    uint8_t index1[256];
114cabdff1aSopenharmony_ci    uint8_t index2[256];
115cabdff1aSopenharmony_ci    uint8_t dither[256];
116cabdff1aSopenharmony_ci    int i;
117cabdff1aSopenharmony_ci    int distance;
118cabdff1aSopenharmony_ci
119cabdff1aSopenharmony_ci    /* Generate lookup-tables for dither and index before looping.
120cabdff1aSopenharmony_ci     * This code relies on c->mc_luma_vals[c->mc_pal_size - 1] being
121cabdff1aSopenharmony_ci     * the maximum of all the mc_luma_vals values and on the minimum
122cabdff1aSopenharmony_ci     * being zero; this ensures that dither is properly initialized. */
123cabdff1aSopenharmony_ci    i = 0;
124cabdff1aSopenharmony_ci    for (a=0; a < 256; a++) {
125cabdff1aSopenharmony_ci        if(i < c->mc_pal_size -1 && a == c->mc_luma_vals[i + 1]) {
126cabdff1aSopenharmony_ci            distance = c->mc_luma_vals[i + 1] - c->mc_luma_vals[i];
127cabdff1aSopenharmony_ci            for(b = 0; b <= distance; b++) {
128cabdff1aSopenharmony_ci                  dither[c->mc_luma_vals[i] + b] = b * (DITHERSTEPS - 1) / distance;
129cabdff1aSopenharmony_ci            }
130cabdff1aSopenharmony_ci            i++;
131cabdff1aSopenharmony_ci        }
132cabdff1aSopenharmony_ci        if(i >= c->mc_pal_size - 1) dither[a] = 0;
133cabdff1aSopenharmony_ci        index1[a] = i;
134cabdff1aSopenharmony_ci        index2[a] = FFMIN(i + 1, c->mc_pal_size - 1);
135cabdff1aSopenharmony_ci    }
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci    /* and render charset */
138cabdff1aSopenharmony_ci    for (charpos = 0; charpos < CHARSET_CHARS; charpos++) {
139cabdff1aSopenharmony_ci        lowdiff  = 0;
140cabdff1aSopenharmony_ci        highdiff = 0;
141cabdff1aSopenharmony_ci        for (y = 0; y < 8; y++) {
142cabdff1aSopenharmony_ci            row1 = 0; row2 = 0;
143cabdff1aSopenharmony_ci            for (x = 0; x < 4; x++) {
144cabdff1aSopenharmony_ci                pix = best_cb[y * 4 + x];
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci                /* accumulate error for brightest/darkest color */
147cabdff1aSopenharmony_ci                if (index1[pix] >= 3)
148cabdff1aSopenharmony_ci                    highdiff += pix - c->mc_luma_vals[3];
149cabdff1aSopenharmony_ci                if (index1[pix] < 1)
150cabdff1aSopenharmony_ci                    lowdiff += c->mc_luma_vals[1] - pix;
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_ci                row1 <<= 2;
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_ci                if (INTERLACED) {
155cabdff1aSopenharmony_ci                    row2 <<= 2;
156cabdff1aSopenharmony_ci                    if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 0][x & 3])
157cabdff1aSopenharmony_ci                        row1 |= 3-(index2[pix] & 3);
158cabdff1aSopenharmony_ci                    else
159cabdff1aSopenharmony_ci                        row1 |= 3-(index1[pix] & 3);
160cabdff1aSopenharmony_ci
161cabdff1aSopenharmony_ci                    if (interlaced_dither_patterns[dither[pix]][(y & 3) * 2 + 1][x & 3])
162cabdff1aSopenharmony_ci                        row2 |= 3-(index2[pix] & 3);
163cabdff1aSopenharmony_ci                    else
164cabdff1aSopenharmony_ci                        row2 |= 3-(index1[pix] & 3);
165cabdff1aSopenharmony_ci                }
166cabdff1aSopenharmony_ci                else {
167cabdff1aSopenharmony_ci                    if (multi_dither_patterns[dither[pix]][(y & 3)][x & 3])
168cabdff1aSopenharmony_ci                        row1 |= 3-(index2[pix] & 3);
169cabdff1aSopenharmony_ci                    else
170cabdff1aSopenharmony_ci                        row1 |= 3-(index1[pix] & 3);
171cabdff1aSopenharmony_ci                }
172cabdff1aSopenharmony_ci            }
173cabdff1aSopenharmony_ci            charset[y+0x000] = row1;
174cabdff1aSopenharmony_ci            if (INTERLACED) charset[y+0x800] = row2;
175cabdff1aSopenharmony_ci        }
176cabdff1aSopenharmony_ci        /* do we need to adjust pixels? */
177cabdff1aSopenharmony_ci        if (highdiff > 0 && lowdiff > 0 && c->mc_use_5col) {
178cabdff1aSopenharmony_ci            if (lowdiff > highdiff) {
179cabdff1aSopenharmony_ci                for (x = 0; x < 32; x++)
180cabdff1aSopenharmony_ci                    best_cb[x] = FFMIN(c->mc_luma_vals[3], best_cb[x]);
181cabdff1aSopenharmony_ci            } else {
182cabdff1aSopenharmony_ci                for (x = 0; x < 32; x++)
183cabdff1aSopenharmony_ci                    best_cb[x] = FFMAX(c->mc_luma_vals[1], best_cb[x]);
184cabdff1aSopenharmony_ci            }
185cabdff1aSopenharmony_ci            charpos--;          /* redo now adjusted char */
186cabdff1aSopenharmony_ci        /* no adjustment needed, all fine */
187cabdff1aSopenharmony_ci        } else {
188cabdff1aSopenharmony_ci            /* advance pointers */
189cabdff1aSopenharmony_ci            best_cb += 32;
190cabdff1aSopenharmony_ci            charset += 8;
191cabdff1aSopenharmony_ci
192cabdff1aSopenharmony_ci            /* remember colorram value */
193cabdff1aSopenharmony_ci            colrammap[charpos] = (highdiff > 0);
194cabdff1aSopenharmony_ci        }
195cabdff1aSopenharmony_ci    }
196cabdff1aSopenharmony_ci}
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_cistatic av_cold int a64multi_close_encoder(AVCodecContext *avctx)
199cabdff1aSopenharmony_ci{
200cabdff1aSopenharmony_ci    A64Context *c = avctx->priv_data;
201cabdff1aSopenharmony_ci
202cabdff1aSopenharmony_ci    avpriv_elbg_free(&c->elbg);
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ci    av_freep(&c->mc_meta_charset);
205cabdff1aSopenharmony_ci    av_freep(&c->mc_best_cb);
206cabdff1aSopenharmony_ci    av_freep(&c->mc_charmap);
207cabdff1aSopenharmony_ci    av_freep(&c->mc_colram);
208cabdff1aSopenharmony_ci    return 0;
209cabdff1aSopenharmony_ci}
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_cistatic av_cold int a64multi_encode_init(AVCodecContext *avctx)
212cabdff1aSopenharmony_ci{
213cabdff1aSopenharmony_ci    A64Context *c = avctx->priv_data;
214cabdff1aSopenharmony_ci    int a;
215cabdff1aSopenharmony_ci    av_lfg_init(&c->randctx, 1);
216cabdff1aSopenharmony_ci
217cabdff1aSopenharmony_ci    if (avctx->global_quality < 1) {
218cabdff1aSopenharmony_ci        c->mc_lifetime = 4;
219cabdff1aSopenharmony_ci    } else {
220cabdff1aSopenharmony_ci        c->mc_lifetime = avctx->global_quality / FF_QP2LAMBDA;
221cabdff1aSopenharmony_ci    }
222cabdff1aSopenharmony_ci
223cabdff1aSopenharmony_ci    av_log(avctx, AV_LOG_INFO, "charset lifetime set to %d frame(s)\n", c->mc_lifetime);
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci    c->mc_frame_counter = 0;
226cabdff1aSopenharmony_ci    c->mc_use_5col      = avctx->codec->id == AV_CODEC_ID_A64_MULTI5;
227cabdff1aSopenharmony_ci    c->mc_pal_size      = 4 + c->mc_use_5col;
228cabdff1aSopenharmony_ci
229cabdff1aSopenharmony_ci    /* precalc luma values for later use */
230cabdff1aSopenharmony_ci    for (a = 0; a < c->mc_pal_size; a++) {
231cabdff1aSopenharmony_ci        c->mc_luma_vals[a]=a64_palette[mc_colors[a]][0] * 0.30 +
232cabdff1aSopenharmony_ci                           a64_palette[mc_colors[a]][1] * 0.59 +
233cabdff1aSopenharmony_ci                           a64_palette[mc_colors[a]][2] * 0.11;
234cabdff1aSopenharmony_ci    }
235cabdff1aSopenharmony_ci
236cabdff1aSopenharmony_ci    if (!(c->mc_meta_charset = av_calloc(c->mc_lifetime, 32000 * sizeof(int))) ||
237cabdff1aSopenharmony_ci       !(c->mc_best_cb       = av_malloc(CHARSET_CHARS * 32 * sizeof(int)))     ||
238cabdff1aSopenharmony_ci       !(c->mc_charmap       = av_calloc(c->mc_lifetime, 1000 * sizeof(int))) ||
239cabdff1aSopenharmony_ci       !(c->mc_colram        = av_mallocz(CHARSET_CHARS * sizeof(uint8_t)))) {
240cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to allocate buffer memory.\n");
241cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
242cabdff1aSopenharmony_ci    }
243cabdff1aSopenharmony_ci
244cabdff1aSopenharmony_ci    /* set up extradata */
245cabdff1aSopenharmony_ci    if (!(avctx->extradata = av_mallocz(8 * 4 + AV_INPUT_BUFFER_PADDING_SIZE))) {
246cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory for extradata.\n");
247cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
248cabdff1aSopenharmony_ci    }
249cabdff1aSopenharmony_ci    avctx->extradata_size = 8 * 4;
250cabdff1aSopenharmony_ci    AV_WB32(avctx->extradata, c->mc_lifetime);
251cabdff1aSopenharmony_ci    AV_WB32(avctx->extradata + 16, INTERLACED);
252cabdff1aSopenharmony_ci
253cabdff1aSopenharmony_ci    if (!avctx->codec_tag)
254cabdff1aSopenharmony_ci         avctx->codec_tag = AV_RL32("a64m");
255cabdff1aSopenharmony_ci
256cabdff1aSopenharmony_ci    c->next_pts = AV_NOPTS_VALUE;
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci    return 0;
259cabdff1aSopenharmony_ci}
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_cistatic void a64_compress_colram(unsigned char *buf, int *charmap, uint8_t *colram)
262cabdff1aSopenharmony_ci{
263cabdff1aSopenharmony_ci    int a;
264cabdff1aSopenharmony_ci    uint8_t temp;
265cabdff1aSopenharmony_ci    /* only needs to be done in 5col mode */
266cabdff1aSopenharmony_ci    /* XXX could be squeezed to 0x80 bytes */
267cabdff1aSopenharmony_ci    for (a = 0; a < 256; a++) {
268cabdff1aSopenharmony_ci        temp  = colram[charmap[a + 0x000]] << 0;
269cabdff1aSopenharmony_ci        temp |= colram[charmap[a + 0x100]] << 1;
270cabdff1aSopenharmony_ci        temp |= colram[charmap[a + 0x200]] << 2;
271cabdff1aSopenharmony_ci        if (a < 0xe8) temp |= colram[charmap[a + 0x300]] << 3;
272cabdff1aSopenharmony_ci        buf[a] = temp << 2;
273cabdff1aSopenharmony_ci    }
274cabdff1aSopenharmony_ci}
275cabdff1aSopenharmony_ci
276cabdff1aSopenharmony_cistatic int a64multi_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
277cabdff1aSopenharmony_ci                                 const AVFrame *p, int *got_packet)
278cabdff1aSopenharmony_ci{
279cabdff1aSopenharmony_ci    A64Context *c = avctx->priv_data;
280cabdff1aSopenharmony_ci
281cabdff1aSopenharmony_ci    int frame;
282cabdff1aSopenharmony_ci    int x, y;
283cabdff1aSopenharmony_ci    int b_height;
284cabdff1aSopenharmony_ci    int b_width;
285cabdff1aSopenharmony_ci
286cabdff1aSopenharmony_ci    int req_size, ret;
287cabdff1aSopenharmony_ci    uint8_t *buf = NULL;
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    int *charmap     = c->mc_charmap;
290cabdff1aSopenharmony_ci    uint8_t *colram  = c->mc_colram;
291cabdff1aSopenharmony_ci    int *meta        = c->mc_meta_charset;
292cabdff1aSopenharmony_ci    int *best_cb     = c->mc_best_cb;
293cabdff1aSopenharmony_ci
294cabdff1aSopenharmony_ci    int charset_size = 0x800 * (INTERLACED + 1);
295cabdff1aSopenharmony_ci    int colram_size  = 0x100 * c->mc_use_5col;
296cabdff1aSopenharmony_ci    int screen_size;
297cabdff1aSopenharmony_ci
298cabdff1aSopenharmony_ci    if(CROP_SCREENS) {
299cabdff1aSopenharmony_ci        b_height = FFMIN(avctx->height,C64YRES) >> 3;
300cabdff1aSopenharmony_ci        b_width  = FFMIN(avctx->width ,C64XRES) >> 3;
301cabdff1aSopenharmony_ci        screen_size = b_width * b_height;
302cabdff1aSopenharmony_ci    } else {
303cabdff1aSopenharmony_ci        b_height = C64YRES >> 3;
304cabdff1aSopenharmony_ci        b_width  = C64XRES >> 3;
305cabdff1aSopenharmony_ci        screen_size = 0x400;
306cabdff1aSopenharmony_ci    }
307cabdff1aSopenharmony_ci
308cabdff1aSopenharmony_ci    /* no data, means end encoding asap */
309cabdff1aSopenharmony_ci    if (!p) {
310cabdff1aSopenharmony_ci        /* all done, end encoding */
311cabdff1aSopenharmony_ci        if (!c->mc_lifetime) return 0;
312cabdff1aSopenharmony_ci        /* no more frames in queue, prepare to flush remaining frames */
313cabdff1aSopenharmony_ci        if (!c->mc_frame_counter) {
314cabdff1aSopenharmony_ci            c->mc_lifetime = 0;
315cabdff1aSopenharmony_ci        }
316cabdff1aSopenharmony_ci        /* still frames in queue so limit lifetime to remaining frames */
317cabdff1aSopenharmony_ci        else c->mc_lifetime = c->mc_frame_counter;
318cabdff1aSopenharmony_ci    /* still new data available */
319cabdff1aSopenharmony_ci    } else {
320cabdff1aSopenharmony_ci        /* fill up mc_meta_charset with data until lifetime exceeds */
321cabdff1aSopenharmony_ci        if (c->mc_frame_counter < c->mc_lifetime) {
322cabdff1aSopenharmony_ci            to_meta_with_crop(avctx, p, meta + 32000 * c->mc_frame_counter);
323cabdff1aSopenharmony_ci            c->mc_frame_counter++;
324cabdff1aSopenharmony_ci            if (c->next_pts == AV_NOPTS_VALUE)
325cabdff1aSopenharmony_ci                c->next_pts = p->pts;
326cabdff1aSopenharmony_ci            /* lifetime is not reached so wait for next frame first */
327cabdff1aSopenharmony_ci            return 0;
328cabdff1aSopenharmony_ci        }
329cabdff1aSopenharmony_ci    }
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci    /* lifetime reached so now convert X frames at once */
332cabdff1aSopenharmony_ci    if (c->mc_frame_counter == c->mc_lifetime) {
333cabdff1aSopenharmony_ci        req_size = 0;
334cabdff1aSopenharmony_ci        /* any frames to encode? */
335cabdff1aSopenharmony_ci        if (c->mc_lifetime) {
336cabdff1aSopenharmony_ci            int alloc_size = charset_size + c->mc_lifetime*(screen_size + colram_size);
337cabdff1aSopenharmony_ci            if ((ret = ff_get_encode_buffer(avctx, pkt, alloc_size, 0)) < 0)
338cabdff1aSopenharmony_ci                return ret;
339cabdff1aSopenharmony_ci            buf = pkt->data;
340cabdff1aSopenharmony_ci
341cabdff1aSopenharmony_ci            /* calc optimal new charset + charmaps */
342cabdff1aSopenharmony_ci            ret = avpriv_elbg_do(&c->elbg, meta, 32, 1000 * c->mc_lifetime,
343cabdff1aSopenharmony_ci                                 best_cb, CHARSET_CHARS, 50, charmap, &c->randctx, 0);
344cabdff1aSopenharmony_ci            if (ret < 0)
345cabdff1aSopenharmony_ci                return ret;
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_ci            /* create colorram map and a c64 readable charset */
348cabdff1aSopenharmony_ci            render_charset(avctx, buf, colram);
349cabdff1aSopenharmony_ci
350cabdff1aSopenharmony_ci            /* advance pointers */
351cabdff1aSopenharmony_ci            buf      += charset_size;
352cabdff1aSopenharmony_ci            req_size += charset_size;
353cabdff1aSopenharmony_ci        }
354cabdff1aSopenharmony_ci
355cabdff1aSopenharmony_ci        /* write x frames to buf */
356cabdff1aSopenharmony_ci        for (frame = 0; frame < c->mc_lifetime; frame++) {
357cabdff1aSopenharmony_ci            /* copy charmap to buf. buf is uchar*, charmap is int*, so no memcpy here, sorry */
358cabdff1aSopenharmony_ci            for (y = 0; y < b_height; y++) {
359cabdff1aSopenharmony_ci                for (x = 0; x < b_width; x++) {
360cabdff1aSopenharmony_ci                    buf[y * b_width + x] = charmap[y * b_width + x];
361cabdff1aSopenharmony_ci                }
362cabdff1aSopenharmony_ci            }
363cabdff1aSopenharmony_ci            /* advance pointers */
364cabdff1aSopenharmony_ci            buf += screen_size;
365cabdff1aSopenharmony_ci            req_size += screen_size;
366cabdff1aSopenharmony_ci
367cabdff1aSopenharmony_ci            /* compress and copy colram to buf */
368cabdff1aSopenharmony_ci            if (c->mc_use_5col) {
369cabdff1aSopenharmony_ci                a64_compress_colram(buf, charmap, colram);
370cabdff1aSopenharmony_ci                /* advance pointers */
371cabdff1aSopenharmony_ci                buf += colram_size;
372cabdff1aSopenharmony_ci                req_size += colram_size;
373cabdff1aSopenharmony_ci            }
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci            /* advance to next charmap */
376cabdff1aSopenharmony_ci            charmap += 1000;
377cabdff1aSopenharmony_ci        }
378cabdff1aSopenharmony_ci
379cabdff1aSopenharmony_ci        AV_WB32(avctx->extradata + 4,  c->mc_frame_counter);
380cabdff1aSopenharmony_ci        AV_WB32(avctx->extradata + 8,  charset_size);
381cabdff1aSopenharmony_ci        AV_WB32(avctx->extradata + 12, screen_size + colram_size);
382cabdff1aSopenharmony_ci
383cabdff1aSopenharmony_ci        /* reset counter */
384cabdff1aSopenharmony_ci        c->mc_frame_counter = 0;
385cabdff1aSopenharmony_ci
386cabdff1aSopenharmony_ci        pkt->pts = pkt->dts = c->next_pts;
387cabdff1aSopenharmony_ci        c->next_pts         = AV_NOPTS_VALUE;
388cabdff1aSopenharmony_ci
389cabdff1aSopenharmony_ci        av_assert0(pkt->size == req_size);
390cabdff1aSopenharmony_ci        *got_packet = !!req_size;
391cabdff1aSopenharmony_ci    }
392cabdff1aSopenharmony_ci    return 0;
393cabdff1aSopenharmony_ci}
394cabdff1aSopenharmony_ci
395cabdff1aSopenharmony_ci#if CONFIG_A64MULTI_ENCODER
396cabdff1aSopenharmony_ciconst FFCodec ff_a64multi_encoder = {
397cabdff1aSopenharmony_ci    .p.name         = "a64multi",
398cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64"),
399cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
400cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_A64_MULTI,
401cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY,
402cabdff1aSopenharmony_ci    .priv_data_size = sizeof(A64Context),
403cabdff1aSopenharmony_ci    .init           = a64multi_encode_init,
404cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_CB(a64multi_encode_frame),
405cabdff1aSopenharmony_ci    .close          = a64multi_close_encoder,
406cabdff1aSopenharmony_ci    .p.pix_fmts     = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
407cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE,
408cabdff1aSopenharmony_ci};
409cabdff1aSopenharmony_ci#endif
410cabdff1aSopenharmony_ci#if CONFIG_A64MULTI5_ENCODER
411cabdff1aSopenharmony_ciconst FFCodec ff_a64multi5_encoder = {
412cabdff1aSopenharmony_ci    .p.name         = "a64multi5",
413cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("Multicolor charset for Commodore 64, extended with 5th color (colram)"),
414cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
415cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_A64_MULTI5,
416cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY,
417cabdff1aSopenharmony_ci    .priv_data_size = sizeof(A64Context),
418cabdff1aSopenharmony_ci    .init           = a64multi_encode_init,
419cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_CB(a64multi_encode_frame),
420cabdff1aSopenharmony_ci    .close          = a64multi_close_encoder,
421cabdff1aSopenharmony_ci    .p.pix_fmts     = (const enum AVPixelFormat[]) {AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE},
422cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE,
423cabdff1aSopenharmony_ci};
424cabdff1aSopenharmony_ci#endif
425